Coding Agent 的执行系统
以一次跨越前端、后端与持久化的故障修复为线索,拆解 Coding Agent 如何探索、规划、修改、验证并恢复。
考虑一个看起来并不夸张的需求:
异步任务已经完成,当前页面也能看到结果;但刷新页面后结果消失。请修复问题,并补充回归测试。
它没有要求重写架构,也没有包含一个艰深算法。但它足以区分“会生成代码的模型”和“能够完成工程任务的 Coding Agent”。
要完成这项工作,系统必须先理解“消失”发生在 UI State、缓存、API 返回还是持久化层;必须找到真实调用路径,在不破坏现有行为的情况下修改正确位置;最后还要拿出证据,说明刷新之后结果确实能够恢复。
模型并不会在第一轮就拥有这些信息。它只能通过仓库、工具与反馈不断缩小不确定性。
所以我更愿意这样定义 Coding Agent:
Coding Agent 是把工程目标持续转化为可验证代码变更的闭环执行系统。
代码生成只是其中一步。真正决定它能否交付的,是执行系统如何维持目标、选择 Context、约束修改、收集证据,并在中断后继续。
先把需求变成成功契约
用户给出的是自然语言目标,不是可执行 Specification。Agent 的第一项工作不是立即搜索 async,而是把目标转换成一组可以被检查的条件。
对于这个例子,最小成功契约可能是:
Given: 一个异步任务已经完成并产生 Artifact
When: 用户刷新页面,客户端重新从服务端加载任务
Then: Artifact 仍然可见
And: 旧任务、进行中任务与失败任务的行为不变
And: 存在一个在修复前失败、修复后通过的回归测试
这份契约做了三件事。
第一,它把“结果消失”从视觉描述变成状态恢复问题。第二,它把 Scope 限定在刷新后的重新加载路径,而不是顺手重做整个异步任务系统。第三,它定义了完成所需的证据。
成功契约不一定要生成一份正式 Spec 文件。小改动可以只存在于 Task State 或一份短 Plan 中。关键是 Agent 与 Verifier 必须共享同一组判定条件,否则模型优化的是“看起来像修好了”,测试验证的却可能是另一件事。
可以把任务写成一个谓词集合:
done(task) =
root_cause_explained
∧ minimal_change_applied
∧ regression_test_passed
∧ relevant_existing_checks_passed
∧ diff_within_scope
其中任何一项还没有证据,任务就不应该因为模型写出了总结而结束。
仓库探索不是把所有代码读一遍
接下来,Agent 需要建立一张局部系统地图。
低效做法是从根目录开始逐文件阅读。这样会迅速消耗 Context,却不一定减少最关键的不确定性。更好的探索是问题驱动的:每次读取都服务于一个待验证假设。
这个需求最初可能有四个互斥或部分重叠的解释:
- 结果只保存在客户端内存,没有写入服务端;
- 服务端保存了结果,但查询接口没有返回;
- API 返回了结果,但客户端 hydrate 时丢弃了字段;
- 数据存在,刷新后使用了不同的 Task ID 或缓存键。
Agent 可以沿着“写入路径”和“读取路径”同时建立证据:
Task completes
→ completion handler
→ persistence write
→ task record / artifact reference
Page refreshes
→ route loader
→ task query
→ response mapper
→ client store
→ artifact renderer
仓库级指令也属于探索结果的一部分。构建命令、目录 Scope、禁止修改的文件、测试约定和当前 Worktree 状态,都会改变下一步允许做什么。AGENTS.md 或同类项目说明不是背景装饰,而是执行环境的 Policy 与 Context 输入。
一个成熟的 Coding Agent 在修改前至少应该知道:
- 当前分支与未提交改动,避免覆盖用户工作;
- 哪些文件真正处于调用路径上;
- 最小可复现路径和已有测试入口;
- 项目规定的验证命令;
- 是否涉及 Migration、外部资源或其他高风险操作。
探索的停止条件不是“理解整个仓库”,而是已经获得足以区分主要假设的证据。
Planning 是可修改的假设,不是固定 Workflow
许多 Coding Agent 会显示 Plan 或 Task List。这容易让人误以为系统内部必须先调用一个 Planner,再严格执行固定步骤。
实际更有价值的理解是:Plan 是当前最佳假设的外部表示。
Initial plan
1. Trace completion write path
2. Trace refresh read path
3. Reproduce the mismatch
4. Patch the owning boundary
5. Add regression coverage
6. Run targeted verification
如果第二步发现后端已经正确返回 Artifact,而客户端 mapper 主动删掉了未知字段,第三步之后的工作就应该围绕 mapper 和类型契约展开。如果发现根本没有持久化写入,则需要重新评估数据模型、幂等与旧任务兼容性。
Plan 必须能够因为新证据而改变。它的价值在于:
- 让长任务中的剩余工作可见;
- 让用户发现 Agent 对 Scope 的误解;
- 在中断或压缩后恢复意图;
- 区分“正在调查”与“已经完成”;
- 防止模型在局部成功后忘记回归测试。
因此,并非每个任务都需要独立 Planner 组件,也不是每次改动都需要一份正式 Spec。Plan 可以是模型的隐式策略,也可以通过 Task List 显式化。复杂度应该由任务的不确定性、持续时间和风险决定,而不是由 Agent 产品强制套用同一条 Workflow。
Context Engineering:只让证据进入工作集
仓库可能有数百万行代码,而模型的 Context Window 始终有限。Coding Agent 的核心能力之一,不是“记住整个代码库”,而是为当前判断选择正确切片。
在这次修复里,初始 Context 可能只需要:
Goal and success contract
Repository instructions
Relevant route and component
Task API schema
Completion persistence code
Existing tests around task restoration
Current hypothesis and unresolved questions
搜索结果本身通常不值得永久保留。它们只是通往证据的索引。读取一个文件之后,Agent 应该提取对判断有影响的接口、不变量和调用关系,而不是让几百行源码永远占据 Context。
这要求区分三种信息:
- Working Context:下一步推理需要的代码与证据;
- Task State:当前假设、已修改文件、测试结果和 Blocker;
- Durable Memory:未来任务仍有价值的项目约定与已验证知识。
当 Context 接近上限时,Compression 的目标不是做一份聊天摘要,而是保存执行连续性。一份合格的压缩结果至少应该保留:
Goal and non-negotiable constraints
Confirmed root cause
Decisions and why they were made
Files read and files modified
Failed approaches and their evidence
Tests already run and exact outcomes
Open questions / blockers
Next safest action
Explicit not-to-do items
如果压缩只留下“我们正在修复刷新后结果消失的问题”,Agent 虽然可以继续说话,却已经不能可靠继续工作。
执行 Loop:用行动消除不确定性
探索、规划和修改并不是三个严格分离的阶段。Coding Agent 的真实执行更接近一个证据驱动 Loop:
Observe → Hypothesize → Choose action → Execute
↑ ↓
└──── Update state ← Interpret result ─┘
对于这个例子,一条合理轨迹可能是:
1. Search for the refresh loader
2. Read the task response type
3. Trace where completed artifacts are written
4. Compare the persisted record with the response mapper
5. Reproduce the missing field in an existing test harness
6. Patch the mapper or owning contract
7. Add a regression assertion
8. Run the narrow test
9. Run relevant typecheck / broader suite
10. Inspect the final diff against scope
每一步都应该让至少一个不确定性下降。连续搜索同一关键词却没有产生新证据,说明 Agent 需要改变策略;测试失败但错误与假设无关,说明它应该先解释环境,而不是盲目修改更多代码。
Search、Read、Edit、Execute 是不同能力
把这些能力都叫 Tool Call 会掩盖重要差异。
- Search 扩大候选空间,但结果通常不构成事实;
- Read 获取证据,但可能被截断或缺少运行上下文;
- Edit 改变 Worktree,需要保留用户已有修改并控制 Diff;
- Execute 可以产生编译产物、网络请求甚至外部副作用,风险取决于命令与环境。
编辑原语本身也会影响可靠性。全文重写容易破坏无关格式;基于精确上下文的 Patch 更容易审查,但上下文漂移时会失败;AST Edit 适合结构明确的机械变换,却不适合所有语言与语义改动。没有一种工具天然最好,Harness 应让 Agent 根据变更类型选择,并用 Diff 验证结果。
最小修改是一项控制策略
“只改根因所在边界”不仅是代码审美,也是 Agent 的风险控制手段。Diff 越大,Verifier 需要覆盖的状态空间越大,用户已有改动被冲突或覆盖的概率也越高。
对于刷新恢复问题,如果根因是 API mapper 漏字段,那么同时重构客户端 Store、重命名 Artifact 类型和迁移数据库,会让任务从可验证修复变成多变量实验。
Agent 可以发现邻近问题,但应该把它们记录为后续工作,而不是偷偷扩大当前 Scope。
Sandbox 与 Permissions:执行能力必须有边界
Coding Agent 读取的是不完全可信的环境:仓库文本、依赖脚本、测试输出、网页内容,甚至 Issue 里的指令都可能影响模型。只靠 System Prompt 告诉模型“不要执行危险命令”不是足够的安全模型。
执行系统需要在模型之外控制:
- 可读取与可写入的目录;
- 是否允许网络;
- 哪些命令可以自动运行;
- 哪些动作需要一次性或持续授权;
- Secret 是否进入子进程;
- 是否允许修改 Git、依赖、数据库或外部系统。
权限请求本身也应该携带 Intent:为什么需要这项能力、将影响什么、有没有更小的授权范围。用户审批的是一次明确行动,不是对整个 Agent 的抽象信任。
这也是为什么 Sandbox 不只是安全附加项。它改变了 Agent 的可用行动空间,使自主执行能够被限定在一个可接受的 blast radius 内。
Verification:不要相信“修好了”
修复完成后,Agent 至少有五层不同强度的证据:
- 静态证据:类型、Lint、Schema 或编译检查通过;
- 局部行为证据:新增回归测试在修复后通过;
- 邻近回归证据:相关测试集没有被破坏;
- 变更证据:最终 Diff 只触及预期 Scope,没有覆盖用户改动;
- 运行证据:真实刷新路径或可代表它的集成环境中,Artifact 被重新加载。
不同任务需要的最高证据层级不同。纯函数修改可能以 Unit Test 结束;跨越前后端与持久化的恢复问题,只跑 Typecheck 通常不够。涉及 Production、付款、权限或外部通知时,代码测试更不能替代对 System of Record 的独立回读。
关键是避免同源验证。让生成修改的同一段逻辑只通过“重新阅读自己写的代码”判断成功,证据强度很低。测试、类型系统、数据库约束、运行探针和用户验收提供的是不同来源的反馈。
Verifier 也可能错。测试如果只断言当前页面能显示结果,就没有覆盖“刷新后重新加载”这个原始目标。验证质量取决于它是否忠实表达成功契约,而不是测试数量。
Durable State:任务应该活得比一次模型调用更久
一次真实修复可能因为 Context 压缩、进程退出、用户暂停、权限等待或外部 CI 而跨越多个 Session。能够 resume 对话并不自动等于能够恢复任务。
可靠恢复需要一个最小 Checkpoint:
Task checkpoint
├── goal_version
├── success_contract
├── current_status
├── confirmed_facts
├── active_hypothesis
├── completed_actions
├── modified_files + worktree identity
├── verification_evidence
├── pending_approvals / blockers
├── next_action
└── not_to_do
这里最关键的不是保存每一句对话,而是保存 authority。恢复后,Agent 必须知道哪个 Worktree 包含修改、哪些测试真的运行过、一次外部动作是否处于未知状态,以及用户后来提出的要求是否替代了旧目标。
Transcript 可以帮助解释历史,Checkpoint 决定系统能否继续执行。
三种公开实现的设计取舍
下面不是能力排名,也不推断产品没有公开的内部实现。它只是截至 2026 年 8 月,根据官方文档与公开源码观察到的三种 Harness 形状。
| 维度 | Claude Code | Codex | Pi |
|---|---|---|---|
| 任务连续性 | 本地保存 Session,支持 --continue / --resume;可用 Worktree 隔离并行工作 |
App Server 公开 thread / turn、resume 与 compact 生命周期;CLI 也支持恢复 Session |
Session 与 Agent State 由开源 core 管理,支持自动 Compaction 和分支摘要 |
| 项目 Context | 以 CLAUDE.md、Rules、Skills 与按需子目录指令组织;AGENTS.md 可通过 import 复用 |
分层读取 AGENTS.md,并组合 Developer Instructions、Skills 与工具上下文 |
默认加载项目 Context Files 与 Skills;系统 Prompt 和可用工具保持很小 |
| Planning | Plan Mode 是受权限约束的只读阶段,获批后再修改 | Plan 可以是任务中的显式状态与协作界面,不要求每项工作都使用固定 Planner | Plan Mode 可作为 Extension 实现,体现“小核心、可扩展策略”的方向 |
| 执行边界 | 默认只读,修改与命令受 Permissions / Sandbox 和工作目录边界控制 | Sandbox Policy、Writable Roots 与 Approval Policy 是 Runtime 的显式输入 | 默认提供 read、bash、edit、write;远程执行、权限与行为可由 Extensions 改写 |
| 编辑与验证 | 通过文件工具、Shell 与用户可审查的修改推进,具体检查由任务与项目规则决定 | apply_patch、Shell、Review 与项目验证命令组成证据路径 |
Edit Tool 保持直接,Extensions 可增加 Git Checkpoint、工具包装与自定义事件处理 |
| 扩展方式 | Skills、Hooks、MCP、Subagents、Plugins | Skills、MCP、Apps、Subagents 与 App Server 协议 | Extensions 可以注册或替换 Tools、监听事件并自定义 Compaction |
这几种实现都拥有 Loop、Tools、Context 与某种 Session Continuity,但它们把复杂度放在不同位置。
Claude Code 的公开文档强调项目指令、权限模式、Session 与多种扩展面;例如官方说明 Session 会被本地保存并可恢复,修改性操作受到权限与 Sandbox约束。
Codex 把执行生命周期暴露得更协议化:公开的 App Server 以 Thread、Turn、Item、Approval 与 Compaction 表达客户端和 Agent Runtime 之间的关系;项目约束由分层的 AGENTS.md 提供,执行能力受 Sandbox 与 Permissions控制。
Pi 则展示了一条小核心路线。它的公开 System Prompt 默认围绕 read、bash、edit、write 组织;Extensions 可以替换 Tool、监听生命周期、实现 Plan Mode 或 Git Checkpoint;Compaction 显式保存读写文件与结构化摘要。
对我来说,这份对比最有价值的结论不是谁的功能更多,而是:Coding Agent 不存在唯一标准的组件图,但可靠实现都会显式处理 Context、执行边界、任务连续性与完成证据。
一次任务的完整时序
回到开头的刷新问题,一次较完整的执行可以被画成:
User
│ goal
↓
Goal Contract ────────────────┐
│ │
↓ │
Repository Discovery │
│ instructions + evidence │
↓ │
Working Hypothesis │
│ │ update on new evidence
↓ │
Context Builder ← Task State ─┘
│
↓
Model Decision
│
├─ search / read ─→ observation ─┐
├─ edit ─────────→ diff ─────────┤
└─ execute ──────→ result ───────┤
↓
State Update
│
incomplete?├──→ next loop
│
↓
Verification
│
failed evidence├──→ revise hypothesis
│
↓
Evidence Bundle + Done
“Done” 最终应该携带一个 Evidence Bundle,而不是只有总结:根因是什么、修改了哪些文件、运行了哪些检查、哪些没有运行、还有什么风险,以及用户如何复查。
这使 Final Answer 成为任务状态的可读投影,而不是任务状态本身。
Coding Agent 的真正产品边界
模型越来越擅长写出局部正确的代码,这当然重要。但软件工程的困难很少只存在于某个函数内部。它存在于不完整需求、隐藏调用关系、脏 Worktree、不可逆副作用、跨 Session 状态,以及“我们如何知道它真的好了”。
一个可靠 Coding Agent 必须同时拥有两种能力:
- 用模型处理语义不确定性:理解目标、建立假设、选择下一步;
- 用工程系统控制执行确定性:权限、状态、幂等、Scope、测试与恢复。
前者让它能够在陌生问题中前进,后者让它不会把每一次聪明判断直接放大成不可控行动。
因此,Coding Agent 并不是一个更聪明的代码补全器,也不是模型外面套了一个 while tool_calls。它是一套围绕工程目标运行的执行系统:不断把未知变成证据,把证据变成修改,再把修改变成可验证的结果。
当它说“完成”时,真正重要的不是这句话有多自信,而是系统能否回答:改动在哪里,为什么是这里,什么证明它有效,以及如果任务现在中断,下一次能否从同一个事实世界继续。


讨论
评论区
关于文章本身,也欢迎留下不同意见或补充。