· 深度拆解:一个只负责分活的多智能体研究助理

深度拆解:一个只负责分活的多智能体研究助理

3096 字
15 分钟
深度拆解:一个只负责分活的多智能体研究助理
StarryLin/Geek · 摘要

主代理派工,三个子代理以文件目录作黑板交换产物,钩子记录工具调用生成审计与战报。

示例版本说明:本文对照 CodeBuddy 官方示例仓库 提交 25953e3 核对。下文代码为结构摘录,省略了部分导入、类型和实现;博客仓库不包含这些示例目录。unstable_v2_* 属于不稳定接口,运行时请锁定依赖并对照当前文档。

深度拆解:一个「只负责分活」的多智能体研究助理#

关键词:Multi-Agent · AgentDefinition · Hooks · 文件系统黑板 · Task 工具 配套代码:CodeBuddy 官方示例仓库 的 research-assistant/(Python,codebuddy-agent-sdk) 日期:2026-09-04


先看要点#

  • research-assistant 是一个多智能体研究系统:你抛出一个研究问题,它最终还你一份带图表、带引用的 PDF 研究报告。
  • 架构上只存在一个「前台角色」——lead agent。它的系统提示词写得很直白:你只负责规划与派活,绝不亲自干活,示例通过 allowed_tools=["Task"] 配合提示词,让它以派活为主;该权限配置本身不能证明其他工具已被硬性禁用。
  • 三个「执行者」用 AgentDefinition 注册:researcher(搜集)→ data-analyst(分析出图)→ report-writer(整合成 PDF),是一套标准的流水线式编排。
  • 子智能体之间不直接对话,而是通过约定的 files/ 目录交换产物——这是本示例最值得抄的设计:用文件系统当多智能体的「共享黑板」。
  • 全程由 Hooks 拦截所有工具调用做追踪,落成 JSONL 审计日志与一份「会话记录」,让看不见的多智能体协作变成看得见的流水线台账。

一、先看整体:一次研究是怎么「跑」起来的#

把代码里的真实调用链画出来,长这样:

你:「量子计算的最新进展有哪些?」
│
▼
┌─────────────────────────────────────────────┐
│ LEAD AGENT(研究主管) │
│ system_prompt:你只负责分解任务并派活 │
│ allowed_tools = ["Task"] ← 派活配置 │
│ 绝不直接搜索、绝不直接写文件 │
└─────────────────────────────────────────────┘
│ Task(researcher, description=…)
▼
┌─────────────────────────────────────────────┐
│ 1. researcher(研究员) │
│ tools: WebSearch, Write │
│ 产出 → files/research_notes/*.md │
└─────────────────────────────────────────────┘
│ 拿研究笔记后,Task(data-analyst)
▼
┌─────────────────────────────────────────────┐
│ 2. data-analyst(数据分析师) │
│ tools: Glob, Read, Bash, Write │
│ 读笔记 → 抽数据 → matplotlib 画图 │
│ 产出 → files/charts/*.png │
│ files/data/*.md(数据摘要) │
└─────────────────────────────────────────────┘
│ Task(report-writer)
▼
┌─────────────────────────────────────────────┐
│ 3. report-writer(报告撰写员) │
│ tools: Skill, Write, Glob, Read, Bash │
│ 读全部中间产物 → reportlab 合成 │
│ 产出 → files/reports/<问题>.pdf │
└─────────────────────────────────────────────┘
│
▼
你收到一份带图表与引用的 PDF 研究报告 ✅

三个关键事实,从代码里可以直接验证:

  1. lead 以调度为主:它的 allowed_tools=["Task"],而角色分工写在 system_prompt=lead_agent_prompt 里;
  2. 执行者各配专属工具箱:researcher 连写图表的 Bash 都没有,report-writer 干脆不给 WebSearch——工具配置表达分工,但不能代替文件、进程和网络层的隔离;
  3. 协作不靠嘴、靠文件:files/research_notes/、files/charts/、files/data/、files/reports/ 四个目录,就是子智能体之间的「交接货架」。

二、角色表:三个执行者,各管一段#

角色名称(key)工具箱在流水线中的职责产物
研究员researcherWebSearch, Write全网搜集信息、交叉引用,写研究笔记files/research_notes/
数据分析师data-analystGlob, Read, Bash, Write读笔记、抽数值、用 matplotlib 可视化files/charts/、files/data/
报告撰写员report-writerSkill, Write, Glob, Read, Bash汇总全部材料,reportlab 出 PDF(提示词要求不额外搜索)files/reports/

注意 data-analyst 的 tools 里没有 WebSearch、report-writer 也没有——它们的 description 也反复强调“不进行网络搜索 - 只读取现有研究笔记并创建 PDF 报告”。这些配置用于收窄职责,但 Bash 本身仍可执行网络请求,不能把「未提供 WebSearch」等同于「无法联网」。


三、代码精读:三行配置,拼出一个「团队」#

3.1 定义子智能体:一个 agents 字典#

# research-assistant/research_agent/agent.py(摘录,已精简)
from codebuddy_agent_sdk import CodeBuddySDKClient, CodeBuddyAgentOptions, AgentDefinition, HookMatcher
agents = {
"researcher": AgentDefinition(
description=(
"当需要收集任何主题的研究信息时使用此智能体。"
"研究员使用网络搜索从互联网上查找相关信息、文章和来源。"
"将研究发现写入 files/research_notes/ 供报告撰写员后续使用。"
),
tools=["WebSearch", "Write"],
prompt=researcher_prompt,
# model="claude-opus-4.5" # 不指定模型,使用默认配置
),
"data-analyst": AgentDefinition(
description=(
"在研究员完成工作后使用此智能体生成定量分析和可视化图表。"
"数据分析师从 files/research_notes/ 读取研究笔记,"
"提取数值数据(百分比、排名、趋势、对比),"
"并通过 Bash 使用 Python/matplotlib 生成图表。"
"将图表保存到 files/charts/,数据摘要写入 files/data/。"
),
tools=["Glob", "Read", "Bash", "Write"],
prompt=data_analyst_prompt,
),
"report-writer": AgentDefinition(
description=(
"当需要创建正式研究报告文档时使用此智能体。"
"报告撰写员从 files/research_notes/ 读取研究发现,"
"从 files/data/ 读取数据分析,从 files/charts/ 读取图表,"
"然后使用 reportlab 将它们整合成…PDF 报告,保存在 files/reports/。"
),
tools=["Skill", "Write", "Glob", "Read", "Bash"],
prompt=report_writer_prompt,
),
}

AgentDefinition 的三个字段含义:

  • description —— 写给人看、更写给 lead 看。lead 决定「这活该派给谁」时,靠的就是这段语义描述(模型会把任务与 description 做匹配)。
  • tools —— 该子智能体启动后可见的工具白名单,决定它“手脚”的范围。
  • prompt —— 子智能体自己的系统提示词(从 prompts/ 目录加载),决定它的“行为准则”。

3.2 组装主智能体:lead 的「紧身衣」#

options = CodeBuddyAgentOptions(
permission_mode="default", # 本文改为默认权限模式;上游示例使用 bypassPermissions
setting_sources=["project"], # 从项目 .codebuddy 目录加载技能
system_prompt=lead_agent_prompt, # 研究主管人设
allowed_tools=["Task"], # 配合提示词将职责聚焦于派活,不是系统沙箱
agents=agents, # ← 把团队注册进去
hooks=hooks, # ← 全程埋点
)

这一小段配置就是整个编排系统的全部“胶水”。SDK 的运行时会保证:lead 每次调用 Task,都会按名字从 agents 里取出对应 AgentDefinition,以它的 prompt/tools 起一个子智能体运行时,并把子智能体的最终回复回传给 lead,让主管能「验收后再决定下一步派谁」。

3.3 挂上追踪器:Hook 匹配所有工具#

tracker = SubagentTracker(transcript_writer=transcript, session_dir=session_dir)
hooks = {
'PreToolUse': [
HookMatcher(matcher=None, hooks=[tracker.pre_tool_use_hook]) # 匹配所有工具
],
'PostToolUse': [
HookMatcher(matcher=None, hooks=[tracker.post_tool_use_hook])
],
}

matcher=None 表示不挑工具,所有工具调用都过一遍钩子——因为要观测的不只是 Task,还包括子智能体内部的 WebSearch、Write、Bash,这样才能拼出完整流水线台账。


四、黑盒里的哨兵:SubagentTracker 怎么「看见」协作#

多智能体最大的工程痛点是不可观测:几个分身各干各的,谁先谁后、谁写了什么,lead 心里清楚,但你(开发者/用户)一无所知。

SubagentTracker 的解法是双通道记录:

  1. Hook 通道(结构):PreToolUse 钩子捕获 tool_name / tool_input,PostToolUse 钩子捕获 tool_response。每次 Task 调用会被登记为一个 SubagentSession(以 parent_tool_use_id 为键),子智能体内部每一次工具调用则归入 tool_calls.jsonl 的结构化日志——父子关系、调用时序全被记录下来。
  2. 消息流通道(内容):配合消息流解析维护“当前由哪个子智能体在跑”的执行上下文,把模型的思考与输出对应到正确的会话上。

它的核心数据模型如下(结构示意,省略了构造函数,不能作为完整 Python 类直接运行):

class ToolCallRecord: # 一次工具调用
timestamp: str
tool_name: str
class SubagentSession: # 一个子智能体的执行会话
subagent_type: str
parent_tool_use_id: str # ← 用它关联 lead 的 Task 调用
class SubagentTracker:
# Map: parent_tool_use_id -> SubagentSession
self.sessions: Dict[str, SubagentSession] = {}

而 _log_tool_use() 会在控制台实时打印(print(message, flush=True)),同时写入人类可读的 transcript 与机器可读的 tool_calls.jsonl。于是运行时你会看到类似这样的「实时战报」:

你:量子计算的最新进展有哪些?
[Task] 研究员研究员(Researcher) 开始任务…
→ WebSearch: 量子纠错 2026 进展
→ Write: files/research_notes/quantum-error-correction.md
[Task] 数据分析师(Data-Analyst) 开始任务…
→ Bash: python draw_charts.py
→ Write: files/charts/roadmap.png
[Task] 报告撰写员(Report-Writer) 开始任务…
→ Write: files/reports/量子计算研究报告.pdf

一个「看不见的多智能体会议」,被还原成一条可追溯、可回放、可审计的生产线台账。


五、为什么用「文件系统当黑板」是个好设计#

多智能体间的协作协议,业界主流有几种:共享内存消息总线、数据库、API 调用……而本示例选的是最朴素的一种——文件目录约定。它的收益:

维度文件系统黑板带来的好处
简单子智能体无需感知彼此存在,只要读写约定目录,天然解耦
稳健已完整写入的文件可供后续复用;自动续跑仍需任务状态、完整性校验与重入逻辑
可审每一步产物都是实体文件,人类可直接打开检查、比对、修正
规模谁产出、谁消费由目录结构表达,新加角色只需新加目录与约定
成本不必先部署消息中间件,但需可读写的共享目录,并管理权限、并发和任务隔离

在真实代码里,这条「交接协议」不是写在架构文档里,而是直接写进每个子智能体的 description:

  • researcher 的 description 明确「将研究发现写入 files/research_notes/ 供报告撰写员后续使用」;
  • data-analyst 明确「从 files/research_notes/ 读取…将图表保存到 files/charts/,数据摘要写入 files/data/」;
  • report-writer 明确「从 notes/data/charts 三处读取 → 合成 PDF 到 files/reports/」。

把「交接协议」写进模型可见的 description,而不是埋在代码里——这是比任何类型系统都更直接的“接口文档”。


六、运行起来的样子#

Terminal window
# 在 research-assistant/ 下
uv run python research_agent/agent.py # 交互式 REPL,多轮追问
# 启动后在 REPL 中输入研究问题;上游入口未解析命令行问题参数

交互模式底层是一个常驻的 CodeBuddySDKClient:同一客户端 while True 循环里反复 query(),每轮沿用同一 options(也就共享同一套智能体团队与钩子)。上游入口在进入 REPL 前调用一次 setup_session(),因此一次程序运行内的多轮研究共用日志目录,并非每个问题自动新建目录。files/ 产物目录也需要另外按任务隔离,避免旧材料混入新报告。

最终交付物:

research-assistant/
├── research_agent/
│ ├── agent.py # 入口:chat() 组装 agents/options/hooks,含 REPL 输入
│ ├── prompts/ # lead/researcher/data-analyst/report-writer 提示词
│ └── utils/
│ ├── subagent_tracker.py # 追踪:SubagentSession / JSONL / 控制台战报
│ ├── transcript.py # 会话记录(setup_session / TranscriptWriter)
│ └── message_handler.py # assistant 消息解析(区分文本与 tool_use)
└── files/ # 产出物(gitignore)
├── research_notes/ # 中间笔记(researcher 产出)
├── charts/ # 图表(data-analyst 产出)
├── data/ # 数据摘要(data-analyst 产出)
└── reports/ # PDF 报告(report-writer 产出)

七、这套实现中可复用的做法#

把研究助理「去业务化」之后,留下的是一套通用的多智能体工程范式:

  1. 一个调度者,一串执行者 —— 调度者以 Task 派活,执行者各有工具配置;职责通过工具配置和提示词表达;强边界还需拒绝规则、权限回调及系统隔离,不能称为「物理锁死」。
  2. 流水线而非星形总线 —— 角色间按“产物依赖”排成链:研究 → 分析 → 成稿;没有谁需要“知道所有人”。
  3. 产物即接口 —— 每个角色的输出落成约定目录中的实体文件,description 里写清“我把什么放哪、我从哪拿什么”。
  4. 一切皆可观测 —— Pre/PostToolUse 钩子把每个工具调用、每次子智能体生成变成日志与 JSONL;没有日志的多智能体系统,出了错只能靠猜。

这套范式尤其适合研究报告、代码审查、数据分析、长文档生成这类「多阶段、多专业、产物可分割」的任务。反过来,如果你的任务是一个连续对话的问答(不需要分工),就不要硬套多智能体——直接走会话 API 更划算。


本系列其余文章:

  1. 全览:从七个示例看懂一个生态
  2. 深度拆解:多智能体研究助理是怎么「分活」的
  3. 从零构建 AI 聊天应用
  4. 会话机制深潜:Session 与多轮对话

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

深度拆解:一个只负责分活的多智能体研究助理
https://mjzy.tech/posts/ai-engineering/03-多智能体研究助理拆解/
作者
JIA
发布于
2026-09-04
许可协议
CC BY-NC-SA 4.0

评论区

正在加载 GitHub 评论…

Tips · 随手记
仅自己可见 · 每篇文章单独保存

文章目录