ADR-0006: Orchestrator engine — tools, agents, routing, observability¶
- Status: Accepted
- Date: 2026-06-04
- Deciders: AOP core
Context¶
Phase 3 adds the multi-agent engine: an MCP-compliant tool registry, four agent strategies, a supervisor that routes by capability, observability, and the HTTP API surface (deferred from Phase 1). It builds on the Phase 0–2 substrate (LLM adapters, RAG, memory).
Decisions¶
1. MCP-compliant native tool registry (external MCP client deferred)¶
Tools declare arguments as a Pydantic model, which is the validation layer and
the MCP inputSchema (JSON Schema). Tool.manifest() emits MCP-format manifests;
calls are validated against the schema and failures return ok=False rather than
crashing the agent. Native tools wrap real platform capabilities (rag_answer,
rag_search, memory_recall, warehouse_query, http_fetch). A JSON-RPC client
for external MCP servers is deferred (the registry is already MCP-shaped, so it
slots in later).
2. Four agent strategies over the unified adapter¶
ReAct (Thought/Action/Observation loop), Plan-and-Execute (plan → per-step
execution with optional tools → synthesis), Reflexion (attempt → self-critique
→ retry), and CRITIC (generate → tool-verified critique → revise). Each is an
LLM-driven loop emitting a structured AgentResult trace (Step list with tool
calls + token usage). Output parsing is deliberately tolerant for small local
models.
3. Supervisor routes by capability manifest¶
Agents advertise a CapabilityManifest (name, description, tags, tools) built
from class attributes (no instantiation needed). The supervisor asks the LLM to
pick the best agent, with a deterministic fallback to ReAct, then dispatches and
exports the trace.
4. Observability: Langfuse v2 (Postgres-only), best-effort¶
We run Langfuse v2 self-hosted (Postgres-only — far lighter than v3's
Clickhouse/Redis/S3 stack) via docker-compose, bootstrapped with fixed dev API
keys (LANGFUSE_INIT_*) so tracing works out of the box. The Tracer exports
each AgentResult as a trace + spans, wrapped so a missing/broken Langfuse
never breaks a run — every step is also emitted to structlog. The native
AgentResult trace is the source of truth; Langfuse is an exporter.
5. Live agent state via a per-step callback¶
Agent accepts an async on_step callback invoked as each Step is recorded.
The FastAPI WebSocket (/ws/agent) uses it to stream agent state live — the
contract the Phase-4 canvas consumes.
6. FastAPI surface (REST + WebSocket)¶
POST /agent/run, /rag/search, /rag/ask, /ingest; GET /health,
/agents, /tools; WS /ws/agent. The app reuses the same subsystems as the
CLI. Run with aop serve (uvicorn).
Consequences¶
- Positive: MCP-shaped tools, four real agent strategies, capability routing, resilient tracing, and an API that the canvas can build on — all local-first.
- Caveats / Phase-4+ hand-off:
- External MCP-server consumption (JSON-RPC client) is not yet implemented.
- Small local models (
llama3.2:3b) follow ReAct formats imperfectly; the parsers are tolerant and the loop is iteration-bounded. A stronger model (cloud or larger local) improves multi-step tool use. - Langfuse keys default to dev values matching compose; rotate for any shared deployment.