芥末
发布于 2026-04-28 / 0 阅读
0
0

Harness Engineering 工程实践:让 AI Agent 稳定交付代码

AI(人工智能)辅助编程发展到 Agent(智能体)阶段后,最大的难点已经不只是“模型会不会写代码”,而是“模型写出的代码能不能稳定进入工程流程”。

一次简单改动,AI 可能表现很好;一旦需求跨多个模块、涉及历史约束、还要符合团队规范,单靠一句 Prompt 很快会失控。Harness Engineering 解决的正是这个问题:不要只给 AI 下指令,而是给 AI 搭一套可以工作、可以检查、可以纠错的工程环境。

Harness 直译是“挽具”或“约束装置”。马有力气,但没有马具就很难稳定拉车;AI Agent 有代码生成能力,但没有规则、工具、验证和回退机制,也很难稳定交付复杂需求。

OpenAI 的实践曾给过一个很强的信号:一个小团队在几个月内让 Codex 产出约 100 万行代码和 1500 个 PR(Pull Request,代码合并请求),人的主要工作不再是逐行手写代码,而是设计 Agent 能理解、能执行、能被约束的工程系统。

Harness Engineering 的核心可以压缩成一句话:

人负责掌舵和定义规则,Agent 负责执行和产出;当 Agent 出错时,不是让它“再试一次”,而是补齐缺失的能力、规则或验证环节。

从 Prompt 到 Context,再到 Harness

AI 编程大致经历了三种使用方式。

维度Prompt 阶段Context 阶段Harness 阶段
人怎么指挥 AI用一句话描述任务提供设计文档、代码片段、上下文搭建环境、规则、工具和反馈闭环
AI 出错后怎么办改 Prompt 再问一次补充更多上下文把错误转成规则、脚本或流程约束
知识存在哪里人脑、聊天记录设计文档仓库中的规则文件、脚本、基线数据、阶段文档
质量怎么保证人工肉眼检查编译、测试、Review声明式规则、自动验证、测试基线、回退流程
可复现性很低中等高,新 Agent 可以按仓库规则继续工作
人的角色写代码时的辅助者写需求和设计的人设计 AI 工作环境的系统工程师

这三个阶段不是互斥关系,而是逐层叠加。

Prompt 仍然需要,因为任务总要被描述;Context 仍然重要,因为 Agent 必须理解项目背景;Harness 则把“临时对话”升级成“可持续运行的工程机制”。

flowchart LR
    A[Prompt<br/>告诉 AI 做什么] --> B[Context<br/>告诉 AI 项目背景]
    B --> C[Harness<br/>告诉 AI 如何工作、如何检查、错了如何回退]

    C --> D[规则文件]
    C --> E[工具技能]
    C --> F[自动验证]
    C --> G[多 Agent 流程]
    C --> H[测试与质量基线]

一个贯穿案例:Unity 项目管理工具的 AI 工程化演进

假设有一个 Unity 项目管理工具,使用 WPF(Windows Presentation Foundation,Windows 桌面 UI 框架)和 .NET Framework 4.8 开发。它负责处理游戏开发团队常见的本地工作流:

  • SVN(Subversion,版本控制系统)更新;
  • 启动 Unity 编辑器;
  • 替换 Library 缓存;
  • 处理冲突;
  • 执行定时任务;
  • 发送状态通知。

这类工具功能本身不一定复杂,但细节很多:UI(User Interface,用户界面)不能乱写硬编码文本,SVN 命令必须带认证参数,日志格式要统一,旧版本 .NET 不能使用新 C# 语法,新增文件必须同步到 .csproj

这正适合观察 AI 编程从粗放到工程化的变化。

Prompt 阶段:一问一答,很快失控

最早的使用方式通常是这样:

帮我写一个 WPF 窗口,左边是项目列表,右边是详情区域,有一个“更新”按钮,点击后执行 svn update。

AI 会立刻给出代码。小功能看起来没问题,但项目一变大,问题会集中出现:

问题表现
项目背景丢失每次新对话都要重新解释项目结构和约束
代码风格不一致项目封装了 MessageDialog.Show,AI 却写 MessageBox.Show
隐性约束缺失XAML 里写中文、日志没有前缀、SVN 命令漏认证参数
改动影响不可控修改一个模块时不知道会不会破坏另一个模块
历史决策无法继承上个版本定下的规则,新对话完全不知道

Prompt 阶段的根本问题是:知识没有沉淀在工程里,而是散落在对话中。

Context 阶段:设计文档能说明“做什么”,但管不住“怎么做”

当需求变复杂后,设计文档会显著改善 AI 的输出质量。比如为一个版本写一份 V3_5_DESIGN_SPEC.md,把这些内容说清楚:

  • 功能目标;
  • 方案设计;
  • 文件变更列表;
  • UI 布局示意;
  • 本地化键值;
  • 颜色规范;
  • 测试清单;
  • 风险评估。

AI 不再猜颜色、猜交互、猜文件位置,而是按规格实现。Context 阶段的收益很明显:需求越清楚,生成结果越稳定。

但设计文档仍然有边界。

文档能解决文档解决不了
功能要做什么AI 是否一定遵守编码规范
UI 大致怎么布局AI 是否写了禁用语法
哪些文件要改AI 是否忘了更新 .csproj
有哪些测试点AI 是否删除了已有测试
风险在哪里AI 是否在长上下文里遗忘旧规则

文档主要约束“目标”,但很难机械化约束“过程”。一旦任务很长,AI 会出现注意力衰减:最早加载的规则明明写过,后面改到第十几个文件时还是可能忘掉。

Harness 阶段:把约束变成仓库里的系统

Harness 阶段的思路不是继续堆长文档,而是把工程知识固化到仓库中,让 Agent 每次工作都能加载、执行、检查和回退。

一个典型目录可以这样组织:

.cursor/
├── rules/
│   ├── build-after-changes.mdc
│   ├── coding-style.mdc
│   └── localization.mdc
├── skills/
│   ├── msbuild-compile/
│   │   ├── SKILL.md
│   │   └── run_compile.bat
│   ├── mstest-run/
│   │   ├── SKILL.md
│   │   └── run_tests.bat
│   └── post-verify/
│       ├── SKILL.md
│       └── run_verify.bat
└── agents/
    ├── pm-orchestrator.md
    ├── requirement-analyst.md
    ├── solution-architect.md
    ├── gate-reviewer.md
    ├── developer.md
    ├── code-reviewer.md
    └── qa-tester.md

scripts/
└── verify_all.ps1

docs/
└── features/
    └── FeatureName/
        ├── 01_REQUIREMENT_ANALYSIS.md
        ├── 02_SOLUTION_DESIGN.md
        ├── 03_GATE_REVIEW.md
        ├── 04_DEVELOPMENT.md
        ├── 05_CODE_REVIEW.md
        └── 06_TEST_REPORT.md

verification_baseline.json
verification_history.log

Harness 阶段的关键变化是:

  • 规则不只写给人看,还写给 Agent 加载;
  • 能自动检查的约束,不再依赖 Agent 记忆;
  • 每个阶段都有文档产物,不靠聊天记录追溯;
  • 质量门不通过时有明确回退路径;
  • 测试数量、规则检查结果都有基线,不能随意倒退。

多 Agent 协作:职责分离本身就是质量控制

复杂需求不适合交给一个 Agent 从需求分析一路干到测试。原因和人类软件工程一样:自己写需求、自己设计、自己实现、自己 Review,很容易形成盲区。

单 Agent 常见问题如下。

问题典型表现
自我确认自己设计的方案很少主动推翻
上下文过载十几个文件一起改,容易漏改或误改
没有质量门需求或方案的问题被一路带进代码
缺少审计事后很难知道某个决策为什么产生
修复边界混乱Review 发现问题后,Reviewer 自己动手改代码

更稳的做法是把研发流程拆成多个 Agent,每个 Agent 只负责一类工作,并通过阶段文档交接。

Agent职责输出物模型选择思路
PM Orchestrator流程编排、阶段推进、回退决策阶段状态、调度记录、最终结论调度和判断为主,可用轻量模型
Requirement Analyst需求拆解、歧义识别、验收标准01_REQUIREMENT_ANALYSIS.md文本分析为主
Solution Architect模块划分、接口设计、风险预判02_SOLUTION_DESIGN.md设计文档为主
Gate Reviewer开发前审查方案是否可执行03_GATE_REVIEW.md模板化审查为主
Developer Agent按方案写代码、编译自检04_DEVELOPMENT.md 和代码改动真正写代码,需要强模型
Code Reviewer检查逻辑、需求遗漏、设计偏离05_CODE_REVIEW.md需要深度理解代码
QA Tester测试用例、缺陷分类、测试报告06_TEST_REPORT.md测试组织和验证为主

这不是为了制造复杂流程,而是为了让每个阶段都有人“反向检查”上一个阶段。

flowchart LR
    A[需求输入] --> B[Requirement Analyst<br/>需求分析]
    B --> C[Solution Architect<br/>方案设计]
    C --> D[Gate Reviewer<br/>开发前闸门]
    D -->|通过| E[Developer Agent<br/>开发实现]
    D -->|不通过| B

    E --> F[Code Reviewer<br/>代码评审]
    F -->|通过| G[QA Tester<br/>测试验证]
    F -->|必改问题| E

    G -->|通过| H[PM Orchestrator<br/>交付判定]
    G -->|阻塞缺陷| E

PM Orchestrator 要跑状态机,而不是只转发消息

PM Orchestrator 的核心职责是控制流程边界。它不应该只是把一个 Agent 的输出复制给另一个 Agent,而要根据质量门结果决定是否继续、回退或暂停。

典型规则如下:

情况处理方式
闸门评估存在阻塞项不允许开发,回退到需求分析或方案设计
代码评审存在必改项不允许进入测试,回退到开发
测试存在阻塞缺陷回退到开发修复
同一阶段连续回退 3 次暂停并重新审视需求或方案
阶段输出缺少正式文档不允许进入下一阶段

流程可以抽象成状态机:

stateDiagram-v2
    [*] --> RequirementAnalysis
    RequirementAnalysis --> SolutionDesign: 需求清楚
    RequirementAnalysis --> RequirementAnalysis: 需求有歧义

    SolutionDesign --> GateReview: 方案完成
    GateReview --> Development: 闸门通过
    GateReview --> RequirementAnalysis: 需求层问题
    GateReview --> SolutionDesign: 方案层问题

    Development --> CodeReview: 编译通过
    Development --> Development: 编译失败

    CodeReview --> QATest: 评审通过
    CodeReview --> Development: 代码问题
    CodeReview --> SolutionDesign: 设计偏离

    QATest --> Delivery: 测试通过
    QATest --> Development: 阻塞缺陷

    Delivery --> [*]

一个关键原则必须写进 Agent 定义中:

发现问题的人不能自己修复问题。

Gate Reviewer 不能自己改方案再宣布通过,Code Reviewer 不能自己改代码再 Review,QA Tester 不能自己修 Bug 再出测试报告。否则角色分离就失去了意义。

Rules、Skills、Scripts 三层分离

很多团队会把所有约束都写成规则文件,例如:

  • XAML 里禁止中文;
  • 禁止直接调用 MessageBox.Show
  • SVN 命令必须带 --username
  • 禁止使用 C# 8.0+ 语法;
  • 测试数量不能减少;
  • 改完代码必须编译;
  • 编译后必须测试。

早期这样做很直观,但规则一多就会出问题。十几条 always apply 的规则加起来上千行,AI 在长任务中很容易遗忘细节。不是规则没写,而是规则没有变成可执行的检查。

更合理的拆分方式是三层:

作用适合承载什么
Rule告诉 Agent 原则和流程“改完必须验证”“禁止绕过检查”“为什么不能写硬编码中文”
Skill告诉 Agent 怎么操作工具“执行哪个脚本”“看哪个输出文件”“失败后如何处理”
Script机械化执行检查正则扫描、编译、测试、基线对比、文件同步检查
flowchart TD
    A[Rule<br/>必须做验证] --> B[Skill<br/>知道如何执行验证]
    B --> C[Script<br/>真正跑检查]
    C --> D{结果}
    D -->|PASS| E[进入下一阶段]
    D -->|WARN| F[记录风险后继续]
    D -->|FAIL| G[回退修复]
    G --> B

能用机器检查的规则,不要只让 AI 记

可以把规则迁移成脚本检查项。

原规则脚本检查方式适合自动化的原因
XAML 禁止中文rg 正则扫描 Unicode 中文范围模式明确
禁止 MessageBox.Show关键字扫描并排除合法文件模式明确
禁止 C# 8.0+ 语法匹配 ??=using varawait foreach语法特征明确
SVN 命令必须带认证参数扫描命令行是否包含 --username参数检查明确
测试数量不能减少verification_baseline.json 对比数值规则明确
.cs 文件必须进入 .csproj对比文件系统和项目文件结构规则明确

Rule 可以保留精简版,只解释原则:

---
alwaysApply: true
---

# 改动后验证规则

任何代码改动完成后,必须依次执行:

1. 编译验证;
2. 自动化测试;
3. 事后规则检查。

如果任一步失败,必须修复后从编译重新开始。禁止通过删除测试、降低基线或修改验证脚本绕过失败。

Skill 负责告诉 Agent 如何调用工具:

# post-verify Skill

用途:执行事后验证,检查编码规范、测试基线、项目文件同步等问题。

执行方式:

```bash
scripts/run_verify.bat

结果文件:

artifacts/post_verify_report.txt

判定规则:

  • FAIL:必须修复,不能进入下一阶段;
  • WARN:可以继续,但必须在阶段报告中列出;
  • PASS:允许进入下一阶段。

Script 负责真正检查:

```powershell
# 示例:检查 XAML 中是否存在中文字符
$violations = rg "[\x{4e00}-\x{9fff}]" . -g "*.xaml"

if ($violations) {
    Write-Host "[FAIL] A.1 XAML Chinese Characters"
    $violations
    exit 1
} else {
    Write-Host "[PASS] A.1 XAML Chinese Characters"
}

这样做的好处很直接:AI 可以忘记“不要写 MessageBox.Show”,但脚本不会忘。只要流程 Rule 规定“改完必须跑验证”,机械检查就能兜底。

事后验证:Harness 的安全网

Harness 不是只靠事前规则约束 Agent,还要在改动完成后确认系统仍然健康。一个实用的验证流程通常分三步:

flowchart TD
    A[代码改动完成] --> B[Step 1 编译]
    B -->|0 Error| C[Step 2 自动化测试]
    B -->|失败| F[修复后重跑]

    C -->|测试全通过<br/>数量不减少| D[Step 3 事后验证]
    C -->|失败| F

    D -->|0 FAIL| E[任务可进入交付判断]
    D -->|FAIL| F

    F --> B

验证项可以分成三类:

  1. 编码规范;
  2. 编译测试;
  3. 工程结构和规则文件同步。
编号检查项严重性检测方式
A.1XAML 中文字符Error正则扫描
A.2XAML EmojiError正则扫描
A.3C# 8.0+ 语法Error语法模式匹配
A.4MessageBox.ShowError关键字扫描,排除合法 fallback 文件
A.5硬编码 UI 文案Warning检测直接赋值中文
A.6日志缺少前缀Warning扫描 Logger 调用格式
A.7直接访问 LastStateError扫描违规 API 使用
A.8SVN 缺少认证参数Warning扫描 SVN 命令参数
A.9文件超过 1000 行Warning统计 .cs 文件行数
B.1编译零 ErrorError调用 MSBuild
B.2测试全通过Error调用测试运行器
B.3测试数量不低于基线Error对比基线 JSON
C.2规则文件多平台副本同步Warning检查 .cursor.github 等目录
C.5.cs 文件已加入 .csprojError对比文件系统和项目文件

测试基线可以用 JSON 固化:

{
  "test_count_baseline": 369,
  "excluded_files": [
    "Legacy/OldGeneratedFile.cs"
  ],
  "last_updated": "2026-03-27"
}

如果 AI 试图通过删除测试来让测试通过,基线检查会拦住:

[FAIL] B.3 Test Count Guard
Current tests: 361
Baseline tests: 369
Result: test count decreased by 8

一份结构化报告应该让 Agent 和人都能快速判断结果:

===========================================
Post-Verification Report
Date: 2026-03-27 11:08:11
===========================================

[PASS] A.1 XAML Chinese Characters
[PASS] A.2 XAML Emoji
[PASS] A.3 C# 8.0+ Syntax
[PASS] A.4 MessageBox.Show
[WARN] A.9 File Length Limit          13 violations
[PASS] B.1 Build Success
[PASS] B.2 All Tests Pass
[PASS] B.3 Test Count Guard
[WARN] C.2 Rule File Sync             34 violations
[PASS] C.5 .cs in .csproj

Summary: 9 PASS / 2 WARN / 0 FAIL / 3 SKIP
Result: PASSED WITH WARNINGS
===========================================

好的事后验证要满足几个要求:

要求说明
覆盖高频错误把最容易反复出现的问题优先脚本化
误报率低误报多了,Agent 和人都会倾向于忽略结果
Error 必须闭环Error 不能跳过,必须修复后重新验证
Warning 要记录Warning 不一定阻塞,但要进入阶段报告
基线只升不降测试数量、覆盖范围、规则检查不能随意倒退
留历史记录每次验证追加到日志,方便追踪趋势

如何启动一次完整的多 Agent 研发流程

完整流程不需要手工逐个呼叫所有 Agent。更好的方式是让 PM Orchestrator 统一调度。

准备需求文档

需求文档不一定很长,但必须说清楚四件事:

内容示例
背景目标为什么要做这个需求,要解决什么问题
功能范围哪些功能必须做,哪些不做
约束条件技术栈、兼容性、性能、UI、权限等限制
验收标准怎么判断需求完成

比如做一个“定时更新超时中断”功能,需求文档至少要描述:

  • 现有定时更新流程;
  • 超时时间如何配置;
  • 超时后是终止 SVN、跳过后续步骤,还是进入失败状态;
  • UI 如何提示;
  • 日志和通知怎么记录;
  • 哪些旧行为不能破坏。

给 PM Orchestrator 一个启动指令

可以使用类似指令:

请以文档驱动方式推进一个完整的软件研发需求闭环。

需求内容:@docs/requirements/schedule-timeout.md

要求:
1. 自动规划需求目录和阶段文档。
2. 先进行需求分析,再做方案设计,再做开发前闸门评估。
3. 闸门评估未通过时,不得进入开发。
4. 闸门通过后,进入开发、代码评审、测试验证和交付汇总。
5. 每个阶段必须输出正式文档内容。
6. 每次阶段切换必须形成文档化交接记录。
7. 最终输出可上线结论、阻塞项、遗留风险和后续建议。

请先输出:
- 当前阶段;
- 建议创建或更新的文档;
- 第一阶段应该交给哪个 Agent;
- 给该 Agent 的任务说明。

PM Orchestrator 收到后,应该先生成任务目录和阶段计划,而不是直接开始写代码。

docs/features/ScheduleTimeout/
├── 01_REQUIREMENT_ANALYSIS.md
├── 02_SOLUTION_DESIGN.md
├── 03_GATE_REVIEW.md
├── 04_DEVELOPMENT.md
├── 05_CODE_REVIEW.md
└── 06_TEST_REPORT.md

人只在关键检查点介入

完整七阶段并不意味着人要一直盯着每条输出。更合理的介入点是:

阶段人的介入方式
需求分析确认歧义是否被识别,验收标准是否完整
方案设计检查是否偏离架构方向
闸门评估重点看阻塞项和高风险点
开发实现通常看编译和验证结果
代码评审重点看 Major 问题和需求遗漏
测试验证看阻塞缺陷和测试覆盖
交付汇总判断是否接受遗留 Warning

任务越复杂,流程越不能省;任务越小,流程可以轻量化。

任务类型推荐流程
跨工程大需求、架构迁移完整七阶段
单工程中等需求可合并需求和方案,但保留代码评审
小 Bug 修复根因分析 → 开发 → 评审 → 测试
十行以内的小改动直接修改 + 编译 + 自动验证

关键原则是:流程重量要和任务风险匹配。十行修复跑七阶段是浪费,跨工程迁移跳过闸门则是在冒险。

项目级任务看板:给 Agent 跨会话记忆

AI Agent 最大的问题之一是没有天然项目记忆。一个项目开发几个月后,历史需求、设计决策、踩过的坑都分散在旧文档里。新开一个对话时,如果没有索引,Agent 很可能重复造轮子。

项目级任务看板可以解决这个问题。

# 项目任务看板

| 任务ID | 任务名称 | 状态 | 文档路径 |
|--------|----------|------|----------|
| T-012 | 定时更新基础功能 | 已完成 | docs/features/ScheduleUpdate/ |
| T-015 | 定时更新后自动打开 Unity | 已完成 | docs/features/SchedulePostUpdate/ |
| T-023 | 定时更新超时中断 | 进行中 | docs/features/ScheduleTimeout/ |

当新需求和“定时更新”有关时,Requirement Analyst 应该先搜索看板,找到 T-012 和 T-015,再读取历史阶段文档。这样新功能会沿着已有状态机扩展,而不是重新设计一套不兼容的流程。

任务看板本质上是在仓库中给 Agent 建立“可恢复记忆”。每次新会话开始,PM Orchestrator 读取看板,就能快速重建项目上下文。

人的角色:从写代码转向写系统

Harness Engineering 并不是让人闲下来,而是把人的工作位置上移。

传统工作Harness 工作
写业务代码设计 Agent 职责边界
手动 Review建立自动化检查项
手动跑测试定义测试基线和失败策略
写说明文档写规则、Skill 和阶段模板
管进度设计 PM 状态机和回退规则
Debug 单点问题分析系统缺了哪种护栏

当 Agent 犯错时,处理思路可以按层次分类。

问题类型示例Harness 改进方式
编码规范违反写了 MessageBox.Show、XAML 中文、C# 新语法Rule + Script
操作步骤遗漏改完没编译、没跑测试、不知道工具路径Skill
流程交互混乱Reviewer 自己修代码、Gate 自己改方案SubAgent 定义
外部能力缺失需要读取内部 Wiki、查询外部系统MCP(Model Context Protocol,模型上下文协议)
系统环节缺失没有事后验证、没有任务看板新阶段或新组件

这几层改动的频率和影响不同。

层次改动频率影响范围
Rule + Script高频解决具体违规
Skill中频固化操作步骤
SubAgent 定义低频改善流程质量
MCP 集成按需扩展 Agent 能看到和能操作的范围
新阶段 / 新组件很低改变整套研发流程

越往下,改动越少,但对系统能力的影响越大。

Harness Engineering 的落地原则

想让 AI Agent 稳定参与工程交付,可以抓住几条原则。

原则含义
仓库是真相源规则、文档、脚本、基线都要落在仓库里,而不是留在聊天记录中
人定义“什么是对”编码规范、验收标准、质量门由人设计
Agent 执行具体任务需求分析、设计、开发、评审、测试都可以分配给 Agent
能自动检查就脚本化不要把可机械检测的规则只写进 Prompt
失败必须有回退路线每个质量门都要知道不通过时退回哪里
发现问题的人不修问题保持角色独立,避免自我确认
基线只升不降测试数量和质量标准不能为了通过而降低
每次翻车都补护栏把重复错误转成规则、Skill、Script 或流程组件

AI 编程的上限不只取决于模型能力,也取决于工程环境。Prompt 让 AI 听懂任务,Context 让 AI 理解项目,Harness 让 AI 在规则和反馈闭环里持续工作。

当规则是缰绳,验证是刹车,多 Agent 流程是分工体系,仓库知识是记忆,AI Agent 才能从“偶尔帮忙写几段代码”变成“可被纳入研发流程的执行者”。


评论