How it works

Agents that do the work, people who make the calls

A guide to agentdesk in seven parts. Every part starts with a plain-language summary, then the technical detail. If something is unclear, ask the guide: an agent of this platform answers from these pages and the running code.

01

What it is

A team of AI agents that handles customer support for an online store.

In plain words

Customers write in by email, chat or a contact form. The agents read each message, look up the customer and the order, and write a reply, and sometimes suggest a refund. They are not allowed to send anything or give money back on their own: those actions wait for a person to press Approve. Everything they do is visible live on this dashboard and recorded step by step.

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

In a hurry? Read this tab and Paths & examples. Have a question instead? Press Ask the guide at the bottom right: an agent of this platform answers from this guide and from the running code, in your language.

03

A two-minute tour

Where to click, in order, to see the whole system work.

04

Architecture

One request path from left to right, one decision path along the bottom, and observability under all of it.

In plain words

Messages come in on the left and wait in line. A worker hands each one to two agents: the first sorts it, the second looks things up and drafts an answer. Purple boxes are the safety rules; the amber box is you. Nothing reaches the store on the bottom row without passing through you.
CHANNELSEmail · chat · formSimulatorVoice (ElevenLabs)n8n workflowsAPIFastAPI, bearer tokenidempotent ingestJob queuePostgres, SKIP LOCKEDbackoff · dead lettersWORKER · role desk_agentTriage agenttier 0 · no toolsResolver agenttier 1 · 3 toolsModel routerMistral → Claude → offlineretry · breakerTool gatewaygranted tools onlybound to the senderGuardsinvented orders · refund limits · leaked data · injectionProposalswritten atomicallynothing executed yetHuman decisiondashboard · Telegramapprove or rejectApproval executorrole desk_apire-validates in the dbStorerefunds · outboundmessagesObservabilityEvery step → run_steps → Supabase Realtime → this dashboard · every model and tool call → Langfuse · every decision → audit log · alerts → Telegramenforces governancehuman in the loopplanned

05

The life of a ticket

What happens between “my order arrived broken” and a refund, and where each step lives in the code.

Customern8nAPI + queueAgentsGuardsYouStorewrites in the contact formPOST /tickets (retried ×3)202: queued“Message received”worker claims the jobtriage + resolvermodel ↔ toolsdraft reply + refundpending proposalTelegram card · dashboardapprove → executereply sent to the customer
Read top to bottom: each arrow is one message between two parts of the system. Dashed arrows are answers. The amber step is the only one that needs a person.
  1. 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. 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. 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. 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. 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. 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. 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. 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
NextAgents→