Today the bot is a ReAct agent: one big loop where the model decides each step. The refactor makes
it an explicit StateGraph β a router that sends each message down a fixed pipeline. Both diagrams
below are drawn from really-compiled graphs. Click any node to see what it does.
Now Β· ReAct agent
One loop, the LLM drives
The live bot (create_react_agent). Every turn the model reads the whole chat, decides
whether to call a tool or reply, and loops β capped at 3 tool calls. There's no fixed path; the
behavior lives in a ~300-line system prompt.
π Tap any step to see what it does β and what it did on this run.
β send_last_draft is just another tool in the loop β the model can fire a
send on any turn; only a prompt rule says "ask first." That's the safety gap the refactor closes
structurally.
The tool belt β ~25 tools the model can pick from
The guardrails that keep the loop honest β all prompt rules
Never claim an untaken action
It can't say "sent" unless it actually called send_last_draft this turn.
Follow through on "yes / send it"
A confirmation triggers the send immediately β no "which email?".
Resolve the active venue
Pronouns ("the wifi", "send it") resolve to the venue from recent turns.
Never fabricate data
Every fact must come from a tool result β no invented contacts or times.
Don't loop
β€3 tool calls per turn; never the same tool twice with the same args.
Refactor Β· one graph, three business functions
Docs in Β· Email Β· Questions β around the sheet
Everything TM GO does is three jobs, all revolving around the Google DB (system of record):
π Docs in (upload β parse β βΈreview β write), β Email (assess what's missing β draft β
βΈapprove β send; replies come back in and update the DB), β Questions (read β answer) β plus a
tool loop for everything else. Two human gates: review-before-write and approve-before-send.
The amber trail below is a real "advance a show" run.
π Buttons = deterministic β a button knows its intent and enters at its node,
skipping the router. π¬ Chat = free-form β the router classifies it. Same graph either way.
read Β· answers nowwrite Β· stage β approve β commithuman gate βΈGoogle DBβ¬ amber trail = the path this run took
π Click any step to see what it does.
Why this shape?
One shared core, two doors: chat runs the graph,
the app's buttons call the same nodes. The LLM only drafts & parses; the approval gate is a real
interrupt(), not a prompt rule; and send is pulled out of the tool loop so the
cycle can read / draft / log but never send.
The safety win
The approval gate is structural, not a prompt rule
Advancing spans draft β send. Today the ReAct bot can fire the send on an LLM "send" classification.
In the StateGraph, send has one inbound edge β from an interrupt() a human must resume.
Draft = LLM Β· Approve = human Β· Send = code. No router or LLM path can reach send.