为什么 Agent 跑不了长任务
让 Agent 构建一个完整 Web 应用看似简单,但现实中充满了交接失败和上下文断裂的陷阱。
这不是一个小任务。一个完整的聊天应用需要认证系统、对话管理、流式输出、文件上传、Markdown 渲染、多轮历史…加起来可能有 200+ 个独立功能点。让 Agent 从零到一完成这样的项目,会遇到什么问题?
Agent 试图在一次会话中完成所有功能,结果:
- 上下文窗口在实现到一半时被用完
- 下一个 Agent 接手时,面对半成品代码,只能猜测前任做了什么
- 大量时间浪费在让基本功能重新跑起来,新功能反而没时间做
- 即使有 Compaction(上下文压缩)也不够,压缩后的指令不够清晰,新 Agent 依然迷路
Agent 看到已经实现了一些功能,就认为项目已经基本完成:
- 声明项目完成,实际上只完成了核心功能的 30%
- 缺乏任务清单,Agent 不知道还差什么没做
- 没有验证机制,以为做完了但没有端到端测试证明
Initializer + Coding Agent
核心思路:把一个 Agent 拆成两个角色,一个负责规划,一个负责执行。每次交接都留下干净的状态。
- 创建
init.sh脚本搭建开发环境 - 写
claude-progress.txt进度文件 - 把用户的高级提示展开成详细的功能清单(JSON 格式)
- 做第一次 git commit,确保仓库状态干净
- 读 progress 文件,了解当前状态
- 一次只做一个功能
- 完成后更新 progress 文件
- git commit 并写清楚做了什么
一种验证有效的做法是用 JSON 格式来记录功能清单,Markdown 不适合这个用途。原因是:模型更不容易错误修改结构化的 JSON,而 Markdown 容易被模型顺手重写。
- 1 每次完成一个功能后,代码处于可合并状态:没有半成品、没有语法错误
- 2 Git commit 提供回滚点:如果下一轮搞坏了什么,可以回到上一个干净状态
- 3 Progress 文件提供上下文:新 Agent 不用猜做到哪了,直接读文件就知道
- 4 上下文窗口不会溢出:每轮只需关注一个功能的上下文,不会积累到爆
Agent 容易以为做完了却没有端到端验证。它说「实现了登录功能」,但实际上按钮根本点不动。解决方案:
要真正打开浏览器、点击按钮、验证结果,光有单元测试还不够。让 Agent 用 Puppeteer / Playwright 写 E2E 测试,作为功能是否真正"passes"的判定标准。
Managed Agent:脑手分离
核心洞察:把 Agent 的思考和执行拆成独立组件,让系统在任何部分出错时都能优雅恢复。
容器挂了 = 会话丢失 = 无法调试 = 任务彻底失败。
就像养了一只宠物,挂了就完了,无法替代。
容器挂了 = 工具调用失败 = Claude 决定重试 = 新建容器继续。
就像牧场的牛,挂了重来一个,系统照常运行。
-
容器变成工具调用:
execute(name, input) -> string,对 Harness 来说就是一个普通函数 - 容器挂了 = 工具调用返回错误 = Claude 自行决定是否重试 = 自动新建容器继续工作
- Harness 可以在容器启动前就开始处理,不需要等容器 ready
TTFT = Time to First Token(首 Token 响应时间)
- 旧方案:Agent 生成的代码和 API 密钥在同一个容器里,Prompt Injection 可以直接偷密钥
- Git Token:在克隆仓库时注入容器环境变量,沙箱内可以用,但 Agent 看不到 token 值
- MCP OAuth Token:存在外部 vault 里,通过代理转发 MCP 调用,沙箱内无法直接访问 token
这意味着你可以让一个 Brain 同时操控多个 Sandbox(并行执行),也可以让同一个 Sandbox 在不同的 Brain 之间传递(接力执行)。组件之间完全解耦。
Session 不等于 Context Window
两个最容易混淆的概念:Claude 当前能看到的,和所有发生过的事。它们必须分离。
- 1 Compaction(压缩)和 Trimming(裁剪)都是不可逆操作,一旦压缩,原始细节就丢了
- 2 压缩时很难知道未来哪些 Token 重要,今天看似无关的信息,可能是明天关键决策的依据
- 3 如果压缩后原始信息丢了,就永远回不来了,这是信息论的基本约束
所以正确的做法是:原始事件永久保存在 Session 里,Context Window 只是从 Session 中临时取景的一个视角。丢了 Context 没关系,因为 Session 还在,随时可以重建。
这就像 REPL 中的对象一样,LLM 可以写代码来查询和过滤 Session 中的事件。Brain 从任意位置开始读、倒回到某个时间点、重读某个决策前后的上下文。
- 优化 Prompt Cache 命中率:保持前缀稳定,减少重复计算的 Token 开销
- 做上下文工程:根据当前任务类型,选择性地放入最相关的历史事件
- Harness 是可换的:不同模型可能需要不同的上下文策略,换 Harness 不影响 Session
| 维度 | Context Window | Session |
|---|---|---|
| 持久性 | 临时,用完即丢 | 永久,持久化存储 |
| 内容 | 精选后的 Token | 所有原始事件 |
| 操作 | 只读(对 Claude 来说) | 可追加(append-only) |
| 大小 | 有限(模型上限) | 无限制 |
| 用途 | 当前推理 | 历史回溯、状态恢复 |
你不会把所有文件同时加载到内存里,那样内存会爆。同样的道理,你不应该把所有历史事件都塞进 Context Window,那样 Token 会爆。正确的做法是按需加载:Session 存全量,Context 取子集。