跳到主要内容

04 · 巧妙之处、边界、横向对比与代码地图

这章把前三章的精华抽出来当"可带走的技术",诚实列局限,做横向对比,最后给一张可直接 grep 的代码地图。

5. 巧妙之处(可借鉴的技术)

① 用「自递归 + 旋钮衰减」表达搜索树。 不引入任何 planner/state machine,deepResearch 调自己就把广度×深度的树展开了,breadth/2depth-1 保证收敛。妙在"整棵树"的复杂行为压进一个约 120 行的函数(src/deep-research.ts:176-294)。可借鉴:能用递归表达的树形 agent,别急着上编排框架。

② 让 LLM 自己写「下钻方向」。 想搜索词时顺带产出 researchGoalsrc/deep-research.ts:66-70),这段目标就直接当成下一层的题目。等于把"下一步查什么"的规划外包给 LLM,代码只负责搬运。可借鉴:让模型在同一次调用里既产出动作、又产出"这个动作的意图/后续",省一次规划调用。

③ schema 即 prompt。 zod 的 .describe() 字段描述同时是校验规则和给模型的微提示(src/feedback.ts:18-24 等),一处声明两处生效。可借鉴:结构化输出场景,把"你要什么"写进 schema 描述而不是只写在 prompt 正文。

④ 中间件剥离 reasoning。 extractReasoningMiddleware({ tagName: 'think' })src/ai/providers.ts:44)让业务代码对"推理模型会吐思考过程"这件事无感。可借鉴:用模型无关的中间件层吸收"某类模型的怪癖"。

⑤ token 截断的双重防死循环。 trimPrompt 里"截完还一样长就硬切"和"chunkSize 低于下限就硬切"(src/ai/providers.ts:81-94)两个兜底,是真跑过才会加的补丁。可借鉴:凡是"估算 + 递归收敛"的算法,都要为"估算失灵"准备一条硬退路。

⑥ 来源列表不过 LLM。 ## Sources 是代码手工拼的(src/deep-research.ts:144-146),绕开模型,保证引用链接不被幻觉篡改或丢失。可借鉴:能确定性生成的部分,别交给 LLM。

6. 边界与局限(诚实)

  • 失败静默降级。 单支搜索/提炼出错就返回空并继续(:275-285),最终报告可能悄悄缺一整支的发现,用户无从察觉。
  • learnings 只在顶层去重。 递归下传时直接拼接(:238),同一条要点会随深度复制进多个子 prompt,深度大时 token 浪费明显。去重仅发生在最外层 Set:290-293)。
  • 无"够了就停"的判断。 停止条件纯粹是 depth 耗尽,不看"信息是否已饱和"。depth 给大了会无谓地搜。
  • 无引用溯源到句子。 报告尾部只有一个总 Sources 列表,某条论断具体来自哪个 URL 无法对应。
  • 进度上报不精确。 并发共享一个 progress 对象就地改(:200-203),字段是"最近一次"快照,非逐支账本。
  • src/api.ts/api/generate-report 有 bug。return reportsrc/api.ts:84)却从不把报告写进 HTTP 响应(没有 res.json/res.send),客户端拿不到结果——CLI 路径(run.ts)才是主力,这个 HTTP 端点像未完成品。
  • 上下文靠堆 learnings,非向量检索。 没有向量库/RAG,全靠把 learnings 文本拼进 prompt,规模上不去。

7. 横向对比

维度dzhng/deep-research典型"重"型 research agent
编排一个自递归函数显式状态机 / 图 / planner
停止depth 耗尽信息饱和判断 / 反思循环
记忆learnings 文本拼接向量库 + RAG 检索
搜索Firecrawl 一把梭(搜+抓正文)搜索 API + 独立抓取 + 清洗
定位<500 LoC 教学参考实现生产级、可观测、可恢复

取舍很清楚:它用"极简 + 递归"换"可读、易改、易学",放弃了饱和判断、细粒度溯源、可恢复性这些生产特性。作为"deep research agent 最小骨架"的参考,它的价值正在"少"。

8. 代码地图(导航索引)

主题文件路径符号名
CLI 入口/问答流程src/run.tsrunaskQuestion
HTTP 入口src/api.tsapp/api/research/api/generate-report
澄清问题生成src/feedback.tsgenerateFeedback
递归引擎(心脏)src/deep-research.tsdeepResearch
搜索词 + researchGoal 生成src/deep-research.tsgenerateSerpQueries
结果提炼 learningssrc/deep-research.tsprocessSerpResult
长报告汇总src/deep-research.tswriteFinalReport
精确答案汇总src/deep-research.tswriteFinalAnswer
进度类型src/deep-research.tsResearchProgressResearchResult
并发上限常量src/deep-research.tsConcurrencyLimit
模型多提供商切换src/ai/providers.tsgetModelo3MiniModeldeepSeekR1ModelcustomModel
token 截断src/ai/providers.tstrimPromptMinChunkSizeencoder
递归字符分割器src/ai/text-splitter.tsRecursiveCharacterTextSplittermergeSplitssplitText
统一系统人格src/prompt.tssystemPrompt

怎么用这张表

  • 想改"树怎么长" → grep deepResearchnewBreadth/newDepth
  • 想换/加模型 → grep getModel,改 src/ai/providers.ts 的构造与优先级。
  • 想调 prompt / 输出结构 → 找对应调用点的 generateObject + schema
  • 想改截断行为 → grep trimPromptRecursiveCharacterTextSplitter