智能体
mp.weixin.qq.com ·
@百度Geek说
Agentic Harness Workflow:把 AI Coding 变成工程化流程
文章针对把整段需求交给长上下文 Agent 时出现的上下文膨胀与结果漂移、跳过需求澄清与方案评审、中断后过程不可恢复、缺少人工门禁等问题,提出 Agentic Harness Workflow 框架:将需求从提出到归档拆成固定流水线(init、specify、plan、tasks、test-plan、plan-review、implement、code-quality、e2e-run、human-acceptance、commit-push、archive),每阶段由只做一件事的 Sub-Agent 执行,主会话 Agent 负责调度与状态管理,关键节点等待人工确认;implement 到 e2e-run 为自动执行区。框架以 CLAUDE.md 作为入口与项目知识库,指向 state-graph.json、protocol.md、state-schema.md 三份规则文件,并配合状态落盘支持中断续跑。
AI推荐理由
百度Geek说发布的开发者实践文章,提出 Agentic Harness Workflow:将 AI 编码拆为 init→specify→plan→tasks→test-plan→plan-review→implement→code-quality→e2e-run→human-acceptance→commit-push→archive 固定流水线,每阶段由专职 Sub-Agent 执行、主代理调度,配合状态落盘与人工门禁,并给出 CLAUDE.md 作为框架入口与项目知识库的设计。针对长上下文 Agent 的上下文膨胀、跳过工程步骤、过程不可恢复、缺少人工门禁四类问题给出工程化方案,对 AI Coding 落地有参考价值。局限是作者自研实践,未提供可复现的量化效果数据,且明确说明并非真正意义上的 Multi-Agent。
74
正文
原创 欢迎关注的 2026-09-10 18:00 北京

AI 编码的工程化实验:把开发拆成固定流水线,每阶段由专职子代理执行、主代理把关,配合状态落盘与人工门禁,让 AI 写代码从"碰运气"走向"可交付"。
。
点击蓝字,关注我们

作者 | 李忠泽
导读
introduction
把整段需求直接丢给 AI 写代码,结果越写越偏、中途断了还接不上?本文作者给出的答案是:别去造更聪明的 Agent,而是给 AI 编程套上一副"工程框架"。他自研的 Agentic Harness Workflow,把一次开发拆成需求澄清、方案设计、编码实现、审查验收、提交归档等阶段,每阶段交给一个只干一件事的子代理,由主代理统一调度把关,关键节点留人拍板;配合状态落盘,中断、换人、换工具都能无痛续跑。文末还附上了完整实战复盘与踩坑清单。
*全文 8019 字,预计阅读时间 6 分钟*
GEEK TALK
01
为什么需要 Harness
相信大多数开发者都亲身体会过,如果把一个真实需求一次性丢给一个长上下文 Agent,会面临如下几个问题:
1. ****上下文膨胀与结果漂移****:需求、方案、代码、测试、日志全堆在一个上下文里,越到后面它越容易忘了前面说好的约定,慢慢偏离你最初想要的东西。
2. ****跳过必要的工程步骤****:Agent 可能直接写代码,遗漏需求澄清、方案评审或测试设计。
3. ****过程不可恢复****:会话被压缩、重启或中断后,很难可靠判断“现在在哪一步、正在等待什么、哪些任务已完成”。
4. ****自动化过程缺少人工门禁:****没有明确的门禁和人工确认点,错误方向可能一路运行到最后,之后返工会消耗更多的人力。
所以我的思路是,****不是"再造一个很聪明的 Agent",而是给 AI 编码套一副"工程框架"——把一个需求从提出到归档拆成一条固定的流水线,每个阶段交给一个只干一件事的 Sub-Agent,主会话 Agent 负责调度、管状态,并在每个要紧的地方停下来等人拍板。****
GEEK TALK
02
三类应用拓扑对比
从概念上来讲,目前有如下三种结构:

我设计的这套 Harness:****Workflow 为主,阶段级 Multi-Agent 协作,并保留 Human-in-the-loop****。
> 注:这里并不是真正意义上的 Multi-Agent,只是目前 Coding Agent 工具,比如 Claude Code、Codex 等自带的 Sub-Agent 能力。
GEEK TALK
03
总体架构
**3.1 一条完整的生命周期**
一个需求沿固定顺序推进:
```
init → specify → plan → tasks → test-plan → plan-review
→ implement → code-quality → e2e-run → human-acceptance
→ commit-push → archive
```
其中,`implement → code-quality → e2e-run` 是自动执行区;其余大多数阶段在完成后会停下来等待用户确认。`human-acceptance` 和 `archive` 没有独立 Agent,由 Manager 负责呈现、验收和收尾。

**3.2 CLAUDE.md:框架入口与项目知识库**
`CLAUDE.md` 在这套框架里承担两件事:一是****框架的入口****,二是****项目知识库****。
****一、框架入口。**** Coding Agent 在会话开始时会自动加载 `CLAUDE.md`,这是整条链上唯一会被"自动"读到的文件。所以它本身不写任何流程逻辑,只做三件事:点明阶段流程、指向三份规则文件(流转表 `state-graph.json` / 通信协议 `protocol.md` / 状态字段说明 `state-schema.md`)、以及最关键的一句——****主会话必须始终遵循****`.harness/rules/manager.md`。Manager 的行为准则、状态机字段含义、阶段次序,全都在那几份文件里;`CLAUDE.md` 只负责把它们挂上来,harness 就算 Ready 了。
****二、项目知识库。**** Harness 本体是通用的,但它跑在什么项目上是特定的——有哪些代码库、怎么起服务、连哪个测试库、提交要建什么卡、代码库之间谁依赖谁。这些"项目事实"全部登记在 `CLAUDE.md` 的知识库索引里,目前分四块:

这里有一个约定:****只有Manager读 CLAUDE.md,所有Sub-Agent一律不自己读。**** Manager 按当前需求的相关性,从知识库里挑出需要的片段,通过 input 的具名字段下发——比如把环境表相关内容装进 `environment_knowledge` 给 e2e-run,把建卡空间装进 `icafe_space` 给 commit-push,把文档链接装进 `knowledge_refs` 给 plan。
这么设计有三个好处:
1. ****不让每个 Sub-Agent 都把整个知识库吞进上下文****——它只拿到与自己这一步有关的那几行;
2. ****Sub-Agent 保持通用****——它只认 input 里的字段,不认某个项目的具体布局,所以换个项目只需要换一份 CLAUDE.md,`agents/` 和 `rules/` 一行都不用动;
3. ****知识变更只改一处****——新增一个代码库就在表里加一行,不用去改十个 Agent 文件。
还有一条是被实际测试坑出来的原则:****不假设、缺就问。**** 不同代码库的配置位置和形态可能完全不一样(我们这里有一些服务的配置是在 `conf/servicer/*.toml`,但那不是通用规则)。某种意义上,`CLAUDE.md` 就是****知识层插件****:`rules/` 是骨架、`agents/` 是阶段,都是通用件。
**3.3 调度者:主会话 Manager**
这个 Harness 的核心是****主会话 Manager****。它一个人分饰三个功能:
* ****调度****——决定这一轮该派哪个阶段的 Sub-Agent 执行,是往前走还是往回退。
* ****状态管理****——它是唯一有权写 workflow-state 的人,也负责把需求在"进行中 / 挂起 / 已归档"几个目录之间搬来搬去。
* ****对话输出****——Sub-Agent 只跟它用结构化协议对话,你只跟它用自然语言对话,两边的话都由它来转。
Manager 有一条我设计的铁律:****它只认 Sub-Agent 回传的那个结构化协议,不读任何产物的正文(spec、方案、任务、测试、报告),也不读业务代码。**** 它只从协议里取"结论"(通过 / 打回 / 要你确认),从状态文件里取"进度"。
因为 Manager 从不把一堆产物正文和代码吞进自己的上下文,主会话才能在一个需求走完十几个阶段、来回折腾很多轮之后,仍然是轻的、稳的、不飘的。你要是问它"刚才那条结论为什么这么判",它也不会自己去翻代码回答你,而是把问题重新丢回给当初给出这个结论的子代理,让它来解释。
这里的调度不是单靠 Manager 自己的理解,我们把状态之间的流转设计成了一张“图”,存放在了`state-graph.json`,这样 Manager 就很清楚的知道下一个阶段该是什么,并且每一个后置的阶段都可以回退到上一个阶段,这里是通过 Sub-Agent 的“建议”,以及 Manager 对于用户输入的理解判断,共同决定。

**3.4 执行者:阶段 Sub-Agent**
每个 Sub-Agent 有一个独立的 Agent.md 文件来控制这个 Sub-Agent 是做什么的。而每个阶段干什么、产出什么,列个表:

每一个阶段都会通过定好的****结构化输出协议****来和 Manager 进行通信,Manager 会将 Sub-Agent 的输出整理呈现给用户,Manager 不用去读取每个 Sub-Agent 的阶段产物,只需要 Sub-Agent 定好一个对人可读性友好的输出即可。
而且这里有一个小设计,就是将“裁判员”和“运动员”分开设计,不让 Agent 自己设计自己评估,分成两个阶段 plan 和 plan-review,由一个 Sub-Agent 进行技术设计,另外一个 Sub-Agent 进行评估,整体设计参考了****对抗性****的思想,但凡涉及到审查的环节(比如技术设计、测试),都需要挑战设计中的假设,而不是确认正常流程能跑通。
**3.5 状态机:workflow-state**
流程状态不依赖聊天记忆,而是落在每个需求目录的 `workflow-state.json` 中。最重要的字段包括:
* `current_state`:当前阶段;
* `pending`:当前等待点,例如等待前进确认、澄清、回退确认、人工验收、推送确认或 CR 评审;
* `states`:阶段历史、状态和产物路径;
* `review_findings`:门禁发现的问题及其应跳转的阶段;
* loop 计数、涉及仓库、验收服务等运行数据。
其实这个设计思想就是,我们不应该让 Agent 完全依赖本身自己的上下文记忆,应该有一个 Handoff 机制,我举个例子,如果你当前在用 Claude Code 做到某一个阶段,接下来由 Codex 接手,如果 Codex 不具备 Claude Code 的记忆,那么他其实并不知道当前执行到哪里了,而且尽管在同一个 Coding Agent 内部,如果他的上下文压缩了、Memory 清空或者开了一个新的会话,都可以继续接手执行。
那么整体的交互如下:

**3.6 协议:protocol**
Manager 与每个阶段 Sub-Agent 之间只使用一个协议对象,它是一个****双向协议****:派发时由 Manager 填请求侧字段,返回时由 Sub-Agent 返回响应侧字段。两侧共用同一套字段约定。
```
{
"state": "派给哪个阶段(Manager 填)",
"user_message": "用户这一轮的原话(Manager 填)",
"input": { "本阶段所需上下文(Manager 填)": "..." },
"status": "ok | needs_input | fail(Sub-Agent 填)",
"output": { "结构化结果(Sub-Agent 填)": "..." }
}
```
逐个说明:

> 注:`protocol.md`、各阶段模板、业务代码,都是 ****Sub-Agent 自己读****的,不由 Manager 传。
3.6.1 请求侧:input
各阶段的`input`专属 Key 是提前定义好的,这也是"Sub-Agent 保持通用"的关键:

请求侧三个字段:`state` 是派给哪个阶段,`user_message` 是****用户原话逐字透传****,`input` 是这一阶段需要的上下文。`input`****中每个阶段的 Key 由****`manager.md`****的调度规则定义****。
而且协议里有三个通用可选 Key,只在特定情形出现:

3.6.2 响应侧:status + output
响应侧只有三种 `status`:
* `ok` —— 这一阶段做完了,等用户确认后前进;
* `needs_input` —— 没做完,需要人介入(多轮澄清、环境卡住、或要回退到上游补东西);
* `fail` —— 阶段失败,`output.next_state` 给出建议打回的目标(比如 e2e-run 不过 → `implement`)。
`output` 里有****四个通用字段****(几乎每个阶段都有),加上各阶段自己的小字段:

个别阶段的`output`也有自己的专属 Key:

> 注:响应侧的信息会包装在一个`control-result`里面,这是让阶段 Sub-Agent 结构化输出,Manager 去解析的一个规则。
**3.7 执行到测试的 Loop Engineering**
之前 Codex 发布了`/goal`这个命令,实现一个需求,直到 Agent 做完它才会停止,后面也推出了 Loop Engineering 的概念,我的这里其实也参考了这个 Loop 的设计。
Agent 可以自动连续完成****“implement 写代码 → code-quality 静态审查 → e2e-run 跑系统测试 → implement 定向修复”****,但自动化不是无限循环。我给每条自动修复的循环都设了****次数上限****,达到阈值后还没过,就停下来把问题呈现给用户,让用户三选一:继续修、直接放行、或者有疑问要阶段 Sub-Agent 解释。
**3.8 阶段回退:判根因、过门禁**
这套框架里****所有回退都走同一套机制****,不管触发它的是谁:
1. ****用户主动推翻上游****(做到 plan 才想起需求说漏了);
2. ****下游 Sub-Agent 报出上游缺口****(test-plan 发现 plan 没定义接口路由,写不出可执行用例);
3. ****门禁打回****(plan-review 审出问题,指向最早出问题的那个阶段);
4. ****自动执行区的测试打回****(code-quality / e2e-run 不过 → 回 implement)。
人工验收不通过、或者 CR 评审有意见时,Manager 照一张固定的映射表判目标:

****回退的目标阶段必须在流转表****`order`****里位于当前之前,且在这个需求的历史里已经真正完成过****。这防止"回退"变成乱跳。
校验通过、用户确认之后,Manager ****在同一次写入里****完成状态转移:当前阶段落终态、目标阶段由"已完成"改回"进行中"、`current_state` 指向目标阶段,并把刚才那个回退原因作为 `rollback_reason` 放进目标 Sub-Agent 的 input,让阶段 Sub-Agent 知道这次回来要做什么、补什么。
这里有个细节:****等待用户确认的期间,当前阶段一直是"进行中",终态只在真正转移的那一次写入里才落到状态及文件。**** "已完成"严格意味着"用户已经拍板、流程已经离开它",绝不在等确认的时候提前写。这是为了让****冷启动恢复时状态永远是正确的,****任何时刻至多一个阶段处于"进行中",且它必然等于 `current_state`。
**3.9 Human-in-the-loop:人需要确认的地方**
状态文件里有一个 `pending` 字段,它表示的是每个阶段都要停下来等人拍板,****每一个类型都明确记着"在等什么、用户确认后去哪个阶段"****:

GEEK TALK
04
从 Worktree 到验收:多需求如何具备隔离性的工作
**4.1 工作目录结构**
Harness 工作空间目录结构
```
/ ← 主会话(Manager)的 cwd
│
├── CLAUDE.md ← 框架入口 + 项目知识库索引
│ (环境表 / 研发流程 / 依赖顺序,Manager 从这读取作为 input 下发)
├── .mcp.json ← MCP 服务(Playwright,给 e2e-run 跑前端)
│
├── .harness/ ← ★ 框架主目录
│ ├── rules/ ← 规则与模板("插件"里的规则层,可替换)
│ │ ├── manager.md ← Manager 规则(always active,主会话必遵守)
│ │ ├── protocol.md ← 通信协议(协议结构 + message 话术规范)
│ │ ├── state-schema.md ← workflow-state.json 字段与状态置位时机
│ │ ├── state-graph.json ← 流转表:order(阶段次序)+ next
│ │ ├── spec-template.md ← ↓ 各阶段产物模板
│ │ ├── plan-template.md
│ │ ├── tasks-template.md
│ │ ├── test-plan-template.md
│ │ ├── plan-review-template.md
│ │ ├── code-quality-template.md
│ │ └── e2e-run-template.md
│ │
│ ├── active/-/ ← 正在做的需求,全局至多一个
│ ├── process/-/ ← 已开始、挂起的需求
│ ├── archived/-/ ← 已完成归档的需求
│ └── worktrees/-/ ← 本需求的 git worktree 根
│ ├── / ← 每个涉及仓一个子目录(implement 在此施工)
│ └── /
│
├── .claude/
│ ├── agents/ ← ★ 阶段("插件"里的阶段层,加/删/改即改流程)
│ │ ├── init-agent.md
│ │ ├── specify-agent.md
│ │ ├── plan-agent.md
│ │ ├── tasks-agent.md
│ │ ├── test-plan-agent.md
│ │ ├── plan-review-agent.md
│ │ ├── implement-agent.md
│ │ ├── code-quality-agent.md
│ │ ├── e2e-run-agent.md
│ │ └── commit-push-agent.md
│ │ ← human-acceptance / archive 无 agent(Manager 自己管)
│ └── skills/ ← ★ 可插拔能力("插件"里的能力层)
│ ├── huibo-test-mysql/ ← 连测试库
│ ├── huibo-test-redis/ ← 连测试缓存
│ ├── icafe-card-assistant/ ← 建 iCafe 卡
│ ├── icode/ ← 建远程分支 / 推 CR
│ └── get-ugate-token/
│
├── / ← 主仓(各自独立 git 仓;顶层不是 git 仓)
└── /
```
单个需求目录(`-/`)内部
```
.harness/active/-/
├── workflow-state.json ← ★ 状态文件(Manager 独占写)
│ current_state / pending / 各阶段 status /
│ repos / skip_gates / acceptance_services /
│ review_findings / 三个 loop 计数
├── spec.md ← specify 产物
├── spec_user_message.md ← 用户原话存档(抗上下文丢失)
├── plan.md ← plan 产物
├── plan_user_message.md ← 用户原话存档(抗上下文丢失)
├── tasks.md ← tasks 产物(可勾选清单)
├── test-plan.md ← test-plan 产物
└── reports/ ← 审查与测试报告
├── plan-review.md ← 惰性:只有打回过才产
├── code-quality.md ← 每轮追加
├── e2e-run.md ← 每轮追加
└── screenshots/ ← e2e-run 前端 TC 的 Playwright 截图
└── TC-003-xxx.png
```
**4.2 需求空间隔离**
我们设计这个框架,本质是希望可以兼容多需求并行的场景,所以这里的设计是用了`git worktree`,在`Implement`阶段之前,Manager 根据 `tasks` 阶段产出的仓库集合,为每个业务仓建立同名特性分支和独立 worktree。实现、静态审查和 e2e 都复用同一 worktree;修复轮不重建,归档时才清理。这样就让多个需求之间完全隔离,我们的根目录下面,只需要有全部的代码库,而且每个代码库都及时同步 master 分支保证最新的代码,每个需求会根据当前最新代码新建`git worktree`,而且每个需求下都有自己的`workflow-state.json`及阶段产物。
这个设计有三个直接收益:
1. 主工作区不被临时产物污染;
2. 代码审查看到的是同一份改动,而不是每轮重新推导的快照;
3. 中断或回退后可以继续已有任务,已勾选的 task 不需要重复执行。
**4.3 提交代码的依赖关系**
`commit-push` 分为两段:
1. `prepare`:整理改动、建立 iCafe 卡、生成提交确认表;
2. `execute`:用户确认后才真正 commit、push 和创建 CR。
CR 评审由人工完成,Harness 只根据评审结果继续下一层或归档,不代替评审者合入主分支。归档阶段负责停掉验收服务、迁移需求目录、删除 worktree 和已合入分支。
多仓代码库一般会有一个公共库的设计,这样避免同一份代码文件散落到不同代码库中需要多份维护,所以我们在`commit-push`做了一个特别设计,我们的代码库依赖关系会写到`CLAUDE.md`,Manager 会把这部分内容带给`commit-push` ,`commit-push`会先提交依赖代码库的 CR,然后等待用户反馈是否合入(因为目前 icode CR 还需要人工 +2,或者如果有 Skill 能直接去 icode 拉取评分,这块可以改成自动化),合入了之后,会派发`implement`去修改其他代码库的依赖关系(比如`go get xxx@latest`),这里有一个设计就是在`implement`阶段加了一个可选 Key `skip_gates`,这样刚才介绍的“执行到测试的 Loop Engineering” 就可以只执行然后直接跳过测试(因为本身也存在一些自测需求或者小的改动,不需要进行复杂的 e2e 测试)。等这部分准备好后,`commit-push` 会继续提交其他仓的 CR。
GEEK TALK
05
DeepSeek Harness:"Everything is a Plugin"
DeepSeek Harness 是 2026 年 8 月开源的一套 Agent 框架(MIT 协议,命令行叫 `dsh`),它最核心的一句话就是 ****"everything is a plugin"(一切皆插件)****:模型、工具、技能、会话、沙箱、存储、循环、调度、连 UI,全都是可以随意替换的插件;甚至****驱动每一轮对话的那个 agent loop 本身,也是一个能从配置里换掉的插件****。它的哲学被总结成一句很妙的话——"从一个已经能用的完整 Agent 出发去替换零件,而不是从一堆零件出发去攒一个 Agent"。底层它还有一条 append-only 的会话日志,resume、fork、replay 都基于这条事件流。
在这几个月我逐渐研究 Harness Engineering 的时候,我觉得这个东西也应该是一个****“框架”,****如果这个 workflow 实现了,那么其中所有的****“组件”****,不管是需求澄清还是实现代码的 Agent 都可以被替换,用户也可以根据自己的设计添加 Hook、Tools、Skill 包括 `CLAUDE.md`,这些****“组件”****都可以根据业务自身去做定制化,包括这套设计,****中间是一个极简、几乎不动的核心(Manager),外面的一切都是可以替换、可以定制的"文件插件"。****
具体到能换什么:
* ****每个阶段都是插件。**** 一个阶段就是一个 markdown 定义的 Sub-Agent 文件。你想改某个阶段的行为,就改那个文件;想加一个新阶段(比如插一道"安全审计"),就加一个 agent 文件、在流程表里排上;想去掉某个阶段,删掉就是。
* ****能力(skill)是插件。**** 查库、连缓存、建 iCafe 卡、推 iCode CR、用 Playwright 驱动浏览器……这些都是按名字挂上去的****可插拔****技能。
* ****规则和知识是配置。**** 通信协议、流程次序、状态字段、各阶段模板、以及项目知识库(哪些仓、怎么起服务、连哪个库),全是框架外的文件。流程次序想调整,就改流程表那个文件;换个项目,就换一份知识库,核心逻辑照旧。
* ****进度也是可恢复的。**** 前面说的"状态全落盘",本质上和 DeepS