Teaching Your Research Agent to Cite Sources
Part 11's agent cites source URLs, but only as a plain-text instruction the model may or may not follow precisely. This part makes citation genuinely structural — tracking exactly which real search result backs each specific claim.
The real problem with plain-text citation instructions
"Cite the real source URL for every claim" is a real, honest
instruction, but nothing structurally VERIFIES the model actually
followed it correctly for every single sentence — a genuinely
common, real gap between instruction and guaranteed behaviorThis directly extends the same honest limitation the AI Projects series flagged for RAG citations — an instruction is a real, effective nudge, not a structural guarantee.
Real, structured source tracking during the research loop
def run_research_agent_with_tracked_sources(request: str) -> dict:
messages = [{"role": "user", "content": request}]
all_sources = [] # real, actual accumulated source list
for _ in range(8):
response = client.messages.create(
model="claude-sonnet-5", max_tokens=2048,
system=SYSTEM_PROMPT, tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return {"report": response.content[0].text, "sources": all_sources}
tool_call = next(b for b in response.content if b.type == "tool_use")
result = web_search(**tool_call.input)
all_sources.extend(result) # real, structural tracking, not
# relying on the model to report it
messages.append({
"role": "user",
"content": [{"type": "tool_result", "tool_use_id": tool_call.id, "content": str(result)}],
})This real, structural change — accumulating all_sources directly in your own code, rather than trusting the model's final text to accurately list every source it consulted — is genuinely more reliable, exactly the same principle from part 5: safety and correctness belong in code, not solely in a prompt instruction.
A real, second-pass claim verification step
def verify_claims(report: str, sources: list[dict]) -> str:
source_text = "\n".join(f"{s['url']}: {s['snippet']}" for s in sources)
response = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
system=(
f"Review this research report against the real sources "
f"actually consulted. Flag any specific claim NOT "
f"genuinely supported by these sources:\n\n{source_text}"
),
messages=[{"role": "user", "content": report}],
)
return response.content[0].textThis real, second, independent LLM call directly applies the LLM & Advanced AI series' own LLM-as-judge evaluation technique — a genuine, structural check flagging any claim in the final report that isn't actually traceable back to a real, consulted source, catching exactly the kind of subtle drift a plain instruction alone can't guarantee against.
This two-pass approach — generate, then independently verify — is a real, meaningfully more rigorous pattern than a single-pass instruction, directly worth the added real cost for Bright Leaf Coffee's competitive research specifically, since the marketing team may make real, actual budget decisions based on this report's claims.
Presenting real, structured citations to the marketing team
def format_report_with_footnotes(report: str, sources: list[dict]) -> str:
footnotes = "\n".join(f"[{i+1}] {s['url']}" for i, s in enumerate(sources))
return f"{report}\n\n---\nSources:\n{footnotes}"A real, genuine footnote-style presentation — every source consulted, listed explicitly and numbered — gives the marketing team a real, direct way to independently verify any specific claim before acting on it, the same honest transparency principle running throughout this site's entire AI curriculum.
A real, honest cost trade-off
Part 11's version: one real research pass, trusting the citation
instruction
This part's version: research pass + a real, second verification
pass — genuinely more reliable, at real, additional API costFor a genuinely high-stakes real report informing actual marketing spend decisions, this added real cost and rigor is a reasonable, deliberate trade-off — directly the same calibrated judgment the LLM & Advanced AI series applied to re-ranking: reach for added rigor when the real stakes genuinely justify it.
Next: building a real AI coding assistant — applying this series' agent patterns to a genuinely technical, code-focused real task.