从 Copilot 到 Harness Engineering:我在 Android 工程里的 AI 开发实践
今年3月份 突然 Harness Engineering 这个词挺火。 所以就去 官方看了下 openai.com/zh-Hans-CN/… ,当时大概看懂什么意思了,但是还不知道如何实践,不过顺着官方文档也看明白了Harness为什么会出现。
过去一年AI 写代码越来越快,但真实工程并没有因此自动变简单。相反,当项目变大、业务变复杂、上下文变长之后,AI 带来的问题也越来越突出,比如:有时候理解错误导致实现方案不对, 或者 工具重复调用,有时候出现幻觉代码生成了一些 根本没有的 api 等等问题 ,Harness 就是要给AI 开发搭建一个开发环境。
这篇文章算是我的一篇实践笔记。只是想顺着我自己的体验(虽然可能总结的有点晚了 毕竟都八月份了 项目刚忙完一段落 哈哈哈),从 GitHub Copilot,到 Codex 这种能理解代码上下文的 coding agent,再到 Vibe Coding、SDD,聊聊我理解里的 Harness Engineering 到底怎么落地。
Copilot
我第一次明显感到 AI 对写代码有帮助,大概还是 GitHub Copilot 那个阶段。
那时候它给人的感觉很直接:你写到一半,它帮你补后半段。比如写一个 DTO、一个 Adapter、一个简单的工具函数,它能把重复劳动补掉。你刚写出方法名,它大概能猜到参数怎么传;你刚写一个测试用例,它也能把剩下几个边界 case 补出来,那个阶段的 AI 更像“超强补全”。
但是它不太理解整个工程,也不太关心你这个业务为什么这么写。它只是站在当前文件、当前函数附近,帮你把代码续下去。
但它的问题也很明显:它没有项目记忆,也没有工程判断。 copilot 补出一个看起来很顺的实现,但那个实现不一定符合你项目里的分层方式、日志规范、错误模型、国际化要求。你如果让它写一个 Android 页面,它可能知道 Activity、ViewModel、Repository,但它不知道你的项目里ViewModel 接收意图的方法叫 handleIntent,不是 dispatch;它也不知道用户可见文案必须同步三套语言资源。 所以 Copilot 阶段,本质上还是人在写代码,AI 只是帮你省手速。
Codex
后面 Cursor 、Claude Code、Codex 这类工具起来之后,感觉就不一样了。 它们不再只是补全当前行,而是能读仓库、搜文件、理解调用链、批量改代码、跑命令、看报错再修。这个变化很大,真正意义上的 Agent 降临了
我们真实开发不是“写一个函数”,而是:
- 先知道这个需求属于哪个模块;
- 再找到已有实现,对照接口文档和历史方案;
- 判断应该复用哪个 Repository、哪个 DataSource、哪个 UI 基类;
- 改完还要跑编译、处理资源、多语言、权限、异常、生命周期。
以前这些动作都靠人脑串起来。现在 coding agent 至少有机会帮你串。 比如我工作中的 Android 工程,技术栈是 Kotlin、XML + Compose、MVI、Hilt、Ktor、Room、PubSub、Firebase、Billing。表面上是一个客户端项目,实际上里面有很多高风险链路:登录、会员支付、Push、长链、普通聊天、消息缓存、图片上传、协议推送。
如果 AI 只能看当前文件,它基本不可能稳。但如果它能读完整仓库,它至少可以知道:
- 页面层放在
ui/<feature>/; - 数据层分为
source和repository; - 长链协议在
pubsub/; - 聊天相关要先看
docs/handoff/; - 接口和产品实现前要查
docs/specs/;
这时候 AI 就从“补全工具”变成了“工程协作者”。 但问题也来了:一旦你让 AI 真的开始改工程,它犯错的代价也变大了。
Vibe Coding 的爽感和幻觉
Vibe Coding 这个词我觉得挺准确。 它描述的不是某个具体工具,而是一种开发状态:我大概说一下我要什么,AI 就开始写;我看着差不多,再让它调;哪里不对,再继续说。尤其是做 demo、做新页面、做小工具的时候,几句话就能从无到有。你会有一种“我终于不用和细节纠缠了”的感觉。
但到了真实业务工程里,这种爽感就没有了。
比如还是上边的例子 :
- 新旧协议的差别 和服务端约定参数的同步;
- 图片本地校验、上传、失败重试和本地图片缓存逻辑处理等;
- 忘记这个项目是多语言项目 需要同步多语言变更;
这类需求如果只靠 Vibe Coding,很容易出问题。 AI 可能会使用旧协议字段,漏掉本地db缓存或者没有配置多语言配置,等等问题。最后你会发现,AI 写得确实快,但你 review、修正、回滚、补上下文的时间也不少。
这也是我对 Vibe Coding 的基本判断:它适合低风险、低耦合、可快速丢弃的东西;但在复杂工程里,只靠 vibe 不够。 这个时候就需要 你得给它规矩,定标准。
我理解的 SDD 思想
SDD,Specification-Driven Development,规范驱动开发。 这个概念本身并不新。软件工程里一直都有需求文档、设计文档、接口文档、测试用例。只不过以前很多团队嘴上重视文档,实际还是代码为王。文档常常写完就过期,过期之后没人看。
但 AI 出现以后,文档的地位变了。 因为对 AI 来说,聊天记录是不稳定的,人的口头说明是不可见的,钉钉聊天、会议里的共识也是不可见的。它能用的东西,要么在当前上下文里,要么在仓库里。 如果重要信息不在仓库里,它就等于不存在。所以 SDD 在 AI 时代重新变得重要,不是因为大家突然爱写文档了,而是因为规范变成了 AI 的输入接口。
在我自己的工程里,我现在越来越倾向把几类东西放进仓库:
- 产品 PRD;
- 接口文档;
- 协议版本差异;
- 模块交接文档;
- 架构规则;
- 高风险链路的排查顺序;
- 给 AI 的工程规则;
- 每次需求生成的技术方案和实现 Prompt。
比如这个工程里有 docs/specs/,放会员、聊天、Push、角色详情等规格;有 docs/handoff/,专门讲 IM、长链接、普通聊天的调用链;还有 .claude/rules 和 .cursor/rules,把 MVI、数据层、构建测试、多语言、日志、Git 协作这些规则写给 AI 看。 不是为了让文档看起来正规,而是为了让 AI 每次动手前有地方查。
开源 SDD 框架到底在解决什么
网上开源的 SDD / 上下文工程框架也有很多,我觉得背后原因很简单:大家都发现“直接让 AI 写代码”不够稳。 因为SDD只能算是一种思想,在 Github 搜索超过 10 万 Star 的相关框架,有五个:SpecKit BMAD GSD OpenSpec Superpowers。不同框架只是选择了不同的约束方式。
Spec Kit 的思路更重规范。它强调先写 spec、plan、tasks,再实现。适合新项目,产物很整齐,但小需求会显得流程重。
BMAD 更像把传统软件团队搬进 AI 里,虚拟出 PM、架构师、开发、测试等角色。它适合大组织、强流程项目,但个人项目或者节奏很快的团队可能会觉得负担重。
Pasted image 20260811204301.png
GSD 是为 Claude Code 开发的轻量级且功能强大的元提示、上下文工程和规范驱动开发系统。更强调上下文隔离和任务拆分。把大需求拆成多个 wave,每个子任务用更干净的上下文执行。好处是减少上下文污染,坏处是 token 和流程成本会上来。
OpenSpec 我个人觉得很适合老项目。它有 specs/ 和 changes/ 的思路:全局规范放一边,每个新需求在自己的变更目录里写 proposal、design、tasks。这样既不会污染全局文档,又能让每次变更可追溯。
OpenSpec 的接入成本其实不算高。最简单的方式就是在项目根目录初始化一个 openspec/ 目录,然后用 CLI 做校验。比如:
npx -y @fission-ai/openspec@latest init
npx -y @fission-ai/openspec@latest validate
它不是要求你一上来把整个项目文档重写一遍,而是先把“当前系统的稳定规范”和“每次需求的变更说明”分开。一般会有两个核心目录:
openspec/specs/:放已经确认的长期规范,比如聊天消息协议、会员规则、支付状态流转;openspec/changes/:放某一次具体需求的变更,比如“短对话支持发图片消息”。
每个需求下面通常会有几个文件:
proposal.md:说明这次为什么要做,解决什么问题,范围是什么;design.md:说明技术方案怎么落地,涉及哪些模块、接口、数据结构和边界;specs/**/spec.md:真正写业务规范的地方,用场景和验收标准描述“系统应该如何表现”;tasks.md:把实现拆成可执行任务,方便 AI 或人一步一步做,也方便最后检查有没有漏。
Superpowers 则更像把工作流技能化。它关心的不只是“AI 怎么写”,而是“AI 写完以后怎么测、怎么 review、怎么走分支”。这对支付、交易、聊天、长链这种高风险链路很有意义。
轻量化 OpenSpec 本地使用
我没有一上来就把 OpenSpec 用得特别重。老项目已经在跑了,历史文档、接口文档、代码结构都有自己的样子。直接套一套完整流程,成本不低,而且一开始也未必贴合。所以我现在更倾向于先轻量接入:不追求一步到位,而是先让每个新需求有一个稳定的变更目录。
比如一个新的业务需求过来,我会先在 openspec/changes/ 下面建一个变更目录,例如:
openspec/changes/short-chat-image-message/
├── proposal.md
├── design.md
├── tasks.md
└── specs/
└── chat-message-image/
└── spec.md
一开始校验不过也很正常,老项目的文档不可能天然符合某个框架的目录结构。我的做法是只修最小必要文件,比如补 proposal.md、design.md、tasks.md,或者把某个需求的 spec.md 放到对应能力目录下。先让 CLI 能识别,再逐步把它变成开发习惯。
这个过程对我来说,最大的价值不是“文档更正规”,而是 AI 动手前终于有了一个稳定入口。以前很多上下文都散在聊天记录、接口文档、临时 Markdown 里;现在新需求至少能沉淀到一个固定目录里。Codex 再来改代码时,我就可以直接告诉它:先读这个 change,再按 tasks 检查实现。
从 SDD 到 Harness Engineering
SDD 解决的是“规范在哪里”的问题。 但 Harness Engineering 还要更进一步:它要解决“AI 如何在一个完整工程系统里持续工作”的问题。 我理解的 Harness Engineering,不是再写一个更长的 prompt,也不是把所有文档一股脑塞给模型。
Harness Engineering 有几个比较重要的组成部分:
- 信息系统 : AI 要能读到项目当前事实:代码、规格、接口、交接文档、架构规则、历史方案。信息不能只在人的脑子里,也不能只在聊天记录里。
- 约束系统 :AI 要知道什么能改、什么不能改。业务修改的临界点在哪里 如何不修改错误文件。
- 执行系统 :复杂需求要拆成 proposal、design、tasks。小需求可以轻量一点,但也要有边界、验收标准和风险点。
- 验证系统 : AI 改完要能跑编译、单测、lint,必要时能看日志、截图、DOM、模拟器或真机路径。只靠“看起来对”是不够的。
- 反馈系统 : review 结果、测试失败、线上 bug、文档过期,都要沉淀回仓库。否则 AI 每次都在重复踩坑。
改变永远都是好事
从刚毕业的查看官方文档 ,技术博客寻找修改bug的办法,到自己慢慢总结持续输出了七八年的博客,期间收益颇多,AI 时代下的现在,我个人认为开发人员是要全力拥抱AI,新的业务需求尽量接入Ai可感知的开发环境,学习SDD开发思维,提效AI开发效率,解放人力去完成更重要的事情。