1. 从一次失败的“深度研究”说起
你可能遇到过这种场景:让大模型“帮我调研一下 2025 年国内新能源车企的出海策略”,它噼里啪啦给你一段看似完整的回答,但仔细一看——数据是 2023 年的,引用来源是编的,结论和问题对不上。这不是模型不行,而是单次问答范式根本撑不起“研究”这件事。
Deep Research(深度研究)能力要解决的,就是把大模型从“一问一答”升级成“自主研究 Agent”:它会先规划任务、再拆解子问题、然后调用搜索/代码/浏览器等工具去执行、拿到中间结果后反思调整、最后整合成带引用的报告。整个过程不是一次 forward,而是一个多步骤、可迭代、带工具调用的 Agent 循环。
这套能力背后有两个关键机制:Agent 工具调用(Tool Calling)和多智能体协作(Multi-Agent Orchestration)。前者决定 Agent 能不能“动手”,后者决定 Agent 能不能“分工”。本文不空谈原理,直接给你一份可复制的 Agent 配置骨架,包含工具调用字段和多智能体编排字段,并用 TaoToken 作为统一 Key/API 通道接入一次,最后跑一次端到端验证,确认 Deep Research 流程能真正跑通。
适合谁看:想自己搭一套 Deep Research 骨架的开发者、正在做 Agent 编排但工具调用总是失败的工程师、以及想理解“多智能体到底怎么协作”的技术同学。下面从配置骨架开始,一步步来。
2. TaoToken 前置:统一 Key 与 API 通道接入
在写 Agent 配置之前,先把模型调用通道打通。Deep Research 流程里会频繁调用大模型(规划、反思、报告生成各一次甚至多次),如果每个 Agent 都单独配 Key、单独处理不同厂商的 endpoint,配置会迅速失控。我试过用统一通道接入,配置量能砍掉一大半。
TaoToken 在这里的角色是统一 Key/API 通道:你只需要一个 API Key,就能在 Agent 配置里通过统一的 base_url 调用模型,不用为每个模型单独维护一套鉴权逻辑。官网入口见 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,API 地址是 https://taotoken.net/api (注意 API 地址不带 UTM 参数)。
接入分三步:
第一步,在控制台创建 API Key。打开 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后进入 API Keys 页面,新建一个 Key,复制保存。这个 Key 后面会写进 Agent 配置的api_key字段。
第二步,确认 API base_url。所有模型调用统一走https://taotoken.net/api,兼容 OpenAI 风格的/v1/chat/completions接口。也就是说,你原来用 OpenAI SDK 写的代码,只需要改base_url和api_key两个字段就能切换过来。
第三步,在 Agent 配置里引用。下面这段是 Deep Research 骨架里模型调用的基础配置,你可以直接复制:
# model_provider.yaml provider: name: taotoken base_url: "https://taotoken.net/api" api_key: "${TAOTOKEN_API_KEY}" # 从环境变量读取,不要硬编码 api_style: "openai" # 兼容 OpenAI 接口风格 timeout: 120 # Deep Research 单步可能较慢,超时给足 max_retries: 3 # 工具调用失败时自动重试注意:
api_key一定要走环境变量,不要写死在配置文件里。Deep Research 流程会多次调用模型,Key 泄露风险比单次问答高得多。
如果你还没创建 Key,先去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 建一个。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的接口说明和参数列表。
通道打通后,下面进入正题:Agent 配置骨架。
3. 可复制配置:Agent 工具调用与多智能体编排骨架
Deep Research 的配置骨架分两层:工具调用层和多智能体编排层。工具调用层定义 Agent 能“动手做什么”,编排层定义多个 Agent 怎么“分工协作”。两层都配好,流程才能跑通。
3.1 工具调用配置:让 Agent 能动手
工具调用是 Deep Research 的地基。没有工具调用,Agent 只能空想;工具调用配错,Agent 会反复调同一个工具或者传错参数。下面这份配置定义了三个核心工具:搜索、网页抓取、代码执行。
# tools.yaml tools: - name: web_search description: "根据查询词检索网页,返回标题、摘要和 URL 列表" parameters: type: object properties: query: type: string description: "搜索查询词,支持多关键词组合" top_k: type: integer default: 5 description: "返回结果数量" required: ["query"] endpoint: "https://taotoken.net/api/v1/tools/search" # 工具网关统一走 TaoToken auth: header: "Authorization" value: "Bearer ${TAOTOKEN_API_KEY}" - name: fetch_page description: "抓取指定 URL 的正文内容,返回纯文本" parameters: type: object properties: url: type: string description: "目标网页 URL" max_chars: type: integer default: 8000 required: ["url"] endpoint: "https://taotoken.net/api/v1/tools/fetch" auth: header: "Authorization" value: "Bearer ${TAOTOKEN_API_KEY}" - name: run_python description: "执行 Python 代码片段,用于数据处理和计算" parameters: type: object properties: code: type: string description: "待执行的 Python 代码" required: ["code"] endpoint: "https://taotoken.net/api/v1/tools/code" auth: header: "Authorization" value: "Bearer ${TAOTOKEN_API_KEY}"三个工具的分工很明确:web_search负责发现信息源,fetch_page负责深入读取,run_python负责处理数据。Deep Research 的“多跳推理”就靠这三个工具交替调用实现——先搜到一批 URL,抓取其中几个,发现新线索后再搜,如此迭代。
工具配置里有两个容易踩的坑:一是description写得太模糊,模型不知道什么时候该调这个工具;二是parameters的required字段漏写,模型可能传空参数。上面这份配置把这两个点都处理了。
3.2 多智能体编排配置:让 Agent 分工协作
单智能体也能做 Deep Research,但复杂任务下容易“顾此失彼”——规划的时候想着执行,执行的时候忘了反思。多智能体架构把职责拆开:Planner(规划者)负责拆解任务,Researcher(研究员)负责工具调用和信息收集,Reporter(报告员)负责整合输出。
# agents.yaml agents: - name: planner role: "任务规划者" model: "claude-sonnet-4" # 规划需要强推理能力 system_prompt: | 你是研究任务规划者。将用户的研究问题拆解为 3-6 个可执行的子任务, 每个子任务必须明确:目标、所需工具、预期产出。 输出 JSON 格式的任务列表,不要输出其他内容。 tools: [] # 规划阶段不调用工具 max_turns: 1 output_key: "research_plan" - name: researcher role: "信息研究员" model: "claude-sonnet-4" system_prompt: | 你是信息研究员。根据分配的子任务,调用工具收集信息。 每次调用工具后,评估结果是否足够;不足则调整查询词继续检索。 最多迭代 5 轮,每轮记录:查询词、工具、结果摘要。 tools: ["web_search", "fetch_page", "run_python"] max_turns: 5 depends_on: "planner" input_key: "research_plan" output_key: "research_findings" - name: reporter role: "报告撰写者" model: "claude-sonnet-4" system_prompt: | 你是报告撰写者。整合研究员的发现,生成结构化报告。 要求:每个结论必须标注来源 URL;数据用表格呈现;最后给出局限性说明。 tools: [] max_turns: 1 depends_on: "researcher" input_key: "research_findings" output_key: "final_report" orchestration: mode: "sequential" # 顺序编排:planner → researcher → reporter shared_context: true # 共享上下文,后一个 Agent 能读到前一个的输出 max_total_turns: 10 # 全局轮次上限,防止死循环 on_tool_error: "retry_then_skip" # 工具报错先重试,再跳过继续这份编排配置的核心是depends_on和input_key/output_key三个字段。depends_on定义执行顺序,output_key把当前 Agent 的产出写入共享上下文,input_key指定下一个 Agent 从共享上下文里读哪个字段。这样 Planner 产出的research_plan会自动传给 Researcher,Researcher 产出的research_findings会自动传给 Reporter。
orchestration块里的max_total_turns是防死循环的关键。Deep Research 流程里,Researcher 可能陷入“搜了不满意再搜”的循环,全局轮次上限能强制它停下来。on_tool_error设为retry_then_skip,意思是工具调用失败先重试,重试还失败就跳过这个工具继续,避免整个流程卡死。
3.3 把两层配置串起来
工具配置和 Agent 配置是分开的两个文件,需要一个入口把它们加载起来。下面这段 Python 代码负责加载配置并启动流程:
# run_deep_research.py import os import yaml from openai import OpenAI # 1. 加载配置 with open("tools.yaml") as f: tools_config = yaml.safe_load(f) with open("agents.yaml") as f: agents_config = yaml.safe_load(f) # 2. 初始化模型客户端,统一走 TaoToken client = OpenAI( base_url="https://taotoken.net/api", api_key=os.environ["TAOTOKEN_API_KEY"], ) # 3. 按编排顺序执行 Agent def run_pipeline(query: str): context = {"user_query": query} for agent in agents_config["agents"]: # 从共享上下文读取输入 if "input_key" in agent: agent_input = context.get(agent["input_key"], "") else: agent_input = query # 调用模型(这里简化了工具调用循环,实际需按 tools 字段挂载工具) response = client.chat.completions.create( model=agent["model"], messages=[ {"role": "system", "content": agent["system_prompt"]}, {"role": "user", "content": str(agent_input)}, ], ) # 写入共享上下文 context[agent["output_key"]] = response.choices[0].message.content print(f"[{agent['name']}] 完成,输出长度 {len(context[agent['output_key']])}") return context["final_report"] if __name__ == "__main__": report = run_pipeline("调研 2025 年国内新能源车企出海策略") print(report)这段代码是骨架版,实际生产里需要在researcher那一步挂上工具调用循环(把tools.yaml里的工具定义转成 OpenAI function calling 格式传给模型)。但骨架已经能跑通“规划→研究→报告”的完整链路,下面验证一下。
4. 验证请求:跑一次端到端 Deep Research
配置写完了,得验证它真能跑通。验证分两步:先确认模型通道通,再确认多智能体流程通。
4.1 先验证模型通道
在跑完整流程之前,先用一个最小请求确认 TaoToken 通道正常:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4", "messages": [{"role": "user", "content": "回复 OK 两个字母"}], "max_tokens": 10 }'如果返回里choices[0].message.content包含OK,说明通道正常。如果返回 401,检查 Key 是否正确;返回 404,检查 base_url 是不是写成了带/v1的完整路径(base_url 只写到/api,SDK 会自动拼/v1/chat/completions)。
4.2 再跑完整流程
通道确认后,运行上面的run_deep_research.py:
export TAOTOKEN_API_KEY="你的Key" python run_deep_research.py预期输出类似:
[planner] 完成,输出长度 412 [researcher] 完成,输出长度 2860 [reporter] 完成,输出长度 1930三个 Agent 依次完成,说明编排链路通了。planner输出最短(任务列表),researcher输出最长(信息收集),reporter输出居中(结构化报告),这个长度分布符合预期。
4.3 验证工具调用是否真的发生
光看 Agent 输出还不够,得确认 Researcher 真的调了工具。在run_pipeline里加一行日志,打印 Researcher 那一步的tool_calls字段:
if response.choices[0].message.tool_calls: for tc in response.choices[0].message.tool_calls: print(f" 调用工具: {tc.function.name}, 参数: {tc.function.arguments}")如果看到类似调用工具: web_search, 参数: {"query": "2025 新能源车企 出海"}的输出,说明工具调用真的发生了。如果tool_calls为空,说明模型没触发工具调用,检查tools.yaml里的description是否足够清晰,以及researcher的system_prompt是否明确要求“调用工具收集信息”。
5. 本篇常见错排查
配置骨架跑通之前,大概率会踩几个坑。下面是我实测下来最常见的四类报错和对应排查方法。
5.1 工具调用返回 401/403
现象:Researcher 调用web_search时返回鉴权失败。
原因:tools.yaml里的auth.value写的是${TAOTOKEN_API_KEY},但环境变量没导出,或者导出的是空值。
排查:先echo $TAOTOKEN_API_KEY确认环境变量有值;再检查tools.yaml里auth.header是不是Authorization,value是不是Bearer ${TAOTOKEN_API_KEY}(注意Bearer后面有个空格)。如果用的是配置文件加载,确认加载时做了环境变量替换,YAML 本身不会自动展开${}。
5.2 多智能体流程卡在 Researcher 不往下走
现象:Planner 完成了,Researcher 一直不结束,或者结束后 Reporter 没启动。
原因:max_turns设太大,Researcher 陷入“搜了不满意再搜”的循环;或者depends_on写错了,Reporter 没等到 Researcher 完成。
排查:先把researcher.max_turns从 5 降到 2,看流程能不能走完。如果能走完,说明是轮次太多;再检查orchestration.max_total_turns是否小于所有 Agent 的max_turns之和。另外确认reporter.depends_on写的是researcher,input_key写的是research_findings,和 Researcher 的output_key一致。
5.3 模型不触发工具调用
现象:Researcher 直接输出一段文字,没有tool_calls。
原因:tools.yaml里的description太模糊,模型不知道什么时候该调;或者system_prompt没明确要求调用工具。
排查:把web_search的description从“搜索网页”改成“根据查询词检索网页,返回标题、摘要和 URL 列表,适用于需要获取实时信息的场景”。同时在researcher.system_prompt里加一句“你必须调用工具收集信息,不能凭记忆回答”。模型对工具调用的触发,很大程度上取决于description和system_prompt的明确程度。
5.4 报告里没有来源引用
现象:Reporter 输出的报告没有 URL 引用,结论像是编的。
原因:Researcher 的output_key里没保留来源 URL,或者 Reporter 的system_prompt没要求标注来源。
排查:在researcher.system_prompt里加“每次工具调用后,记录结果中的 URL 和关键信息”;在reporter.system_prompt里加“每个结论必须标注来源 URL,没有来源的结论不要写”。如果 Researcher 的输出里确实没有 URL,检查fetch_page工具返回的内容是否包含 URL 字段。
提示:排障时如果怀疑是模型通道问题,可以先用模型对话页面单独测一下模型是否正常响应,排除通道因素后再查配置。模型对话入口见 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 。
6. 继续往下走:从骨架到可用系统
上面这套骨架跑通后,你已经有了一个能规划、能调工具、能出报告的最小 Deep Research 系统。但骨架离生产还有距离,几个方向可以继续补:
工具生态扩展。目前只有搜索、抓取、代码执行三个工具。实际研究任务可能需要 PDF 解析、数据库查询、图表生成。每加一个工具,就在tools.yaml里加一段定义,然后在对应 Agent 的tools字段里挂上。工具越多,Researcher 的能力边界越宽。
反思机制加强。当前骨架里 Researcher 的反思靠max_turns和system_prompt约束,比较粗糙。可以加一个独立的criticAgent,专门评估 Researcher 的发现是否充分、来源是否可靠,不通过就打回重做。这就是从“顺序编排”升级到“带反馈的循环编排”。
长任务与 Coding Plan。如果你的 Deep Research 流程需要长时间运行(比如批量调研几十个主题),单次 API 调用模式在成本和稳定性上都不划算。长期编码和 Agent 任务可以走 Coding Plan,入口见 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,适合需要持续调用模型的场景。
接入文档常查。配置字段和接口参数会更新,遇到不确定的字段含义,直接查接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,比翻源码快。
最后说一个实测下来的经验:Deep Research 的瓶颈往往不在模型能力,而在工具调用的稳定性。模型再强,搜索接口超时、网页抓取被拦、代码执行报错,流程照样断。所以on_tool_error的重试和跳过策略、max_total_turns的全局上限,这两个字段一定要配好。骨架跑通之后,先把这两个字段的日志打出来,观察工具调用的成功率和耗时分布,再决定要不要加工具、加 Agent。