Build a Multi-Agent Research Team · 45 min · Python · Anthropic Python SDK · Serper API
Objective
The pattern: search, fetch, extract, cite
def web_researcher_node(sub_task: str, client) -> dict:
search_results = web_search(sub_task) # reuse Course 03's Serper wrapper pattern
articles = []
for result in search_results[:3]:
text = extract_article(result["link"]) # reuse Course 03's extractor
if not text.startswith("Error"):
articles.append({"url": result["link"], "text": text[:3000]})
else:
articles.append({"url": result["link"], "text": result["snippet"]}) # fallback
prompt = f"""Research sub-task: {sub_task}
Sources:
{format_sources(articles)}
Write findings with inline citations like [1], [2] referencing the sources above."""
response = client.messages.create(model="claude-haiku-4-5-20251001", max_tokens=1500,
messages=[{"role": "user", "content": prompt}])
return {"web_findings": response.content[0].text, "citations": [a["url"] for a in articles]}
Notice the fallback on line 7: when extract_article fails (Course 03 already taught you why this happens constantly -- paywalls, JS-rendering, blocking), this specialist doesn't skip the source or crash, it falls back to the search snippet, which is worse than full text but far better than losing the citation entirely. This is the exact "handle the realistic failure case" instinct from Course 03's preprocessors, reused here because the same real-world messiness applies to any web-facing tool.
Citations aren't optional polish -- they're what makes a claim checkable
"[1], [2]" inline markers referencing a real sources list is what turns "the model said X" into "source Y said X, verify it yourself if you want." This distinction is the entire reason a multi-agent research system with citations is more trustworthy than asking Claude the same question directly with no tools -- and it's also why the Synthesizer (Lesson 6) has real work to do deduplicating citations across 3 specialists who might cite the same source independently.
The technical analyst: same pattern, different focus
The Technical Analyst reuses the exact same search-fetch-cite mechanism, with a different prompt focus: code examples, implementation patterns, architectural tradeoffs, rather than general facts. This is deliberate — the specialists differ in WHAT they look for and how they synthesize it, not in the underlying tool mechanics.
Both the Web Researcher and Technical Analyst search for the same research sub-task and both find the same top article, but each generates its own citation entry for that URL. Whose job is it to notice this is a duplicate?
Specialists run in parallel (Lesson 5) specifically so they DON'T wait on each other -- which means neither can see the other's citations while running, and building cross-specialist awareness into each specialist would reintroduce the sequential dependency parallelism exists to avoid. Deduplication belongs downstream, in the one node (Synthesizer) that receives all 3 specialists' output at once and can actually compare them.
What you're building
specialist_agents.py: web_researcher_node and technical_analyst_node, both following the search → fetch (with snippet fallback) → structured findings-with-citations pattern, each tested in isolation against a real sub-task from Lesson 3's Planner output — not the full parallel graph yet, that's Lesson 5.
Unlock the full lesson
You've read the first 2 sections. The rest of this lesson covers The Problem, Build, Check Yourself, Key Terms & Next — plus a hands-on lab, quiz, and project artifact.
Create a free account — the opening phases of 29 of 35 courses are free, no credit card.