The earlier lessons take the pieces one at a time. This one follows a single case to show how they fit together: answering from documents, teaching the model a fixed output shape, letting it act only with a person's approval, and tracking the work of building it. It points at code more than any other lesson; the links open each repository on its principal branch.
A caveat: Adapta is an unaudited working prototype, docket is beta software for a single operator, and Tack's execution fleet is not in a tagged release (see each README: Adapta, docket, Tack). None is hardened for production. We use them because you can read every line of their code.
Scenario and architecture
Adapta supplies the knowledge (and behavior, if needed), docket governs the actions, and Tack tracks the building work. In the diagram, your app gets a cited answer from Adapta, and if the model wants to open a ticket, docket holds it until a person approves.
- Adaptaknowledge: answers with citations
- docketactions: a person approves each ticket
- Tacktracks the work of building it
No product here ships the "support app"; that code, user sign-in and the interface are yours.
1. Knowledge: answering from the manuals with Adapta
Answering from the manuals means finding the passages that matter for each question and handing them to the model. That is (Embeddings and RAG), and Adapta packages it as a service. make up (README.md) starts its API, a background worker and its databases (docker-compose.yml). RAG alone needs only an ordinary CPU.
In Adapta
- Your applicationany OpenAI-compatible client
- API serviceanswers questions, never trains
- Search and answer
- Projects, keys, usage
- Training workerslow GPU jobs, taken from a queue
- Train adapter
- Eval gate
- Storageon your own server
- RecordsPostgres
- VectorsChroma
- Job queueRedis
Using it takes four API calls: create a project of type rag, upload the manuals, create an endpoint, and issue an access key. The commands are in Adapta's guide Knowledge and behavior. Here is what happens inside:
- Upload.
upload_filechecks the file type, saves it and replies at once, leaving indexing to run in the background. - Indexing.
_index_fileextracts the text, cuts it into chunks (chunk_text) and stores them with their (index_chunks). Re-indexing replaces a file's chunks instead of duplicating them. - Endpoint.
create_endpointrefuses to create one for a RAG project with nothing indexed. - Question.
_resolve_endpointchecks the key.retrievefinds the best passages,_build_systemadds them, numbered, to the model's instructions, and_fit_contextdrops the least relevant ones if they don't fit.
The app calls the endpoint with any OpenAI-compatible client and gets citations with the answer.
2. Behavior: when an adapter is worth training
RAG supplies facts. It does not reliably make the model answer in a fixed shape, such as one label from a closed list or the same JSON structure every time. When instructions alone can't hold that shape, you train an , a small add-on that adjusts the model's behavior (Fine-tuning, LoRA and QLoRA). Here, the ticket classifier is the natural candidate. The risk is serving an adapter that learned nothing, and Adapta blocks that with an : a test the adapter must pass before it can be served.
The score measures how well the adapter predicts held-out answers, not label accuracy, so you still need your own checks (Evaluating models).
3. Acting safely: a governed tool with docket
Opening a ticket, unlike answering, changes another system. As Governance and human-in-the-loop argues, code should decide whether an action runs, not the model. In docket, every tool call passes one checkpoint that applies rules, asks a person when needed and records the outcome.
Your own application uses docket as a library, docket-runtime, built from source and not published to any package index (DOCKET Architecture). In the repository's example (examples/runtime_embed.py), the application registers each tool, marking whether it writes, and sends every call through the runtime instead of calling the function directly.
In docket
- Your applicationregisters tools, sends every call
- docket-runtimeone checkpoint for every call
- Rules
- Human approval
- Trace
- Audit log
- Toolhere, the ticketing system
The rules are JSON files in ~/.docket/policies/, matched against each call as a line of text that starts with the tool's name. This illustrative policy, in the format of the real template high-risk-deploy.json, requires approval for every open_ticket call:
{
"id": "approve-open-ticket",
"applies_to": ["*"],
"hook": "pre_tool_call",
"match": {"type": "regex", "pattern": "^open_ticket\\b"},
"action": "require_approval",
"message": "Opening a ticket needs a support lead."
}
Inside dispatch_tool, a call goes through these steps:
- An unknown tool, or missing or unreadable arguments: denied.
- The rules are checked (
evaluate_tool_call), and the most restrictive verdict wins. - If approval is required, docket stores a request with sensitive values hidden (
approval_create) and waits (wait_for_approval) for a person to answer, from the command line or over HTTP, or Telegram. No answer before the deadline means denied. - Every decision goes into an where each entry is chained to the previous one by a hash, so later edits show (
audit_log,verify_chain).
docket policies test tries a rule without executing anything (docs/commands.md), and run_agent_turn lets docket run the whole through the same checkpoint.
4. Tracking the work: items and runners in Tack
Building the assistant is itself a project. Tack is a project manager that can also hand a work item to a (such as Claude Code or Codex) and record the run in the item's history. Its documentation describes two parts: a board that decides and records, and that do the work.
In Tack
- Boardone; the plan and the record
- Items and workflows
- Chooses what runs, and when
- Keeps each run's history and cost
- No code execution, no keys
- Runnersmany; on your machines
- Ask the board for work
- Run the agent in a separate checkout
- Keep the model keys
You add an item, then create an execution that assigns it to a runner, the machine that runs the (agent-runners.md). A scheduler picks a runner with the right capabilities (select_runner). The runner writes down what it is about to do before touching any files (run_claimed), then launches the agent. Every run moves through a fixed set of states (validate_transition); how it survives crashes is covered in Durable agent execution.
5. A deployed chat: the yielab.com widget
For contrast, this site's pre-sales chat widget is actually deployed. Its repository is private, so only the architecture is described.
- Runtime and model. Next.js on Cloudflare Workers via OpenNext. The browser resends the conversation each turn, and the server treats it as untrusted. One call per turn, with no retries, goes to an Anthropic model through Vercel AI Gateway, which holds the key and caps spending.
- Checks, cheapest first. Size and origin, a bot check with Turnstile, a signed ticket for later turns, a turn cap, a per-address rate limit and a daily counter. Every check fails closed, blocking the request when in doubt, and the model call comes last.
- Grounding. No RAG: the service catalog fits in the instructions, built from an allowlist of fields so prices never reach the model.
- Output. Every reply is scanned for mentions of money, a hidden marker that would reveal leaked instructions, and excess length; a failure shows a fixed message instead. The lead email is built from validated data, never from the model's free text.
- Monitoring and . Conversations are traced in Langfuse (EU region) via OpenTelemetry. An offline test suite runs on every build; a paid model comparison is still pending.
6. Before real production
| Area | What to add |
|---|---|
| Sign-in | Employee single sign-on in front of your app |
| Permissions | Per-user document filtering; docket has no notion of separate tenants |
| Secrets | A secrets manager; rotate Adapta's default credentials; TLS in front of docket serve |
| Evals | A golden set with expected documents; adversarial open_ticket cases, run on every change |
| Monitoring | Adapta's deep health check; alerts on pending approvals |
| Data audit | Adapta has no audit log or soft delete yet (TODO.md) |
Putting it into practice
- Decide what you buy, adopt and write. The application, sign-in and helpdesk integration are always yours.
- Ask each component for evidence of its real state: known limits, roadmap, tests.
- a security review before a prototype touches sensitive data.
Sources
- OpenNext for Cloudflare
- Vercel AI Gateway
- Cloudflare Turnstile
- Rate limiting in Cloudflare Workers
- Langfuse and OpenTelemetry
Go deeper
Each product's own documentation goes further into the architecture this lesson walks through:
- Architecture maps Adapta's containers and follows a question and a training job through them, step by step.
- DOCKET Architecture explains how docket's code is layered, what state it keeps (the chained audit log included) and how an application embeds its runtime.
- Introduction presents Tack's split into a board and runners, and why they are kept apart.
- Agent Runners & Fleet Execution covers enrolling runners, where credentials live, and which parts of the pipeline are proven end to end today.
Key takeaways
- Components solve parts of the problem; the application, identity and glue code stay yours. Tack tracks the building work, not the running assistant.
- Adapta serves an adapter only after it passes an eval gate on examples it never trained on.
- In docket, every action passes one checkpoint: rules, then a human approval that denies on silence, then a audit entry.
- In production, run cheap checks before the model and review the whole output before anyone sees it.
Check yourself
What stops Adapta from serving an adapter that learned nothing?
The eval gate: the adapter must score well on examples it never trained on, or clearly beat the base model there. A job that fails never gets an endpoint.
Nobody answers the approval for `open_ticket`. What happens, and why is that the right default?
The deadline passes and the call is denied, so no ticket is opened. Treating silence as "no" means a forgotten request can never become an unapproved action.
Why doesn't the yielab.com chat use RAG or an approval engine?
Its knowledge is a small catalog that fits in the instructions, and it has no tools that act. Its risks are in the input, the cost and the output.