- Config: COST_PER_1M_INPUT y COST_PER_1M_OUTPUT configurables via .env
- OpenAI adapter: stream_options include_usage para capturar tokens reales
- base.py: acumula input/output tokens de cada iteración del agente
- planner.py: devuelve usage junto con el plan
- engine.py: suma tokens de planner + steps + review, calcula coste USD
- Response incluye usage{input_tokens, output_tokens} y total_cost_usd
Formato compatible con el bridge de Claude Code CLI para integración
con el frontend y reporting a Acai webservice.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
- Semantic search — embeddings (OpenAI text-embedding-3-small) para seleccionar los docs más relevantes por similitud semántica
- Task history compaction — al completar una tarea, se comprime a ~50 tokens; en sesiones largas el agente recuerda todo sin desbordar el contexto
- 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 + generar embeddings para semantic search
curl -X POST http://localhost:8001/api/v1/knowledge/load \
-H "Content-Type: application/json" \
-d '{"docs_path": "docs"}'
# → {"status": "loaded", "count": 13, "embeddings": true, ...}
Los embeddings se generan en batch via OpenAI text-embedding-3-small (~$0.001 por los 13 docs) y se almacenan en Redis. Solo se recalculan al hacer /knowledge/load de nuevo.
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. Al cargar se generan embeddings automáticamente para semantic search.
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 por semantic search + índice de títulos de todos |
task_history |
55 | Resúmenes compactos de tareas anteriores (~50 tokens c/u) |
task_state |
70 | Objetivo, plan, step actual, facts, constraints |
artifact_memory |
50 | Resúmenes de outputs de herramientas (solo tarea actual) |
working_context |
30 | Items de trabajo recientes entre steps |
Semantic search: genera embedding del objetivo/step del usuario y busca por cosine similarity contra los embeddings de todos los docs. Los más relevantes entran completos (budget 15k tokens), el resto aparece como título + summary para que el agente sepa que existen y pueda pedir rehidratación.
Task history compaction: al completar cada tarea, sus artifacts, facts y resultados se comprimen en un bloque de ~50 tokens que se guarda en task_history. Los artifacts viejos se borran de Redis. En una sesión de 20 tareas, el historial ocupa ~1000 tokens vs los miles que ocuparían los artifacts completos. El agente nunca pierde contexto de lo que hizo antes.
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.
Presupuesto típico tras 20 tareas:
immutable_rules: ~250 tokens (fijo)
project_profile: ~15 tokens (fijo)
knowledge_base: ~15,000 tokens (semantic search)
task_history: ~1,000 tokens (20 tareas × ~50 tokens)
task_state: ~400 tokens (tarea actual)
artifact_memory: ~500 tokens (solo tarea actual)
working_context: ~500 tokens (solo step actual)
─────────────────────────────────────
TOTAL: ~17,700 tokens / 120,000 disponibles
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)
Arquitectura per-session: el microservicio corre como un Docker único, y cada sesión arranca sus propios subprocesos MCP con las env vars del proyecto del usuario.
Modelo de operación
Docker (microservicio — uno solo corriendo)
└── mcp.json ← template: QUÉ servers arrancar (global)
└── mcp-server/ ← código MCP (baked-in)
POST /sessions { mcp_env: { ACAI_WEB_URL: "https://tienda.com", ... } }
→ Arranca subprocesos MCP con esas env vars
→ Session aislada con su propio MCPManager
→ 55 tools descubiertas (acai-code: 33, playwright: 21, fetch: 1)
DELETE /sessions/{id}
→ Mata los subprocesos MCP de esa sesión
Configuración
1. Template global (mcp.json en la raíz — define QUÉ servers existen):
{
"mcpServers": {
"acai-code": {
"command": "node",
"args": ["mcp-server/stdio.js"],
"env": {},
"timeout": 30,
"startup_timeout": 10
},
"playwright": {
"command": "npx",
"args": ["@playwright/mcp", "--headless"],
"timeout": 30,
"startup_timeout": 15
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"],
"timeout": 30,
"startup_timeout": 15
}
}
}
# En .env
AGENTIC_MCP_CONFIG_PATH=mcp.json
2. Per-session env vars (cada usuario/proyecto pasa las suyas al crear sesión):
curl -X POST http://localhost:8001/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{
"project_profile": {"name": "tienda-online"},
"mcp_env": {
"ACAI_WEB_URL": "https://superadmin_tienda.forge.acaisuite.com/",
"ACAI_WEBSITE": "tienda.com",
"ACAI_PROJECT_DIR": "/projects/tienda"
}
}'
Las env vars de mcp_env se fusionan con las del template y se pasan al subproceso MCP. El MCP server lee el token desde el .acai del proyecto automáticamente.
3. Legacy (un solo server global, sin mcp.json):
AGENTIC_MCP_SERVER_COMMAND=node
AGENTIC_MCP_SERVER_ARGS=["mcp-server/stdio.js"]
Tool namespacing
En modo multi-server, los tools se namespean con __ (compatible con OpenAI que no acepta puntos): acai_code__compile_module, playwright__browser_navigate. En modo single-server los tools mantienen su nombre original.
API de gestión MCP
# Ver estado de todos los MCP servers activos (por sesión)
curl http://localhost:8001/api/v1/mcp/status
# Hot-reload del template (re-lee mcp.json, no afecta sesiones activas)
curl -X POST http://localhost:8001/api/v1/mcp/reload
Ciclo de vida
POST /sessionsconmcp_env→ arranca subprocesos MCP para esa sesiónPOST /sessions/{id}/messages→ el agente usa los tools del MCP de esa sesión- Si el server se reinicia y la sesión sigue en Redis → el MCP se reconecta automáticamente al siguiente mensaje
DELETE /sessions/{id}→ mata subprocesos MCP de esa sesió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_CONFIG_PATH |
— | Ruta a mcp.json (multi-MCP per-session) |
AGENTIC_MCP_SERVER_COMMAND |
— | Legacy: comando del servidor MCP (single) |
AGENTIC_MCP_SERVER_ARGS |
[] |
Legacy: 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:embeddings:knowledge:{doc_id} → Embedding vector (1536 floats JSON)
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 + similarity search
│ │ └── embeddings.py # OpenAI embedding service
│ ├── adapters/ # Model provider adapters
│ │ ├── base.py # ModelAdapter interface
│ │ ├── claude_adapter.py # Anthropic Claude (streaming)
│ │ └── openai_adapter.py # OpenAI GPT (streaming)
│ ├── mcp/ # MCP (per-session, multi-server)
│ │ ├── client.py # stdio transport, tool registry
│ │ ├── config.py # mcp.json parser (Pydantic)
│ │ ├── manager.py # MCPManager (aggregates tools, routes calls)
│ │ └── registry.py # Per-session MCP lifecycle
│ ├── 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)
├── mcp.json # MCP server template (qué servers arrancar)
├── mcp.json.example # Ejemplo con múltiples servers
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env.example