Overview read this first
The intelligence layer sits above the agent execution engine as an advisory router. POST /v1/intelligence/route recommends which agent to use for a task — it never invokes the agent and never moves money. Actual execution still goes through the existing chain engine.
Cost signals fd:intelligence:cost:<agentSlug>
Cost is the agent's real, declared per-call price — not a historical average.
Why declared, not learned
Execution telemetry doesn't record what a call cost — only whether it settled or failed, and how long it took. Estimating cost from seller earnings would mean assuming a fixed revenue split across every agent, which isn't true for every agent type. So cost is read straight from the agent's own listed price.
| Field | Meaning |
|---|---|
| last_cost_pence | The agent's current declared price |
| avg_cost_pence | Same value — there's no variance to average, since price is fixed, not observed |
| sample_size | 1 if a price is known, 0 if not |
Latency signals fd:intelligence:latency:<agentSlug>
Average and 95th-percentile latency, computed from the agent's real, recent settled calls.
| Field | Meaning |
|---|---|
| avg_latency_ms | Mean duration across recent settled calls |
| p95_latency_ms | 95th-percentile duration — a better read on worst-case wait than the average alone |
| sample_size | How many real settled calls this is based on |
Quality signals fd:intelligence:quality:<agentSlug>
Real success and error rate, with a confidence adjustment for thin samples.
| Field | Meaning |
|---|---|
| success_rate | Settled calls ÷ total calls, from real history |
| error_rate | 1 − success_rate |
| sample_size | Total real calls (settled + failed) this is based on |
Fallback / circuit breaker fd:intelligence:fallback:<agentSlug>
An agent that's actively failing gets automatically excluded from routing for a cooldown period.
| Rule | Value |
|---|---|
| Opens after | 3 consecutive real failures (most recent calls, real telemetry) |
| Cooldown | 5 minutes |
| Auto-closes | Yes — the breaker key has its own expiry; there's no separate "close" step and no way for it to lock permanently |
all_breakers_open: true in the response, so the caller knows every option is currently degraded.A/B testing fd:intelligence:ab:<testId>
Compare two agents head-to-head on real traffic, with a fixed, repeatable split.
Creating a test
POST /v1/intelligence/ab-test/create (admin-only)
{
"agent_a": "agent-x",
"agent_b": "agent-y",
"split_ratio": 0.5,
"ttl_seconds": 604800
}
split_ratio is the share of buyers sent to agent_a — the rest go to agent_b. Tests expire automatically; there's no manual cleanup step.
How a buyer's bucket is decided
A buyer's bucket is derived from a hash of their buyer ID and the test ID — the same buyer always lands in the same bucket for a given test, every time. There's no randomness involved, so a routing decision can always be reproduced exactly.
Composite scoring fd:intelligence:score:<agentSlug>
The final ranking combines quality, latency, and cost into one deterministic number per candidate.
− 0.5 if breaker open
These weights are fixed constants today, not learned or tuned automatically. Every input is deterministic — the same real signals always produce the same score, and therefore the same choice.
Buyer-specific signals fd:intelligence:by_buyer:<buyerId>:agent:<agentSlug>
If a buyer has their own real history with an agent, that history is used instead of the platform-wide average.
Where this comes from
Derived directly from that buyer's own completed workflows — never from anyone else's. If a buyer has run 5 real chains through an agent, their own 5-call success rate and latency are used for that buyer's routing decisions. A buyer with no history yet simply falls back to the platform-wide signal.
Decision logs
Every routing decision is recorded twice, for two different purposes.
| Key | Purpose |
|---|---|
| fd:intelligence:route_log | A bounded, most-recent-first feed of recent decisions |
| fd:intelligence:decision:<id> | One individually-lookupable record per decision, kept for 30 days |
Both record which agent was chosen, every signal that went into the score, the final composite score, and the timestamp — never the task's raw text or content.
Calling the router
POST /v1/intelligence/route — requires a real API key. The buyer identity is always resolved from that key; it's never accepted as a field in the request body, so one buyer can never read or influence another buyer's signals by simply naming their ID.
Request
POST /v1/intelligence/route
Authorization: Bearer sk_fd_...
{
"task": "Summarize this document.",
"capability": "summarization"
}
Response
{
"decision_id": "dec_...",
"selected_agent": "summarization-v1",
"predicted_cost_pence": 10,
"predicted_latency_ms": 195522,
"confidence": "low",
"all_breakers_open": false,
"ab_test": null,
"composite_score": 0.559,
"signals_used": { "quality": {...}, "latency": {...}, "cost": {...}, "fallback": {...}, "per_buyer": null },
"candidates_considered": [ {...}, {...} ]
}
confidence is honest about sample size — low below 5 real calls, medium below 20, high above that. It's never inflated to look more certain than the real data supports.