Guía para desarrolladores que quieran contribuir a Cortex o entender su arquitectura interna.
| Herramienta | Versión | Propósito |
|---|---|---|
| Python | 3.11+ | Backend |
| Node.js | 18+ | Frontend |
| Docker | 24+ | Servicios de desarrollo |
| Git | 2.30+ | Control de versiones |
# 1. Clonar repositorio
git clone https://github.com/DeepRatAI/cortex-knowledge-assistant.git
cd cortex-knowledge-assistant
# 2. Crear entorno virtual
python -m venv .venv
source .venv/bin/activate
# 3. Instalar dependencias de desarrollo
pip install -e ".[dev]"
# 4. Verificar instalación
pytest tests/ -v# Configuración mínima para desarrollo
export DATABASE_URL=sqlite:///./dev_cortex.db
export CKA_LLM_PROVIDER=Fake
export JWT_SECRET_KEY=dev-secret-key-for-testing-only
export CKA_LOG_LEVEL=DEBUG# Iniciar solo Qdrant y Redis para desarrollo
docker compose up -d qdrant redis postgres
# Verificar que están corriendo
docker compose ps# Con auto-reload
uvicorn src.cortex_ka.api.main:app --reload --host 0.0.0.0 --port 8088cd ui
npm install
npm run dev
# Acceder a http://localhost:5173cortex-knowledge-assistant/
├── src/
│ └── cortex_ka/ # Código principal
│ ├── api/ # FastAPI endpoints
│ ├── application/ # Lógica de aplicación (RAG, DLP)
│ ├── domain/ # Modelos y puertos
│ ├── infrastructure/ # Adaptadores (Qdrant, HF, Redis)
│ ├── auth/ # Autenticación
│ ├── transactions/ # Dominio transaccional
│ ├── system/ # Setup, status, admin
│ ├── scripts/ # Ingesta, evaluación
│ ├── demos/ # Modo demo
│ ├── eval/ # Evaluación PII
│ └── maintenance/ # Mantenimiento
├── tests/ # Tests unitarios e integración
├── ui/ # Frontend React
├── docs/ # Documentación
├── k8s/ # Manifests Kubernetes
├── docker/ # Dockerfiles
├── scripts/ # Scripts de utilidad
├── pyproject.toml # Configuración del proyecto
├── docker-compose.yml # Compose principal
└── Makefile # Comandos útiles
# Ejecutar todos los tests
pytest tests/ -v
# Con coverage (requiere pytest-cov)
pytest tests/ -v --cov=src/cortex_ka --cov-report=html
open htmlcov/index.html# Un archivo específico
pytest tests/test_pii.py -v
# Un test específico
pytest tests/test_pii.py::TestRedactPii::test_redacts_dni -v
# Tests que coincidan con un patrón
pytest tests/ -k "pii" -v| Archivo | Propósito |
|---|---|
test_pii.py |
Redacción de datos sensibles (DNI, CUIT, etc.) |
test_chunking.py |
Particionado semántico de documentos |
test_models.py |
Modelos de dominio (DocumentChunk, Answer) |
# Ejecutar tests en contenedor
docker compose -f docker/compose.dev.yml run --rm api pytest tests/ -vUsamos las siguientes herramientas:
| Herramienta | Propósito | Comando |
|---|---|---|
| Black | Formateo | black src/ tests/ |
| isort | Ordenar imports | isort src/ tests/ |
| Flake8 | Linting | flake8 src/ tests/ |
| MyPy | Type checking | mypy src/ |
# Formatear todo
black src/ tests/
isort src/ tests/
# Verificar estilo
flake8 src/ tests/
mypy src/
# Todo junto (usando Makefile)
make lint[tool.black]
line-length = 120
target-version = ["py311", "py312"]
[tool.isort]
profile = "black"
line_length = 120
[tool.mypy]
python_version = "3.12"
strict = truecd ui
npm run lint┌─────────────────────────────────────────────────────────────────┐
│ API Layer (FastAPI) │
│ - Endpoints REST │
│ - Middlewares (auth, rate limit, security headers) │
│ - Request/Response models (Pydantic) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ - RAGService (orquestación) │
│ - PromptBuilder, Chunking, Reranking │
│ - DLP/PII enforcement │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Domain Layer │
│ - Value Objects: DocumentChunk, Answer, RetrievalResult │
│ - Ports (interfaces): RetrieverPort, LLMPort, CachePort │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ - Adapters: QdrantRetriever, HFLLM, RedisCache │
│ - External services: PostgreSQL, Qdrant, Redis, HF API │
└─────────────────────────────────────────────────────────────────┘
- Request llega a FastAPI
- Middleware valida auth, rate limit
- Endpoint delega a
RAGService - RAGService usa
RetrieverPort(implementado porQdrantRetriever) - Re-ranking y selección de chunks
- PromptBuilder construye prompt
- LLMPort (implementado por
HFLLM) genera respuesta - DLP aplica redacción de PII
- Response retornada al cliente
# src/cortex_ka/infrastructure/llm_openai.py
from ..domain.ports import LLMPort
class OpenAILLM(LLMPort):
"""OpenAI GPT provider."""
def __init__(self, api_key: str, model: str = "gpt-4"):
self.api_key = api_key
self.model = model
self._client = openai.OpenAI(api_key=api_key)
def generate(self, prompt: str) -> str:
response = self._client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
def generate_stream(self, prompt: str) -> Iterator[str]:
stream = self._client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.contentLuego modificar _select_llm() en api/main.py:
def _select_llm():
provider = settings.llm_provider.lower()
if provider == "hf":
return HFLLM(...)
elif provider == "openai":
return OpenAILLM(api_key=settings.openai_api_key)
return _FakeLLM()# En src/cortex_ka/api/main.py
from pydantic import BaseModel
class NewFeatureRequest(BaseModel):
param1: str
param2: int = 10
class NewFeatureResponse(BaseModel):
result: str
metadata: dict
@app.post("/api/new-feature", response_model=NewFeatureResponse)
def new_feature(
payload: NewFeatureRequest,
current_user: CurrentUser = Depends(get_current_user),
):
"""Descripción del nuevo endpoint.
Solo accesible para usuarios autenticados.
"""
# Verificar permisos si es necesario
if getattr(current_user, "role", "") != "admin":
raise HTTPException(403, "Forbidden")
# Lógica del endpoint
result = process_new_feature(payload.param1, payload.param2)
# Auditar si es operación sensible
_audit(login_db_session, operation="new_feature", outcome="success")
return NewFeatureResponse(result=result, metadata={})# Fork en GitHub, luego:
git clone https://github.com/TU_USUARIO/cortex-knowledge-assistant.git
cd cortex-knowledge-assistant
git remote add upstream https://github.com/DeepRatAI/cortex-knowledge-assistant.gitgit checkout -b feature/mi-nueva-funcionalidadConvención de nombres:
feature/descripcion- Nueva funcionalidadfix/descripcion- Bug fixdocs/descripcion- Documentaciónrefactor/descripcion- Refactoring
# Hacer cambios...
# Ejecutar tests
pytest tests/ -v
# Verificar estilo
make lintSeguimos Conventional Commits:
git add .
git commit -m "feat: añadir soporte para OpenAI GPT-4"
# o
git commit -m "fix: corregir rate limiting para endpoints streaming"
# o
git commit -m "docs: actualizar guía de despliegue"git push origin feature/mi-nueva-funcionalidad
# Crear Pull Request en GitHub- Tests pasan localmente
- Coverage no disminuye significativamente
- Código sigue estilo del proyecto
- Documentación actualizada si aplica
- CHANGELOG.md actualizado
- Sin secrets o datos sensibles
Usamos formato Google:
def calculate_score(chunk: DocumentChunk, query: str) -> float:
"""Calculate relevance score for a chunk.
Combines semantic similarity with keyword matching
using a weighted average.
Args:
chunk: The document chunk to score.
query: The user's search query.
Returns:
Relevance score between 0 and 1.
Raises:
ValueError: If chunk or query is empty.
Example:
>>> score = calculate_score(chunk, "requisitos cuenta")
>>> print(f"Score: {score:.2f}")
Score: 0.85
"""# Documentación está en docs/
# Formato: Markdown
# Para preview local (si usas Docusaurus):
cd docs
npm run startexport CKA_LOG_LEVEL=DEBUG
uvicorn src.cortex_ka.api.main:app --reload.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Cortex API",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["src.cortex_ka.api.main:app", "--reload"],
"env": {
"CKA_LLM_PROVIDER": "Fake",
"DATABASE_URL": "sqlite:///./dev.db"
}
},
{
"name": "Tests",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["tests/", "-v", "-s"]
}
]
}# Para profiling de performance
import cProfile
import pstats
with cProfile.Profile() as pr:
result = rag_service.answer("mi query")
stats = pstats.Stats(pr)
stats.sort_stats("cumulative")
stats.print_stats(20)Seguimos Semantic Versioning:
- MAJOR: Cambios incompatibles en API
- MINOR: Nueva funcionalidad retrocompatible
- PATCH: Bug fixes retrocompatibles
# 1. Actualizar versión en pyproject.toml
# 2. Actualizar CHANGELOG.md
# 3. Commit
git commit -am "chore: release v1.1.0"
# 4. Tag
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0
# 5. GitHub Actions construye y publica- RAG: Retrieval-Augmented Generation
- DPR: Dense Passage Retrieval
- ColBERT: Efficient Passage Retrieval
- Testabilidad: Cada componente puede testearse aisladamente
- Flexibilidad: Cambiar LLM provider sin tocar lógica de negocio
- Claridad: Separación clara de responsabilidades
- Simplicidad: No requiere servidor adicional
- Portabilidad: El archivo
.dbes autocontenido - Compatibilidad: El código es igual que con PostgreSQL gracias a SQLAlchemy
- Añadir regex en
application/pii.py - Actualizar
classify_pii()enpii_classifier.py - Añadir test en
tests/test_pii_classifier.py - Actualizar documentación de seguridad