336 lines
13 KiB
Markdown
336 lines
13 KiB
Markdown
# 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
|
||
```
|