我们直接用 AI 写代码的团队,十有八九都会遇到同一个瓶颈:单点用着挺爽,一问一个准,一提交就翻车。上下文一长就丢前忘后,代码风格千人千面,测试覆盖全凭运气,最后 review 的时候代码review机器人比人还忙。我一开始也以为是模型不行,后来把几个主流模型都换了一遍,发现治标不治本,真正的问题是——整个编码流程根本没有人去设计,AI 只是在一个没有约束的空间里自由发挥。
后来接触到 Harness 工程这个概念,才算把问题想明白。
Harness 不是某个工具,也不是某个框架,它是一整套“把 AI 编码能力工程化”的方法论。核心思路是:不要指望一个超大上下文窗口解决所有问题,而是把编码过程拆成多个有明确目标、明确输入输出、明确验收标准的阶段,每个阶段由一个 Skill 来承担,最后用一条流水线把 Skill 串起来,形成端到端的 AI Coding 全链路。
这篇文章把我最近在做的“8 个 Skill 串起全链路”的完整实战过程写下来。从环境搭建、Skill 开发、链路编排到问题排查,全部是实测过的方案,希望能给正在做 AI Coding 工程化的团队一些参考。
1. Harness 工程是什么,为什么不用裸 Agent
1.1 一次失败的自动化尝试
今年年初,我让团队里最优秀的工程师用半天时间,把项目里一个老模块用 AI 重写一遍。结果很有意思:代码写得飞快,不到两个小时就交差了。但 review 的时候问题全出来了——命名风格跟项目规范不一致,缓存逻辑全部走默认值,异常处理只 catch 不处理,连数据库索引都没建。这个工程师自己的评价是:“我觉得它写得比我快,但我不知道它为什么要这么写。”
这个案例很典型。裸 Agent 的默认行为是“尽快完成你的指令”,它的目标函数是“生成看起来合理的代码”,而不是“生成符合项目长期维护要求的代码”。你给它一个需求,它会调取记忆里的最佳实践,但它不知道你们项目的上下文是什么、规范是什么、坑在哪里。
所以问题的核心不是“模型不够聪明”,而是“流程缺少约束”。
1.2 Harness 的四层结构
我理解的 Harness 工程,是把 AI Coding 从“聊天式开发”升级为“流水线式开发”的一套体系,分四层:
- 第一层是模型层,底层的大模型,负责具体生成能力,可替换、可降级。
- 第二层是 Skill 层,把编码任务拆成原子能力单元,比如“需求澄清”“架构设计”“代码生成”,每个 Skill 有内置的提示词模板、工具调用脚本、输出规范。
- 第三层是编排层,也就是 Harness 的核心,负责调度 Skill 的执行顺序、传递上下文、判断分支条件、执行人工审批节点。
- 第四层是反馈层,把测试结果、lint 结果、review 结论、线上监控数据回传,驱动 Skill 不断调整。
简单类比:模型是员工,Skill 是岗位职责说明书,Harness 是项目经理,反馈层是 KPI 考核。
1.3 Harness 和 Agent 的区别
现在市面上的 Agent 框架很多,很多人觉得有 Agent 就够了。但我实际对比下来,Harness 和 Agent 是两种完全不同的思路。
Agent 的核心是“自主决策”,给它一个目标,它自己规划步骤、调用工具、完成任务,具有高度自主性。听起来很好,但在生产环境里,自主性越高,不确定性越大,你越难控制它什么时候调用什么工具、生成什么内容。
Harness 的核心是“确定性编排”,把每一个 Skill 的输入、输出、触发条件、验收标准都定义清楚,本质上是一条流水线。每个环节做什么是预先设计好的,Skill 只是流水线上负责一道工序的机器人。
拿个生活例子类比:Agent 是请了个自由职业者,你告诉他需求,他全权负责;Harness 是开了一条生产线,每道工序都有明确的作业指导书和质量标准,工人只需要按标准执行。
对于追求工程化、流程化、可审计的企业团队,确定性远比自主性重要。
2. 动手前:搭建 Harness 运行环境
2.1 工具链选型与安装
Harness 工程可以基于开源框架自建,也可以用商业工具。我这边实测下来,Codex Harness 和 DeepSeek Harness 是目前社区活跃度比较高的两个方向,前者生态完善,后者对国产模型接入更友好。团队如果主要用 OpenAI 系模型,建议从 Codex Harness 入手;如果用的是 DeepSeek 或需要私有化部署,DeepSeek Harness 更合适。
安装过程我给一个通用流程,两个框架大同小异:
- 第一步,准备 Python 3.10+ 环境,建议用 conda 或 venv 隔离,不要直接装到系统环境。
- 第二步,安装核心依赖,命令行执行
pip install harness-core,有些框架还需要playwright做浏览器自动化。 - 第三步,配置模型 API 密钥,一般在
~/.harness/config.yaml里统一管理,支持配置多个模型 Provider,方便做模型切换和降级。 - 第四步,初始化项目,
harness init --template default会生成一套默认目录结构和示例 Skill。
安装过程中最容易踩的坑有两个:一是 Python 版本太低导致依赖冲突,二是 API 密钥权限配置不对导致调用超时。建议安装前先确认 Python 版本,配置密钥后跑一次最小的 smoke test,确认基础调用正常再往下走。
2.2 Skill 的基本形态:一个文件夹 + 三个文件
在 Harness 里,一个 Skill 通常是一个独立文件夹,里面包含三个核心文件:
SKILL.md是技能说明文件,描述这个 Skill 的目标、适用场景、输入输出接口。prompt.md是提示词模板,定义模型在执⾏这个 Skill 时需要遵循的角色设定、思考框架和输出格式。tool.py或execute.py是工具脚本,封装这个 Skill 需要调用的外部能力,比如读取仓库代码、运行测试、调用 Git 命令等。
这三个文件的分工很明确:SKILL.md让编排层知道“这个 Skill 是干什么的”,prompt.md让模型知道“这个任务具体怎么做”,tool.py让 Skill 具备“实际操作的能力”。
有个常见误区是把所有逻辑都塞进 prompt,觉得提示词写得多就万事大吉。实际工程里,能用脚本实现的逻辑就不要让模型猜,因为在执行的确定性上,脚本远好于模型的自由发挥。比如“扫描当前 Git 分支上改动了哪些文件”,这种操作适合写在tool.py里,而不是让模型去想象。
2.3 第一个 Skill 的开发与调试
开发一个 Skill 最快要三步。第一步,创建文件夹和三个核心文件;第二步,在SKILL.md里定义触发条件和输入输出;第三步,注册到 Harness 的配置文件里,harness skill list能看到说明注册成功。
写第一个 Skill 的时候,建议选一个最不起眼但最高频的原子能力,比如“获取当前分支的变更文件列表”。这个 Skill 的tool.py核心逻辑就三行:
import subprocess def get_changed_files(): result = subprocess.run( ["git", "diff", "--name-only", "origin/main..."], capture_output=True, text=True ) return result.stdout.strip().splitlines()调试的时候用harness run skill_name --input '{"repo_path": "/path/to/repo"}'来单独执行一个 Skill,不需要每次都跑完整链路。我开发过程中 80% 的时间都花在单 Skill 调试上,链路跑不通基本都是因为单个 Skill 的边界没定义清楚。
3. 8 个 Skill 串起全链路设计
3.1 链路整体图景
我把一个相对完整的全链路拆成了 8 个 Skill:需求澄清、架构设计、代码实现、自测验证、评审审查、缺陷修复、重构优化、文档沉淀。
这 8 个 Skill 对应的是研发流程里最核心的 8 个环节。选择它们的主要原因有两个:一是每个环节都有明确的输入输出和验收标准,适合做成标准化工序;二是覆盖了从需求到上线的完整生命周期,中间任何一环出了问题都能量化定位。
整体链路的关系是这样的:需求澄清 Skill 把模糊输入变成明确的验收标准,架构设计 Skill 把这个验收标准转化成技术方案,代码实现 Skill 按方案写代码,自测验证 Skill 检查代码是否满足验收标准,评审审查 Skill 从规范角度挑毛病,缺陷修复 Skill 处理测试和评审发现的问题,重构优化 Skill 做性能和结构优化,最后文档沉淀 Skill 把决策和变更记录下来。
3.2 需求澄清 Skill
这个 Skill 处理的是全链路的第一道工序:如何把一句“给我把登录模块优化一下”变成可执行、可验收的任务描述。
需求澄清 Skill 的核心是约束与追问。它内置的prompt.md会引导模型输出一份格式化的需求规格说明书,内容包含:业务背景、目标用户、核心场景、功能需求、非功能需求、验收标准、边界条件。
它的tool.py里通常会内置一个问题模板,包含“这个需求的优先级是什么”“影响范围涉及哪些模块”“预期的性能指标是多少”这类问题,确保模型不是直接开工,而是先收集完整信息。
实操心得:需求澄清这个环节,重点不是让模型把需求写得花团锦簇,而是逼着需求方把模糊描述转化成可验证的条目。如果需求方说“页面要快一点”,你需要追问“具体是首屏时间小于多少毫秒,还是接口响应低于多少秒”。可验证性是这一环节的验收标准。
3.3 架构设计 Skill
需求澄清的产物是“做什么”,架构设计 Skill 解决的是“怎么做”。
这个 Skill 会基于需求描述,结合当前仓库的代码结构、依赖关系、已有模块,生成一份技术方案,包含:涉及的系统模块、核心类与接口设计、数据库变更、缓存策略、第三方服务依赖、风险点评估。
架构设计 Skill 的tool.py通常会调用代码分析工具,比如读取仓库的目录结构、扫描核心服务的入口文件、分析当前数据库表结构。这些信息是模型在生成方案时的重要参考,没有这些信息,架构方案就是空中楼阁。
这里要特别强调一个细节:架构设计 Skill 的输出不应该是大段的架构描述文字,而应该是结构化的决策记录。每条决策要包含三个部分:方案选择、选择理由、被否决的替代方案。这样后续的代码实现和评审才能溯源,否则你永远不知道当初为什么选了 A 方案而不是 B 方案。
3.4 代码实现 Skill
代码实现 Skill 是在架构方案的基础上生成代码,同时也承担着“实现与方案一致性”的校验职责,不是纯粹的“根据 prompt 写代码”。
它的tool.py里有几个关键能力:读取项目现有的编码规范文件,扫描目标目录的已有代码风格,检查新增代码的依赖是否已经在requirements.txt或package.json里声明。这些检查都是脚本化的,优先级高于模型生成。
代码实现 Skill 的prompt.md里会特别强调:不要在单个请求里生成超大段的代码,而是按文件、按模块分批生成,每生成一个文件就做一次本地编译检查。这样做的好处是错误能被尽早暴露,而不是等最后一次性暴雷。
实际操作中,为了让代码实现更可控,我一般会在代码实现 Skill 和架构设计 Skill 之间加一个人工确认节点,架构方案需要人确认后才进入代码实现阶段。这个节点看似多了一道流程,实际是省时间的——架构错了,后面的代码全部白写,返工成本远高于确认成本。
3.5 自测验证 Skill
代码写完了,怎么确认它真的能跑?自测验证 Skill 就是干这个的。
这个 Skill 的职责范围包括:自动生成单元测试用例、执行现有测试套件、运行静态代码检查工具、检查代码风格规范。它的tool.py里会封装 pytest、eslint、golangci-lint 这类工具的调用,并把执行结果结构化返回。
自测验证 Skill 的设计重点在于“测试用例的有效性”判断。模型很容易生成“为了覆盖而覆盖”的测试,看起来每行代码都被执行了,实际上断言全部是恒真的,跟没测一样。所以这个 Skill 的prompt.md里必须要求模型对每个测试用例标注“这个用例在验证什么行为”,并且不允许出现没有断言的测试函数。
我在实际使用中会在自测验证 Skill 后面加一个强校验:新增测试用例必须至少能捕捉到一个缺陷,如果整轮测试用例一个失败都没有,反而要怀疑测试有没有写到位。这个方法听起来有点反直觉,但确实能有效防止测试写得太水。
3.6 评审审查 Skill
评审审查 Skill 是模拟 Code Review 过程,对代码实现 Skill 的产物进行系统性的代码走查。
它的检查点设置得非常具体:是否存在明显的命名不规范,是否有异常被吞掉但不记录日志,SQL 是否缺少必要的索引,是否有并发问题,是否存在不必要的重复代码,是否引入了多余的依赖。
评审审查 Skill 的输出格式是问题清单,每条问题包含:问题文件与行号、严重级别(P0/P1/P2)、问题描述、修改建议。这个输出格式非常重要,它决定了后续缺陷修复 Skill 能不能自动化处理。
在团队里推行评审审查 Skill 之后,我明显感受到一个变化:人工 Review 的负担大幅降低,Reviewer 不再需要花大量时间找低级的规范问题,而是把精力集中在两件事上——架构层面的合理性判断,以及业务语义是否真正满足需求。机器能干的活交给机器,人的时间应该花在更有价值的地方。
3.7 缺陷修复 Skill
缺陷修复 Skill 的输入是评审审查 Skill 输出的问题清单,以及自测验证 Skill 发现的失败用例。
这个 Skill 的处理逻辑分三步:先复现问题,确认问题真实存在;再定位根因,找出问题的触发条件;最后实施修复,并运行相关用例验证修复有效。每一步都有专门的处理规则,比如“如果 5 分钟内无法定位根因,就主动上报人工”,而不是在一个问题上死磕到底。
这个 Skill 的tool.py里包含了一个关键函数——在修复前自动创建一条独立的 Git 分支,修复完成并且测试通过后再合并到主分支。这个设计是为了保证主分支随时处于可发布状态,不会因为自动化修复引入新的半成品代码。
一个容易忽视的点:缺陷修复不是改完就行,修复本身可能引入新的问题。所以修复后必须重新跑一遍受影响模块的全部测试,不能只跑失败的用例。我加了一个规则,修复后的测试范围必须是最小受影响集合,不是单个用例。
3.8 重构优化 Skill
重构优化 Skill 是在功能验证通过之后做质量加固,重点处理三件事:消除重复代码,优化性能瓶颈,改善代码结构。
这个 Skill 有一个铁律:重构只做结构调整,不做行为变更。重构完成后,必须保证原有测试全部通过,并且测试不能因为重构而进行任何逻辑上的修改。如果重构导致既有测试需要修改,那说明重构改变了行为,这是不允许的。
重构优化 Skill 的触发条件不是“每次迭代都必须执行”,而是由反馈驱动。比如自测验证阶段发现某模块的测试执行时间越来越长,或者评审审查阶段频繁报出同一类性能问题,这时才会主动触发重构。
实际执行中,重构 Skill 会优先选择改动范围小的优化点,比如合并重复代码、提取公共方法、优化循环中的重复数据库查询。这类优化风险低、收益直观,模型做起来可靠性高。至于大范围的架构级重构,我一般还是会拉人来定方案,这也符合人工和机器的能力边界。
3.9 文档沉淀 Skill
最后一个 Skill 是文档沉淀,负责把整个研发链路中的关键信息沉淀下来。它的输出物包括:变更日志、架构决策记录、接口变更说明、本地开发环境说明更新。
这个 Skill 的核心能力是信息抽取和自动生成。它会扫描链路中的需求澄清记录、架构设计文档、代码变更记录、评审问题清单,自动生成一份结构完整的变更说明。
文档沉淀 Skill 容易被忽视,但它实际上是价值最被低估的一个环节。因为 AI Coding 的迭代速度比传统开发快得多,如果文档跟不上,三个月后的维护者根本看不懂当初为什么这么设计。有了这个 Skill,至少保证每次迭代都有对应的文档产出,长期下来就是非常有价值的团队知识库。
4. 串联起来:Skill Orchestration 实战
4.1 配置文件怎么编排
8 个 Skill 都开发完之后,真正的 Harness 工程考验在于串链配置。通过 Harness 的编排配置文件,把 8 个 Skill 按照顺序连接起来,同时定义好每个环节的输入输出、分支逻辑和人工审批节点。
核心配置大概是这样的:
chain: - skill: requirement_clarify output: requirements.md - skill: architecture_design input: requirements.md output: architecture.md human_approval: true - skill: code_implement input: architecture.md output: changed_files - skill: self_verify input: changed_files output: test_report.json on_fail: defect_fix - skill: code_review input: changed_files output: review_comments.json - skill: defect_fix input: review_comments.json output: fixed_files - skill: refactor input: fixed_files output: refactored_files - skill: document_update input: all_outputs output: changelog.md注意,配置里不是简单的线性执行,还有分支逻辑:自测验证失败会跳转到缺陷修复,缺陷修复完成后要重新回到自测验证,形成一个循环。这个闭环是整条链路的灵魂,没有闭环就没有质量保障。
4.2 状态传递与上下文管理
8 个 Skill 串起来之后,最头疼的问题不是单个 Skill 写不好,而是上下文传递。前一个 Skill 的输出要变成后一个 Skill 的输入,这听起来简单,但实际工程里充满了隐藏的地雷。
我用的办法是每跑完一个环节,就把关键信息梳理成结构化的状态文件,传进下一个环节。比如需求澄清完了,生成一份requirements.md;架构设计完了,生成一份architecture.md。每个 Skill 只读自己需要的文件,不直接读上一个 Skill 的完整上下文。
这样做的好处有两个:一是上下文长度可控,不会越传越长最后把模型上下文撑爆;二是每个 Skill 的输入输出都有留痕,出了问题可以精确定位到是哪个环节丢的信息。
状态传递里最容易踩的坑是“隐式依赖”。比如代码实现 Skill 用了某个工具函数,这个信息没有明确写进架构设计文档,只在 prompt 里顺带提了一句,结果换了一个模型之后,这个工具函数就没人知道要用了。所以我在每个 Skill 的SKILL.md里都会加一个字段:输出必须声明自己依赖的上下文和外部工具,没有声明的依赖后续一律不认。
4.3 回滚机制与人工介入点
有了链条,就一定要有回滚和人工介入机制。一条没有任何人工卡点的全自动链路,再聪明也不敢在生产环境放心用。
我在这条链路里设了三个人工介入点:第一个在架构设计完成后,技术方案必须有人确认才能进入开发;第二个在代码实现完成后,建议有人过一眼核心模块的代码;第三个在链路全部跑完后,最终的变更集需要人工 Review 并合并。
回滚机制也做了两级。第一级是 Skill 级回滚,某个 Skill 执行失败后重新走上一级,比如代码实现质量太差,评审问题超过设定阈值,就直接回退到代码实现阶段重新生成。第二级是链路级回滚,整个链路跑完发现结果不可接受,把 Git 分支直接回退到初始状态,所有中间产物丢弃重来。
这套机制设计的出发点是:AI Coding 最大的风险是“看起来很成功但实际有毒”。代码能跑通、测试能过,但这些都不代表变更可以无脑上线。人工介入点要放在“决策”和“验收”上,而不是放在“执行”上。
5. 常见问题与排查实录
5.1 排查速查表
把这段时间实际踩过的坑整理成一张速查表,按问题现象、可能原因、解决方案三列展开,方便大家遇到问题时直接对号入座:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Skill 执行超时 | 上下文过大或外部工具调用阻塞 | 检查输入状态文件是否过大,给工具调用加超时时间,必要时拆分子任务 |
| 上下文信息丢失 | 状态传递时关键信息未结构化 | 每个 Skill 输出必须声明依赖和上下文,禁止隐式依赖 |
| 代码风格不统一 | 代码实现 Skill 未读取项目规范 | 在 tool.py 中增加代码规范文件扫描,并把规范内容注入 prompt |
| 测试用例太水 | 缺少有效断言约束 | 在 prompt.md 中强制要求每个用例标注验证行为,禁止无断言用例 |
| 评审问题无限循环 | 缺陷修复后未重新触发评审 | 配置循环次数上限,超过次数自动转人工处理 |
| 模型换了输出格式变了 | 依赖模型自由发挥 | 在 prompt.md 中使用强确定性输出模板,禁止修改字段名称和结构 |
| 链路跑通但结果不可用 | 人工介入点缺失 | 在关键节点配置 human_approval,尤其架构和最终验收 |
5.2 三个实战踩过的坑
第一个坑:盲目追求全自动。最早我把 8 个 Skill 的链路配置成全自动,跑了几轮后发现,代码质量和人工 Review 的预期差得很远。后来加了架构确认和最终人工合并两个节点,质量一下子就稳定了。AI Coding 工程化的正确姿势,不是把所有流程都自动化,而是把人和机器的分工做对。
第二个坑:Skill 的边界没划清楚。一开始我的代码实现 Skill 里塞了代码风格检查的逻辑,自测验证 Skill 也塞了同样的逻辑,两边都管等于两边都没管好。后来明确职责:代码实现只负责生成,风格检查归自测验证管。边界一旦清晰,整个链路的稳定性和可调试性都上了一个台阶。
第三个坑:上下文无脑全传。早期为了贪图方便,把每一个中间产物都完整传给下一个 Skill,结果上下文越积越大,模型开始丢前忘后。后来改成按需传文件,每个 Skill 只读自己要的输入文件,问题迎刃而解。上下文管理这件事上,克制比堆砌更重要。
6. 一些真实的体会
写了这么多,最后说点实际的个人心得。
Harness 工程不是银弹,它解决的核心问题是“AI 编码结果不可控”,但它本身也需要投入不少成本来建设。要不要引入这套体系,取决于团队的使用场景:如果只是自己写写脚本、做点小工具,用裸 Agent 就够了,没必要一上来就上全链路;但如果是团队协作、多模块迭代、代码要长期维护的项目,那 Harness 工程的投入就是值得的,它换来的是可持续性和可审计性。
做 Skill 的过程中我最大的体会是:写 Skill 和写业务代码完全是两种思维方式。业务代码追求的是功能实现,Skill 追求的是“过程可控”。一个好 Skill 不是能力越强越好,而是边界越清晰越好。
最后分享一个我在实践中坚持的原则:一个 Skill 只做一件事,并且把这一件事做到极致。把大需求拆成小 Skill 的过程,本身就是对研发流程的一次再梳理。当你把一个复杂的编码流程拆成 8 个职责清晰的 Skill 时,你会发现你不仅拥有了一个更可控的 AI Coding 流程,你还拥有了一套更清晰的需求表达和协作框架。