A small, opinionated agent with a small, opinionated toolbox.
One LLM agent loop, four tools, one supervisor that can interrupt it. The whole system fits on one page if you print it. That’s the goal: the smallest thing that solves the problem and stays solved.
Architecture
┌────────────────────────┐
form submit ───▶ │ ingest + enrich │
│ (Clearbit, ZeroBounce)│
└──────────┬─────────────┘
▼
┌────────────────────────┐
│ agent loop (GPT-4o) │
│ tools: │
│ - crm.search │
│ - email.send │
│ - calendar.book │
│ - slack.notify │
└──────────┬─────────────┘
▼
┌────────────────────────┐
│ supervisor (rules) │
│ trip wires + escal. │
└──────────┬─────────────┘
▼
booked meeting + brief
Stack
Python 3.12
FastAPI
OpenAI gpt-4o
LangGraph
Postgres + pgvector
Modal (workers)
Resend
Cal.com API
HubSpot API
Linear (escalation)
Logfire
Key decisions
- One agent, not five. The first instinct in 2026 is to wire up a multi-agent system. I built a single agent with a clear toolbox because I could observe it, debug it, and explain it to the team. We can split later if scaling demands it. It hasn’t.
- Tools, not freeform email. The agent doesn’t draft email bodies from scratch — it picks from a small set of templated reply strategies and fills slots. This collapses an entire class of brand-voice failures and makes outputs reviewable.
- The supervisor is rules, not another model. A small set of hard checks (don’t book outside business hours, don’t reply to free-email-domain ICP mismatches without flagging, etc.) sits between the agent’s decisions and any external action. Deterministic guardrails for the things you don’t want to think about twice.
- RAG for context, not for answers. A pgvector store of the team’s past sales conversations gives the agent recall (“we’ve seen this objection before, here’s how Sarah handled it”) — surfaced in the brief to the AE, not pushed into outbound copy.
- Full replay. Every run produces a trace. The team can scrub through any conversation, see the model’s reasoning, the tool calls, and the supervisor’s interventions. This was the single biggest factor in earning the team’s trust.
What a single run looks like
[14:02:11] ingest → form lead: M.Lin · acme.co · 850-employee SaaS
[14:02:13] enrich → role:Head of RevOps · matched ICP segment B
[14:02:14] crm.search → 0 existing records · 2 historical website touches
[14:02:18] agent → strategy:qualify-with-context · template:T7
[14:02:21] email.send → "Hi Marcus, before I grab time with Priya…" (3 Qs)
[14:11:46] (reply) → answers received · seat count = 1,200
[14:11:50] agent → strategy:book · proposed 3 slots
[14:12:05] supervisor → ok (within business hours, ICP confirmed)
[14:18:09] (reply) → "Thursday 2pm works"
[14:18:11] calendar → booked w/ Priya · invite sent
[14:18:12] slack → #inbound: brief posted (147 words)