Agent Engineering 课程阅读
首页/课程十 · 常驻主动式 Agent/增量研究回路:只研究变化,不重研全量

在 GitHub 查看原文

本页目录

Lesson 03 — 增量研究回路:只研究变化,不重研全量

本课目标:把 watcher 的变化集接进研究图——变化条目直接变成焦点子题(split 跳过 LLM 拆题),旧结论注入 prompt「已知这些,只补新的」,产出走 TaskLedger 增量简报(🆕新增/✏️修正/➡️不变)。矛盾不再被静默覆盖。

学完你能回答面试官那句:「你的 Agent 第二次研究同一主题时,怎么不重复劳动?」——答案分三层:研究面缩到变化(焦点子题)、已知结论不重研(旧结论注入)、产出只讲 diff(增量简报)。


0. 起点:L00 基线的第③环

基线 Day3:只多了一条小更新(框架 Y 补丁),现状仍重研全部 3 个子题,新增内容埋在全量报告里要人肉 diff。Day4 更糟:item-c 反转(撤回 AGUI 支持),报告没有任何修正标注——旧结论被静默覆盖,矛盾要读者自己发现

本课的数据流(接在 L02 之后):

watcher.scan_source() → ChangeSet
        │
run_incremental(topic, change_set) 三分支:
        │
   ┌────┴─────────────┬──────────────────────────┐
   ▼                  ▼                          ▼
ok=False          is_no_change()             有变化
「没能看到」       「确认无变化」          build_incremental_focus()
不进图、不产结论    不进图、零成本          变化条目 → 焦点子题
(L02 纪律消费端)                              │
                                    invoke(extra_state={focus, prior})
                                                │
                              split:焦点直用(跳过 LLM 拆题)
                              researcher:旧结论注入「只补新的,矛盾用『更正:』开头」
                                                │
                              record_and_brief():ledger 记进度 → 增量简报 🆕/✏️/➡️

1. 焦点即子题:研究面由「主题」缩到「变化」

增量模式下 split 不再问 LLM「这个主题怎么拆」,而是直接采用变化集生成的焦点:

# nodes.py · split 开头(enable_incremental_run 且有焦点时)
focus = state.get("incremental_focus") or []
if settings.enable_incremental_run and focus:
    return {"subtopics": [f.strip() for f in focus if f.strip()][:8]}

🎯 核心认知:LLM 拆题回答的是「研究这个主题该看哪几块」——它天然把研究面扩回全量。而变化集已经指认了「世界动了哪几条」,拆题这一步在增量模式下是多余的(还省一次 LLM 调用)。code.py Part 1 量化了 Day3 的差距:全量 587 token vs 增量 92 token,省 84%。变化越小省得越多——而 L02 已证明大多数日子变化为零。

焦点的构造(incremental.py)带着通道语义

  • 新增条目 → 【新增】标题:内容——这条新信息的内容、背景与影响
  • 变更条目 → 【内容变更】标题:最新内容——与此前已知结论有何出入?若矛盾,用「更正:」开头明确指出

变更条目的措辞是故意的:它把「检查矛盾」的指令编码进子题本身,researcher 的产出若以「更正:」开头,简报层就能识别为 ✏️(见第 3 节)。


2. 旧结论注入:「已知这些,只补新的」

prior_conclusions(topic) 从 TaskLedger 取已确认结论(done 状态的任务结果),经 prior_context 字段流入子图,route_to_researchers 把它装进每个 Send 载荷(researcher 是 Send 驱动的,看不到子图全量 State——旧结论必须随载荷下发),researcher 的 prompt 里:

已确认的历史结论(不要重复研究):
- 框架X支持AGUI:X 宣布全面支持 AGUI
只提炼新信息;若新信息与上述结论矛盾,用「更正:」开头明确指出。

与 frontier 记忆系统的分工:enable_memory 的 recall 注入的是语义相关的旧经验(模糊、跨主题);本课注入的是本主题账本上的已确认结论(精确、结构化)。两者可同时开,互不替代。


3. 账本协作闭环:世界增量 → 工作增量

frontier-L10 的 TaskLedger 此前只有模块与测试,没有运行时调用方——本课是它首次接入主链路:

watcher(世界增量:信源变了什么)
   → 焦点研究(只研究变化)
      → ledger.update_status(done, result=finding)(工作增量:我确认了什么)
         → 下次 run 的 prior_conclusions(「已知这些」)
            → generate_incremental_brief(🆕新增 / ✏️修正 / ➡️不变)

record_and_brief 的顺序是关键:对照「昨天为止的历史」生成简报,把今天的发现入账——今天的结论明天才是「历史」。code.py Part 2 的 Day4 简报:

## 本次新增
- 🆕 新增: 重磅:框架 X 撤回 AGUI 支持转投 A2A……
- ✏️ 修正: 更正:框架 X 宣布支持 AGUI 协议……此前结论已不成立
## 不变项
- ➡️ LangGraph 发布 1.2 稳定版: 仍成立

对照 L00 基线 Day4:现状矛盾被静默覆盖;现在旧结论仍在场(➡️),反转显式标注(✏️)——读者一眼看到「世界观变了哪一块」。

⚠️ 复用资产的已知局限(诚实标注)generate_incremental_brief 判「不变项」用的是简单关键词启发式(frontier-L10 原样复用,红线不重写)——Day4 里被修正的「框架 X 支持 AGUI」同时出现在 ✏️ 和「仍成立」里。修法(把 ✏️ 命中的任务从不变项剔除)留作练习 4;它不影响 ✏️ 通道本身的正确性。


4. 流派对比:第二次研究同一主题,怎么做?

流派 做法 取舍
每次全量重研(现状) 当世界是全新的 ✅ 永不漂移(每次都是完整快照);🚫 大多数日子在烧重复的钱(L00 Day2/Day3 实测),增量埋在全量里
增量研究(本课) 只研究变化条目 + 旧结论注入 ✅ 成本随变化量缩放(Day3 省 84%)、矛盾显式修正、产出是 diff 可读性高;🚫 漂移风险——每次只看变化,全局图景可能与世界脱节(旧结论错了但没条目触发重查,就一直错下去)
只做摘要不研究 变化条目直接进简报,不进研究图 ✅ 最便宜(零 LLM);🚫 没有「研究」——不核实、不关联旧结论、不判断影响,是 RSS 阅读器不是研究员

选 ② + 定期校准的理由:② 的漂移风险有标准解法——定期全量校准(如每 30 天强制一次 full run,重建基线),成本上等于「把 ① 的频率从每天降到每月」。本课不实现校准调度(属于 L07 的班次策略,留了练习),但 first_scan 的建仓语义已经为它留好了路径。③ 适合纯资讯流场景,但那不是「研究助手」的定位。


5. 跑起来

cd ambient-agent-lessons/03_incremental_research
python code.py        # 零 API、零联网、零等待

诚实标注:Part 2 的「研究」步骤用确定性 mock(真实图要 API key);变化检测、焦点构造、账本记账、增量简报全部走真实落地模块。


6. 落地清单

文件 改动 如何验证
src/research_assistant/incremental.py 新增build_incremental_focus / prior_conclusions / record_and_brief / run_incremental(三分支入口) 见下
src/research_assistant/state.py 新增incremental_focus / prior_context 字段(两层 State) import 冒烟
src/research_assistant/nodes.py split 焦点直用(3 行,开关内);route_to_researchers 载荷携带 prior_context;researcher prompt 注入旧结论(开关内);research_team 透传两字段 pytest tests/test_incremental.py -q
src/research_assistant/service.py _initial_state 补两字段;invokeextra_state 可选参(不传=现状) 现有测试全绿
src/research_assistant/config.py 新增enable_incremental_run(默认关) .envENABLE_INCREMENTAL_RUN=true
tests/test_incremental.py 新增:14 个测试(三分支/焦点直用/开关不变式/载荷下发/账本闭环/✏️ 修正) pytest tests/test_incremental.py -q

验收

cd portfolio-projects/research-assistant
python -m pytest -q          # 245 + 14 = 259 passed
# 开关关 = 现状(核心不变式):即使 state 里有焦点,split 仍走 LLM 拆题
python -m pytest tests/test_incremental.py::test_split_ignores_focus_when_disabled -q

7. 本课在两条主线上的位置

  • 倒置主线:第③环——「增量靠人肉 diff」变成「机器只研究变化、产出只讲 diff」。人从 diff 工具升级为 diff 的读者。
  • 注意力经济主线:增量简报是为注意力设计的产出格式——🆕/✏️/➡️ 让「值得看的部分」自解释(✏️ 永远最值得看)。这直接为 L04 铺路:判级器判「major/minor」时,看的正是简报里有没有 ✏️ 和 🆕 的分量。

🎯 面试话术

「我的 Agent 第二次研究同一主题时做三层增量:一,研究面缩到变化——变化检测指认哪几条动了,这些条目直接变成研究子题,拆题的 LLM 调用都省了,Day3 场景实测省 84% token;二,已知结论不重研——账本里已确认的结论注入 prompt『只补新的』,并要求矛盾用『更正:』开头显式指出;三,产出只讲 diff——增量简报分 🆕新增/✏️修正/➡️不变三通道,重大反转不会被静默覆盖。

增量的代价是漂移——只看变化,全局图景可能与世界脱节。所以要配定期全量校准,相当于把全量重研的频率从每天降到每月,成本和正确性各拿一头。」