Agent categories
One execution spine (ADR-008)
LifecycleManager.run() is the envelope for every agent run — ADR-006
idempotency layers, run-log state transitions, and lifecycle events happen
here regardless of what the agent itself does. Custom agent bodies are
dispatched through the manifest’s "entrypoint": "<module>:build_runner",
so an agent can own real nested Pydantic schemas and multi-step logic (a
tool call before an LLM call, deterministic post-processing) without
touching the shared envelope. Every step-executing agent in agents/*/*/ agent.py exposes ENTRYPOINT / build_runner() / execute() — enforced
by a test that imports and checks every agent module.
This replaced an earlier split where 30+ agents used a direct-composition
pattern separate from the shared lifecycle class. ADR-008 folded both
into one spine so quality/cost/evaluation improvements apply everywhere
at once, not per-agent.
Evaluation (ADR-009): never a silent auto-pass
Evaluation is opt-in per agent — assigned via the manifest’s"evaluator": "<module>:factory". This is a deliberate design choice: an
agent with no evaluator is recorded as not evaluated (NULL score,
seo_agent_evaluations_total{outcome="not_evaluated"}), never silently
scored as a pass. The dashboard and alerting both distinguish
not_evaluated from passed — visibility into which agents are gated and
which aren’t is the point.
For content specifically, full-content-pipeline.yaml adds a dedicated
content-evaluation step (ADR-011) between review and human approval,
scoring nine dimensions — SEO, E-E-A-T, medical compliance,
readability, hallucination risk, AI-detection risk, internal linking,
schema validity, brand voice — with medical compliance and hallucination
risk as hard, fail-closed dimensions: a YMYL (medical) article with no
fact-check output fails the gate outright, regardless of composite score.
A FAIL routes the article to review_required; a PASS still requires
human approval — the gate never auto-publishes.
Multi-LLM routing (ADR-007) and resilience (ADR-010)
Providers — Anthropic, OpenAI, Google, Ollama, vLLM — load lazily through a provider registry (core/agent_runtime/providers/registry.py); an agent’s
model_config in its manifest picks the model, never a hardcoded provider
in code. ModelRouter retries with exponential backoff, then fails over to
a configured fallback model. A process-wide circuit breaker per
provider trips after consecutive failures and fast-fails to the fallback
chain — no wasted timeout/retry budget on a provider that’s actually down —
and closes again after a cooldown probe succeeds.
Idempotency (ADR-006)
Every agent/step execution is protected by three layers: a Redis lock (prevents concurrent execution of the same logical run), a unique constraint on the run-log insert (prevents duplicate rows even under a lock race), and consumer-level dedup on the event that triggered the run. A Celery task redelivered after a visibility timeout, or a duplicate event delivery, cannot cause double execution or double side effects.Cost tracking
Every LLM call is recorded to a partitionedbilling.usage_events table
with per-call cost attribution, rolled up per agent run and per tenant.
Budget Guard enforces tenant monthly_budget_usd pre-dispatch — a run that
would exceed budget never starts. Spend alerts fire at 200/hour (critical), with a documented kill-switch procedure.