01
What it is
A team of AI agents that handles customer support for an online store.
In plain words
Controlled
Each agent can only use the tools it was given, and its database login cannot refund or send at all.
Verified
Every answer is checked by rules before a person sees it, and a test suite of known tickets runs on every change.
Observable
Every step is live on this dashboard, every model call is in Langfuse, every decision is in the audit log.
The store and its customers are a demo with simulated traffic. Everything else is real: the queue, the models, the rules, the approvals from a phone, the tracing and the evaluation that guards every change.
02
Start here
The guide has seven tabs, written to be read in order. Each one starts in plain words, then goes into the detail.
In plain words
- 1Overview5 minWhat it is, where to click, the architecture and the life of a ticket.
- 2Agents6 minThe two agents: their prompts, their tools and the forms they fill in, live from the code.
- 3Paths & examples8 minEvery way a ticket can end, six real worked examples, and what the person in the loop sees.
- 4Safety6 minThe layers that keep an agent from acting alone, and the attacks they stop.
- 5Quality5 minThe golden suite, the judge and the gate that runs on every change.
- 6Operations7 minn8n at the edges, Langfuse underneath, and what happens when things go wrong.
- 7Reference3 minGlossary, stack and code map, and the live links.
03
A two-minute tour
Where to click, in order, to see the whole system work.
- 1OverviewIs it healthy? Tickets, success rate, latency, cost, and what is waiting for you.
- 2RunsOpen any run to see each step, then jump to its full trace in Langfuse.
- 3ApprovalsApprove a refund. It runs only now, after a re-check, and lands in the audit log.
- 4Top bar“simulate outage” on a provider: watch retries, fallback and the circuit breaker.
- 5QueueJobs out of retries wait here with a Retry button instead of being lost.
- 6AgentsThe registry: who owns each agent, its risk tier, the exact tools it may call.
- 7EvalsThe golden suite, case by case, and whether the gate is open.
- 8Audit logWho decided what, and when: agents, people, the system.
04
Architecture
One request path from left to right, one decision path along the bottom, and observability under all of it.
In plain words
05
The life of a ticket
What happens between “my order arrived broken” and a refund, and where each step lives in the code.
- 1
A message arrives
From the n8n contact form or webhook (or the simulator), the API receives it. The sender's message id makes a redelivered webhook a no-op; the ticket and its job are written in one transaction.
api.py · POST /tickets - 2
It waits in a durable queue
A Postgres table, not a separate broker. Workers claim jobs with FOR UPDATE SKIP LOCKED, are woken instantly by LISTEN/NOTIFY, and hold a lease so a crashed worker's job is picked up again.
jobs.py · claim() - 3
Triage classifies it
A tier 0 agent with no tools returns intent, language and urgency as a schema-checked tool call. A malformed answer gets one repair turn; a second failure fails the attempt and the queue retries it later.
agents/runner.py · run_triage() - 4
The resolver looks things up
A tier 1 agent alternates model calls and tool calls. The gateway offers it three read-only tools, always scoped to the ticket's sender, so it cannot read another customer's orders.
gateway.py · Gateway.call() - 5
Guards check the draft
Order ids it never looked up, refunds above what is left on the order or on another customer's order, and leaked emails block the run. Promises and prompt injection are flagged for the reviewer.
guards.py · check_resolution() - 6
Effects become proposals
A reply (tier 2) and maybe a refund (tier 3) are written as pending proposals, together with the ticket status and an audit entry, in one transaction: all or nothing.
pipeline.py · process_ticket() - 7
A person decides
On the dashboard or with a button in Telegram. Both use the same executor, which locks the proposal, re-checks the refund against the order and ignores a second click.
approvals.py · decide() - 8
Everything is on the record
Each run and step is in the database as it happens (that is what the dashboard shows live), each model and tool call is in Langfuse, and each decision is in the audit log.
recorder.py · RunRecorder
06
Where things live
The pieces outside this dashboard, one click away. “Live” links are the real self-hosted services; “local” ones only answer when the stack runs on your machine.
- n8n · agentdesk folderliveThe six workflows: entry points, traffic, the daily report, the error handler.n8n.senaproject.online/projects/7d6T3aexJ0y2nZTb/folders/OET1fRISh0fNtg0T/workflows ↗
- Langfuse · tracesliveEvery model and tool call, one trace per ticket. Environment agentdesk-dev.langfuse.senaproject.online/project/cmuzbb1t2000gp708h15hk9ns/traces ↗
- Langfuse · datasetsliveThe golden suite as a dataset, one run per evaluation, with scores.langfuse.senaproject.online/project/cmuzbb1t2000gp708h15hk9ns/datasets ↗
- Core API · /metalocalPrompts, tool schemas and settings, straight from the running code.127.0.0.1:8000/meta ↗
- Core API · /docslocalThe HTTP API (tickets, decisions, retries, chaos, explain), as OpenAPI.127.0.0.1:8000/docs ↗
- Supabase StudiolocalThe database: tables, the three roles, grants and row level security.127.0.0.1:54323 ↗