模式矩阵 /模式白皮书/P3
P3 · Progressive Discovery · 渐进发现
| 字段 | 值 |
|---|---|
| 双轴坐标 | 感知 Perception × 循环 Loop(转) |
| 成本档 | ②(一轮 forage-focus-deepen 约 18K token) |
| 课程对应 | 02-04 |
| 目录归属 | 全集 33 模式之一 · 感知模块 4 模式之一 |
| 一句话 | Agent 面对陌生信息空间且不知道相关信息在哪时,先看一眼再决定钻多深,通过广扫→精读→深追三阶段循环把它探明白。 |
它解决什么问题
上下文分诊和语义压缩有个共同前提:你已经知道相关信息在哪。这个前提常常不成立——Agent 面对一个 15000 文件的遗留 codebase、一份没看过的合同、一个长长的事故日志,它不知道相关代码叫什么、不知道关键证据在哪一页。把整个 codebase 喂进去会爆窗口,做 RAG 又常常召不回(相关代码的变量名是 merge_user_state,注释里没有 "order" 字样,语义检索匹配不上)。
渐进发现把这件事工程化:用广扫拿一批候选,让看到的内容重塑下一步该看什么,迭代到信号足够强为止。它管的是当下 session 内对未知空间的主动探索,核心约束是成本——一轮探索约 18K token、约 45 秒,远比把整个 codebase 索引进 prompt 划算。
为什么坐标是「感知 × 循环」
- 纵轴 · 感知:渐进发现是"先看一眼再决定钻不钻深"的阶段性信息获取,属于感知端的渐进式注意力。它决定 Agent 接下来去看什么,是输入侧的事,不是推理或记忆。
- 横轴 · 循环:看→决定→再看→决定,每轮带着前一轮的发现做下一轮决策,终止条件是"信息够了"或"成本到顶"。它是迭代式的,不是分诊那种单次完成的路由,也不是压缩那种线性级联。
核心机制
一次发现走 forage-focus-deepen 三阶段,广度递减、深度递增:
| 阶段 | 动作 | 工具与代价 |
|---|---|---|
| Forage 广扫 | 扫陌生空间拿 30-50 个候选,只看文件名、路径、匹配行上下文 | grep / glob / find,~3K token、~10 秒 |
| Focus 精读 | 从候选里挑 5-10 个最可能的完整读,看依赖关系和调用链 | read,~8K token、~20 秒 |
| Deepen 深追 | 沿可疑链追下去(被引用的函数、测试、历史 commit),只追一两条最有信号的 | read,~7K token、~15 秒 |
三个机制决定发现的成败。第一,给 Agent grep、read、glob 三件 atomic 工具,不要给 search_codebase() 这种高级封装——只有 atomic 工具支持"看到的内容重塑下一步"的反馈循环。第二,硬上限。循环数封顶(多为 3 轮,事故场景 2 轮),单循环 token 封顶(约 20K),3 轮还没找到说明关键词不对或需要人介入,继续循环只是烧 token。第三,关键词推导是杠杆。第一轮的关键词决定 forage 的命中质量,做得粗糙后面再多循环也救不回来,建议先用一个轻量模型把任务描述翻译成 5-8 个精确关键词再 grep。一轮信号不够时回到 forage 用更准的关键词重来,而不是硬猜。
适合的生产场景
- 陌生 codebase 的 bug 根因定位:原作者跑了、文档稀疏,没人知道某条 pipeline 走哪几个文件。Anthropic 披露 Q1 2026 有 78% 的 Claude Code 会话涉及多文件编辑,意味着 Agent 越来越频繁面对不熟悉的代码区域。
- 运维事故响应:接到告警后从 metric 反推关键词,按时间窗口裁剪日志,三阶段定位故障源,给值班工程师初判报告和"第一步该做什么"建议。
- 合同条款风险扫描、研究文献综述:任何符合"接到任务→不知道相关信息在哪→探索定位"模式的场景。
- 隐私敏感且 codebase 不大(< 50K 文件):grep + read 让代码留在文件系统不进向量库,是比 RAG 更安全的选择。
容易出错的地方
- Forage 关键词太宽:任务"用户登录变慢了"被写成
["login", "slow"],返回 5000 个候选,forage 阶段 token 直接爆。让关键词窄而准,["LoginController", "auth_timeout", "session_create"]精确得多。 - Focus 阶段挑错文件:scorer 算分错把测试文件排在生产文件前,Agent 读了一堆 spec 文件,关键的
services/auth.rb没读到。给 scorer 带业务权重:生产文件 > 测试文件、最近修改 > 旧代码、核心目录 > 边缘目录。 - Deepen 死胡同:追依赖追到第三方库源码,读几百行没拿到任何 signal。给 Deepen 划边界:不追第三方库(除非 bug 报告点名)、不追超过 2 跳的依赖。
- Discovery 跟 RAG 撞车:同一个 Agent 同时上 Discovery 和 RAG,两边结果重叠或冲突,Agent 不知道信谁。选一个,不要混用——< 50K 文件用 Discovery,> 400K 用 persistent indexing。
关键指标
- 找到答案的中位循环数 cycles_to_success_p50(健康线 = 1):一轮三阶段搞定 80% 任务。p50 涨到 2-3 说明关键词推导能力下降,可能模型升级、业务场景变了或用户描述变长,一升立刻调。
- forage/focus token 比(健康区 0.3-0.5):forage 应比 focus 轻。超过 0.7 是关键词太宽(拿了太多无关候选),低于 0.2 是关键词太窄(没拿到足够候选)。
- 零信号率 zero_signal_rate(健康线 < 5%):跑完三阶段还没找到任何 signal 的 session 占比。涨高几乎一定是 atomic 工具坏了(grep 索引过时、read 权限错配、scorer bug),比任务失败率更早报警。
最小骨架
discover(task, keywords):
循环 至多 max_cycles(默认 3):
Forage:对每个 keyword 跑 grep → 汇总候选 → 按 task 相关性打分 → 留 top-30
若 cycle_tokens 超 budget → 停
Focus:挑 top-8 完整 read,记下依赖关系
Deepen:从已读内容抽依赖(import / 函数引用),最多深追 5 个
若信号足够 → 成功,跳出
否则 → 用已发现内容 refine keywords,再来一轮
每阶段落一条 DiscoveryEvent(phase / keyword / 候选数 / files_read / tokens / wall_time)
三件 atomic 工具(grep / read / scorer)用注入而非封装,让同一份代码能跑在本地文件系统、MCP server 或外部索引引擎上,只换注入实现。
企业落地一例
一个电商客户的 bug:订单确认邮件偶尔混入其他客户的订单条目。开发团队面对 15000 文件的遗留 monolith,试过把整库喂给 Agent(800K token 爆窗口)、试过 RAG(相关代码无 "order" 字样召不回),最后让 Agent 用 grep + read 自己探。Forage:grep "send.*confirm" 拿 30 个候选,按文件名挑出 mailers/order_confirmed.rb。Focus:完整读 5 个文件,看到调用链 MailerWorker → Cache.get_user → render。Deepen:追 Cache.get_user,发现 cache key 用了 order_id 却没用 customer_id,不同客户订单 id 撞桶就命中错误缓存——bug 找到了。一轮、约 18K token、约 75 秒。复盘结论是这个 bug 跟语义没关系、跟代码结构有关系,只有直接接触结构的 grep + read + follow imports 才能找到。
与其他模式的关系
- 上下文分诊(P1):天然耦合。分诊的 P3 句柄就是渐进发现按需拉取的入口,一个被动选、一个主动找。最关键的区别是主动性——分诊和压缩都被动(信息送来或窗口满才动),发现是 Agent 自己决定 grep 什么、读什么。
- 语义压缩(P2):三者按 token 的不同时间维度分工,发现管未知 token(决定哪些找)。把探索推给 sub-agent、主 agent 只拿回摘要,正是压缩里 ObservationMasking 思路在发现层的复刻。
- 程序性记忆(Memory 模块):建议做成"discovery 找到 + memory 沉淀"双层架构。这次探出的 final_files 跨 session 保留下来,下次类似任务先查记忆,没命中再启动发现,Agent 的经验就开始累积。
一句话记住它
最强的 Agent 不是知道得最多的,是知道哪里去找的——渐进发现是信息觅食理论在 LLM 时代的回响,Agent 不试图理解整个 codebase,它找够用的就停。
本页属于 ADPS 33 模式白皮书。返回 模式矩阵与白皮书目录, 或查看配套 可运行代码目录。