作者:少年弈
地址:
https://zhuanlan.zhihu.com/p/1931818140771268336
目前想到的:用好的模型(劣质的模型甚至无法根据你的要求生成格式正常的json,以及无法评估与plan),设计精妙的Prompt(应该包含哪些角色,planner和反馈怎么设计),内存管理。
1
开篇
Deep Research如其名——深度研究,它的重心是「撰写高质量、可读性强的长篇研究报告」。
这不仅仅是搜索信息,更是一项系统工程,需要整合有效的可视化元素(如图表、表格),采用合理的章节结构,确保子章节之间逻辑顺畅,全文术语一致,避免信息冗余,并运用流畅的过渡句衔接上下文。
深度研究型搜索的关键在于模仿人的「思维链搜索」:
模型首先根据问题进行初步推理,确定基础搜索方向 执行初始搜索,获取第一批信息 基于已获取的信息,进行下一轮推理,确定进一步的搜索方向 执行细化搜索,获取更精准的信息 不断迭代这个"推理→搜索→推理"的循环,直到收集足够信息。在这个过程中,每次搜索都建立在前一次搜索结果的基础上,形成一个连贯的推理链。
因此Deep Search是Deep Research的基石——只有掌握了这种迭代式、思维链式的搜索能力,才能支撑起完整的深度研究。除了Search以外,Deep Research的复现也离不开长上下文与推理模型。
2
一些Bachground
Google与OpenAI的DeepResearch
谷歌在去年发布Gemini 2.0 Flash同时,也发布了Deep Research:
OpenAI的deep research有可能模仿了谷歌的Deep Research,只不过谷歌的Deep Research背后是定制化的Gemini 2.0 Flash,而OpenAI的deep research是定制化的o3。
这里的模型是通过RL在需要浏览和Python工具使用的现实推理任务上进行训练。通过这种训练,它学会了规划和执行多步骤的研究轨迹,以找到所需的数据,并在必要时回溯和实时响应信息。该模型还能够浏览用户上传的文件,使用Python工具绘制和迭代图表,将生成的图表和网站图片嵌入到回答中,并引用其来源中的具体句子或段落。
用户是怎么被驯化的?
OpenAI 在 2024 年 9 月发布的o1-preview,它引入了“推理时计算”(test-time compute)的概念,并潜移默化地改变了行业认知。所谓“推理时计算”,指的是在推理阶段(即大语言模型生成最终结果的阶段)投入更多计算资源,而非集中在预训练或后训练阶段。经典的例子包括思维链(Chain-of-Thought,CoT)推理,以及诸如"Wait" 注入(也称预算强制)等技术,这些技术赋予模型更广阔的内部思考空间,例如评估多个潜在答案、进行更深入的规划,以及在给出最终答案前进行自我反思。
这种“推理时计算”理念,以及专注于推理的模型,都在引导用户接受一种“延迟满足”的观念:「用更长的等待时间,换取更高质量、更具实用性的结果」。 就像著名的斯坦福棉花糖实验,那些能够抵制立即吃掉一个棉花糖的诱惑,从而获得稍后两个棉花糖的孩子,往往能取得更好的长期成就。Deepseek-R1 进一步巩固了这种用户体验,无论你是否喜欢,大多数用户都已经默默接受了这一点。
因此,当用户愿意等待5-30分钟来获得一份研究报告,他们期待的必然是高质量的内容,能为他们节省大量时间和精力。这也是所有成功的Deep Research 应用的关键—如果最终报告无法让用户产生"啊哈"时刻,无法让他们在某些时刻惊叹"这报告真厉害""这报告部分内容写得比我还好""考虑得比我还全面",那么这款应用很可能注定失败。
3
Search& AI Search
& Deep Search
3.1 传统搜索引擎与RAG
两者在 pipeline 结构和解耦逻辑专业化方面有相通之处。但 RAG 更侧重知识的语义检索与整合,以支持自动问答、对话、摘要等自然语言生成任务。而传统搜索引擎的目标是匹配和排列现有的网页文档,为用户提供导航服务。
3.2 AI Search
AI 搜索分两种,一种是「AI + 搜索」,另一种是「搜索 + AI」。虽然从形式和体验上类似,但二者还是有细微的差别。
对于「AI + 搜索」来说,搜索是服务于 AI 的工具调用(如GPT),是为了给大模型提供更多知识;对于「搜索 + AI 」来说,AI 是服务于搜索的进一步总结摘要(如Google),是为了让搜索结果更加简洁明了。
3.3 Deep Search
DeepSearch有两种实现方式:以OpenAI端到端为例的大力出奇迹(这很吃模型的性能),用一定的训练手段让模型自己去进行搜索与反思。另一种是使用非特化的模型,但是辅助工程化的实现实现这个搜索->推理->搜索的过程(下文中会详细讲)
3.3.1 端到端的模型训练
Search-o1、 Search-R1、R1-Searcher 的研究方向基本一致:构建长的搜索推理思维链,在思维链中不断调整搜索策略。一个重要共识是,强化学习比监督微调(SFT)能带来更好的泛化性。
3.3.2 工程化的DeepSearch实现
Deep Search是文本生成和搜索工具调用交替的过程,通过迭代执行搜索补充模型缺少的知识生成最终的答案。 与RAG相比,会执行搜索多次,把原始问题拆解为多个搜索query逐步进行搜索。Deep Search中一般通过特殊token标记搜索的调用,常见做法:prompting-based、sft-based及RL-based。
下图是Jina团队给出的关于深度搜索的实现:

美团与京东的外卖市场对比分析 京东在外卖领域的竞争优势 外卖市场未来发展趋势与挑战 京东外卖进军市场的战略分析
当系统认为已经收集到足够的信息后,它会生成答案,并附上答案的引用(即通过“阅读”步骤(visit)得到的详细文章内容)
生成答案并不意味着流程就此结束。系统会对答案进行进一步的检查:
当前回答的是否为原始问题,而非reflect 得到的子问题。假如只是回答了 reflect 出来的子问题,那代表原始问题还没回答,继续进入 loop 中。 答案是否通过了所有必要的评估标准,例如这个问题中,我们要通过"明确性"和"完整性"两个评估标准。
4.反思
Reflect阶段是实现"Deep"的关键环节。在这一阶段,系统会维护一个「gaps问题列表」,并不断识别出知识的缺口,从而生成子问题,假如到这个列表中。例如,这这个例子中,第四步,模型认为当前需要通过Reflect阶段,生成需要进一步研究的子问题。
需要特别注意的是,Reflect问题与Search查询是有本质区别的:
FIFO
FIFO的轮转机制遵循以下原则:
新的知识空白问题会被优先推到队列头部。 原始问题始终位于队列尾部。 系统在每个步骤都从队列头部提取问题进行处理。
这种设计的精妙之处在于,它为所有问题维护了一个共享的上下文。也就是说,当一个知识空白问题被解决后,获得的知识可以立即应用于所有后续问题,最终也会帮助我们解决最初的原始问题。
递归
在递归模式下,系统必须先完全解决 Q1(及其可能衍生的子问题),才能继续处理其他问题!这与队列方法形成鲜明对比,队列方法会在处理了 3 个知识空白问题之后,就会重新回到 Q1。
在实际应用中,Jina团队发现递归方法很难控制预算。因为子问题可能会继续衍生新的子问题,没有明确的指导原则,很难确定应该为它们分配多少 Token 预算。跟复杂的预算控制和可能出现的延迟返回问题比起来,递归带来的清晰上下文隔离优势就显得有点微不足道了。相反,FIFO 队列的设计则很好地平衡了深度和广度,确保系统不断积累知识,逐步改进,并最终回到原始问题,而不是深陷于潜在的无限递归泥潭里。
(4)内存管理:多步推理的一个关键挑战是如何有效管理代理内存。jina设计的内存系统区分了什么算作"记忆",什么算作"知识"。但无论如何,它们都是 LLM 提示词上下文的一部分,用不同的 XML 标签分隔。
3.3.3 工程手段的局限性
不论是workflow还是多Agent系统,都是在模型能力不足时使用工程的技巧提高模型上限的方式。我们必须知道的是,工程手段的优化是有上限的。而工程的奇技淫巧在未来必将被更优质的模型能力所取代,但当前的工程优化并非完全没有价值。 我们不能因为当前模型的能力在某些场景未达预期就选择干等,而放弃去做力所能及的事情。
4
复现Deep Research的核心
4.1 核心目标
在复现Deep Research时,请始终记住目标是「让你的agent变得更"Deep"」,围绕着这个目标去构建工作流、调整 prompt 、微调等等等等。
4.2 核心机制——循环推理
DeepResearch 的核心在于其循环推理机制。与大多数 RAG 系统试图一步到位地回答问题不同,Deep Research往往采用了一种迭代循环的方式。它会持续搜索信息、阅读相关来源并进行推理,直到找到答案或耗尽 token 预算。以下是这个大型 while 循环的精简骨架:
工程化通常需要构造这个循环,使用Langchain等框架协助可以构造这个循环,但是Langchain等框架不是必须的。甚至框架隐藏了许多直接与原生LLM交互的细节,也许拥抱原生的LLM不过度依赖框架才是更优解。
4.3 评估标准
deerflow的提示词是这样的
可以看出拥有类似的标准,“全面覆盖”对应「完整性」,“足够深度”对于「明确性」,“足够数量”对应「多样性」
5
实现中需要关注的其他点
将任务拆解后,不同模型可以分工协作,各司其职。例如:专门针对规划任务训练过的小模型负责规划,推理模型负责处理复杂推理,擅长写作的模型负责总结写作等生成任务。 好的系统设计更倾向于多模型协同,“All-in-one”并不一定是最优解
6
其他值得参考的东西
Co-stream
Co-Stream是斯坦福的一个实现,多个Agent进行圆桌讨论(论文链接 Assisting in Writing Wikipedia-like Articles From Scratch with Large Language Models)
Co-STORM 的简要工作流程如下:
用户设定研究主题。 多个 AI Agent(专家与协调者)围绕主题展开对话,主动提问、搜索网页、分析结果并讨论信息。 用户可旁观学习,也可随时插入问题或引导讨论。 系统实时将讨论内容整理为动态的思维导图,便于理解和回顾。 讨论结束后,系统可生成基于讨论和导图的结构化研究报告,包含引用来源。
Chain-of-Thought Prompting Elicits Reasoning in Large Language Models
这篇论文叙述了通过思路链引导模型在回答前逐步分解思路,提升复杂推理与逻辑能力。论文链接 https://arxiv.org/abs/2201.11903
7
最简实现
当时参考
https://github.com/swirl-ai/ai-angineers-handbook/tree/main/building_agents_from_scratch/deep_research_agent 实现了一版,现在回看有点拙劣。
importopenai
fromdataclassesimportdataclass,field
fromtypingimportList
importjson
fromtavilyimportTavilyClient
client=openai.OpenAI(
api_key="XXX",
base_url="https://ark.cn-beijing.volces.com/api/v3",
)
SYSTEM_PROMPT_REPORT_STRUCTURE=r"""
You are a Deep Research assistant. Given a query, plan a structure for a report and the paragraphs to be included. Make sure that the ordering of paragraphs makes sense. Once the outline is created, you will be given tools to search the web and reflect for each of the sections separately. Format the output in json with the following json schema definition:
```ts
interface Paragraph {
title: string;
content: string;
}
interface Report {
report_title: string;
paragraphs: Paragraph[];
}
```
Title and content properties will be used for deeper research. Make sure that the output is a json object with an output json schema defined above. Only return the json object, no explanation or additional text.
"""
SYSTEM_PROMPT_FIRST_SEARCH=r"""
You are a Deep Research assistant. You will be given a paragraph in a report, it's title and expected content in the following json schema definition:
```ts
interface Paragraph {
title: string;
content: string;
}
interface Input {
paragraphs: Paragraph[];
}
```
You can use a web search tool that takes a 'search_query' as parameter. Your job is to reflect on the topic and provide the most optimal web search query to enrich your current knowledge. Format the output in json with the following json schema definition:
```ts
interface SearchResult {
search_query: string;
reasoning: string;
}
interface Output {
searchResults: SearchResult[];
}
```
Make sure that the output is a json object with an output json schema defined above. Only return the json object, no explanation or additional text.
"""
SYSTEM_PROMPT_FIRST_SUMMARY=r"""
You are a Deep Research assistant. You will be given a search query, search results and the paragraph of a report that you are researching following json schema definition:
```ts
interface FirstSummaryInput {
title: string
content: string
search_query: string
search_results: []string
}
```
Your job is to write the paragraph as a researcher using the search results to align with the paragraph topic and structure it properly to be included in the report. Format the output in json with the following json schema definition:
```ts
interface FirstSummaryOutput {
paragraph_latest_state string
}
```
Make sure that the output is a json object with an output json schema defined above. Only return the json object, no explanation or additional text.
"""
SYSTEM_PROMPT_REFLECTION=r"""
You are a Deep Research assistant. You are responsible for constructing comprehensive paragraphs for a research report. You will be provided paragraph title and planned content summary, also the latest state of the paragraph that you have already created all in the following json schema definition:
```ts
interface ReflectInput {
title: string
content: string
paragraph_latest_state: string
}
```
You can use a web search tool that takes a 'search_query' as a parameter. Your job is to reflect on the current state of the paragraph text and think if you haven't missed some critical aspect of the topic and provide the most optimal web search query to enrich the latest state. Format the output in json with the following json schema definition:
```ts
interface ReflectOutput {
search_query: string
reasoning: string
}
```
Make sure that the output is a json object with an output json schema defined above. Only return the json object, no explanation or additional text.
"""
SYSTEM_PROMPT_REPORT_FORMATTING=r"""You are a Deep Research assistant. You have already performed the research and constructed final versions of all paragraphs in the report. You will get the data in the following json format:
```ts
interface SummaryInput {
parts: []Part
}
interface Part {
title: string,
paragraph_latest_state: string
}
```
Your job is to format the Report nicely and return it in MarkDown. If Conclusion paragraph is not present, add it to the end of the report from the latest state of the other paragraphs.
"""
deftavily_search(query,include_raw_content=True,max_results=3):
tavily_client=TavilyClient("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")
returntavily_client.search(query,
include_raw_content=include_raw_content,
max_results=max_results,include_images=False)
defplan_tool(question):
print("plan tool")
response=client.chat.completions.create(
model="doubao-1-5-pro-32k-250115",
messages=[{"role":"system","content":SYSTEM_PROMPT_REPORT_STRUCTURE},
{"role":"user","content":question}],
temperature=1
)
print(response.choices[0].message.content)
report_structure=json.loads(response.choices[0].message.content)
paragraphs=report_structure["paragraphs"]
returnparagraphs
defsearch_prompt_test(paragraphs):
print("run search_prompt_test")
response=client.chat.completions.create(
model="doubao-1-5-pro-32k-250115",
messages=[{"role":"system","content":SYSTEM_PROMPT_FIRST_SEARCH},
{"role":"user","content":json.dumps(paragraphs)}],
temperature=1
)
search_query=json.loads(response.choices[0].message.content)
print("search_prompt_test res = ",search_query["searchResults"])
returnsearch_query["searchResults"]
# need a string
defsingle_search_tool(search_query):
print("single_search_tool: ",search_query)
search_result=tavily_search(search_query)
returnget_search_results(search_result)
defsummary_tool(summary_input):
print("summary_tool")
response=client.chat.completions.create(
model="doubao-1-5-pro-32k-250115",
messages=[{"role":"system","content":SYSTEM_PROMPT_FIRST_SUMMARY},
{"role":"user","content":json.dumps(summary_input)}],
temperature=1
)
result=response.choices[0].message.content
print("summary_tool result = ",result)
returnjson.loads(result)
defget_search_results(search_results):
results=[]
ifsearch_results["results"]==None:
print("results is nil")
returnresults
else:
forsearch_resultinsearch_results:
ifsearch_results["results"][0]["raw_content"]==None:
print("raw_content is nil")
else:
raw_content=search_results["results"][0]["raw_content"][0:20000]
results.append(raw_content)
results
defreflection_tool(data):
print("reflection_tool")
response=client.chat.completions.create(
model="doubao-1-5-pro-32k-250115",
messages=[{"role":"system","content":SYSTEM_PROMPT_REFLECTION},
{"role":"user","content":json.dumps(data)}],
temperature=1)
print(response.choices[0].message.content)
reflect_result=json.loads(response.choices[0].message.content)
print("reflect_result = ",reflect_result)
returnreflect_result
defreport_tool(data):
print("report_tool")
response=client.chat.completions.create(
model="doubao-1-5-pro-32k-250115",
messages=[{"role":"system","content":SYSTEM_PROMPT_REPORT_FORMATTING},
{"role":"user","content":json.dumps(data)}],
temperature=1)
print("report_tool result")
print(response.choices[0].message.content)
returnresponse.choices[0].message.content
max_iter=3
defmain():
question="how to be a better programmer?"
paragraphs=plan_tool(question)
search_querys=search_prompt_test(paragraphs)
query_len=len(search_querys)
report_parts=[]
foriinrange(query_len):
part={}
summary_result=""
summary_input={}
reflect_input={}
title=paragraphs[i]["title"]
content=paragraphs[i]["content"]
# first search
search_query=search_querys[i]["search_query"]
search_res=single_search_tool(search_query)
forjinrange(max_iter):
summary_input["title"]=title
summary_input["content"]=content
summary_input["search_query"]=search_query
summary_input["search_results"]=search_res
summary_result=summary_tool(summary_input)
ifi<max_iter-1:# 最后一次不反思
reflect_input["title"]=title
reflect_input["content"]=content
reflect_input["paragraph_latest_state"]=summary_result["paragraph_latest_state"]
reflect_res=reflection_tool(reflect_input)
search_query=reflect_res["search_query"]
search_res=single_search_tool(search_query)
part["title"]=title
part["paragraph_latest_state"]=summary_result["paragraph_latest_state"]
report_parts.append(part)
report_res=report_tool(report_parts)
print("report parts ===")
print(report_parts)
print("report result ====")
print(report_res)
if__name__=="__main__":
main()
✦ END ✦
推荐阅读
使智能体情商爆表直追GPT-4o!腾讯混元开源RLVER框架,融合RL与可验证情感奖励
AI智能体推理新范式!CMU开源Agentic-R1,双策略蒸馏实现推理最优选择
ICML2025 Oral | NUS与上海AI Lab首提智能体超网概念并推出MaAS框架,可自动演化多智能体系统