Day 3: Integrate the Agent Loop

Build a Production-Grade AI Support Agent for ShopStream (Python) ~6 min read 15 min hands-on

From a dispatcher to an agent

Day 1's dispatcher answers one message with one rule. Real support conversations don't look like that: "Where is my order?" … "It's ORD-1012." Or "I want a refund" … "ORD-1080, it came late." Answering them needs three things the dispatcher doesn't have: memory of the conversation, a model that decides what to do next, and a loop that lets the model call a tool, look at the result, and decide again. That loop is what people mean by "agent", and it is small enough to read in one sitting.

What you build

  • supportdesk/model.py — one interface, two models. decide(history, tools) returns either {"type": "tool", "name", "args"} or {"type": "reply", "text"}, plus estimated token usage.

  • OfflineModel is deterministic: it reuses Day 1's intent rules, remembers an order id mentioned earlier in the session, and remembers what it was asking about (a pending intent). It is the default and what the tests and verify.sh use, so your results match ours exactly.

  • ClaudeModel sends the same history and the tool schemas from Day 2 to the Anthropic Messages API with urllib when ANTHROPIC_API_KEY is set, and maps tool_use blocks back to the same decision shape.

  • supportdesk/agent.py — the loop, with sessions stored in a new messages table.

  • POST /agent — {"message", "session_id"} in; reply, steps, tool calls and session id out.

The loop itself:

Three details carry most of the weight:

  1. MAX_STEPS = 5. A model that keeps calling tools is the agent version of an infinite loop. Five is plenty for these tasks (a refund takes three: look up, refund, reply) and caps the worst-case cost of a confused model.

  2. Tool errors become data, not exceptions. run_tool turns a 400/422-style error into {"error": "..."} in the history. The model sees it and can explain it to the customer instead of the request crashing.

  3. State lives in SQLite, not in the process. Any server process can continue any session, which is what lets you run more than one later.

A refund is the clearest example of the loop. The model doesn't know the price, so it can't refund in one step:

The idempotency key is derived from the session and the order, so if the same conversation is retried it can't refund twice.

Read the full lesson free

This lesson is one of the first 3 in its course, free with an account — the full article, implementation guide and scripts. No card needed.

Already a member? Sign in