Agents and Tool Use — Building Autonomous AI Systems
LLMs that plan, use tools, and execute multi-step tasks autonomously. ReAct, tool calling, memory, and the architecture patterns behind production AI agents.
Module 54 built a toy ReAct agent with text parsing. This module builds production agents — structured tool calling, persistent memory, failure recovery, and the architectural patterns top tech teams actually ship.
The gap between a demo agent and a production agent is enormous. A demo agent works when everything goes right. A production agent handles tool failures gracefully, detects when it is stuck in a loop, maintains context across sessions, asks for clarification instead of hallucinating, and refuses irreversible actions without confirmation. These are not edge cases — they are the majority of real interactions.
Stripe's internal dispute resolution agent handles merchant queries that span 8–12 tool calls: look up transaction, check dispute status, retrieve relevant policy, draft response, validate response, send email, update CRM, close ticket. Any step can fail. Any step can return unexpected data. The agent must handle all of this without a human in the loop on every call. Getting this right is an engineering problem as much as an ML problem.
A junior employee (chatbot) answers questions. A senior employee (basic agent) uses tools when needed. A reliable professional (production agent) uses tools correctly, handles failures without panicking, escalates when genuinely stuck, keeps records of what they did and why, and never sends an important email without double-checking the draft. The gap between junior and reliable professional is not knowledge — it is judgment, error handling, and knowing when to stop and ask.
Production agents are not smarter LLMs. They are better-engineered systems around the same LLMs. The reliability comes from the scaffolding — structured tool schemas, retry logic, loop detection, confirmation gates, and comprehensive logging.
Structured tool calling — JSON schemas, not text parsing
Module 54 parsed tool calls by extracting text between "Action:" and "(" with regex. This breaks constantly — the LLM formats output slightly differently each run, adds punctuation, or skips the format entirely. Structured tool calling solves this: you define tools as JSON schemas, the API enforces that the LLM returns a structured tool_call object, and you execute the corresponding function with validated arguments. No regex. No parsing.
Production agent — loop detection, failure recovery, confirmation gates
Three memory types — conversation, episodic, and semantic
A stateless agent forgets every conversation the moment it ends. Production agents need three types of memory working together. Conversation memory is the message history within the current session — the agent knows what was said earlier in this conversation. Episodic memory stores summaries of past sessions — the agent knows this merchant called last week about the same issue. Semantic memory is the knowledge base (RAG from Module 67) — the agent knows Stripe's policies and documentation.
Task decomposition and planning — breaking multi-step work into reliable steps
Simple queries need one tool call. Complex tasks — "process this batch of 50 dispute emails and resolve what you can, escalate the rest" — need a plan. Planning separates the reasoning about what to do from the execution of doing it. A planner LLM call generates the sequence of steps. Each step is then executed independently with its own error handling. This separation makes complex tasks more reliable because each step can be retried or skipped without re-planning the entire task.
Observability, rate limiting, and graceful degradation
Every common production agent mistake — explained and fixed
Graduated autonomy — how production teams decide what an agent is allowed to do on its own
No serious engineering team ships an agent with full autonomy on day one. The universal rollout pattern is a ladder: an agent earns more autonomy as its track record on real traffic accumulates, and every rung on the ladder is tied to how reversible the action is and how expensive a mistake would be — not to how capable the underlying model seems in a demo.
Agent runs on real traffic but every action is logged, never executed. Engineers compare what it would have done against what a human actually did. Runs for weeks before anyone trusts the numbers.
Tools that only fetch data (get_transaction, search_knowledge_base) execute automatically. Nothing the agent does can change any system of record, so mistakes cost a wrong answer, not a wrong action.
Low-risk writes (create_support_ticket) execute automatically. Anything irreversible (initiate_refund, send an email to a customer) still stops the loop and requires an explicit human confirmation before it runs.
Irreversible actions execute without a human in the loop, but only within a hard budget: a capped dollar amount per action, a capped number of actions per session, and a capped blast radius (for example, refunds under $50 only).
Stripe's dispute agent, Intercom's Fin, and GitHub's Copilot Workspace all follow some version of this ladder publicly. The reason is not caution for its own sake — it is that agent failures are usually not dramatic, they are quiet and cumulative. A support agent that occasionally sends a slightly-too-generous refund does not trigger an outage page; it shows up three weeks later as a line item in a finance review. The ladder exists to catch that kind of failure while it is still cheap.
Five things people get wrong about agents and tool use
Mechanically, an agent is the same structured tool-calling API this module opened with, called repeatedly inside a loop that feeds each tool's result back in as context for the next decision. Nothing about the underlying model changes between 'a chatbot that calls one function' and 'an agent that resolves an eight-step dispute.' What actually distinguishes a production agent is the engineering wrapped around that loop — state tracking across turns, loop detection, retry logic, confirmation gates, and budget enforcement. Calling it a different kind of AI obscures the fact that the reliability work is ordinary software engineering, not a smarter model.
Autonomy and reliability trade against each other, they do not both increase together. An agent that can act without any confirmation gate resolves simple cases faster, but it also executes its mistakes just as fast — an incorrect refund amount or a wrongly closed ticket happens instantly instead of being caught by a human glancing at a proposed action first. The right amount of autonomy is not the maximum available; it is whatever amount matches how reversible the action is and how expensive a mistake would be. A ticket creation and a wire transfer do not deserve the same autonomy level even if the same model is choosing both.
The ReAct-style thought text is still next-token generation, conditioned to look like reasoning because that format was reinforced during training — it is not a guarantee the underlying decision process is logically sound, only that the output resembles a chain of reasoning. A model can produce a perfectly coherent-sounding 'Thought:' that leads to calling the wrong tool, and it can arrive at a correct action with a thought paragraph that does not actually justify it. The reliability of a production agent comes from validating outcomes and gating risky actions in code, not from trusting that fluent intermediate reasoning implies a correct final decision.
Identical-call loop detection, shown earlier in this module, only catches the exact same (tool, arguments) pair repeating — it does nothing about a semantic loop, where the agent cycles through slightly different but equally unproductive calls (searching the knowledge base with five different phrasings of the same question, for instance). A hard max_calls ceiling stops the runaway cost eventually, but by then the session has already burned through its full budget without making progress. Real loop protection needs a second signal beyond exact-match hashing: is the agent's state actually changing between calls, or is it just varying its inputs while making zero forward progress on the task.
Tool selection accuracy degrades as the number of available tools grows, especially when several tools have overlapping or ambiguous descriptions — the model has to pick correctly from a longer, more confusing menu on every single turn, and each wrong pick either wastes a call or produces a wrong answer. A twenty-tool agent is not automatically more capable than a six-tool agent covering the same use case; it is often less reliable, because the LLM's tool-selection step is itself a classification problem that gets harder as the number of plausible-looking options increases. Curating a small, clearly-distinguished tool set usually beats exposing everything the backend can technically do.
Agents and tool use — 5 questions interviewers actually ask
ReAct interleaves reasoning and acting in a loop: the model generates a short thought about what it needs to find out, takes an action (a tool call), observes the result, and repeats until it has enough information to answer. A single function-calling turn is just one iteration of that loop — the model decides once whether to call a tool, gets a result, and answers. ReAct is what turns that one-shot capability into something that can handle a multi-step task: look something up, decide the next step based on what it learned, look up something else, and only answer once the accumulated observations actually support a conclusion.
Several layers, enforced in code rather than left to the model's judgment: a hard cap on tool calls per session so a stuck agent cannot loop indefinitely, a classification of every tool as reversible or irreversible with irreversible actions gated behind explicit human confirmation, a per-action and per-session dollar or resource budget for anything that spends money or changes state, and rate limiting on both the LLM calls and the downstream tools it invokes. None of these live inside the prompt — a prompt instruction like 'do not call this more than 3 times' is a suggestion the model can ignore under the wrong conditions; the budget has to be enforced by the code executing the tool calls.
Start with an offline evaluation set built from real production-style queries, not synthetic ones, scored on task success rate, not just whether it produced an answer. Track secondary signals: how many turns it took to resolve, how often it escalated to a human ticket instead of resolving, and how often loop detection or a confirmation gate fired. Before full rollout, run it in shadow mode against live traffic so its proposed actions can be compared to what a human actually did, without it executing anything. Once live, sample a percentage of real sessions for human review every week, and treat any case where a confirmation gate blocked a mistaken irreversible action as a near-miss worth analysing, not just a system working as intended.
It needs to stop the agent loop entirely before the tool executes, surface the exact parameters of the planned action to the user in plain language — the transaction, the amount, the reason — and require an explicit confirming response in the next human turn, not an inference from the LLM about whether the user seemed to agree. Critically, the enforcement has to live in the code path that executes the tool, checking a boolean that was only set by a genuine human turn, not inside the prompt asking the model to 'confirm before acting' — a model can be talked out of that instruction by an adversarial or just oddly-phrased user message, but a code-level gate cannot be talked out of anything.
This is only answerable if every tool call was logged with its inputs, its result, the reasoning turn that preceded it, and a session identifier — which is why comprehensive logging is treated as a first-class production requirement, not optional observability. I would pull the full message history and tool call log for that session, reconstruct the sequence of decisions, and check whether any gate (loop detection, confirmation, budget) fired along the way. If the logs show the action was reasonable given what the agent knew at the time, the fix is usually a tool description or system prompt change; if the logs show a gate should have caught it and did not, that is a code bug in the scaffolding, not a model problem.
The Generative AI section is complete. Section 11 — MLOps and Production — begins next.
You have now covered the full generative AI landscape across 9 modules: what generative AI is, GANs, VAEs, diffusion models, LLM pretraining and RLHF, LLM fine-tuning, multimodal models, advanced RAG, and production agents. Each module built on the last. Section 11 shifts from building models to shipping them — ML pipelines, experiment tracking, model deployment, monitoring, and the full MLOps lifecycle that keeps production models healthy over time.
Feature pipelines, training pipelines, inference pipelines. Feast and Tecton for feature stores. Airflow, Kubeflow, and Prefect for orchestration.
🎯 Key Takeaways
- ✓Production agents differ from demo agents in error handling, not capability. The gap is: loop detection (hash every tool call, break on repetition), confirmation gates (irreversible actions must pause for explicit human approval), retry logic with backoff, hard max_calls enforcement, and comprehensive logging of every decision for debugging.
- ✓Structured tool calling via JSON schemas eliminates text parsing failures. Define tools as OpenAI-compatible function schemas — the API returns structured tool_call objects with validated arguments. Never parse tool calls from LLM text output. The tool description must be precise: what the tool does, what arguments it needs, and whether it is irreversible.
- ✓Three memory types work together: conversation buffer (deque of recent messages, compressed to summary when full), episodic memory (per-user summaries of past sessions stored in Redis/Postgres, injected into system prompt), semantic memory (RAG knowledge base, retrieved per query). Each addresses a different temporal scale of context.
- ✓Task planning separates reasoning from execution. A planner LLM call generates a dependency graph of steps. Each step executes independently with its own retry logic. Failed steps mark dependent steps as skipped. This structure makes complex multi-step tasks debuggable — you can see exactly which step failed and why, and retry it without re-planning.
- ✓Four production infrastructure requirements: logging (every tool call is an audit trail with inputs, outputs, and latency), rate limiting (prevent runaway costs from looping agents), caching (identical tool calls within a session hit the database once), and metrics (success rate, tool failure rate, loop detection rate, latency percentiles).
- ✓Latency management: use a fast model for tool selection (Groq LLaMA-3: 300ms), reserve slower models for final generation only. Cache tool results across turns. Stream final answers token by token. Show progress indicators during multi-step execution. Target first visible output under 2 seconds even when full resolution takes 10+ seconds.
Discussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.