乐于分享
好东西不私藏

我越来越确信:AI 编程真正难的不是模型,而是 Harness 工程

我越来越确信:AI 编程真正难的不是模型,而是 Harness 工程

前阵子我让一个 coding agent 帮我修一个不大的 bug。

问题其实很具体:

一个接口 500

只在某个 feature flag 打开时出现

复现路径明确

影响范围也不大

按理说,这种活儿很适合交给 AI。结果它上来先读了 8 个文件,跑了 5 次测试,顺手改了两个无关组件,又把 lint 配置动了,最后还非常自信地告诉我:

“问题已经修复,建议你跑一下验证。”

我一看 git diff,差点笑出声。

真正的 bug 还在,倒是 eslint 配置被它改松了,console.log 多了两个,README 还被顺手润色了一段。

如果你用过一阵 AI 编程工具,对这种场面应该不陌生。

很多人会把这类问题理解成:

模型还不够聪明

提示词没写好

这家模型最近变笨了

我现在越来越不这么看。

我更愿意把问题说得更直一点:

AI编程真正难的,往往不是模型本身,而是你有没有给它搭一套像样的工作环境。

这个“工作环境”,就是我今天想聊的:Harness 工程

它听上去很像一个会把读者劝退的词,但你要是真开始做 AI 编程系统,就会发现它根本绕不过去。

因为你很快就会撞上这些问题:

工具应该给到什么粒度?

哪些规范应该长期生效,哪些应该按需加载?

复杂任务要不要先规划?

长时间会话怎么避免上下文烂掉?

多个 agent 并行改代码时怎么不互相踩死?

MCP 是不是越多越好?

怎么防止 agent 把“我觉得差不多了”当成“真的验证通过了”?

这些问题,靠“更强的 prompt”都解决不了。

它们是纯工程问题。

而所谓 harness 工程,说白了就是一句话:

给模型搭一个能长期稳定干活的工作环境。

不是让它看起来更像人。

而是让它真的更像一个靠谱的同事。


一、先别把 Agent 想得太玄,它的心脏通常只有一条循环

我现在看各种 AI Agent 产品,第一反应已经不是“哇,太智能了”,而是“来,我看看你这层壳子到底是怎么接的”。

因为你把外壳剥开以后,内核经常朴素得让人不好意思:

while True:    response = model(messages, tools)    if response asks for tools:        execute tools        append results    else:        return

模型看上下文,决定下一步。

你负责把它要做的动作真的做掉。

再把结果喂回去。

如果你愿意,可以把这套东西想象成一个特别能说会道、脑子也不错,但手脚全靠外包的实习生:

它知道“下一步应该查日志”

但它自己不会真的去查

你得给它一个 read_file

或者给它一个 bash

所以一个 AI 编程系统最核心的第一步,不是“怎么让模型变聪明”,而是:

怎么让模型有手、有脚、有眼睛。

这就是 tools。

但事情到这里才刚开始。

因为光有手脚,不代表它会有条理地干活。

我见过太多 agent 一旦开始工作,就进入一种非常熟悉的人类状态:

明明要修 bug,先去整理目录

明明要加鉴权,先改按钮颜色

明明要跑测试,先写文档

明明是修一个接口,最后把半个项目重构了

这个时候你就会意识到,AI 编程最可怕的问题,从来不是“不会写”,而是:

它太会动手了,但不一定知道自己在干什么。

所以真正有效的 harness,不会停在 loop + tools,而是会继续补三样东西:

planning

context management

verification

这三样,缺哪个都迟早翻车。


二、我最看重的一条原则:复杂任务先规划,再执行

这件事对人类程序员成立,对 AI 更成立。

一个很现实的例子:

假设我要加一套“市场结算后自动通知用户”的功能。

这听起来很直白,但实际上至少会牵出这些东西:

数据表是不是要加通知记录

用户偏好要不要建模

邮件和站内信要不要一起发

市场结算的触发点在后端哪个事件上

前端要不要加未读消息入口

大量用户同时通知时会不会打爆

如果我只是丢一句:

“帮我把通知功能做了。”

那很容易出现一种 AI 经典事故:

它先从最容易写的 UI 入手

再绕回后端

中途发现缺表

然后边写边改方案

最后产出一堆“看起来很忙”的 diff

但你很难说它真的在解决问题。

我现在更倾向于强制让 AI 先做一件事:

先把任务重新说清楚,再拆计划,再列风险,再等确认。

这一步的意义非常大,因为它做了三件事:

把模糊需求变成共享认知

把风险提前暴露,而不是埋进代码里

给后续执行建立一条“别跑偏”的轨道

很多人觉得 planning 很浪费 token。

我刚开始也这么想。

后来我发现,真正浪费 token 的从来不是 planning,而是:

没计划直接写

写了半天方向错了

再回来重做

再解释一次自己为什么改了这些东西

那才是真正的 token 黑洞。

所以如果你让我只给一条最先落地的 harness 建议,我会说:

复杂任务先规划,这不是礼貌,这是系统稳定性的门槛。


三、别把所有东西都塞进一份总提示词,规则、技能、命令必须分层

这是我后来踩坑踩出来的一个血泪教训。

很多人刚开始搭 agent 配置时,最自然的做法是:

把团队规范塞进去

把技术栈注意事项塞进去

把 TDD 原则塞进去

把安全要求塞进去

把代码风格塞进去

把自己不喜欢的写法也塞进去

最后得到一个 3000 行的“神级系统提示词”。

然后你每次开会话,都在拿一辆满载的卡车去买瓶水。

问题不是“信息不够”,问题是:

什么都常驻,就等于什么都不重要。

后来我越来越喜欢把东西拆成三层:

1. Rules:长期生效的硬约束

比如:

不准硬编码 secret

提交前必须验证

代码风格底线

目录结构约束

审批红线

这层像法律。

它回答的是:

“无论你现在在做什么,有哪些事情就是不能越线。”

2. Skills:按需加载的方法论

比如:

怎么做安全审查

怎么做 TDD

怎么做上下文压缩

怎么做 context budget 审计

怎么做某个框架下的实现

这层像 SOP。

它回答的是:

“遇到这种任务时,应该按什么套路做。”

3. Commands:高频工作流入口

比如:

/plan

/tdd

/verify

/code-review

/learn

这层像快捷操作台。

它回答的是:

“哪些流程值得我以后别再每次重讲,直接一键触发。”

这套分层对开发者特别有用,因为它能帮你避免一种很经典的配置病:

什么都想放进去,结果系统越来越重,越来越难维护。

我现在的判断很明确:

永远有效的,放规则

某类任务才需要的,放技能

高频动作,做成命令

只要这层分不清,后面上下文和工作流一定会乱。


四、别让 AI 靠记忆做人,能自动化的都尽量自动化

这应该是我最近一年对 AI 编程系统最大的观念变化。

我以前总觉得:

“没事,模型够聪明,它会记得的。”

后来我发现这句话和“我以后会记得健身”一样不靠谱。

什么东西最容易被忘?

git push 前再看一眼 diff

改完文件记得删 console.log

长时间跑 dev server 最好别堵在主会话里

compact 前把当前状态写下来

编辑 TS 文件后顺手 typecheck 一下

这些事对不对?

对。

重要不重要?

重要。

但它们都太机械、太高频、太容易漏。

所以对这类动作,我现在越来越倾向一个简单粗暴的标准:

如果它又高频、又机械、又重要,那就别再指望模型或者人类“记得做”,直接做成 hook。

举几个非常典型的开发场景:

场景 1:dev server 把主会话堵死

你让 agent 跑 npm run dev,它就真的在前台傻跑。

然后你眼睁睁看着主会话被日志淹没。

更好的做法是:

识别 dev server 命令

自动放进 tmux 或独立窗口

主会话继续工作

日志可追踪

场景 2:agent 改完一堆代码,末尾留了 4 个 console.log

这类问题特别适合做 stop hook。

每次响应后扫一遍改动文件,发现就提醒。

场景 3:你写着写着上下文爆了

最糟糕的不是 compact,而是:

在错误时机 compact

刚好把还没落盘的状态压没了

所以 compact 前自动记一笔当前状态,这类 hook 非常值。

我对 hook 现在的理解不是“增强项”,而是:

它是把工作习惯从“靠记忆”变成“靠系统”的关键层。

而真正成熟的 hook 系统,还得满足两件事:

能区分“提醒”和“阻断”

能分级,不是只有开和关

研究探索阶段和生产交付阶段,治理强度当然不一样。

把这两种状态混成一套固定规则,只会让人越用越烦。


五、上下文不是聊天记录,它更像一根内存条

这个比喻虽然土,但特别准。

很多人嘴上知道“上下文有限”,但行动上还是把 Claude Code 当微信聊天。

今天塞:

团队规范

框架指南

历史 bug

API 文档

架构说明

PR diff

明天再塞:

一堆 MCP

一堆 rules

一堆 skills

一堆 agent 描述

最后开始抱怨:

“怎么越用越钝?”

这事儿我现在看得很明白:

不是模型突然失忆了,是你的上下文预算被你自己干烂了。

我现在特别认同一个观点:

上下文不是自然资源,是预算。

你必须知道预算被谁花掉了。

比如:

常驻规则吃多少

已加载 skills 吃多少

MCP tools schema 吃多少

agent 描述吃多少

当前会话历史吃多少

一旦你不拆这个账本,就会陷入一种非常熟悉的错觉:

“最近对话有点长,compact 一下应该就好了。”

其实不一定。

可能真正的问题是:

你同时开了 12 个 MCP

一堆技能常驻了

你把项目说明写成了一本小书

你给每个 agent 都写了八百字人物小传

所以对上下文,我现在有三条硬习惯:

1. Always-on 内容必须短

凡是常驻加载的,都要克制。

2. 重知识一律按需加载

不是所有会话都要带着一整本操作手册。

3. Compact 要在人类能看懂的阶段边界发生

最适合 compact 的时候通常是:

调研结束,准备执行前

一个阶段做完,切到下一阶段前

一条死胡同走完,准备换方向前

最糟糕的 compact,是正在改到一半时突然压缩。

那种感觉就像你写代码写到一半,脑子被人拔电源。


六、Subagent、后台任务、Worktree,这三种“分出去做”根本不是一回事

这是我觉得开发者最容易搞混的一组概念。

表面上看,它们都像是在“把事情分出去做”。

但分出去的,其实不是同一种东西。

1. Subagent:把“思考和探索”分出去

比如我要回答一个问题:

“这个仓库的测试框架到底怎么接起来的?”

这时候我不一定要让主 agent 带着几十轮读文件记录去查。

更好的做法是:

派一个 subagent

让它自己读文件、自己搜

最后只带摘要回来

这解决的是:

上下文污染。

2. Background task:把“等待”分出去

比如:

npm install

pytest

docker build

这些命令本身不需要另一个脑子,它们只是慢。

这种时候你真正想分出去的不是“思考”,而是:

等待时间。

3. Worktree:把“工作空间”分出去

这又是第三种完全不同的分离。

假设一个 agent 在改登录流程,另一个 agent 在改通知系统。

如果两个人都在同一个目录里改,你迟早会看到:

脏 diff 互相污染

回滚边界失控

一堆“这个文件到底是谁改的”事故

这时候要分出去的不是思考,也不是等待,而是:

执行空间。

所以对这三者,我现在会这么记:

subagent

:分出去的是脑力

background task

:分出去的是等待

worktree

:分出去的是工位

这个区分一旦分清,很多 agent 系统设计问题会突然变得非常清楚。


七、多 agent 真正难的,从来不是“多”,而是“乱”

很多人一说到 AI agent 并行,就很兴奋:

多开几个窗口

多起几个 agent

一起干活

听起来很美。

但只要你真的试过,很快就会进入一种熟悉的项目管理地狱:

谁在做什么?

谁已经做完了?

谁改了哪几个文件?

谁的结果可信?

哪个任务被谁卡住了?

哪个 worktree 现在还能删,哪个不能动?

也就是说,多 agent 系统一旦往前走,真正的瓶颈根本不是“会不会并行”,而是:

有没有控制平面。

我现在对这件事的理解越来越像管理一个小团队。

一个靠谱的多 agent harness,至少得有这些东西:

1. 任务系统

不是 TodoList,而是:

有状态

有依赖

可认领

可并行

2. 角色边界

谁负责:

规划

实施

审查

安全

验证

3. handoff 机制

不是“我把上下文全丢给下一个人”,而是:

当前结论是什么

哪些文件动了

风险是什么

下一个 agent 只需要知道什么

4. 工作区隔离

并行改代码,不隔离目录,就是在等事故。

所以我现在越来越相信一件事:

AI 多 agent 的成熟度,不取决于你能同时开几个实例,而取决于你有没有把控制平面搭出来。


八、MCP 很重要,但越用越觉得:它不是越多越好

我对 MCP 的态度这两年变化挺大。

刚开始会觉得:

“太爽了,什么都能接。”

后来用久了发现,MCP 和公司里开 SaaS 账号有点像:

开的时候都觉得必要

过一阵发现一半根本没在用

但成本和复杂度一直在那儿

MCP 当然有价值,尤其是:

跨系统检索

浏览器自动化

一些平台交互

外部能力接入

但它的问题也很真实:

tool schema 占上下文

server 多了会拖慢体验

健康状态得管

认证、限流、异常恢复得管

我现在更赞同一种比较现实的策略:

1. 不是所有能力都要常开

2. 高频、边界清楚的能力,很多时候可以退回 CLI + commands

3. 真正复杂、动态、需要探索的外部系统,再优先上 MCP

4. MCP 一旦多起来,就必须进入治理

比如:

数量控制

健康检查

backoff

reconnect

fail-open / fail-close 策略

说白了:

MCP 不是装饰品,而是基础设施。

基础设施一旦不治理,迟早会反噬你。


九、我现在最信的一条:Harness 本身必须进入测试

这条以前我真没那么重视。

总觉得:

hooks 反正能跑

rules 反正是 Markdown

commands 反正只是入口

manifests 反正配置一次就好了

后来越写越发现,这种想法非常危险。

因为真正长期使用后,最容易坏掉的往往不是业务代码,而是这些“外围基础设施”:

command 指向了一个早就不存在的 skill

agent frontmatter 写错,结果整个委托链失效

hook JSON 格式漂了

一条路径写成了个人电脑绝对路径

一个安装 manifest 跟 README 里的数量对不上

这些问题单看都不大,但它们会持续破坏整个工作系统的可信度。

所以我现在很认同一个很硬的结论:

如果你的 harness 是长期资产,那它就必须像代码一样被测试。

不是“顺手测一下”,而是:

有 CI

有校验链

有单元测试

有不变量

什么时候我会觉得一套 AI 工程系统开始像样了?

不是它能不能写出一段漂亮代码。

而是它能不能证明:

自己的规则没漂

自己的 hooks 没坏

自己的命令没失效

自己的配置没泄漏私人垃圾

这件事听上去一点也不性感。

但恰恰是这种“不性感”的地方,决定了一套 harness 能不能活久。


十、最后说一句我自己的感受

我现在越来越不相信“AI 编程的未来,拼的是谁家模型更像一个程序员”这套说法。

我反而越来越相信另一件事:

未来真正拉开差距的,是谁更会给模型搭工作环境。

同一个模型,放进不同的 harness 里,表现差异会大得离谱。

有的像实习生第一天上班:

找不到文件

改代码没计划

没验证就说完成

多开几个实例就互相打架

有的则越来越像一个成熟团队里的靠谱同事:

有规则边界

有任务系统

有自动化提醒

有长期记忆

有并行机制

有恢复能力

有验证闭环

这两者拼的根本不是“智商”,而是外面那层工程。

所以如果今天有人问我:

“AI 编程真正值得研究的是什么?”

我现在大概率会回答:

不是怎么写一句更神的prompt。而是怎么把loop、tools、rules、skills、hooks、memory、tasks、worktrees、verification 这些东西,组合成一套长期稳定工作的 harness。

这件事听上去没那么炫。

但真做进去以后,你会发现它比“提示词工程”要硬核得多,也值钱得多。

转载:https://juejin.cn/post/7620758996278444095