专栏名称: 架构师
架构师云集,三高架构(高可用、高性能、高稳定)、大数据、机器学习、Java架构、系统架构、大规模分布式架构、人工智能等的架构讨论交流,以及结合互联网技术的架构调整,大规模架构实战分享。欢迎有想法、乐于分享的架构师交流学习。
目录
相关文章推荐
英国大家谈  ·  什么是CRS ·  21 小时前  
新疆是个好地方  ·  定了!乌鲁木齐天山国际机场 ·  昨天  
新疆949交通广播  ·  乌鲁木齐春季道路修补大升级,提前一个月告别 ... ·  昨天  
英国那些事儿  ·  我从婆家买下旧谷仓,动手改造10年,终有了今 ... ·  4 天前  
英国那些事儿  ·  韩国大爷独自一人,用20年建造N座石塔!这造 ... ·  4 天前  
51好读  ›  专栏  ›  架构师

DeepSearch 与 DeepResearch 的设计和实现

架构师  · 公众号  ·  · 2025-03-13 22:29

正文

架构师(JiaGouX)
我们都是架构师!
架构未来,你来不来?



这才 2 月份,深度搜索(Deep Search)就已经隐隐成为 2025 年的新搜索标准了。像谷歌和 OpenAI 这样的巨头,纷纷亮出自己的“Deep Research”产品,努力抢占这波技术浪潮的先机。(我们也很自豪,在同一天也发布了开源的 node-deepresearch )。

Perplexity 紧随其后,也推出了他们的 Deep Research。而马斯克的 X AI 则更进一步,直接把深度搜索功能整合进了他们的 Grok 3 模型里,本质上是 Deep Research 的一种变体。

坦白说,深度搜索这个概念并不算什么创新,它本质上就是我们去年常说的 RAG(检索增强生成)或者多跳问答。但在今年一月底,随着 Deepseek-r1 的发布,它获得了前所未有的关注和发展。

就在上周末,百度搜索和腾讯微信搜索都已经把 Deepseek-r1 整合到他们的搜索引擎里了。AI 工程师们意识到, 通过把长期的思考和推理过程融入到搜索系统中,能够实现比以往任何时候都更精准、更深入的检索效果。

但这种转变为何偏偏发生在当下?整个 2024 年,“Deep(Re)Search”似乎都未引起太多关注。要知道,早在 2024 年初,斯坦福 NLP 实验室就发布了 STORM 项目,基于网络的长篇报告生成。难道仅仅因为“Deep Search”听起来比多跳 QA、RAG 或 STORM 更时髦吗?不过说实话,有时候一次成功的品牌重塑,就能让行业突然接受那些早已存在的东西。

我们认为真正的转折点是 OpenAI 在 2024 年 9 月发布的 o1-preview ,它引入了“ 推理时计算”(test-time compute) 的概念,并潜移默化地改变了行业认知。 所谓“推理时计算”,指的是在推理阶段(即大语言模型生成最终结果的阶段)投入更多计算资源,而非集中在预训练或后训练阶段。经典的例子包括思维链(Chain-of-Thought,CoT)推理,以及诸如 "Wait" 注入(也称预算控制)等技术,这些技术赋予模型更广阔的内部思考空间,例如评估多个潜在答案、进行更深入的规划,以及在给出最终答案前进行自我反思。

这种“推理时计算”理念,以及专注于推理的模型,都在引导用户接受一种“延迟满足”的观念: 用更长的等待时间,换取更高质量、更具实用性的结果。 就像著名的斯坦福棉花糖实验,那些能够抵制立即吃掉一个棉花糖的诱惑,从而获得稍后两个棉花糖的孩子,往往能取得更好的长期成就。Deepseek-r1 进一步巩固了这种用户体验,无论你是否喜欢,大多数用户都已经默默接受了这一点。

这标志着与传统搜索需求的重大背离。 过去,如果你的解决方案无法在 200 毫秒内给出响应,那几乎等同于失败。但在 2025 年,经验丰富的搜索开发者和 RAG 工程师们,将 top-1 精确率和召回率置于延迟之前。用户已经习惯了更长的处理时间:只要他们能看到系统在努力

在 2025 年,展示推理过程已经成为一种标准做法,许多聊天界面都会在专门的 UI 区域中渲染 的内容。

在本文中,我们将通过研究我们的开源实现来讨论 DeepSearch 和 DeepResearch 的原理。我们将介绍关键的设计决策,并指出潜在的注意事项。


什么是 Deep Search?

DeepSearch 的核心理念是通过在搜索、阅读和推理三个环节中不断循环往复,直到找到最优答案。 搜索环节利用搜索引擎探索互联网,而阅读环节则专注于对特定网页进行详尽的分析(例如使用 Jina Reader)。推理环节则负责评估当前的状态,并决定是应该将原始问题拆解为更小的子问题,还是尝试其他的搜索策略。

DeepSearch - 持续搜索、阅读网页、推理,直到找到答案(或超出 token 预算)。
DeepSearch - 持续搜索、阅读网页、推理,直到找到答案(或超出 token 预算)。

与 2024 年的 RAG 系统不同,RAG 一般只运行一次搜索-生成过程,DeepSearch 执行多次迭代,需要明确的停止条件。这些条件可以是基于 token 使用限制,或者失败尝试的次数。

在 search.jina.ai 尝试 DeepSearch,观察 中的内容,看看你是否能发现循环发生的位置。

换个角度来看, DeepSearch 可以被视作一个配备了各类网络工具(比如搜索引擎和网页阅读器)的 LLM Agent。 这个 Agent 通过分析当前的观察结果以及过往的操作记录,来决定下一步的行动方向:是直接给出答案,还是继续在网络上探索。这就构建了一种状态机架构,其中 LLM 负责控制状态间的转换。

在每一个决策点,你都有两种可选方案:你可以精心设计提示词,让标准的生成模型产生特定的操作指令;或者,也可以利用像 Deepseek-r1 这样专门的推理模型,来自然而然地推导出下一步应该采取的行动。然而,即使使用了 r1,你也需要定期中断它的生成过程,将工具的输出结果(比如搜索结果、网页内容)注入到上下文之中,并提示它继续完成推理过程。

归根结底,这些都只是实现细节而已。无论你是精心设计提示词,还是直接使用推理模型, 它们都遵循 DeepSearch 的核心设计原则:搜索、阅读和推理 的持续循环。

那 DeepResearch 又是什么呢?

DeepResearch 是在 DeepSearch 的基础上,增加了一个结构化的框架,用于生成长篇的研究报告。它的工作流程一般从创建目录开始,然后系统性地将 DeepSearch 应用于报告的每一个所需部分:从引言到相关工作、再到方法论,直至最后的结论。报告的每个章节都是通过将特定的研究问题输入到 DeepSearch 中来生成的。最后将所有章节整合到一个提示词中,以提高报告整体叙述的连贯性。

DeepSearch 作为 DeepResearch 的基础构建块。通过 DeepSearch 迭代构建每个章节,然后在生成最终长报告前改进整体连贯性。
DeepSearch 作为 DeepResearch 的基础构建块。通过 DeepSearch 迭代构建每个章节,然后在生成最终长报告前改进整体连贯性。

在 2024 年,我们内部也做过 "Research" 项目,当时为了保证报告的连贯性,我们采用了比较笨的办法,每次迭代都会把所有章节都考虑进去,进行多次连贯性改进。但现在看来,这种做法有些用力过猛了,因为如今的大语言模型已经拥有了超长的上下文窗口,完全可以一次性完成连贯性修订,效果反而更好。

但我们最终还是没有发布 “Research” 项目,原因有如下几条:

最主要的是报告质量始终未能达到我们的内部标准。我们用两个内部熟知的查询测试,分别是“Jina AI 的竞争对手分析”和“Jina AI 的产品策略”。结果却让人失望,报告内容平庸,缺乏亮点,没有给我们带来任何“啊哈”的惊喜。其次,搜索结果的可靠性也很差,幻觉问题严重。最后,整体可读性也很糟糕,各个部分之间存在大量的重复冗余。总而言之,就是毫无价值。而且报告篇幅冗长,读起来既浪费时间又毫无收获。

不过,这个项目也为我们积累了宝贵的经验,并催生了一些子产品:

比如, 我们深刻认识到搜索结果可靠性,以及在段落甚至句子级别进行事实核查的重要性,这直接促使我们后来开发了 g.jina.ai 端点。 我们还意识到了查询扩展的价值,并开始投入精力训练小型语言模型(SLM)来进行查询扩展。最后,我们非常喜欢 ReSearch 这个名字,它既巧妙地表达了重塑搜索的理念,又是一个双关语。不用实在可惜,所以最终把它用在了 2024 年的年鉴上。

2024 年夏季,我们的“研究”项目则采用了“渐进式”方法,专注于生成篇幅较长的报告。它首先同步生成报告的目录(TOC),然后同步生成所有章节的内容。最后,再以异步的方式对每个章节进行渐进式修订,每次修订都会考虑到报告的整体内容。在上面的演示视频中,我们使用的查询是“Jina AI 的竞品分析”。


DeepSearch vs DeepResearch

很多人容易把 DeepSearch 和 DeepResearch 混为一谈。但在我们看来,它们解决的是完全不同的问题。 DeepSearch 是 DeepResearch 的构建模块,是后者赖以运转的核心引擎。

DeepResearch 的重心是撰写高质量、可读性强的长篇研究报告。 这不仅仅是搜索信息,更是一项系统工程 ,需要整合有效的可视化元素(如图表、表格),采用合理的章节结构,确保子章节之间逻辑顺畅,全文术语一致,避免信息冗余,并运用流畅的过渡句衔接上下文。这些要素与底层的搜索功能并没有直接关联,因此我们更将 DeepSearch 作为公司发展重点。

总结 DeepSearch 和 DeepResearch 的区别,详见下表。值得一提的是, DeepSearch 和 DeepResearch 都离不开长上下文和推理模型,但原因略有不同。

DeepResearch 生成长报告需要长上下文,这很好理解。而 DeepSearch 虽然看起来是搜索工具,但为了规划后续操作,它也需要记住之前的搜索尝试和网页内容,所以长上下文同样不可或缺。


了解 DeepSearch 实现

开源链接: https://github.com/jina-ai/node-DeepResearch

DeepResearch 的核心在于其循环推理机制。与大多数 RAG 系统试图一步到位地回答问题不同,我们采用了一种迭代循环的方式。它会持续搜索信息、阅读相关来源并进行推理,直到找到答案或耗尽 token 预算。以下是这个大型 while 循环的精简骨架:

// 主推理循环
while (tokenUsage < tokenBudget && badAttempts <= maxBadAttempts) {
  // 追踪进度
  step++; totalStep++;

  // 从 gaps 队列中获取当前问题,如果没有则使用原始问题
  const currentQuestion = gaps.length > 0 ? gaps.shift() : question;

  // 根据当前上下文和允许的操作生成提示词
  system = getPrompt(diaryContext, allQuestions, allKeywords,
                    allowReflect, allowAnswer, allowRead, allowSearch, allowCoding,
                    badContext, allKnowledge, unvisitedURLs);

  // 让 LLM 决定下一步行动
  const result = await LLM.generateStructuredResponse(system, messages, schema);
  thisStep = result.object;

  // 执行所选的行动(回答、反思、搜索、访问、编码)
  if (thisStep.action === 'answer') {
    // 处理回答行动...
  } else if (thisStep.action === 'reflect') {
    // 处理反思行动...
  } // ... 其他行动依此类推
}

为了保证输出的稳定性和结构化,我们采取了一个关键措施: 在每个步骤中,有选择地禁用某些操作。

比如,当内存里没有 URL 时,我们会禁止 “visit” 操作;如果上次的回答被拒绝,我们会阻止 Agent 立即重复 “answer” 操作。这种约束机制能引导 Agent 沿着正确的方向前进,避免在原地打转。

系统提示词

在系统提示词的设计上,我们使用 XML 标签来定义各个部分,这样可以生成更健壮的系统提示词和生成内容。同时,我们发现直接在 JSON Schema 的 description 字段中加入字段约束,效果更好。诚然,像 DeepSeek-R1 这样的推理模型,理论上可以自动生成大部分提示词。但考虑到上下文长度的限制,以及我们对 Agent 行为的精细控制需求,这种显式地编写提示词的方式在实践中更可靠。

function getPrompt(params...) {
  const sections = [];

  // 添加包含系统指令的 Header
  sections.push("你是一个高级 AI 研究助理,擅长多步骤推理...");

  // 添加已积累的知识片段(如果存在)
  if (knowledge?.length) {
    sections.push("[知识条目]");
  }

  // 添加之前行动的上下文信息
  if (context?.length) {
    sections.push("[行动历史记录]");
  }

  // 添加失败的尝试和学习到的策略
  if (badContext?.length) {
    sections.push("[失败的尝试]");
    sections.push("[改进策略]");
  }

  // 根据当前状态定义可用的行动选项
  sections.push("[可用行动定义]");

  // 添加响应格式指令
  sections.push("请以有效的 JSON 格式响应,并严格匹配 JSON schema。");

  return sections.join("\n\n");
}

遍历知识空白问题

在 DeepSearch 中, “知识空白问题”指的是在回答核心问题之前,Agent 需要先补足的知识缺口。 Agent 不会直接尝试回答原始问题,而是会识别并解决那些能够构建必要知识基础的子问题。

这种处理方式非常优雅。

// 在“反思行动”中识别出知识空白问题后
if (newGapQuestions.length > 0) {
  // 将新问题添加到队列的头部
  gaps.push(...newGapQuestions);

  // 始终将原始问题添加到队列的尾部
  gaps.push(originalQuestion);
}

它创建了一个带轮转机制的 FIFO(先进先出)队列,遵循以下规则:

  1. 新的知识空白问题会被优先推到队列头部。

  2. 原始问题始终位于队列尾部。

  3. 系统在每个步骤都从队列头部提取问题进行处理。

这种设计的精妙之处在于,它为所有问题维护了一个共享的上下文。也就是说,当一个知识空白问题被解决后,获得的知识可以立即应用于所有后续问题,最终也会帮助我们解决最初的原始问题。

FIFO 队列 vs 递归

除了 FIFO 队列,我们还可以采用递归的方式,这其实对应于深度优先搜索策略。对于每一个“知识空白”问题,递归都会创建一个全新的调用栈,拥有独立的上下文。系统必须彻底解决每一个知识空白问题(及其所有潜在的子问题)之后,才能返回到父问题。

举个例子, 一个简单的 3 层深度知识空白问题递归,圆圈内的数字标注了解决问题的顺序。

在递归模式下,系统必须先完全解决 Q1(及其可能衍生的子问题),才能继续处理其他问题! 这与队列方法形成鲜明对比,队列方法会在处理了 3 个知识空白问题之后,就会重新回到 Q1。

在实际应用中,我们发现递归方法很难控制预算。因为子问题可能会继续衍生新的子问题,没有明确的指导原则,很难确定应该为它们分配多少 Token 预算。跟复杂的预算控制和可能出现的延迟返回问题比起来,递归带来的清晰上下文隔离优势就显得有点微不足道了。相反,FIFO 队列的设计则很好地平衡了深度和广度,确保系统不断积累知识,逐步改进,并最终回到原始问题,而不是深陷于潜在的无限递归泥潭里。

查询重写

我们遇到的一个颇有意思的挑战,是如何有效地重写用户的搜索查询:

// 在搜索行为处理器中
if (thisStep.action === 'search') {
  // 搜索请求去重
  const uniqueRequests = await dedupQueries(thisStep.searchRequests, existingQueries);

  // 将自然语言查询重写为更有效的搜索表达式
  const optimizedQueries = await rewriteQuery(uniqueRequests);

  // 确保不重复之前的搜索
  const newQueries = await dedupQueries(optimizedQueries, allKeywords);

  // 执行搜索并存储结果
  for (const query of newQueries) {
    const results = await searchEngine(query);
    if (results.length > 0) {
      storeResults(results);
      allKeywords.push(query);
    }
  }
}

我们发现, 查询重写的重要性远超预期,甚至可以说是决定搜索结果质量的最关键因素之一。 一个优秀的查询重写器,不仅能将用户的自然语言转化为更适合 BM25 算法处理的关键词形式,还能扩展查询,从而覆盖不同语言、语调和内容格式下的更多潜在答案。

在查询去重方面,我们最初尝试过基于 LLM 的方案,但发现难以精确控制相似度阈值,效果并不理想。最终,我们选择了 jina-embeddings-v3 。它在语义文本相似度任务上的出色表现,让我们得以轻松实现跨语言去重,而且不必担心非英语查询被误判过滤。说来也巧,最终发挥关键作用的,居然是 Embedding 模型。一开始我们也没打算把它用于内存检索,但意外地发现它在去重任务上表现得非常高效。







请到「今天看啥」查看全文