Skip to content

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.