乐于分享
好东西不私藏

拆解 Claude Code 源码里的 deep-research:3 个 Agent 互相驳斥的 AI 工作流,凭什么比 Perplexity 更可信?

拆解 Claude Code 源码里的 deep-research:3 个 Agent 互相驳斥的 AI 工作流,凭什么比 Perplexity 更可信?

大多数 AI 研究工具让验证 agent「确认这个答案对不对」——这会产生确认偏误。deep-research 反过来:让 3 个 agent 试图驳斥每一条主张,驳不倒的才写进报告。默认立场是「不可信」。

读完全文,你不只是了解一个系统——你会拿到四层可以直接用到自己项目里的设计原则。


先说它做了什么

deep-research 是一个多阶段、多 Agent 的深度研究流水线。给它一个自然语言问题,它会:

  1. 把问题拆成 3-6 个互补搜索角度

  2. 每个角度并行搜索,抓取网页,提取可证伪的主张

  3. 对每条主张开 3 个独立验证 agent 投票——默认立场是「试图反驳」

  4. 活下来的主张合成最终报告,被驳倒的单独列出

一次完整研究,典型规模 60-120 次 agent 调用。这不是"快速给个摘要"的工具,是"我要一个靠谱结论"的工具。


一、四阶段流水线总览

阶段名称Agent 数做什么关键设计
Phase 0Scope1 个问题 → 3-6 个互补搜索角度领域自适应:医学类用解剖/病因/鉴别/参考文献/红旗标志,技术类用前沿/基准/局限/采用率/成本
Phase 1Search + Fetch5 搜索 + ≤15 抓取并行搜索 → 去重 → 抓取网页 → 提取主张无 barrier pipeline:搜索结果即产即抓,不等全部搜索完成
Phase 2Verify≤25 主张 × 3 票每条主张 3 个 agent 独立投票,≥2/3 否决就剔除对抗性验证:prompt 要求"试图反驳",不确定时默认否决
Phase 3Synthesize1 个合并语义重复 → 分组 → 定级 → 写摘要失败兜底:agent 返回 null 时输出原始数据,不抛异常

整个流程的核心思路:Fan-out(发散)→ Fan-in(收敛)。先并行铺开收集信息,再逐层收拢验证,最后单点合成。


二、最精巧的部分:搜索-抓取流水线

为什么要用 pipeline 而不是 parallel?

传统做法是 parallel(搜索) → 等全部完成 → parallel(抓取)。问题在于:5 个搜索里如果有一个特别慢,其他 4 个搜完了也得干等。

deep-research 用 pipeline:第 1 个搜索完成就开始去重+抓取,其余搜索继续跑。 搜索和抓取在不同 URL 上并发重叠,wall-clock 时间显著降低。

模式等待时间问题
parallel(barrier)max(5个搜索) + max(15个抓取)最慢的搜索拖住全局
pipeline(无 barrier)搜索和抓取重叠执行第一个搜完就开始抓,不等其他

预算控制:双层门控

每条搜索结果进来时过两道关:

条件结果
URL 已见过记录到 dupes[],丢弃
预算用完(15 个 slot)且相关度 ≥ medium记录到 budgetDropped[],丢弃
通过两道关入 seen Map,消耗 1 个 slot,进入抓取

关键设计:被丢弃的结果不会静默消失,全部记录在 stats 里返回。你知道丢了什么、为什么丢。

抓取容错:单个失败不拖全局

每个 URL 抓取失败时 .catch() 兜底,返回 { sourceQuality: "unreliable", claims: [] }——标记为不可信,但不影响其他 URL 的抓取。


三、核心创新:3 票对抗验证

这是整个系统最值得说的部分。

大多数 AI 工具怎么验证?

Perplexity、ChatGPT 搜索、各种 AI 研究助手——它们让验证 agent 做的事是「确认这个主张是否正确」

问题在于:确认偏误。agent 天然倾向于说"是的,这个看起来对"。

deep-research 怎么验证?

每条主张开 3 个独立验证 agent,prompt 设计为对抗性:

Be SKEPTICAL. Try to REFUTE this claim.refuted=true if: unsupported by quote / contradicted / low-quality source...Default to refuted=true if uncertain.

注意最后一句——不确定时默认否决。这和大多数系统"不确定时默认通过"完全相反。

裁决规则

有效票分布结果逻辑
0 否决 / 3 有效✓ 存活一致认可
1 否决 / 3 有效✓ 存活多数认可
2 否决 / 3 有效✗ 剔除多数否决
3 否决 / 3 有效✗ 剔除一致否决
0-1 有效票(skip/error)✗ 剔除法定人数不足,默认 kill

最后一行是关键设计决策:quorum 不足时默认 kill,不是默认 survive。 防止 agent 全部 skip 导致虚假主张漏进报告。

宁可错杀,不可错放。


四、排序策略与可观测性

有限预算花在刀刃上

验证预算只有 25 条主张,怎么决定哪些值得验?

三级排序: 先按重要性(central > supporting > tangential),同级再按来源质量(primary > secondary > blog > forum > unreliable),截断到 25 条。

这不是简单的"取前 25 条",是按价值密度排序后的优先级截断

每一步都有记录

可观测项记录什么为什么重要
dupes[]被去重的 URL 及原因你知道哪些来源被合并了
budgetDropped[]因预算不足被丢弃的结果你知道还有哪些没看
stats.agentCalls精确的 agent 调用次数成本可核算
Refuted claims 区被驳倒的主张列表不删,单独列在报告里

最后一行尤其重要——被驳倒的主张没有被悄悄删除,而是显式列在报告的"Refuted claims"区域。读者能看到"系统找到了这些主张,但认为它们不可信,这是理由"。

这和 ChatGPT 给你一个看起来完整的回答然后不告诉你它跳过了什么,是两种完全不同的哲学。


五、六个可改进的点

读完源码,我标了几个可以优化的地方:

问题影响建议
URL 标准化不处理 http↔https同一内容的两个协议版本被抓两次协议无关化
URL 标准化不处理 query string?utm_source=... 等追踪参数产生假重复去 query string 再标准化
15 source / 25 claim 硬上限复杂问题覆盖面可能不足改为 token-budget 驱动,或加 --deep 模式翻倍
3 个 voter 之间无信息隔离同模型、同知识边界可能产生相同盲点给不同 voter 注入不同搜索 query 或不同角色提示
无时间衰减2015 年的文章和上周的文章权重相同排序加入 publishDate 的时间加权
领域模板有限只有医学和技术两类扩展金融/法律/社科等领域模板

第四个(voter 隔离)是最有价值的改进——当前 3 个 voter 用的是同一个模型、同一套知识边界,如果这个模型对某个事实有系统性盲点,3 票都会犯同样的错。冗余只有在不相关时才有效。


六、你自己的系统能学到什么——四层可迁移的设计原则

以上是技术分析。但如果你只看架构就太可惜了——deep-research 真正的价值在于,它展示了一套「用工程手段弥补 AI 不可靠性」的系统方法论

我把能从中提取的设计原则分成四层,从"下周就能用"到"升级你的思维方式"。

第一层:可直接复用的模式

这三个模式不需要改你的架构,插入即用。

模式一:什么时候用 Pipeline,什么时候用 Barrier

deep-research 在搜索→抓取阶段用 pipeline(无屏障),在验证阶段用 barrier(必须等齐)。

判断标准不是"快慢",而是:下游是否需要全局比较。

搜索不需要等齐——第一条搜索结果到了就可以开始抓取。但验证必须等齐——你没法排序"哪些主张更重要"如果还没看到全部主张。

一句话记住:如果下游只需要消费(消费一条算一条),用 pipeline;如果下游需要排序或聚合(必须看到全局),用 barrier。

模式二:预算不是硬切的,是按优先级截断的

常见做法是 source.slice(0, 15)——简单硬切。但 deep-research 对 15 个 slot 按 relevance(high → medium → low) 排序后逐条消费,被截断的 source 会被记录到 budgetDropped[],不会静默消失。

差别很小,但效果很大——你永远知道"有什么东西因为预算不够被丢掉了"。下次升级系统,优先补的就是它。

模式三:阶段间用 JSON Schema 做强契约

6 个 Schema(SCOPE / SEARCH / EXTRACT / VERDICT / REPORT),阶段 A 的输出在结构上直接兼容阶段 B 的输入。不需要解析逻辑,不需要容错代码。

一个 schema 三四十行,能消灭下游 80% 的异常处理代码。

第二层:质量工程理念

这三个理念解决同一个问题:AI 输出的可信度从哪来?

原则一:让验证方"想驳斥",而不是"想确认"

几乎所有 AI 工具在验证阶段问的都是"这个结论对吗?"——这是确认偏误。deep-research 问的是"这个结论有什么漏洞?"——这是对抗验证。

提示词设计决定了验证的质量。Be SKEPTICAL. Try to REFUTE this claim. Default to refuted=true if uncertain.——这三句话比任何架构设计都值钱。

如果你在做多 Agent 系统,给 Reviewer 一个具体的驳斥角度,而不是让它从同一个方向看。

原则二:信息不足时,默认否决

裁决规则里最关键的一条:quorum 不足时默认 kill。不是"放行",不是"标记为低置信度",是直接否决。

这违背直觉——信息不足难道不应该保守处理吗?但在 AI 质量场景里,信息不足恰恰是危险信号:如果三个独立验证 agent 都找不到足够依据来投票,这条主张大概率就是虚的。宁杀错不放过在这里是一种故意的偏置。

原则三:voter 之间的差异比共识更有价值

三个 voter 都同意"这条正确"——这其实没多少信息量,它们可能是共享了同一种盲区。但如果 voter A 说"来源不可靠",voter B 说"结论与另一来源矛盾",voter C 说"数据没问题但推论跳跃"——这三个不同的否决理由告诉你比"全票通过"多得多的东西。

给不同 voter 不同的 prompt 方向(事实准确性、逻辑严谨性、覆盖完整性),是用成本换信号密度。3 个同质 voter 不如 2 个差异化 voter 有用。

第三层:设计哲学

这两个理念不是具体技术,是你看问题的方式。

哲学一:暴露不确定性是一个功能,不是缺陷

整个 deep-research 架构里最打动人的东西不是它的验证机制,是 budgetDropped[] 和 dupes[] 这两个数组——它把"被丢弃的东西"也记录下来了。

大部分系统追求的是"干净输出":给你最终结果,过程中丢掉的、砍掉的、忽略的东西都当作没有发生过。deep-research 反其道而行——被去重的 URL、因预算不足被丢弃的来源、被否决的主张,全部在最终报告里有单独的章节。

哲学二:隔离不是性能折衷,是防污染策略

验证阶段的 3 个 voter 是独立进程,互不通信。不是技术限制——是故意的。

错误在一个隔离环境中被拦截,不会在运行时互相污染。这和生物学里的免疫系统原理一样:每个白细胞独立判断"这是不是入侵者",不互相商量——商量反而会导致集体误判。

第四层:三条具体的成长路径

这三条不是"理念"也不是"模式",是你现在就可以加到自己系统里的小改动

1. 给每个输出加「盲区声明」章节

最终报告末尾加一个 ## Things we rejected(and why)。读者看到的不只是"我们确认了什么",还有"我们否定了什么,以及为什么否定"。

2. 用至少 3 个不同视角审查同一输出

不要三个 reviewer 用同一套 prompt。给 reviewer A 问"事实对吗",给 reviewer B 问"逻辑严密吗",给 reviewer C 问"覆盖全面吗"。三个不同视角的否决理由,比三个相同视角的全票通过更有价值。

3. 建一个「可观测性层」

不只是一个 log 函数——是一个结构化的 stats 对象,记录:本次调用了多少次 agent、多少被去重(为什么)、多少因预算被丢弃(优先级是什么)、多少主张被否决(几票否决、什么理由)。

下次分析为什么某个任务完成率低时,这个 stats 对象能让你在 10 秒内定位到"是搜索太慢还是验证太严",而不是翻几十页日志。


七、一个判断

deep-research 不教你怎么让 AI 更强——它教你一套"用工程手段弥补 AI 不可靠性"的系统方法论。

学完不是你的 AI 变聪明了,是你设计系统的方式变了——从"相信 AI 能给出正确答案"变成"不相信 AI 能给出正确答案,所以设计一套机制来兜底"。

这个思维转变,比任何一个具体技术都值钱。

大多数 AI 研究工具追求的是「回答速度」——给你一个看起来完整的答案,越快越好。

deep-research 追求的是「回答可信度」——宁可只给你确认的结论 + 被驳倒的主张列表,也不给你一个 100% 但藏着错误的完美报告。

这两种哲学的差异,在简单问题上看不出来。但在复杂问题上——医疗决策、投资判断、技术选型——差异是致命的。

前者给你信心,后者给你诚实。而诚实,比信心贵得多。


本文基于 deep-research workflow 源码逐行分析。所有架构描述、常量参数、容错逻辑均来自实际代码,如有不准确之处欢迎指正。

推荐阅读标签: #deep-research #AI研究 #多Agent #AI可靠性 #对抗验证 #Perplexity #AI搜索 #Agent编排 #AI幻觉 #系统设计