Initial commit

This commit is contained in:
Jordan
2026-04-01 23:16:45 +01:00
commit bc4199aed2
201 changed files with 25612 additions and 0 deletions

335
README.md Normal file
View File

@@ -0,0 +1,335 @@
# Agentic Microservice
Microservicio de orquestación agéntica con context engineering avanzado, multi-agent orchestration, integración MCP, SSE streaming y knowledge base. Diseñado como backend para Acai Code.
## Arquitectura
```
Client → API (FastAPI) → Orchestrator → Subagents (planner/coder/collector/reviewer)
↓ ↓
Context Engine Model Adapter (Claude / OpenAI)
↓ ↓
Memory Store + KB MCP Client (stdio)
Redis (sessions, events, knowledge)
```
**Principios:**
- **Context over chat history** — el modelo recibe estado estructurado, nunca raw tool outputs
- **Knowledge filtering** — solo los docs relevantes al task entran en contexto (keyword scoring)
- **Compaction** — extracción de facts, eliminación de redundancia, prioridades por sección
- **Artifact summarization** — outputs de tools resumidos antes de entrar al contexto
- **Error recovery** — steps fallidos se saltan, timeout global, sesión nunca se queda en executing
- **Concurrency lock** — Redis SETNX impide ejecución simultánea en la misma sesión
## Quick Start
### Con Docker Compose
```bash
# 1. Configurar
cp .env.example .env
# Editar .env — al menos una API key (ANTHROPIC o OPENAI)
# 2. Levantar todo (Redis + microservicio)
docker compose up --build
# 3. Solo Redis (para desarrollo local)
docker compose up redis -d
```
### Desarrollo local
```bash
# 1. Redis
docker compose up redis -d
# 2. Dependencias
pip install -r requirements.txt
# 3. Configurar .env
AGENTIC_OPENAI_API_KEY=sk-...
AGENTIC_DEFAULT_MODEL_PROVIDER=openai
AGENTIC_DEFAULT_MODEL_ID=gpt-4o
AGENTIC_REDIS_PORT=6380
# 4. Arrancar
python3 -m uvicorn src.main:app --reload --port 8001
# 5. Dashboard en http://localhost:8001/dashboard/
```
### Cargar Knowledge Base
```bash
# Cargar docs desde el directorio docs/
curl -X POST http://localhost:8001/api/v1/knowledge/load \
-H "Content-Type: application/json" \
-d '{"docs_path": "docs"}'
```
## Dashboard
Interfaz web para testing integrada en el microservicio. Se accede en `/dashboard/`.
**Features:**
- Gestión de sesiones (crear, eliminar, seleccionar)
- Chat con envío sync (Send) o streaming (Stream)
- Event Log en tiempo real con filtros por categoría y export JSON
- Inspector de estado (task, plan, facts, constraints, timeline)
- Context Debug — muestra exactamente qué recibe cada agente (secciones, tokens, previews)
- Dark/light mode
- Responsive (3 columnas → 1 columna en móvil)
**Atajos de teclado:**
- `Enter` → Send
- `Ctrl+Enter` → Stream
- `Shift+Enter` → Nueva línea
## API Reference
Base URL: `/api/v1`
### Sesiones
```bash
# Crear sesión
curl -X POST http://localhost:8001/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{
"project_profile": {"name": "mi-proyecto", "tech_stack": ["python", "twig"]},
"immutable_rules": ["Responde siempre en español", "Usa Tailwind CSS"],
"metadata": {}
}'
# → {"session_id": "abc123...", "status": "idle"}
# Obtener estado
curl http://localhost:8001/api/v1/sessions/{session_id}
# Eliminar
curl -X DELETE http://localhost:8001/api/v1/sessions/{session_id}
```
### Mensajes
```bash
# Sync — espera la respuesta completa
curl -X POST http://localhost:8001/api/v1/sessions/{session_id}/messages \
-H "Content-Type: application/json" \
-d '{"message": "Crea un módulo FAQ con acordeón"}'
# Streaming — retorna inmediatamente, resultados vía SSE
curl -X POST http://localhost:8001/api/v1/sessions/{session_id}/messages \
-H "Content-Type: application/json" \
-d '{"message": "Crea un módulo FAQ con acordeón", "stream": true}'
```
Respuesta sync:
```json
{
"session_id": "...",
"task_id": "...",
"content": "### Step 1\n...\n### Review\n...",
"steps_completed": 5,
"steps_failed": [],
"artifacts_count": 0,
"review": "...",
"status": "completed"
}
```
### SSE Streaming
```bash
curl -N http://localhost:8001/api/v1/sessions/{session_id}/stream
```
Tipos de evento:
| Evento | Cuándo |
|--------|--------|
| `session.created` | Sesión inicializada |
| `execution.started` | Pipeline arranca |
| `subagent.assigned` | Step ruteado a un agente |
| `agent.delta` | Chunk de texto del agente |
| `tool.started` | Herramienta MCP ejecutándose |
| `tool.completed` | Herramienta terminó |
| `execution.completed` | Task completado |
| `error` | Error en step, planning, o timeout |
### Knowledge Base
```bash
# Cargar docs desde directorio
curl -X POST http://localhost:8001/api/v1/knowledge/load \
-H "Content-Type: application/json" \
-d '{"docs_path": "docs"}'
# Cargar desde ruta absoluta
curl -X POST http://localhost:8001/api/v1/knowledge/load \
-H "Content-Type: application/json" \
-d '{"docs_path": "/ruta/a/mis/docs"}'
# Listar docs cargados
curl http://localhost:8001/api/v1/knowledge
# Eliminar un doc
curl -X DELETE http://localhost:8001/api/v1/knowledge/{doc_id}
```
Los docs se cargan como `*.md` del directorio. Se sobreescriben si ya existen. No requiere reinicio — la siguiente conversación usa los docs actualizados.
### Debug
```bash
# Historial de eventos (persistidos en Redis)
curl http://localhost:8001/api/v1/sessions/{session_id}/events
# Context debug — qué recibió cada agente
curl http://localhost:8001/api/v1/sessions/{session_id}/context-debug
# Health check
curl http://localhost:8001/health
```
## Context Engine
El componente más crítico del sistema. Ensambla el prompt que recibe el modelo con secciones priorizadas:
| Sección | Prioridad | Contenido |
|---------|-----------|-----------|
| `immutable_rules` | 100 | System prompt del agente + reglas de sesión. Nunca se recorta |
| `project_profile` | 80 | Perfil del proyecto (nombre, tech stack) |
| `knowledge_base` | 60 | Docs relevantes filtrados por keywords del task |
| `task_state` | 70 | Objetivo, plan, step actual, facts, constraints |
| `artifact_memory` | 50 | Resúmenes de outputs de herramientas |
| `working_context` | 30 | Items de trabajo recientes entre steps |
**Knowledge filtering:** no carga todos los docs. Extrae keywords del objetivo y step actual, puntúa cada doc (título ×10, tags ×5, contenido ×1), y selecciona los más relevantes dentro de un budget de 15k tokens. Los docs que no caben entran como summary de una línea.
**Token counting:** usa `tiktoken` (encoding `cl100k_base`) para conteo real. Fallback a estimación si tiktoken no está instalado.
**Compaction:** si el total excede el context window (120k tokens default), las secciones de menor prioridad se comprimen o eliminan. `immutable_rules` nunca se toca.
## Orchestrator Pipeline
```
mensaje → planner → [step₁ → step₂ → ... → stepₙ] → reviewer → respuesta
```
1. **Planner** descompone el mensaje en pasos con agent role asignado
2. **Router** rutea cada step al agente apropiado (keyword matching: implement→coder, buscar→collector, etc.)
3. **Subagent** ejecuta el step con su propio contexto controlado
4. Si un step **falla**, se marca como failed y el pipeline continúa con el siguiente
5. **Reviewer** valida el trabajo si hubo >1 step
6. **Timeout global** de 5 min (configurable) — si se excede, la sesión pasa a error
**Concurrency:** Redis lock (SETNX) con TTL de 5 min. Si llega un segundo mensaje mientras uno se ejecuta → `{"status": "busy"}`. El lock se libera automáticamente si el proceso muere.
## MCP (Model Context Protocol)
Cliente stdio que se conecta a un servidor MCP al arrancar:
```bash
# En .env
AGENTIC_MCP_SERVER_COMMAND=node
AGENTIC_MCP_SERVER_ARGS=["mcp-server/stdio.js"]
# Variables del MCP server (se heredan al subproceso)
ACAI_WEB_URL=http://localhost:8080
ACAI_WEBSITE=mi-sitio
ACAI_PROJECT_DIR=/ruta/al/proyecto
```
El cliente descubre tools automáticamente vía `tools/list`, y los agentes las usan durante la ejecución. Los resultados de tools **nunca** entran al contexto como raw output — se resumen como artifacts.
## Configuración
Variables de entorno con prefijo `AGENTIC_`:
| Variable | Default | Descripción |
|----------|---------|-------------|
| `AGENTIC_ANTHROPIC_API_KEY` | — | API key de Anthropic |
| `AGENTIC_OPENAI_API_KEY` | — | API key de OpenAI |
| `AGENTIC_DEFAULT_MODEL_PROVIDER` | `claude` | `claude` o `openai` |
| `AGENTIC_DEFAULT_MODEL_ID` | `claude-sonnet-4-20250514` | Modelo por defecto |
| `AGENTIC_REDIS_HOST` | `localhost` | Host de Redis |
| `AGENTIC_REDIS_PORT` | `6379` | Puerto de Redis |
| `AGENTIC_REDIS_DB` | `0` | Base de datos Redis |
| `AGENTIC_REDIS_PASSWORD` | — | Password de Redis |
| `AGENTIC_MAX_TOKENS` | `4096` | Max tokens de salida por llamada |
| `AGENTIC_CONTEXT_MAX_TOKENS` | `120000` | Max context window |
| `AGENTIC_MAX_EXECUTION_STEPS` | `25` | Max steps por task |
| `AGENTIC_MAX_EXECUTION_TIMEOUT_SECONDS` | `300` | Timeout global (5 min) |
| `AGENTIC_SUBAGENT_MAX_STEPS` | `10` | Max iterations por subagent |
| `AGENTIC_MCP_SERVER_COMMAND` | — | Comando del servidor MCP |
| `AGENTIC_MCP_SERVER_ARGS` | `[]` | Argumentos del servidor MCP |
| `AGENTIC_MCP_TIMEOUT_SECONDS` | `30` | Timeout por tool call |
| `AGENTIC_DEBUG` | `false` | Logging verbose |
## Redis Key Structure
```
agentic:session:{id} → SessionState JSON (TTL 24h)
agentic:session:{id}:artifacts → Hash de ArtifactSummary
agentic:session:{id}:events → Lista de SSE events (cap 500)
agentic:session:{id}:lock → Execution lock (SETNX, TTL 5min)
agentic:sessions:index → Set de session IDs activos
agentic:memory:knowledge:{doc_id} → MemoryDocument JSON
agentic:memory:knowledge:_index → Set de doc IDs
agentic:memory:_tag:{tag} → Set de doc IDs por tag
agentic:memory:_type:{type} → Set de doc IDs por tipo
```
## Project Structure
```
.
├── src/
│ ├── main.py # FastAPI app, lifecycle, wiring
│ ├── config.py # Pydantic settings
│ ├── models/ # Pydantic v2 data models
│ │ ├── session.py # SessionState, TaskState, TaskStep
│ │ ├── context.py # ContextPackage, MemoryDocument
│ │ ├── agent.py # AgentProfile, SubAgentDefinition
│ │ ├── artifacts.py # ArtifactSummary
│ │ └── tools.py # ToolExecution, ToolDefinition
│ ├── context/ # Context Engine (core)
│ │ ├── engine.py # Prompt assembly, knowledge filtering, debug
│ │ └── compactor.py # Compaction, summarization, tiktoken
│ ├── memory/ # Persistent memory
│ │ └── store.py # Redis-backed memory + embeddings
│ ├── adapters/ # Model provider adapters
│ │ ├── base.py # ModelAdapter interface
│ │ ├── claude_adapter.py # Anthropic Claude (streaming)
│ │ └── openai_adapter.py # OpenAI GPT (streaming)
│ ├── mcp/ # MCP client
│ │ └── client.py # stdio transport, tool registry
│ ├── orchestrator/ # Agent orchestration
│ │ ├── engine.py # Pipeline + error recovery + timeout
│ │ ├── router.py # Step-to-agent routing
│ │ └── agents/
│ │ ├── base.py # Shared execution loop
│ │ ├── planner.py # Plan decomposition
│ │ ├── coder.py # Implementation
│ │ ├── collector.py # Context gathering
│ │ └── reviewer.py # Validation
│ ├── streaming/ # SSE streaming
│ │ └── sse.py # Event emitter + Redis persistence
│ ├── storage/ # Persistence
│ │ └── redis.py # Sessions, events, locks
│ └── api/ # REST endpoints
│ └── routes.py # Sessions, messages, knowledge, debug
├── dashboard/ # Testing UI (vanilla JS, zero build)
│ ├── index.html
│ ├── css/main.css
│ └── js/
│ ├── app.js # State, SSE handlers
│ ├── api.js # Fetch + EventSource
│ └── components/ # Sidebar, chat, event log, inspector, timeline
├── mcp-server/ # Acai MCP server (Node.js, stdio)
├── docs/ # Knowledge base documents (*.md)
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env.example
```