Claude Cookbooks 研究型 Agent 架构解析:基于 Claude Agent SDK 的 WebSearch 与 Read 自主研究循环
本篇文章围绕 claude-cookbooks 仓库中 research_agent/architecture_diagram.md 记录的架构与通信流程展开,结合同一目录下的 agent.py 实现与配套教程 notebook,逐层拆解一个基于 Claude Agent SDK 构建的自主研究型 Agent:它如何通过 WebSearch 与 Read 两个内置工具,在"思考—调用工具—获得结果"的循环中自主完成信息搜集与多模态分析。读完本文,你将掌握该 Agent 的组件划分、运行时消息流向、关键配置参数的含义,以及如何把示例原型演进为可复用的生产模块。
为什么研究型 Agent 需要一张架构图
研究型任务是典型的"答案不在输入里"的 Agent 场景:用户只抛出一个问题,真正的答案需要 Agent 主动与外部系统(搜索引擎、文档、图表、PDF)交互才能获得。更重要的是,搜索路径无法预先编排——是先查公司财报还是先看监管文件,取决于调查过程中不断涌现的新信息。这与固定步骤的工作流自动化有本质区别。
正因如此,在 claude-cookbooks 中,research_agent 目录专门用一份 architecture_diagram.md 来固化这套系统的两大视图:静态组件架构(系统由哪些角色和工具组成)与动态通信流程(一次问答如何在各组件间流转)。理解这两张图,就等于拿到了阅读 agent.py 源码与 00_The_one_liner_research_agent.ipynb 教程的导航图。
一、组件架构:User → Agent → Tools 的三层结构
architecture_diagram.md 用 mermaid 的 graph TD 记录了系统的顶层结构,原文如下:
graph TD
User[User] --> Agent[Research Agent]
Agent --> Tools[Tools]
Tools --> WebSearch[WebSearch]
Tools --> Read[Read Files/Images]
style Agent fill:#f9f,stroke:#333,stroke-width:3px
style Tools fill:#bbf,stroke:#333,stroke-width:2px
这张图划分出三层角色,可以在源码中找到一一对应:
| 图中节点 | 架构职责 | 仓库中的对应实现 |
|---|---|---|
User |
研究需求的发起者,提出 Query、接收最终 Answer | Notebook 中的调用方,如教程中的 send_query("What is the Claude Code SDK? ...") |
Agent(Research Agent) |
持有系统提示词与上下文,负责规划、决策与综合 | agent.py 中由 ClaudeSDKClient 驱动的研究 Agent |
Tools |
Agent 与外部世界交互的接口层,按需调度下方具体工具 | agent.py 中的 allowed_tools=["WebSearch", "Read"] |
WebSearch |
联网搜索,负责信息采集 | Claude Agent SDK 内置 WebSearch 工具 |
Read(Read Files/Images) |
读取本地文件与图片等非文本内容,负责多模态分析 | 教程中用它分析 projects_claude.png 图表 |
图中用加粗描边高亮 Agent 与 Tools,意在强调:这是整套系统中唯一真正参与"运行时决策"的两部分,工具层把模型能力与外部信息源解耦——Agent 只负责"想清楚下一步做什么",工具负责"把事做成"。
Agent 组件的源码化理解
图中的 Research Agent 节点,在源码里体现为一次完整的 ClaudeSDKClient 会话。从 agent.py 可以看到其核心配置:
options = ClaudeAgentOptions(
model=model,
allowed_tools=["WebSearch", "Read"],
continue_conversation=continue_conversation,
system_prompt=RESEARCH_SYSTEM_PROMPT,
max_buffer_size=10 * 1024 * 1024, # 10MB buffer for handling images and large responses
)
async with ClaudeSDKClient(options=options) as agent:
await agent.query(prompt=prompt)
async for msg in agent.receive_response():
# ...逐条消费消息流
对照 architecture_diagram.md 中 Agent --> Tools 这条边,它落在代码里就是 allowed_tools 这个白名单——它划定了图中 Tools 节点实际包含哪些具体能力,也决定了 Agent 的自主程度边界。
模型与提示词:塑造 Agent 的专业行为
agent.py 默认选用 claude-opus-4-6 作为研究主力模型,同时通过系统提示词注入"研究纪律":
RESEARCH_SYSTEM_PROMPT = """You are a research agent specialized in AI.
When providing research findings:
- Always include source URLs as citations
- Format citations as markdown links: Source Title
- Group sources in a "Sources:" section at the end of your response"""
这一段提示词在架构图中的位置是"Agent 节点内部的行为约束":它不改变组件连接关系,但决定了 Agent 输出质量——强制附带来源 URL、统一引用为 markdown 链接、在回答末尾汇总 Sources: 区块,保证研究结果可溯源、可验证。
二、通信流程:Think → Search/Read → Results 的循环直到完成
architecture_diagram.md 的第二张图用 sequenceDiagram 刻画了一次问答的动态过程:
sequenceDiagram
participant User
participant Agent
participant Tools
User->>Agent: Query
loop Until Complete
Agent->>Agent: Think
Agent->>Tools: Search/Read
Tools-->>Agent: Results
end
Agent-->>User: Answer
这是理解研究 Agent 运行机制最关键的一张图,它揭示了三个要点:
- Research 是循环而非单次调用:
loop Until Complete意味着 Agent 不是"一次搜索、一次回答"的线性流水线,而是自主决定"何时搜索、搜索什么、是否已有足够信息收尾"。 - 决策完全在 Agent 内部发生(
Agent->>Agent: Think):模型依据已有上下文判断下一步动作,工作流不在代码中写死。 - User 只在头尾出现:用户发起 Query、接收最终 Answer,中间的工具调用与思考环节对用户不可见(或通过活动流可视化展示)。
运行时如何观察到这条消息流
在 Claude Agent SDK 中,这条时序图对应的是 receive_response() 产生的消息流。教程 00_The_one_liner_research_agent.ipynb 里给出了一次真实运行的观察结果:
🤖 Using: WebSearch()
🤖 Using: WebSearch()
🤖 Using: WebSearch()
✓ Tool completed
✓ Tool completed
✓ Tool completed
🤖 Thinking...
User -> Agent: Query 对应调用 agent.query(prompt=...);Agent->>Tools 对应流中携带工具调用块的 Assistant 消息;Tools-->>Agent: Results 对应携带 tool_result 的 User 消息;Agent->>Agent: Think 对应没有工具调用的纯思考消息。loop Until Complete 的终止条件对应流末尾的 ResultMessage(含 result 字段与回合数、耗时、成本等统计信息)。
循环的可视化基础设施
为了让"循环中的每一步"可被人类感知,research_agent 复用了 utils/agent_visualizer.py 提供的一组可视化 API:
print_activity(msg):实时打印每个消息块对应的活动,如🤖 Using: WebSearch()、✓ Tool completed,按消息类型(Assistant/User)分流处理;visualize_conversation(messages):把完整消息列表渲染成对话时间线——Jupyter 环境输出带分类配色的 HTML,终端环境自动回退为纯文本表格字符风格(box-drawing);display_agent_response(messages):把最终回答渲染成样式化的 HTML 卡片,末尾的统计栏会给出 Turns、Tokens、Cost、Duration;reset_activity_context():每个新会话开始前清理全局活动上下文。
其中 visualize_conversation 在 Jupyter 与终端间的自动切换,通过探测 ZMQInteractiveShell 判断是否运行于 Notebook 环境。终端回退视图还会针对 SystemMessage(回显 session_id)、AssistantMessage(区分文本块与工具调用块)、ResultMessage(聚合回合数、token、成本、耗时)分别排版。
三、两个核心工具的能力边界与使用规则
architecture_diagram.md 在 Tools 节点下挂出 WebSearch 与 Read 两个工具,这是本架构中 Agent 能力的全部来源。
WebSearch:自主信息采集
WebSearch 让 Agent 无需预先批准即可联网搜索。它适合图论中描述的那种"信息不自洽"的任务:问题本身不含答案,必须经由外部搜索引擎补全。在首次演示中,Agent 面对"研究 AI agents 的最新趋势"这类开放问题时,会自主发起多次搜索(run 记录里连续出现三次 WebSearch),覆盖不同关键词角度后再综合成带引用的摘要。
Read:文件与图像的多模态分析
Read 把研究能力从纯文本扩展到图表、文档等视觉内容。Notebook 中演示了完整链路:Agent 先用 Glob 定位图表文件 projects_claude.png,再用 Read 读取图片完成"哑铃图"解读——逐条提取个人项目、创业工作、企业工作、教育学习等类别在 Claude.ai 与 Claude Code 两个平台上的占比差异,再自动发起多轮 WebSearch 验证这些数据点与行业报道、Anthropic 官方报告的一致性,最后输出带 Sources 区块的验证结论。这恰好是时序图中 loop Until Complete 的完整复现:Read 结果又触发新的搜索,新的搜索又驱动更深层的综合。
工具权限模型:allowed_tools 决定自主程度
从 Notebook 中对权限模型的解释可以看出,allowed_tools=["WebSearch", "Read"] 的语义并非简单的"能用哪些工具":
allowed_tools中的工具——Agent 可自由使用、无需请求批准,这是自主研究的前提;- 其他可用工具——默认需要批准才能调用;
- 只读类工具(如
Read)——默认放行; disallowed_tools——彻底移出模型上下文,连"提出使用请求"的机会都没有。
配置越窄、Agent 越受约束;配置越宽、Agent 越自主。研究型场景正是利用这一机制让模型在"搜索—思考—再搜索"的开放循环中自行决策。
四、从无状态到有状态:架构图中的会话演进
architecture_diagram.md 描述的组件关系在不同会话模式下都成立,但 research_agent 的实际用法覆盖了两种会话语义,值得区分:
无状态 query():一次性研究
教程中的"一行研究 Agent"使用 query():
from claude_agent_sdk import ClaudeAgentOptions, query
messages = []
async for msg in query(
prompt="Research the latest trends in AI agents and give me a brief summary and relevant citations links.",
options=ClaudeAgentOptions(model=MODEL, allowed_tools=["WebSearch"]),
):
print_activity(msg)
messages.append(msg)
query() 每次调用相互独立、无会话记忆。适合单发研究问题、并行处理互不相关的任务、需要全新上下文的场景。它的局限在于无法承接"先查 A,再基于 A 查 B"的递进式调查。
有状态 ClaudeSDKClient:多轮上下文继承
演进后的研究 Agent 改用 ClaudeSDKClient,通过 context manager 维护跨查询的会话状态:
from claude_agent_sdk import ClaudeSDKClient
messages = []
async with ClaudeSDKClient(
options=ClaudeAgentOptions(
model=MODEL,
cwd="research_agent",
system_prompt=RESEARCH_SYSTEM_PROMPT,
allowed_tools=["WebSearch", "Read"],
max_buffer_size=10 * 1024 * 1024, # Increase to 10MB for image handling
)
) as research_agent:
# 第一次查询:用 Read 分析图表
await research_agent.query("Analyze the chart in research_agent/projects_claude.png")
async for msg in research_agent.receive_response():
print_activity(msg)
messages.append(msg)
# 第二次查询:用 WebSearch 验证图表结论
await research_agent.query(
"Based on the chart analysis, search for recent news or data that validates or provides context for these findings. Include source URLs."
)
async for msg in research_agent.receive_response():
print_activity(msg)
messages.append(msg)
这里的关键是架构图中的 Agent 节点现在具备了"记忆":第二次查询能继承第一次读图得到的图表结论,从而提出真正有依据的验证请求——这正是 stateless 查询做不到的。第二次查询的运行记录里可以看到它在没有搜索指令的前提下自动发起四条 WebSearch,印证了"研究路径在探索中涌现"的设计意图。
五、生产化封装:send_query 与消息解析
统一入口 send_query
架构图中 Agent 节点对应的可复用逻辑,被封装在 agent.py 的 send_query() 中。它把整套会话编排收敛为一个异步函数,对外暴露四个可控参数:
| 参数 | 作用 | 默认值 |
|---|---|---|
prompt |
要发送的研究问题 | 必填 |
activity_handler |
活动回调,可替换为自定义实现(如日志、WebSocket 推送) | print_activity |
continue_conversation |
置为 True 时继续上一段对话,而不是开启新会话 | False |
model |
指定模型 | claude-opus-4-6 |
display_result |
完成后是否渲染最终结果卡片;程序化集成时置为 False 只取文本 | True |
关于 activity_handler,源码注释明确说明同时支持同步与异步两种回调:同步处理器(如 print_activity)适合简单控制台输出,异步处理器适合需要 WebSocket/网络 I/O 的 Web 应用,实际项目中按需二选一。
消息解析辅助函数 get_activity_text
对于想要自定义监控的用户,agent.py 提供 get_activity_text(msg):按消息类名判断属于 Assistant 还是 User,前者提取首个内容块的工具名(输出 🤖 Using: WebSearch()),无内容块则返回"Thinking...",后者标记工具完成。若消息类型不适配则安全返回 None,异常被捕获后静默降级。
多轮延续与结果控制
模块化之后,多轮对话只需重复调用 send_query 并打开 continue_conversation:
result1 = await send_query("What is Anthropic? Only do one websearch and be concise")
# 继续同一段对话,追问更细
result2 = await send_query(
"What are some of their products?",
continue_conversation=True,
)
第二次回答直接引用了第一次搜索结果中 Anthropic 的定位信息来列举其产品线——continue_conversation=True 让 Agent 记住"我们刚才聊过什么"。display_result=False 则用于纯程序化集成,此时只返回文本结果而跳过 HTML 渲染,便于把 agent 嵌进其他应用。
六、实践中的注意事项:上下文窗口与缓冲区
architecture_diagram.md 不会画出的"隐藏细节",恰恰是运行中最常踩的坑。当研究涉及图片或超大输出时,会触发如下错误:
Fatal error in message reader: Failed to decode JSON: JSON message exceeded maximum buffer size of 1048576 bytes
原因是 max_buffer_size 默认仅 1 MB(1,048,576 字节),而图片在消息中会以 base64 编码传输,体积显著膨胀:约 200KB 的磁盘图片编码后可达 270KB 以上,叠加消息开销便容易击穿默认上限。因此 research_agent 将 max_buffer_size 提到 10 MB。经验法则:典型多模态工作取 10MB;处理大型文档时可继续调高;如果单张图片并非必需,用文字描述或缩略图替代更节省。
七、在仓库中完整跑通这套架构
环境准备
按 claude_agent_sdk/README.md 的指引准备环境(对应本仓库的 Python 侧目录):
cd claude_agent_sdk
uv sync
uv run python -m ipykernel install --user --name="cc-sdk-tutorial" --display-name "Python (cc-sdk-tutorial)"
同时在 .env 中配置 ANTHROPIC_API_KEY=your_key_here。依赖与 Python 版本约束记录在 pyproject.toml(要求 Python ≥ 3.11 且 < 3.13,依赖 claude-agent-sdk>=0.1.51、python-dotenv 等)。
三种运行形态对应架构图的三层视角
- 从零起步的完整实现与运行输出,见 00_The_one_liner_research_agent.ipynb:涵盖 stateless 查询、ClaudeSDKClient 多轮会话、多模态图表分析、模块化封装与续聊演示;
- 独立复用的核心模块,见 research_agent/agent.py:
send_query内部自动完成活动显示、上下文重置与结果渲染; - 架构视图本体,即 research_agent/architecture_diagram.md,以及教程中展示的可视化时间线输出。
若要在 Notebook 之外独立导入该模块,README 建议从 claude_agent_sdk/ 目录运行,或执行 uv pip install -e . 以可编辑模式安装。
总结:架构图与源码的相互印证
回顾 architecture_diagram.md 的两张图,可以总结出这套研究 Agent 架构的三个核心设计取舍:
- 能力收敛:工具面收敛为
WebSearch+Read,一个管联网信息、一个管本地多模态内容,恰好覆盖研究任务的两大信息源; - 决策自主:
loop Until Complete明确表达"探索路径不可预编程"——何时搜索、搜几次、何时收尾,全部交由模型基于上下文判断,通过allowed_tools白名单放权、通过系统提示词约束输出(强制引用与 Sources 区块); - 会话可选:同一组件架构同时支持无状态
query()与有状态ClaudeSDKClient,前者用于独立快速问答,后者用于依赖前序发现的递进式调研。
理解了这份架构文档,再回头读 agent.py 与配套教程,代码中的每个配置项(model、allowed_tools、system_prompt、max_buffer_size、continue_conversation)就都回到了它在架构图上的位置。这套"研究型 Agent"打下的基础,也是 claude-cookbooks 后续进阶方向(chief of staff 多 Agent 编排、observability 外部系统接入、site reliability 读写修复等,见 claude_agent_sdk/README.md 的教程总览)共同的起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00