You ship: A ReAct investigation agent built from scratch, which shows its reasoning as it works. Builds on:Day 7.
What you'll build today
After Day 10, WorkBot's chat answers questions, calls a tool or two within a turn and remembers the person it talks to. What it cannot do yet is take a question from a ticket and work on it alone. That means searching, reading what it finds and deciding what to look at next, until it can answer with a source for every fact.
Today you build that from scratch, without a framework. You write an agent loop, which asks the model what to do, runs the tools it picks, hands back the results and asks again. You learn the ReAct pattern, in which the model states its reasoning before each action, and you learn why every loop needs limits on steps, cost and time.
On top of the loop you build WorkBot's first agent, an investigation agent. It searches the company's policies, saves each finding with its source and ends with a report. By the end of the day you can run it from the terminal and watch each step as it happens.
By the end of the day you can
Understand what an agent is, and how it differs from a chat reply or a single tool call
Implement the ReAct pattern from scratch in TypeScript
Build an agent that plans, uses tools and recovers from errors
Topics
What an AI Agent Is
Agent versus chatbot: an agent pursues a goal, reasons over several steps and acts on its own
Agent versus tool use: tool use is one call, while an agent loops until the task is complete
The agent loop: observe, think, act, observe the result, and repeat until done
When to use an agent: tasks that take several steps, gather information or make decisions
When not to use one: simple questions and answers, classification, and one-step operations
Agent Patterns
ReAct (Reason + Act): the agent interleaves reasoning ("I need to find X") with actions (call the search tool). It is the most common pattern.
Plan-then-execute: the agent writes a full plan first, then runs each step. It suits well-defined tasks.
Reflexion: the agent scores its own output and retries when the quality is low. It suits writing and analysis.
Tool-using agents: the simplest agent keeps calling tools until it is done. It is Day 7's loop, made formal.
Tool registry: a map from each tool's name to its handler function, with its schema
State: keep the full message history and the intermediate results
Stopping conditions: the task is complete (the model stops calling tools), the iteration limit, a cost limit and an error threshold
Error handling: decide whether to retry a failed tool, skip it or ask the user. Plan for model refusals and infinite loops too.
Streaming the agent's thoughts: show the user what the agent is thinking and doing as it happens
Agent Design Principles
Give an agent focused, specific tools, not "do anything" tools
Write a system prompt that defines the agent's role, its constraints and when it stops
Always set an iteration limit and a cost guard
Log every thought, tool call and result, so you can debug a run
Build
Features shipped today
WorkBot: A ticket investigation agent that searches the knowledge base and trusted external sources and writes a sourced summary
Build a ReAct investigation agent from scratch, with no framework:
System prompt: the agent investigates questions for Fieldstone Supply's operations and support teams. It searches the knowledge base first and uses external sources only for what the policies do not cover. It saves each fact with its source and ends with one report.
Tools: search_knowledge_base, read_policy, web_search and read_url (when a Brave search key is set), save_finding (stores a key finding with its source) and write_report (ends the run with the report)
Agent loop: the agent decides what to search, reads policies and pages, saves findings and compiles a report
Streaming: show the agent's reasoning ("I'll search for X to find..."), its tool calls and their results as they happen
Guards: at most 15 steps, a cost limit and a time limit
Test: give it a question from a ticket and check that it produces a sourced report
Walkthrough
Part 1
The agent loop
Everything else today runs on one loop, so you build it first and test it against a model you control. An agent is that loop. It asks the model, runs the tools the model calls and hands back the results. Then it asks again, until the model answers in text or calls the tool that ends the task. Day 7's tool loop runs inside one chat turn, while this loop runs a whole task on its own, with explicit limits. A task with a fixed sequence of steps is a chain, like the Day 7 research chain. Keep agents for tasks where the next step depends on what the last one found.
Study first
Read the AI SDK's reference for generateText and see what happens when you declare tools without execute. The call then returns the model's toolCalls instead of running them, along with the response's messages to append. Because nothing has run the tools, your loop runs them and builds the tool message with the results itself.
Read the ReAct paper, whose name is short for reason and act. In this pattern the model alternates reasoning and actions, so it writes a thought, makes a tool call, reads the observation and repeats.
Build
Write runAgent({ model, system, task, tools, done, limits, on }) in src/lib/agent.ts, where done names the tool that ends the task and on holds the event callbacks. The loop makes up to limits.steps model calls, 15 by default. In each step it appends the response messages and runs every tool call through the tools you passed in. Then it appends one tool message with the results, with failures marked as errors. It returns when the model answers in text or when the done tool runs, and it reports why the run ended in stopped, as answered, steps, dollars or time.
Emit 2 events, so that whoever calls the loop can show the run as it happens. thought(text) carries the model's words between tool calls, and tool(call) carries each call with its input, its result and whether it failed.
Write tests with a scripted model, which is a mock model that answers from a script, built with the AI SDK's testing helpers. Cover a round trip through a tool and then the done tool. Cover a failing tool too, which comes back to the model as an error while the loop goes on.
Done when
The tests show that the tool message the model receives holds the result of the tool call before it.
A failing tool reaches the model as an error, and the loop keeps going.
Technical notes
A tool that throws becomes a tool result with an error-text output, which the model can read and recover from.
An invalid call is an error result too, never an exception. A call is invalid when it names a tool the loop was not given, or when the schema rejects its input.
A model that answers in text without calling the done tool also ends the run, and the investigation agent maps both endings to the same result shape.
Part 2
Limits on steps, cost and time
A loop that chooses its own next step can also keep going long after it should stop, or spend more than the task is worth. So every run gets 3 limits, and each one ends the run with what it has so far.
Study first
Read the reference pages for AbortSignal.timeout() and Promise.race(). A signal lets the loop cancel a model call that runs past the deadline, and a race lets it stop waiting for a slow tool.
Build
Count cost with priceOf(modelId), a new function that looks up a model's price in the Day 6 provider list. The loop multiplies that price by the usage of each call and stops once the total reaches the dollar limit.
Count time from a deadline set when the run starts. Pass the deadline to each model call as an abortSignal, and race each tool call against it, so that neither can outlive the limit.
Test that the step, dollar and time limits each end the run with what it has. Test the time limit twice, once with a slow model call and once with a slow tool.
Done when
pnpm test covers the 4 ways a run ends, which are answered, steps, dollars and time.
Technical notes
The loop enforces the time limit in 3 places. It checks the clock before each call, aborts the model call through the signal, and races each tool call against the remaining time.
Part 3
The investigation agent
With the loop in place, an agent comes down to a system prompt, a list of tools and the tool that ends the task. The first one investigates a ticket's question, so its tools search and read the knowledge base, which holds the 8 policies from Day 4.
Study first
Decide which tools the task needs, and in which order the prompt should ask for them. The agent searches the knowledge base first and uses external sources only for what the knowledge base does not cover. It saves a finding for each claim it uses, and it writes one report at the end.
Build
Add searchPolicies(question) to src/lib/policies.ts, which returns the policies that mention the question's words along with the lines that matched. Each matching line counts once and each question word in the title counts 3 times, and the policy with the highest count comes first. Plain word matching is enough, because the corpus is only 8 documents.
Write src/lib/investigate.ts with the agent's tools. search_knowledge_base and read_policy work over the policies, and web_search and read_url from the Day 7 tool registry join them when BRAVE_SEARCH_KEY is set. save_finding keeps a finding's text and source in a list, and write_report ends the task, so you pass it to the loop as done.
Write a system prompt that names the order, asks for one sentence before each tool call and forbids guesses.
Handle a run that stops before the report by returning "The investigation stopped (steps) before a report was written", with the reason in parentheses, followed by the findings so far. A partial run is then still useful.
Write tests with a scripted model, one for the full path and one for a run that is cut off early.
Done when
pnpm test passes for the full path, and the cut-off run returns the findings it saved before it stopped.
Technical notes
Build the agent's tools inside investigate(), because save_finding closes over the run's own list of findings. The registry's tools are shared between runs, and these are not.
read_policy throws for an unknown slug, and the loop turns that into an error result. The test shows the run going on after that error.
WorkBot offers web search and page reading only when a search key is set, as the chat does. Without them, the prompt's sentence about external sources has no effect.
Part 4
Run the agent from the terminal
A scripted model proves the wiring, but only a real model shows whether the prompt and the tools work together. So you connect the agent to the terminal chat and to the live test suite, both from Day 6.
Study first
Look again at the terminal chat (pnpm chat) and its /cost command, and at the live suite, pnpm test:live, which calls the real API on the small model.
Build
Add /investigate <question> to the terminal chat. Show thoughts and tool calls as they happen, and count the run's tokens in /cost.
Add a live test on claude-haiku-5-5, the small model, that runs with pnpm test:live. It asks whether a support agent, meaning a person on the support team, can approve a $250 refund alone. It expects a report that cites the $200 per-order limit from Day 4's refunds policy, with the limits set to 10 steps, $0.10 and 150 seconds.
Done when
/investigate Can a support agent approve a $250 refund alone? searches, reads the refunds policy, saves findings and writes a report with sources.
pnpm test:live passes.
The agent on the small model writes a sentence before each tool call, searches the knowledge base and reads a policy. Then it saves findings with their source and writes the report.Technical notes
The limits default to 15 steps, $0.50 and 120 seconds. The live test allows fewer steps and dollars, 10 and $0.10, and more time, 150 seconds.
The terminal shows a tool's input cut to 160 characters, so a report passed to write_report does not appear twice.
Day 12 adds planning and review as separate agents, run by an orchestrator.