13 KiB
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
# 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
# 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
# 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→ SendCtrl+Enter→ StreamShift+Enter→ Nueva línea
API Reference
Base URL: /api/v1
Sesiones
# 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
# 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:
{
"session_id": "...",
"task_id": "...",
"content": "### Step 1\n...\n### Review\n...",
"steps_completed": 5,
"steps_failed": [],
"artifacts_count": 0,
"review": "...",
"status": "completed"
}
SSE Streaming
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
# 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
# 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
- Planner descompone el mensaje en pasos con agent role asignado
- Router rutea cada step al agente apropiado (keyword matching: implement→coder, buscar→collector, etc.)
- Subagent ejecuta el step con su propio contexto controlado
- Si un step falla, se marca como failed y el pipeline continúa con el siguiente
- Reviewer valida el trabajo si hubo >1 step
- 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:
# 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