字节面试官皱眉:“你这 Agent 跟带搜索的 ChatGPT 有啥区别?“我答:“能多轮搜,搜完接着搜啊“,他追问了一句搜索词……
继续更新 Deep Research Agent 从0到1实战系列。接下来这个系列,我们会一起把一个完整的 Deep Research Agent 从零搭出来,每篇都带可以直接跑的代码,每读完一篇,你手里那套系统就多一个能用的功能。今天先把最底层的框架跑通。
上周有个学员面字节大模型基础设施岗,简历上写了 RAG(检索增强生成,Retrieval-Augmented Generation)系统上线经历,还附了一段 Agent 开发实战。面试官扫了一眼,问:
“你做的 Agent,和一个带搜索插件的 ChatGPT,本质上差在哪?”
他答:“我的 Agent 能多轮调工具,搜到中间结果之后还能接着搜,最后把所有信息综合起来回答。”
面试官点了点头,往下追:“搜到第10轮,第11轮的搜索词是谁定的?根据什么定的?”
他说:“模型根据前面的结果推理……上下文里有完整的历史记录,它自己决定下一步搜什么。”
面试官停了一下:“上下文里塞着完整历史。那10轮搜索大概多少 token?你测过模型在三四万 token 的上下文里,对第一轮找到的那条信息,还剩多少注意力吗?”
他没说话。这个问题他从来没想过,更没量过。
面试官说了句话:“你做的不是 Deep Research,是’带记忆的关键词搜索’。Deep Research 最难的地方从来不是循环转几次,是在记忆有限的情况下,还能把下一步决策做对。”
这道题难的不是概念,是你有没有真的把系统跑到出问题的那个点,然后蹲下来想过它为什么出问题。今天就把 Deep Research Agent 的底层逻辑,连同第一个能运行的框架,一起拆开讲清楚。
一、同样是"搜索",RAG 和 Deep Research Agent 差在哪
RAG 处理的是这样一类问题:用户问一句,系统检索一次,命中相关片段,拼进 prompt 让模型回答,结束。问"重疾险等待期是多少天",它翻到那一条念出来,对这种问题完全够用。
但"全球前五大新能源车企,在固态电池方向的研发投入和技术路线各有什么差异",就不是检索一次能搞定的。你得先确认前五大是哪五家,再分别查每家的投入、查每家的路线,查到一半发现某家拆了两条技术线,又得回头补搜,最后把这些散落各处的信息对齐成一份能横向比的表。
我们做的第一版系统就栽在这。当时有个客户问了一个跨市场新能源政策的比较问题,我们的 RAG 检索一次,返回三篇相关文章,生成了一段回答。客户看完只说了一句:“这不就是搜索引擎加了个总结吗?”
他说得对。那个版本根本不是 Agent,是个 RAG。
差距可以用一张表说清楚。
| 问题类型 | 典型例子 | 需要几轮检索 | 普通 RAG 能做吗 |
|---|---|---|---|
| 单跳问答 | “等待期是多少天” | 1 轮 | 完全够用 |
| 两跳推理 | “A 公司 CEO 毕业于哪所大学” | 2-3 轮 | 勉强应付 |
| 研究型问题 | “五大车企固态电池路线对比” | 10-20 轮 | 无法处理 |
| 复杂分析 | “某政策对三个行业的差异化影响” | 20+ 轮 | 完全失效 |
研究型问题有一个绕不开的特征:后面搜什么,取决于前面搜到了什么。你没法在第一步就规划好第二步的关键词,得看见第一步的结果才知道。这种走一步看一步的动态决策,RAG 那一次性的检索接不住。
所以 Deep Research Agent(深度研究 Agent)说白了就干三件事:动态决定下一步搜什么、把多轮检索来的信息整合到一起、判断什么时候够了可以停。听着简单,每一件单拎出来做好都不容易。
动手之前先想清楚一件事:你手上的问题,是"一次检索能答"还是"得查很多轮才能答"。前者用 RAG 就够,没必要背上 Agent 这一身复杂度。只有确认了要多轮动态检索,才值得引入今天这套框架。
RAG 与 Deep Research Agent 的处理路径对比图
RAG 与 Deep Research Agent 的处理路径对比
二、Deep Research Agent 能干什么,更要先想清楚它不能干什么
在搭框架之前,先把这套系统的能力边界钉死。这一步很多人跳过去,结果是把一堆它天生做不好的活儿硬塞给它,然后怪模型不行。
它擅长的是这几类:多轮迭代检索、多源信息聚合、交叉验证事实、把 A→B→C 的多跳链路一步步推下去、根据中间结果识别"我还缺哪块信息"并调整搜索路径,最后综合成一份结构化报告。这些是它真正值钱的地方。
但下面这四条边界,比上面那串能力更该先记住:
它不是知识库。它本身不存事实,每次回答都要实时去检索。你指望它像背了一肚子答案那样张口就来,方向就错了。
它不保证 100% 准确。它的天花板被信息源质量和自身推理能力死死压着,源里是错的,它大概率也跟着错。
它不适合时效性极强的任务。搜索引擎的索引本身有延迟,刚发生半小时的事,它经常搜不到。
它不适合需要专业工具才能完成的任务。比如要跑一段真实数据分析、要执行一段代码,你不给它接上对应的工具,光靠搜是搜不出来的。
我把这四条放在这么靠前的位置,是因为线上最常见的事故,根源都不在框架,而在一开始就把任务派错了。承认边界不丢人,它恰恰是判断"这活儿该不该用 Deep Research Agent 来干"的第一道闸门。
三、撑起一个 Deep Research Agent 的四根柱子
边界划清楚了,再看全景。不是为了背概念,是因为后面真出了问题,你得能立刻定位到是哪根柱子塌了。
第一根柱子是训练数据。模型没法凭空学会做复杂研究,它得看大量"有人真做过这件事"的轨迹样本:从一个问题出发,一步步搜索、判断、调整,直到拿到答案的完整过程。这种数据现成的没有,得专门构造。这个系列后面会拿好几篇专门讲怎么造。
第二根柱子是 Agent 框架。这是模型的行动逻辑:它按什么格式输出思考、用什么方式调工具、结果回来之后怎么接着往下推。最基础的框架叫 ReAct(推理与行动,Reasoning and Acting),今天就从它开刀。
第三根柱子是训练系统。有了数据和框架,还得先用 SFT(监督微调,Supervised Fine-Tuning)让模型学会基本格式,再用 RL(强化学习,Reinforcement Learning)让它在不同情况下学会选更优的策略。这两步是把"数据"真正变成"模型参数里的能力"的环节。
第四根柱子是工具集。Agent 靠工具和外部世界打交道,核心四件套是 Search(搜索引擎)、Visit(网页内容提取)、Scholar(学术文献检索)、Python(代码执行)。工具设计的好坏,直接卡死了 Agent 能力的上限。
工具这块有四条设计原则,是我后来踩了坑才补上的:一个工具只做一件事(简单性);每个工具自带重试和降级(可靠性);相同输入尽量给相同输出,否则训练阶段模型学不到稳定模式(确定性);每次调用都要有日志能回溯(可观测性)。这四条你现在记不住没关系,等系列讲到工具那几篇会一条条还回来。
四根柱子是这么串起来的:训练数据决定模型能学到什么,训练系统把数据熬成参数里的能力,Agent 框架决定这些能力怎么被激活和组织,工具集是它伸向真实世界的那只手。今天我们只动第二根柱子里最基础的那一截:让 ReAct 先转起来。
Deep Research Agent 四组件全景图
Deep Research Agent 四组件全景
四、ReAct:把"想"和"做"拧在一根绳上
ReAct 的核心就三步,把这三步吃透,代码自然就懂了:
①Thought(思考):模型看着当前手上所有信息,先想一遍,我现在知道什么、还缺什么、下一步该干什么。 ②Action(行动):照着思考的结果,调一个工具,搜一个词,或者打开一个网址。 ③Observation(观察):工具跑完,把结果读回来,追加进对话历史,然后回到第①步。
关键全在第③步那个"回到第①步"。这一轮的 Observation 进了历史,下一轮的 Thought 就是站在新信息上重新判断的,所以第二个搜索词能比第一个更准。这个循环能转几十次,每转一次,模型对问题的掌握就厚一层。
下面是一个能直接跑的 ReAct 最小实现,每个关键决定我都注在代码里了:
import json def react_loop( user_query: str, llm, tools: dict, max_steps: int = 10 ) -> str: """ 最小 ReAct 执行循环。 接任意支持 function calling 的 LLM(OpenAI/DeepSeek/Qwen 都行)。 """ # 初始化对话历史,包含系统提示和用户问题 # 为什么要保留完整历史而不只留最后几轮? # 因为 ReAct 要求模型每次都能看到完整推理链, # 切掉早期搜索结果,模型就不知道"这个信息我已经找到了", # 会重复搜完全相同的内容,白白浪费步数和 token messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_query} ] for step in range(max_steps): # 调用 LLM,让它在"生成最终答案"和"调用工具"之间自己选 # tool_choice="auto":你自己判断,信息够了就直接答, # 还要查就调工具,不强制它必须调 response = llm.chat( messages=messages, tools=list(tools.values()), # 把工具定义传进去,让模型知道有什么可用 tool_choice="auto" ) # 模型选择调用工具(说明它认为信息还不够) if response.tool_calls: tool_call = response.tool_calls[0] tool_name = tool_call.function.name tool_args = json.loads(tool_call.function.arguments) print(f"[步骤 {step + 1}] {tool_name}({tool_args})") # 执行工具,拿到 Observation # 为什么要 try/except 而不是直接调? # 搜索超时、网页 404、网络抖动,这些在生产里每天都在发生。 # 不捕获的话,一个工具失败就能把整个 Agent 拖崩, # 用户的问题只因为一次网络抖动就没了答案,体验很差 try: observation = tools[tool_name](**tool_args) except Exception as e: # 工具失败时,把发生了什么告诉模型,让它自己决定下一步 # 而不是直接抛错,模型可能换个策略就接着走了 observation = f"工具调用失败({type(e).__name__}):{e}。请尝试换一种方式。" # 把这一步的结果追加进对话历史 # 为什么要严格按"assistant 消息 → tool 消息"的顺序追加? # 这是 OpenAI 格式的硬性要求:tool 消息必须紧跟触发它的 assistant 消息, # 靠 tool_call_id 配对。顺序错了 API 直接报错 messages.append(response.to_message()) # Thought + Action messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(observation) # Observation }) # 模型没调工具,说明它判断信息够了,给出最终答案 else: print(f"[完成] 执行了 {step + 1} 步") return response.content # 走到这说明跑满 max_steps 还没结束 # 为什么不直接返回 None 或报错? # 用户等了这么久,哪怕是不完整的答案也比什么都没有强, # 返回模型当前最后一条输出,至少让用户知道系统卡在哪了 last_content = next( (m["content"] for m in reversed(messages) if m["role"] == "assistant"), "已达到最大步数,未能生成完整答案。" ) return f"[已达 {max_steps} 步上限] {last_content}"这段代码今天就能接上 OpenAI 或 DeepSeek 的 API 跑起来。我们内测的第一版结构基本就长这样,当时还没加 try/except,第一次给客户演示,搜索工具超时直接崩在屏幕上,很尴尬。补上错误捕获之后,工具失败时多了一条"换策略继续"的活路,那一类问题的最终成功率从 78% 抬到了 91%。
工具调用的错误处理,是 ReAct 框架里第一个必须做对的地方。不是因为工具多爱失败,而是一旦失败、整个 Agent 跟着崩掉的代价,远比多写那几行 try/except 高得多。
五、第一个 Agent 跑起来了,但它很快会撞上一面墙
把上面的代码跑起来,处理简单问题没毛病:搜个三四次给出答案,上下文不到一万 token,模型表现挺好。
任务一复杂,问题就来了。
我们内测时有个用户问了一个跨市场新能源补贴政策的比较问题:“欧盟、美国、中国三个市场的新能源补贴,2024 年各做了哪些调整,对不同车企的影响有什么差异?” Agent 跑了 8 步,最后那份报告里,欧盟的补贴额度被安到了美国的政策头上。
我们排查了将近两小时,最后在日志里挖出来:到第 7 步时,messages 的 token 数已经过了 28000。搜索结果里欧盟、美国的数据其实都在,但模型对第一步找到的欧盟数据注意力明显塌了,生成报告时把两个来源搅在了一起。
这件事让我们看清,ReAct 这个设计本身就带着一个躲不掉的结构性上限。
长任务里 messages 随轮次线性膨胀。假设每步平均产生 2500 token,跑 10 步就是 25000,跑 15 步就是 37500。一旦冲出大部分模型的"舒适区",注意力开始稀释,靠后的内容权重更高,靠前找到的关键信息越来越容易被晾在一边。
很多人以为上下文越长模型越聪明。实际过了某个阈值,结果反而变差。这不是模型的 bug,是注意力机制的物理特性,你改不掉它。
上下文膨胀之外还有第二个更阴的问题:策略停滞。模型每生成一次 Thought,都得从几万 token 的历史里推出"我还缺什么"。这步推理本身极易出错,你会看到模型拿着几乎一样的关键词反复搜,或者在同一个子问题上原地绕圈,因为它"看不见"前几步其实早就找到答案了。
这两个问题,ReAct 框架自己解不了。解法是引一个固定大小的"研究摘要"进来:每轮执行完,不把原始结果一股脑堆进上下文,而是更新一份结构化的摘要,只记"目前已知什么"和"还有哪些子问题没解决"。这个设计叫 IterResearch(迭代研究框架),下一篇专门来把它实现出来。顺带一提,IterResearch 和上下文膨胀是面试里咬得很紧的一对追问,官网题库里 Agent 这块的高频追问题 按公司和难度排过,可以对着自查一遍。
今天先把眼前这个跑通,同时在你的系统里加一个监控:每次 Agent 执行完,打印一下 messages 的 token 总数。等你看到某类问题搜到第 5 步就已经过两万 token,你心里就有数了,知道该上下一篇那套方案了。
上下文膨胀导致早期信息被稀释的前后对比图
上下文膨胀导致早期关键信息被稀释
六、系统提示:让 ReAct 真正能工作的那块隐藏拼图
上面代码里有个SYSTEM_PROMPT,我一直没展开。这里补上,因为系统提示写得行不行,直接决定 ReAct 能不能按你预期那样跑。
很多人觉得系统提示就是"自我介绍一下你是谁"。在 Agent 里它要干的是三件硬活:定义角色、规定输出格式、设死行为边界。
SYSTEM_PROMPT = """你是一个专业的研究型 AI 助手,擅长通过多轮工具调用完成复杂信息检索任务。 工作方式: - 遇到问题,先分析需要找哪些信息,再逐步检索 - 每次调工具前,先想清楚:调它是为了补哪个具体的信息缺口 - 拿到结果后,判断够不够用;不够就继续搜,够了就综合作答 - 同一个搜索词搜了两次没新信息,换个角度重搜,别重复无效操作 输出格式: - 调工具前,先用一两句话说明你要做什么(Thought) - 结果回来后,先评估质量再决定下一步 - 最终作答时,标注每条关键信息的来源 边界: - 信息来源之间有矛盾,明确指出,不要自己"融合"互相打架的信息 - 某个问题在现有检索结果里找不到答案,直接说找不到,不许编 """最后两条边界,盯的就是前面"把欧盟当成美国"那类事故。在系统提示里把"矛盾信息要指出、不许融合"写死,能压下一截信息混淆的概率。效果是实打实的,加了这两条之后,我们测试集上的信息混淆率从 13% 降到了 6%。
好的系统提示不是写得越长越好,是每一条都对着你在测试里真见过的问题写。先让 Agent 跑起来、让它真实地出错,再有针对性地往里加约束。一上来就堆一大段,通常没用,模型会"读不进去"。
面试怎么答 Deep Research Agent 相关问题?
面试官抛这类问题,多半不是考你能不能背定义,是看你有没有真把系统跑到出过事的地方,然后想过它为什么出事。
先把 RAG 和 Deep Research Agent 的差距讲透,用一个具体对比,别讲抽象(30秒):
别上来就甩技术细节,先讲你见过的真实失败:
“我们第一版用的是 RAG,有个客户问了个跨市场政策比较,系统返回一段摘要,客户直接说’这不就是搜索引擎’。我们这才反应过来,需要多轮动态检索的任务,RAG 那一次性检索根本接不住。”
这比干巴巴说"RAG 只能单跳、Agent 能多跳"有说服力得多。
再讲 ReAct 怎么工作,重点压在"消息历史是怎么攒起来的"(1分钟):
“ReAct 每步执行完,把 Thought、Action、Observation 追加进 messages,下一步的输入就带着完整历史链。我们在这踩过坑:10 轮之后 messages 里三四万 token,模型对早期信息的注意力开始掉,我们内测就因为这个把欧盟数据混进了美国政策的报告里,排查了两小时。”
带具体失败案例的回答,比泛泛说"上下文管理很重要"可信得多。
接着讲你怎么处理工具失败(30秒):
“工具失败在生产里很常见,我们的做法是失败不崩,而是把错误信息写回 Observation,让模型自己决定换策略继续。加了这个之后,成功率从 78% 提到了 91%。”
报出具体数字,说明你测过、量过,不是张口就来。
最后如果面试官追问上下文溢出的解法(加分项):
"我们用了一个叫 IterResearch 的框架,不把原始搜索结果堆进上下文,而是每轮更新一份固定大小的研究摘要,只记’目前已知’和’还缺什么’。这样上下文大小保持常量,跑多少轮都不溢出。"能说到这一层的候选人很少,直接拉开差距。
写在最后
Deep Research Agent 这个系列,从今天起会一路写到能完整部署的系统。我不打算按文档顺序把概念铺一遍,而是每篇都从"我们真踩过的坑"出发,先说清楚为什么要这么设计、坑长什么样,再给出能跑的代码。
今天这个最小 ReAct 框架是起点。下一篇我们让它结实起来:加上重复检测(防模型原地打转)、更细的错误处理、还有 token 监控。跑完下一篇,你手里会是一个能接生产流量的 ReAct 执行器。
这些细节我都整理在了官网,项目案例里有 Deep Research Agent 从架构、数据到训练评估的完整文档,配可运行代码和逐步推导;题库里有 Agent 这块的高频追问题,按公司和难度分好了。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
