Lab M24: build a research agent that searches more than once
You'll need: your venv and the anthropic plus python-dotenv from M4. The core lab needs no
API key and costs nothing (a small local corpus and a mock model). A live run at the end is optional.
Time: about 45 minutes. Work in your breakout pair.
Heads up: this fuses RAG (M7-M8) with the agent loop (M9). The new idea is small: make
searcha tool the agent can call more than once. The corpus is tiny and synthetic. Nothing here can harm your computer.
This lab has two parts: - Part A: watch plain RAG fall short on a multi-hop question. - Part B: run the agentic research agent that searches, reads, searches again, and cites sources.
flowchart LR
Q["question"] --> AG["research agent"]
AG -->|search 1| KB[("knowledge base")]
KB -->|D1: billing -> Payments| AG
AG -->|search 2: who leads Payments| KB
KB -->|D3: Payments led by Dana| AG
AG --> ANS["answer + citations [D1, D3]"]
Part A: where plain RAG stops
Step 1: Set up
Copy the solution/ files and starters/.env.example into
a folder. Activate your venv.
python -c "import anthropic, dotenv; print('deps ok')"
deps ok. (If not: pip install anthropic python-dotenv, the M4 libraries.)
Step 2: Look at the knowledge base
Open corpus.py. Note that answering "who leads the team that runs billing?"
needs two documents: D1 (billing is run by the Payments team) and D3 (Payments is led by Dana
Okafor).
You should now see: no single document answers the question. You must read D1 to know that the next thing to look for is the Payments lead. That is a multi-hop question.
Step 3: Run the demo and read the plain-RAG result
python demo_mock.py
PLAIN RAG:
sources: ['D1']
answer: The billing service is run by the Payments team. I could not find who leads it.
Part B: the agentic research agent
Step 4: Read the agentic result
In the same output, look at AGENTIC RAG:
searches: ['Who leads the team that runs the billing service?', 'who leads Payments']
sources: ['D1', 'D3']
answer: Dana Okafor leads the Payments team, which runs the billing service. [D1, D3]
agent.py and read agentic_rag: search is a tool in the loop,
and the agent calls it again after reading the first result.
You should now see: the second search only became possible because the agent read the first result. That is the whole point: retrieval the agent controls, not a single fixed step.
Step 5: See it decide NOT to search
In the same output, look at the last section:
==== AGENTIC RAG knows when NOT to search ====
searches: []
answer: Hello. Ask me about our teams and services.
Step 6: Prove the second hop yourself
The agent's second search works because, after reading D1, the query now contains "Payments". Check the retrieval directly:
python -c "import corpus; print([i for i,_,_ in corpus.search('who leads Payments')])"
D3. The original question alone does not retrieve D3 (try it:
corpus.search('who leads the team that runs the billing service') returns only D1). That gap is why
the agent has to search twice.
Step 7 (optional, costs a few tokens): run against the real model
Put your key in .env (copy .env.example), then:
cp .env.example .env # then edit .env and paste your key
python agent.py
PLAIN result missing the leader and the AGENTIC result naming Dana
Okafor with citations. A real model decides its own queries; the multi-hop behaviour is the same. Steps
1 to 6 need no key.
Step 8: Show it
Post in the chat the plain answer next to the agentic answer (with its two searches and citations). One picture of why a research agent beats one-shot retrieval.
If you get stuck
ModuleNotFoundError: anthropic->pip install anthropic python-dotenv(M4 libraries).demo_mock.pycannot find a module -> run it from inside the folder with the solution.pyfiles.- Agentic answer is missing the leader -> the second search did not retrieve D3; check
corpus.search('who leads Payments')returns D3 and read the loop inagent.py. ANTHROPIC_API_KEYerror in Step 7 -> your.envis not named exactly.env, or the key line is wrong. Seeapi-keys.md. Steps 1 to 6 need no key.