Jordan Diaz 72da3b7659 Soporte base_url custom en Claude adapter (MiniMax Anthropic-compatible)
MiniMax tiene endpoint compatible con Anthropic API en
https://api.minimax.io/anthropic/v1. Nueva variable
AGENTIC_ANTHROPIC_BASE_URL para usarlo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 10:42:40 +00:00
2026-04-01 23:16:45 +01:00
2026-04-01 23:16:45 +01:00
2026-04-02 00:14:11 +01:00
2026-04-01 23:16:45 +01:00
2026-04-01 23:16:45 +01:00
2026-04-02 00:14:11 +01:00
2026-04-01 23:16:45 +01:00

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/

Tests

# Ejecutar todos los tests unitarios (no necesita Docker, Redis ni LLM)
pip install pytest
python3 -m pytest tests/ -v

# Ejecutar un archivo específico
python3 -m pytest tests/test_compactor.py -v

# Ejecutar un test específico
python3 -m pytest tests/test_cost_calculation.py::TestCostCalculation::test_1m_input_tokens -v

Los tests validan: compactación de contexto, extracción de key_data para historial, fingerprinting de tool calls, y cálculo de costes. Son 100% offline — no consumen tokens ni necesitan servicios externos.

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 → Send
  • Ctrl+Enter → Stream
  • Shift+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
  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)

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

  1. POST /sessions con mcp_env → arranca subprocesos MCP para esa sesión
  2. POST /sessions/{id}/messages → el agente usa los tools del MCP de esa sesión
  3. Si el server se reinicia y la sesión sigue en Redis → el MCP se reconecta automáticamente al siguiente mensaje
  4. 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
Description
No description provided
Readme 1.5 MiB
Languages
JavaScript 56.8%
Python 35.8%
HTML 4.3%
CSS 2.7%
Dockerfile 0.4%