Jina.AI DeepSearch

Deep Research

DeepResearch

次级研究优化器

次级优化器

参数预处理 & 消息标准化

  • 做什么:整理入参、抽出真正的 question,去掉外部传入的 system 消息。

  • 主要数据questionmessages

  • 可扩展:在这里做多模输入预处理(比如把图片/OCR结果拼进 messages)。

语言/Schema 初始化

  • 做什么Schemas 推断语言,必要时设置 searchLanguageCode;初始 schema = SchemaGen.getAgentSchema(...)

  • 主要数据SchemaGen.languageCode、按功能开关生成的 zod schema。

  • 可扩展:支持更多动作(在 schema 里加 action),或按领域切换不同 schema 模板。

上下文容器与生成器

  • 做什么:构造 TrackerContextTokenTracker + ActionTracker),以及 ObjectGeneratorSafe(负责让模型产出结构化对象)。

  • 主要数据context.tokenTrackergenerator

  • 可扩展:自定义 token 预算策略、记录更细的埋点。

全局状态池与标志位

  • 做什么:建一堆“会话内记忆与状态”

    • 队列与集合:gapsallQuestionsallKnowledgeallURLsallWebContentsvisitedURLsbadURLsimageObjects

    • 控制开关:allowAnswer/search/read/reflect/coding

    • 评估框:evaluationMetrics(每个问题需要通过的 evaluator 集)

    • 预算:regularBudget = tokenBudget * 0.85(剩余 15% 预留给 beast mode)

  • 可扩展:把这些状态换成你自己的 state store / KV / DB。

从消息里先挖 URL(冷启动 URL 池)

  • 做什么:扫描 messages 文本,extractUrlsWithDescription()addToAllURLs(),为后续 URL 打分做冷启动。

  • 主要数据allURLs(Map: url → {title, description, date, weight…})。

  • 可扩展:在这里接入“用户指定必须访问的来源白名单/黑名单”。

主循环(直到token预算用完)

循环体是整条“计划→行动→评估→记忆”的状态机——每步只做一个动作(LLM 决策)。
DeepSearch

当前问题选择 & evaluator 设定

  1. 轮询 gapscurrentQuestion

  2. 第一次针对原始问题:evaluateQuestion() 给出需要通过的评估类型集合(含 strict),并记录它们的重复容忍次数 numEvalsRequired

    • 明确性

    • 实时性

    • 多样性

    • 完整性

    • 强制

      从以上几个角度来评估原始问题

  3. 如检测到 freshness(实时性),第一步禁止 answer/reflect,强制先信息收集。

URL 池重排 & 限流

  1. filterURLs(URL正则过滤)rankURLs(cos相似度结合jinaRerank排序URL)keepKPerHostname(同一个域名保留前K个) 得到 weightedURLs(URL权重,如果相似则);并据此更新开关:

    • 有 URL 就允许 visit

    • URL 太多(≥50)就临时禁止 search(对于我们的项目可以去除)

  2. 可扩展:往 rankURLs 注入我们自己的 reranker/多源信号(域名频次、路径深度、时间戳、语义相关性)。

组 Prompt & 拉一次结构化决策

  • 做什么getPrompt() + composeMsgs() 生成 system+messages,喂给 generator.generateObject(),产出一步的 thisStep

    • action ∈ {answer, reflect, search, visit, coding} 之一

    • 动作对应字段(如 searchRequests, URLTargets, questionsToAnswer, answer

    各动作的选择依据和触发条件如下:

    • action-visit(读取网页)

      • 包含条件:allowRead 为 true,且 urlList 非空(从 allURLs 选出前 20 条,带有 score 权重与简要 merged 片段)。

      • 使用场景:需要用外部网页“落地”答案、获取全文证据、补充知识;提示中强调“高权重更相关”“必须检查问题中提到的 URL(如有)”。

      • 选择依据:问题显然需要最新或外部来源、已有相关 URL 可读;权重高的链接优先。

    • action-search(搜索)

      • 包含条件:allowSearch 为 true(默认开启)。

      • 使用场景:需要发现新信息、构造高质量搜索请求,尤其是当前没有足够的可读 URL、或存在知识缺口、或问题涉及多个方面。

      • 选择依据:提示要求“优先单一搜索请求,除非问题确实多方面”,并提供 (来自 allKeywords,记录之前失败的查询)以避免重复错误查询。

    • action-answer(直接回答)

      • 包含条件:allowAnswer 为 true;若 beastMode 为真,还会追加一段“强制进攻”的答题版本,鼓励在不完全确定时也给出有根据的回答。

      • 使用场景:问候、闲聊、一般常识题,或用户让你取用已存在的聊天历史;对于其他问题,则要给“已验证”的答案,若不确定可转用 reflect。

      • 选择依据:如果信息已充足、问题不需外部检索,或属于闲聊/常识,就直接回答;beastMode 会降低犹豫阈值,倾向更积极输出。

    • action-reflect(反思规划)

      • 包含条件:allowReflect 为 true(并且在主循环里会根据问题数量上限控制,且遇到“时效性”评估时可能被禁用)。

      • 使用场景:识别知识缺口、列出关键澄清问题、规划下一步(如先搜索再阅读)。

      • 选择依据:当不确定是否能直接回答、信息不足、需要更好地分解问题、或需要制定更稳健的查询策略时选择。

    • action-coding(编码协助)

      • 包含条件:allowCoding 为 true(在主逻辑里通常只在检测到编程/数据处理类任务时才开启)。

      • 使用场景:用户描述的是计数、过滤、转换、排序、正则提取、数据处理等编程类问题;提示说明“用 JavaScript 解决,由工程师实现,你只需描述问题”。

      • 选择依据:当问题明显是数据处理/编程任务,且不需要搜索或阅读外部内容时优先选择。

    其他影响决策的重要信号:

    • URL 权重与列表:sortSelectURLs 从 allURLs 中挑出最相关的前 20 条,提示里明确“高权重更相关”,引导模型访问更有用的链接。

    • 失败查询记忆:allKeywords 作为 ,避免模型在 action-search 重复低质量或失败的搜索请求。

    • 上下文与行动历史:context 会被插入 ,模型可据此了解已做过的步骤、避免重复无效行动。

    • 最终约束:提示结尾要求“基于当前上下文,必须选择一个动作,并按该动作的 schema 返回”,结合 Zod 的动作 schema 解析,保证只选其一且格式正确。

动作执行(五选一)

A) answer

  1. 当模型“第一步就有把握地直接回答”则直接退出

  2. 答案评估器

    • 从确定性、时效性、多样性、完整性几个方面对答案做出检查
  3. 判断是否是原问题还是子问题

    • 如果评估通过的是子问题,则补齐了一个gap(队列)

    • 如果原问题和子问题都评测失败,则allowAnswer为False,继续后续补全证据

  4. 原题+评估通过 ->成功

  5. 原题+评估失败->复盘/惩罚/切换策略

  6. 子问题+评测通过->补齐gap、沉淀知识

B) reflect

  1. 去重+截断

    • 把候选子问题域all_question去重,这里用了jina dedup,编码后计算相似度来进行去重

      采用 late-chunking 和 jina-embeddings-v3 完美解决了下面三个问题:

      1. 每个块不能太长,因为 embedding 模型无法很好地处理长上下文;

      2. 分块会导致上下文丢失并使块 embeddings 变得独立同分布;

      3. 如何找到最佳的边界线索来同时保持可读性和语义
        jina-embeddings-v3

    • 每一步限制最多新增2个子问题

    1. 如果有新的子问题

      • 加入队列中还有全量问题集
    2. 如果没有新的子问题

      • 对currentQuestion尝试拆解但是发现都问过了

      • 那么给上下文打上需要换一个思路,引导后续策略转向(比如改搜法、换证据、走别的推理路径)。

  2. 降频:为了防止“生成子问题”截断反复空转,这一步之后暂时allow_reflect = False

C) search

  1. 去重+截断

  2. 第一次搜索需要冷启动

    1. 记录要搜什么

    2. 格式化query:构造最终查询文本

    3. 调用具体搜索源,文中使用的jina搜索

    4. 标准化 SERP 结果为最小片段 minResults

      • 字段:{ title, url: normalizeUrl(r.url || r.link), description: r.description || r.snippet, weight:1, date }

      • 过滤url为不合法的

    5. 写入全局缓存与内容摘要

      • 对每个 minResult

        • utilityScore += addToAllURLs(r, allURLs)(统计贡献分;用于后续诊断日志)

        • webContents[r.url] = { title, chunks:[description], chunk_positions:[[0, description.length]] }

      • 最终发出的查询文本(可能含 site:)加入 searchedQueries

    6. 聚类抽取(serpCluster)→ 结构化知识

    7. 无论聚类是否成功,都补一条“side-info”与动作追踪

    8. 返回:提炼出的知识,发出的查询字符串

  3. 拆分子问题,并且并行求解(递归)(人为给定是否并行)

  4. 如果只拆分单个问题则后逐个解决

  5. 基于初步情报改写查询(二次搜索前置)

    • soundBites 触发 query expansion / rewrite

    • 与历史 allKeywords 再去重,避免重复打同一批词

    • 如果同文案有多种“变体”(例如带/不带站点限制),策略是保留更“宽”的版本{ q }),否则保留唯一的那条结构化查询。

  6. 第二次搜索(“热启动细化”)

    • 加上了onlyHostnames限定了域名范围?(为什么?第二次搜索需要去白名单域中搜索,如:权威媒体、官方文档、学术站,提升可信度与可引用性

    • 第一次搜索是召回优先,第二次要求精度优先

  7. 空结果分支与“换思路提示”

    • 若二次查询为空或完全没新查询,记录“无新增信息,建议换角度”的提示,避免无谓重复搜。
  8. 限流下一步动作

    • 搜索后暂时禁止马上再搜或直接作答,促使系统先进行阅读/抽取/验证等严肃步骤,提升答案质量。

D) visit

  1. 把下标转换成 URL,并做去重过滤

    • URLTargets 里是1 基下标(注意 idx - 1),从 urlList 取出 URL。

    • normalizeUrl 格式化URL。

    • 过滤掉空 URL 与已访问过的(visitedURLs)。

  2. 合并“加权候选”并限流

    • 把前面选出来的 URL 与 weightedURLs(搜索/并行子问题阶段积累的高价值 URL)合并去重

    • 只保留前 MAX_URLS_PER_STEP 个,避免一次访问过多页面。

  3. 若有待访问 URL → 执行抓取与解析

    • processURLs 负责实际访问、抽取要点/证据、更新缓存/索引(常见操作:

      • 把成功访问的加入 visitedURLs

      • 可能把异常站点记入 badURLs

      • 解析出的文本/块写入 allWebContents

      • 产出的知识写入 allKnowledge

    • withImages 决定是否抽图;imageObjects 用来承载图像结果。

    • 返回:

      • urlResults:每个 URL 的处理摘要(含 URL、提要、也可能含引用片段)。

      • success:本批是否整体成功(至少有收获)。

  4. 若没有任何可访问 URL->说明没有新东西

  5. 节流,allowRead = False

E) coding

  1. 初始化沙盒

    • 给沙盒的“上下文”包括:全局上下文、前 20 个加权 URL(可作参考/依赖)、以及已知知识。

    • 目的:让求解器在尽量多的可用线索下尝试“自动编写/运行代码”解决 codingIssue

  2. 尝试求解

    • 期望 result.solution 含:

      • output: 运行结果/终端输出/答案说明

      • code: 生成的源代码

  3. 成功

    • 把解法写入知识库 allKnowledge.push({ ... type: 'coding' })

      • question: 以自然语言存储“这次修复了什么问题”

      • answer: result.solution.output

      • sourceCode: result.solution.code

      • updated: 时间戳(formatDateBasedOnType(new Date(), 'full')

  4. 失败

    • logError(...) 记录错误信息。

    • diaryContext 里写明“编码尝试失败,需要换角度”。

    • updateContext({... , result: '...no new information...' }):给出面向策略切换的提示。

  5. 下一步禁止再立刻 coding

上下文存储

保存 落盘 prompt/schema/上下文快照;wait(STEP_SLEEP) 统一限速。

Beast Mode(收束兜底)

  • 触发:循环结束且还没拿到 isFinal。(最终结果)

  • 做什么:关掉全部动作,只保留 answer,用更“激进”的模型 agentBeastMode 基于现有 allKnowledge/diaryContext 给出一个必须产出的最终答案。

8) 答案后处理与引用构建

三种收尾路径

(在answer之后会输出类型的)

  1. 简单题

    只把已有答案转成 Markdown 展示结构;不做引用/图片/清洗,因为这是“一步到位”的场景。

  2. 常规答案(非聚合)

    1. 内容清洗与修补(自内而外的管线)
    • finalizeAnswer:结合 allKnowledge 等对原答案做最终润色/补全

    • repairMarkdownFootnotesOuter:修正脚注标记位置等异常。

    • fixCodeBlockIndentation:修正代码块缩进与围栏。

    • fixBadURLMdLinks(..., allURLs):用全局 URL 索引修复坏的 markdown 链接

    • convertHtmlTablesToMd:把 HTML 表格转为 Markdown 表格。

    • repairMarkdownFinal:最后一遍小修补(统一格式/边缘问题)。

    2. 自动抽取与插入“参考文献”

    • buildReferences:从已缓存的网页内容中,对齐答案片段→打分→挑选引用

      • 关键参数:80(对齐窗口/片段长度)、maxRef(最多几条引用)、minRelScore(最小相关度)、onlyHostnames(是否只允许特定域名)。
    • updateReferences:对引用再规范化/补标题/去坏链。

  3. 聚合答案

    • 直接把各子答案拼接为最终答案,再转 md。

    • 图片:若已有 imageReferences,则

      • relevanceScore 降序排序;

      • dedupImagesWithEmbeddings语义去重,再 filterImages 过滤;

      • 截取前 10 张

    注意:聚合路径此处不重建文本引用;通常引用在聚合时已合并/去重过。

9) 结果打包返回

限制返回 URL 数量;组合

  •   { result: thisStep, context, visitedURLs, readURLs, allURLs, imageReferences? }
    
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐