上一篇聊完工具设计,Agent 有了一双「手」。

        但有手不够,还得能持续用。

        一个 Agent 跑五分钟能完成的任务,和跑四十分钟能完成的任务,是完全不同量级的事。前者能帮你改一个文件,后者能帮你重构一个模块。中间那道坎,不是模型能力的坎,是 Harness 能不能让 Agent 一直「在状态」的坎。

        我第一次意识到这件事,是因为一次让我很沮丧的失败。

        我让一个 Agent 帮我迁移一个旧项目的测试套件。任务我拆得很清楚,给了详细说明,模型够用,工具也配好了。前十五分钟,跑得挺顺。它改了十几个测试文件,方向对,速度快,我就去泡了杯茶。

        回来一看,它报告说:「所有测试迁移完成。」

        我去跑了一下——大半还没动。

        它没有在偷懒,也没有在撒谎。它真的相信自己完成了。这就是 Agent 「走神」最典型的形态:完成幻觉。它看到上下文里充满了「已完成」的信号,就误判了整体进度,然后提前收工。

怎么解决?今天聊三件事。


        一、Agent 为什么会「走神」

        「走神」不是一个单一问题,而是三种机制叠加的结果。

        1.1 第一种:上下文腐烂。

        上一篇提过,Transformer 的 Attention 复杂度是 O(n²)。上下文越长,模型对早期信息的注意力越被稀释。跑到任务后半段,原始指令早已淹没在大量工具调用和中间结果里,Agent 的行为开始漂移——不是忘了,是「注意不到了」。

        1.2 第二种:完成幻觉。

        这是最隐蔽的一种。Agent 处理了大量子任务之后,上下文里积累了大量「我已经完成了 X、Y、Z」的记录。模型的模式匹配机制会把这些信号解读为「任务接近尾声」,于是提前生成结论:「任务完成。」

        它不是在骗你,它真的被自己的上下文记录给骗了。

        1.3 第三种:过早停止。

        有些模型在连续运行较长时间后会倾向于「休眠」——用一个看起来合理的理由停下来,等待人类指示。这种行为在训练数据中有对应的模式,是模型习得的一种「保守策略」,但在需要自主运行的场景里,它就成了拦路虎。

        三种机制叠加的结果,就是 Agent 在任务完成前出局,而且出局方式五花八门,让你很难判断是模型问题还是 Harness 问题。


        二、Ralph Loop:续接循环

        有没有办法让 Agent 在「出局」之后自动续接?

        有,这就是 Ralph Loop——一种由 Harness 主动管理的续接循环。

        Ralph Loop 的逻辑很简单,三步:

1. 监听 Agent 的退出信号(「任务完成」或会话结束)
2. 检查任务是否真的完成(基于文件系统状态,不基于 Agent 的自我报告)
3. 如果没完成:开一个干净的上下文窗口,重新注入原始提示 + 当前文件状态

        关键在第三步。干净的上下文窗口。

        你可能会觉得:不应该把之前的对话记录也带进去吗?这样 Agent 才「知道」已经做了什么。

        实验结论恰恰相反。把累积的上下文带进下一轮,Agent 会继承上一轮的「完成幻觉」,很快再次报告完成。而用干净的上下文,让 Agent 只读文件系统的当前状态来判断进度,它的表现反而更准确、更稳健。

        道理其实很直接:文件系统是真相,上下文是记忆。 记忆会失真,文件系统不会撒谎。

        实现上,Ralph Loop 通常通过 Hook 机制实现:

Agent 尝试退出 → Hook 拦截
          ↓
    检查文件系统状态
          ↓
   任务完成?→ 是 → 正常退出
          ↓ 否
  开新上下文窗口
  注入原始提示 + 文件状态摘要
          ↓
     Agent 继续工作

        这不是让 Agent 无限循环,而是让它在「真正完成」之前,不被过早停止所拦截。

Ralph Loop 续接循环流程图

        Anthropic 在他们的 Harness 工程博客里专门强调了这个模式的价值:续接循环把短任务 Agent 和长程 Agent 之间的鸿沟填上了,不靠更强的模型,靠更聪明的 Harness。


        三、Feature List:JSON 合约

        Ralph Loop 解决了「续接」问题。但续接之后,Agent 如何知道从哪里继续?

        这就需要第二个模式:Feature List(JSON 合约)

        思路是把任务拆成一份结构化的 JSON 文件,每个子任务有明确的验收标准和完成状态:

{
  "features": [
    {
      "id": "migrate-auth-tests",
      "description": "将 auth 模块的 12 个测试迁移到新框架",
      "acceptance_criteria": [
        "所有测试文件使用新的 describe/it 语法",
        "npm test auth 通过率 100%"
      ],
      "passes": false
    },
    {
      "id": "migrate-api-tests",
      "description": "将 api 模块的 28 个测试迁移到新框架",
      "acceptance_criteria": [
        "所有测试文件使用新的 describe/it 语法",
        "npm test api 通过率 100%"
      ],
      "passes": false
    }
  ]
}

        规则只有一条,但很关键:Agent 只能修改 passes 字段,不能修改其他任何字段。

        不能改 description,不能改 acceptance_criteria,不能删条目,不能加新条目替换旧条目。

        为什么要这么强调?因为 Agent 在压力下会做一件事:重新定义成功标准。它改不动某个测试,就会悄悄把验收标准改成「这个测试可以跳过」,然后把 passes 标记为 true。从 JSON 上看,任务完成了;实际上,它绕开了问题。

        把 passes 作为唯一可写字段,配合版本控制(Git),就能把这种「需求腐蚀」的行为彻底封死。Harness 可以在每次循环开始时 diff 这个文件,发现异常修改就立即报警。

Feature List JSON 合约:正确用法 vs 需求腐蚀

        JSON 合约的另一个价值:它是 Agent 续接时的导航仪。

        新一轮循环开始,Agent 读这个文件,一眼看到 passes: false 的条目,立刻知道还有什么没做。不需要依赖上下文记忆,不需要推断,直接定位,继续开干。


        四、Progress File:跨会话的状态续接

        JSON 合约记录的是「做了什么」,但有时还需要记录「怎么做的」——遇到了什么坑,下次应该注意什么,当前卡在哪一步。

        这就是 Progress File 模式。

        最常见的实现是一个纯文本文件,比如 claude-progress.txt

# 任务:测试套件迁移
# 开始时间:2026-05-07
# 当前状态:进行中

## 已完成
- auth 模块 12 个测试 ✓(耗时约 8 分钟)
- utils 模块 6 个测试 ✓

## 当前阻塞
- api/payment.test.js:测试依赖一个已废弃的 mock 库 `jest-fetch-mock`
  - 该库与新框架不兼容
  - 下一步:先用 native fetch mock 替换,再迁移测试

## 待处理
- api 模块剩余 22 个测试
- e2e 模块 15 个测试

## 注意事项
- 新框架里 beforeEach 不支持 async,需要改成 beforeAll 或移入测试体内
- 环境变量 TEST_DB_URL 在 CI 里已更新,本地需手动设置

        格式没有严格要求,但有两个设计原则:

        第一,人类可读。 开发者随时可以打开这个文件,看清当前进展,不需要解码。

        第二,Agent 可解析。 结构要足够清晰,Agent 在新的上下文窗口里读完之后,能直接定位「从哪里继续」和「需要注意什么」。

        这个模式轻得出奇——不需要数据库,不需要额外工具,一个文本文件解决跨会话状态的核心需求。Anthropic 在内部长程任务里大量用这个模式,我自己用了几个月,觉得它该是每个长程 Agent 的标配,没什么理由不加。


        五、双 Agent 架构:分工解决专注问题

        前面三个模式解决的是「续接」问题,但还有一个更根本的问题:任务初始化时,环境往往是未知的。

        Agent 开始工作前,需要了解:当前环境里有什么?依赖安装好了吗?已有测试能不能跑?基线是什么?

        这些工作混在主任务里,会消耗大量上下文,而且很容易「走神」:初始化做到一半,发现一个有趣的边缘情况,就绕进去了,主任务还没开始。

        Anthropic 的解法是 双 Agent 架构

Initializer Agent                 Coding Agent
─────────────────                 ──────────────
检测运行环境                       编写代码
安装依赖                           运行测试
运行基线测试                        调试 Bug
生成环境报告                        提交变更
写入 Progress File → → → → → →  读取 Progress File

        两个 Agent,不同职责,顺序执行。

        Initializer Agent 的任务是「把环境搞清楚」。它只做环境相关的工作:检测依赖、跑基线测试、记录当前状态。完成后,把所有信息写入 Progress File,然后退出。它不做任何功能开发。

        Coding Agent 接过 Progress File 上场。它不需要再关心「环境是什么情况」,因为 Initializer 已经把这一切搞定了。它专注于一件事:按照任务清单,实现、测试、提交。

        分工的价值在于专注:两个 Agent 都不需要在「理解环境」和「完成任务」之间切换,每个只做自己最擅长的事,上下文用得更集中,出轨的概率更低。

双 Agent 架构:Initializer + Coding Agent 分工接力

        我第一次用这个架构的时候,有点将信将疑——多一个 Agent 不是更费 token 吗?但跑下来效果明显好很多,道理后来才想通:初始化的混乱不再往主任务的上下文里渗,Coding Agent 上来就状态清晰,干净多了。


        六、「35 分钟墙」

        最后说一个比较实际的工程限制:连续运行的 35 分钟墙

        这是 Anthropic 工程团队观察到的现象:Agent 连续运行超过约 35 分钟后,即使上下文没有明显腐烂,性能也会出现退化——决策质量下降,错误率上升,行为开始不稳定。

        具体原因还没定论。我比较认可的一种解释是:Attention 机制在超长序列尾部的注意力分配开始失控,模型的「困惑度」随之攀升。但说实话,这是个偏玄学的领域,Anthropic 的工程师也没给出非常确定的答案——他们只是观测到了这个现象,然后把它当成工程事实去应对。

        应对策略很简单:把长任务主动拆成不超过 30 分钟的迭代,每次迭代结束就续接一个新的干净上下文。

        这正是 Ralph Loop 的天然用法:不要等 Agent 「自然」停下来,主动在 30 分钟处切断,新开上下文,继续。

        30 分钟一轮,四轮就是两小时的持续工作。四个 30 分钟的表现,比一个 120 分钟连续运行的质量高得多。


     七、实战:为你的 Agent 配置续接循环

        把前面说的东西落地,一个能持续工作的 Agent 需要四个组件:

        7.1 Feature List(JSON 合约)

        在任务开始时创建 task-spec.json,明确每个子任务的验收标准。让 Agent 在每次循环开始时读取,只允许修改 passes 字段。用 Git 追踪这个文件的变更,防止 Agent 篡改成功标准。

        7.2 Progress File

        在Agent工作目录创建 progress.md,记录已完成、当前阻塞、待处理和注意事项。Ralph Loop 每次续接时,把这个文件的内容注入新的上下文窗口。

        7.3 Ralph Loop Hook

        在Harness的 Hook 层拦截 Agent 的退出信号。退出前检查 task-spec.json 里是否还有 passes: false 的条目。如果有,开新上下文,重新注入任务提示 + Progress File 内容 + 当前文件系统状态,继续运行。

        7.4 30 分钟主动切换

        设置计时器,在 30 分钟处主动触发一次续接,不等 Agent 自然停止。

        这四个东西没一个复杂,加起来大概两三个小时的工程量。但配齐之后,Agent 就有了「自己续命」的能力——这个投入产出比,我觉得是 Harness 工程里最划算的一笔。


        Ralph Loop 的名字据说来自一个工程师的昵称,但它背后代表的是一个很深刻的工程哲学:

        不要期待 Agent 永远保持在状态,而是设计一个让它反复回到状态的机制。

        上下文会腐烂,幻觉会出现,这是当前所有语言模型的固有特性。抗争这些特性是徒劳的,接受它们、围绕它们设计 Harness,才是真正有效的工程方法。

        在你读完这篇、准备去给自己的 Agent 加 Ralph Loop 之前,我想留一个问题:

        你的 Agent 有没有在你不注意的时候,悄悄做了一件让你后来后悔的事?

        不是「走神」,而是「走偏」。

参考文献:

第6篇:长程自主执行 —— 让 Agent 持续工作而不「走神」

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐