AI Coding 如何放大研发交付能力:从 Prompt 到 Context,再到 Harness
之前写 GreenWake 时,我主要想试一件事:一个不熟悉 Go、也不熟悉跨平台客户端开发的人,能不能借助 AI,把自己的需求做成一个能用的软件。那次体验很有意思,框架很快就出来了,但细节也反复折腾了好几轮。
最近这段时间,我又往前走了一步,主要在推进「智相」的两个重点项目:商品理解和用户理解。这次面对的是团队交付,结果要进入下游生产链路,还要经得起数据评测、批量运行和失败重跑。
一支 6 人的小团队,三周内把两条生产链路交付了出去。这个过程中,AI 确实帮了很大的忙,但也给我们制造了不少“看起来已经完成”的惊喜。比如它说上传成功,我去查,结果表根本没有当天分区;它给出一张非常工整的评测表,后来发现,评测口径就是错的。
所以这篇文章想聊的,除了做成了什么,还有怎么做、在哪些地方踩了坑,以及为什么最后我越来越重视 Harness——让 AI 工作的那套上下文、工具、流程和验证环境。

本文根据项目实践和分享材料整理。业务规模、准召、覆盖率等数值用 XX 代替,保留技术结构与研发提效记录;配图沿用原分享,并基于原图做局部脱敏和统一画风。
先看结果:6 个人,3 周,到底交付了什么
「智相」要做的事情,可以用两个问题来理解:这个商品是什么,这个用户可能想要什么。
商品理解把商品标题、图片、OCR、已有属性等素材,转成结构化的商品属性和商品知识。搜索、推荐、营销要使用这些结果,首先得保证它们既能被计算,又足够可信。因此这条链路还包括数据准出评测,也就是检查结果到底准不准、能不能交给下游。
用户理解从多源行为中组织证据,推理兴趣画像,再进一步匹配站内类目和 SPU,支持商品圈选。SPU 可以理解为一类标准化商品的集合。这里既要处理行为比较丰富的用户,也要处理行为稀疏的低活用户,后者需要分桶、分簇和群体推理。
先把前三周的投入和节奏摆出来:
| 观察项 | 实际记录 |
|---|---|
| 团队与周期 | 6 人,3 周,两条链路并行推进 |
| AI 交互 | 65 个对话,5585 轮 |
| Token | 14.67 亿原始 Token,2.8 亿计费 Token |
| 代码 | 生成和修改约 2.3 万行 |
| 工具成本 | 约 4 万元 |
| 第 2 天 | 做出可汇报的 Demo |
| 第 2 周 | 迭代两版,完成 XX 条重点商品刷数 |
| 第 3 周 | 完成 XX 条动销商品刷数,提供给下游生产使用 |
这里的工具成本是实践材料记录的口径,不是把模型生产推理、机器和人工都算进去的项目总成本。代码量也只是过程记录,不能单独证明质量或者提效。
对我来说,更有感的变化是:原来要在数据分析、工程、算法、测试之间来回交接的事情,小团队可以更快串起来了。工程同学也能进入模型、数据和评测环节,做完一条更长的链路。
当然,接这个活的时候,并没有一份现成图纸。排期以天计,方案一边做一边改;同类方案已有基础,我们还要考虑大模型如何提供更好的理解和蒸馏结果;其中不少问题又是工程同学原来不熟悉的算法问题。AI 的价值,就是让想法很快变成可以试、可以跑、可以检查的东西。
但小样本能跑和生产可用之间,还有一大段路。下面先把两条业务链路讲清楚,再看这段路上发生了什么。
商品理解:调一次模型很简单,持续产出合格数据很难
模型前后,各有一层工作
商品素材并不整齐。标题里有营销词,图片里有文字,OCR 可能识别错,商家填的属性可能缺失,同款商品的信息也未必完全一致。
其中 CPV 指的是 Category、Property、Value,也就是类目、属性和值。例如一个商品属于什么类目,有什么材质、规格,以及这些属性对应什么值。已有 CPV 是模型判断的依据之一,并不是拿来就一定正确的答案。
我们采用的方案,是把素材处理、多模态理解和结果校验分开,由三级模型协同承担。前面用较轻的模型清洗 OCR、提取关键信息,中间让多模态大模型做商品理解,后面再检查风险标签并校验、改写或移除相关内容。这样可以分清楚,问题出在原始证据、模型判断,还是输出处理。

原架构的输入、模型层级和输出字段均保留,来源标识泛化,类目覆盖规模用 XX 表示。
这个过程不能只要求模型“返回一个 JSON”。结果的字段含义、可用取值、缺失时怎么表示,都需要先约定。对结果的评测也要按类目、属性和样本类型拆开,否则一个总体数字很容易把局部问题藏起来。
比如商品的颜色、材质和适用场景,依据并不一样。图片可以提供颜色证据,标题可能包含材质,但一些用途描述只是宣传文案。模型说得很顺,不代表每个字段都有可靠依据。我们需要把这些差别落实到样本和准出检查里。
进入批处理后,“工程胶水”成了主角
真正跑起来以后,链路是这样的:
| |
这里每一层都可能出问题:图片下载失败,请求超时,模型被限流,JSON 截断,进程中断,结果落盘但没上传,状态记成完成但没有合格结果。
下面这张图保留了当时的分布式骨架。左边负责发现和加载数据,中间是多机 Worker 与 Redis 协调,右边负责多模型路由和限流,底部才是下游能真正读取的结果。

读这张图,可以抓住几条关键路径。
首先是数据路径。调度器轮询 Hive 分区,通过 WebHDFS 下载,放到本地 Parquet 缓存,再提取商品 ID、去重、入队。预取下一分区,是为了减少处理和下载之间的等待。
然后是执行路径。Worker 领取任务,查缓存、处理图片和 OCR、构造 Prompt、调用模型、解析结果;Writer 按分区组织输出,先落 JSONL,再转换 Parquet 并上传。把推理和写入分开,也方便独立观察两边的积压。
第三条是状态路径。Redis 里有待处理队列、已完成集合、调度状态和分布式锁。这些状态支持多机协作和重跑,但状态本身并不能证明结果已交付,这个坑后面会具体讲。
最后是流量路径。不同模型的协议、QPM、TPM 和可用水位不同,需要统一客户端、加权路由和独立限流。QPM 是每分钟请求数,TPM 是每分钟 Token 数。长请求场景里,只增加并发并不一定能打满配额,甚至可能先把自己的进程撑爆。
AI 在这里特别适合补齐脚本、配置、客户端适配、异常处理、对账和报告。这些工作单个看不太惊艳,但少了其中一项,链路就可能在放大之后出问题。
用户理解:先把行为变成证据,再让模型判断
数据很多,不代表模型能直接看懂
用户链路和商品链路不一样。商品至少有一个相对明确的对象,用户的行为则分散在不同来源和时间段里,而且很多特征原本是给推荐算法使用的统计值。
把一大堆字段直接丢给 LLM,并不等于给了它有用的上下文。它还得知道,某个数代表什么,与什么基准比较,哪些信号是近期兴趣,哪些只是偶然行为。
所以用户链路的第一步,是组织证据:多源行为先做统计加工、压缩和分桶,转成模型可以理解的兴趣线索。之后再做 LLM 推理、Embedding 匹配,回到站内类目、SPU 和 Elasticsearch 检索体系里。

这是完整方案的组织方式。分层模型、微调和反馈闭环用于说明整体设计,不应把图中所有模块都理解为前三周已经完成;用户规模用 XX 表示。
这张图值得保留,是因为它说明了成本和效果不能到最后才考虑。行为丰富的用户,可以使用更完整的证据和更强的模型;行为稀疏的用户,则要选择更合适的推理粒度。分层的意义,是把有限计算花在有信息、能产生价值的位置。
生成兴趣画像也不是终点。模型输出的自然语言,和站内已有类目、商品、检索字段之间,还有一层语义对齐。Embedding 匹配、候选检索和结果检查,就是把“模型觉得这个人可能喜欢什么”变成下游可以使用的结果。
低活用户:用双树分桶减少个体推理
低活用户的难点是,单个人的行为可能很少,却有很大的待处理集合。如果每个用户都用重模型推理,成本高,输入证据还未必够。
方案里采用了两棵树:一棵更多使用行为锚点,把有相近行为证据的人组织起来;另一棵使用画像特征做补充,提供不同的划分视角。随后以桶为单位组织证据和推理,再把结果映射回用户。

保留原方案的六层处理、双树特征、互补推理与输出结构;人数、桶数和调用规模用 XX 表示。
两棵树的用途并不完全相同。行为树可以帮助总结已有兴趣,画像树提供补充视角,帮助探索当前行为里没充分出现的候选兴趣。两边的结果最终还需要经过匹配和评测,不能把桶级推理当成每一个人的真实偏好。
这个项目明显复用了商品链路留下来的批处理、限流、结构化输出、续跑和评测经验。任务本身变了,但执行方式里已经有很多现成的东西。
这也是我开始感受到“经验沉淀有用”的地方:第一条链路踩过的坑,第二条链路可以从修好之后的位置开始。
从 Prompt 到 Context,再到 Harness
回头看这段经历,光把 Prompt 写得更好,肯定不够。

**Prompt 解决怎么问。**任务、角色、限制、输出格式和示例都很重要。单点任务里,一段清楚的指令往往就能起很大作用。
**Context 解决给模型看什么。**代码、方案、日志、样本和历史决策,决定它能不能理解当前项目。不是所有资料都塞进去,而是先给入口和地图,需要细节时再去读。
**Harness 解决怎么把事情稳定做完。**有了上下文,还需要执行工具、阶段约束、验证门禁、状态恢复和经验回写。模型知道代码在哪,并不代表它会主动跑对账;它能解释业务,也不代表它会使用正确的评测口径。
在这篇文章里,我对 Harness 的理解是:
给 AI 一个有上下文入口、有阶段约束、有验证门禁、有经验回写的工程现场,让它能把任务推进到可执行、可验证、可交付。
这是我从项目里总结的理解,不是想给这个词定一个唯一标准。
而且我们要管的范围,比 Coding Agent 的代码生成更长。可以对照着看:
| 位置 | Coding Agent 的工作环境 | 业务交付还需要补什么 |
|---|---|---|
| 上下文 | 代码地图、项目说明、工具入口 | 数据口径、样本、Hive 元数据、检索结果和实时状态 |
| 过程 | 理解、规划、实现、验证 | 刷数、评测、上传、对账逐阶段验收 |
| 质量 | 编译、测试、Review | 数据准出、评测口径、分层抽样和 Badcase |
| 稳定性 | 执行隔离、权限、失败恢复 | 限流、重试、断点、幂等、资源控制与长时间运行 |
| 沉淀 | 说明文档、脚本、Skill | 评测结论、异常模式、准出标准与复用能力 |
下面三个案例,分别落在阶段验收、系统稳定性和质量门禁上。也正是这些事,让我从“让 AI 多写点代码”,转向了“把 AI 工作的现场搭好”。
第一个坑:“全部完成”,但结果表没有当天分区

有一次,AI 给我的反馈很完整:上传成功,日志已生成,全部完成。
我去线上查结果,发现那张 Hive 表根本没有当天分区。它实际上没有走到上传那条路径。前面的脚本可能跑过,本地也可能有文件,但下游拿不到结果,任务就没有交付。
另一个问题更隐蔽:刷数失败、重试超过 4 次的 item,会被放进 Redis 的 completed 集合。下次重跑时,这些 item 直接被跳过。
从程序的角度看,它是在避免无限重试;从数据的角度看,它却把“停止尝试”写成了“已经完成”。状态看着都过了,实际结果是缺的,而且重跑还补不上。
完成条件要落在最后的结果上
后来给链路加的关键约束,是跨阶段对账。不能只看日志中的成功字样,要逐分区检查:该处理哪些商品,Redis 记了什么,Hive 实际写入了什么。
这里可以用一个简化的集合表达式说明。它是教学示意,真正使用时,还要限定日期、结果版本、主键和分区:
| |
本轮要求全部产出时,missing 不归零就不能宣告交付完成。如果任务确实允许排除某些对象,也应该把它们单独列出来,记录原因和数量,而不是混在成功集合里。
同样,成功、可重试失败、达到重试上限、按规则跳过,是不同的状态语义。把它们区分开,后面才知道哪些需要补刷,哪些需要人工检查,哪些已经真正交付。
这个案例给我的教训很直接:“执行结束”只能说明程序停了,“结果验过”才说明阶段完成。
代码没错,语义也可能不对
还有一类相似的问题,是实现看起来合理,却不满足恢复和对账要求。以分片为例,下面是两种写法的示意:
| |
这段示例不是说轮转分配永远不能用。关键是当前任务需不需要跨运行保持分片。需要的话,就要使用明确稳定的哈希规则,并检查打乱输入、进程重启后是否仍然一致;Worker 数量变化时,还要考虑状态迁移。不能直接假定语言内置的 hash 就满足跨进程稳定的要求。
这种约束,不能只写一句“注意稳定性”。把它变成验收用例,AI 才有具体东西可以检查。
第二个坑:QPM 配额 2100,实际只有约 70
商品理解有一轮为了追求更好的效果,把模型切换到了 Kimi K2.5。结果单请求耗时到了 2~3 分钟。小批量时还能接受,一放大,吞吐就卡住了。
当时记录的一个现象是:QPM 配额给了 2100,实际每分钟请求只有约 70,整体吞吐在 1 QPS 左右。这里 QPM 和 QPS 是排障期间的近似观察值,不是同一时间窗口的精确换算。
交付窗口很紧,我让 AI 去优化,把配额利用起来。它连续做了三轮修改,提出的方向看起来都合理,但速度还是提不上去。
问题是,它一直围绕并发逻辑打转,真正的瓶颈已经落到了系统层。

保留原排障图的效率数据和处理步骤。批次业务条数用 XX 表示,提交标识换成轮次。图中不同批次的耗时不能直接当作严格的同规模倍数对比。
并发加上去了,自己的机器先扛不住
当时排查出来几件事:
- 单进程里的 CPU 工作和 GIL 约束,使这条处理路径没能充分利用多核。
- 大约 6000 路长连接带来很高的内存压力,相关排障记录中出现过约 12GB 的内存碎片问题和 OOM。
- 单请求约 1.4MB 的 payload,序列化在当时的剖析里占了约 40% CPU。
- 启动阶段的 429 限流错误,又把自适应速率压下去了。
这几件事叠在一起,继续增加异步任务,很可能只会增加排队和内存消耗。异步 I/O 可以减少等待,但不自动让 CPU 密集的序列化、图片处理等工作跑满多个核。
于是修改方向变成了:拆多进程,调整内存释放,替换热点序列化,增加启动预热,重新设计速率反馈。AI 这时就很好用了,方向明确以后,它可以快速实现、试跑、整理结果,再继续改。
限速不能一直记着启动时的错误
其中一个很值得说的细节,是错误率反馈。
如果用从启动到现在的累计错误率控制 QPM,启动时的一次 429 风暴会在后面很长时间里影响决策。服务已经恢复,限速器还觉得错误率很高,速率就迟迟上不去。
所以这次把近期滑动窗口、启动预热和限速调整放在一起处理。图中的记录是 5 分钟预热;近期窗口用于观察当前水位,避免一直被早期错误拖住。窗口样本少时也要限制调整幅度,不能一会儿加速、一会儿急刹车。
这里没有一个放之四海皆准的并发参数。我们真正要看的是,当前请求时延、近期错误、实际吞吐、CPU、内存和写入速度之间,谁在限制谁。
修复后的记录,比“优化完成”更有说服力
这轮攻坚用了约 72 小时,吞吐从约 1 QPS 提升到 22 QPS。排障材料还记录了几组变化:
| 观察项 | 修复前后记录 |
|---|---|
| 吞吐 | 约 1 QPS → 22 QPS |
| 内存 | 28GB → 8GB |
| 系统失败率 | 63% → 0.5% |
| 单批耗时 | 15h / XX 条 → 2.5h / XX 条 |
这些是这轮实践的阶段记录,尤其最后一行来自不同批次,不能直接得出一个严格的耗时倍数。真正让我确认方向有效的,是持续运行时吞吐、资源占用、失败率和结果落盘一起变好了。
这件事也让我更清楚人和 AI 怎么配合:AI 可以很快实现很多方案,但最开始得有人判断,究竟应该解决哪个问题。领域知识没有失去价值,反而决定我们能不能把它的执行能力用在正确的位置。
第三个坑:报告很漂亮,评测口径却错了
用户理解还有一个险些被带过去的问题。
AI 给了一张静态画像覆盖率的评测表,覆盖率是 XX%,还附了看起来合理的原因。数字、表格、归因都有了,很容易直接放进汇报,甚至据此判断这个数据源没什么价值。
但我看着不太对,因为平时看到的样本里,有信息的用户并不少。当时我的反应是:
我感觉有数据的挺多的,你再 check 一下。
再查之后发现,它只用 gender 这一个字段判断“有没有画像”。这个字段为空,并不代表年龄、设备等其他字段也没有有效信息。口径修正后,覆盖率变成了另一个 XX%。
数据没有在这次检查里突然变好,错的是评估脚本。

检查脚本也要被检查
这类错误麻烦的地方是,它没有语法错误,输出也很稳定。你让 AI 再跑一遍,它会再次得到同一个错误数字。
所以需要先把“覆盖”的含义说清楚:统计的是哪个用户集合、哪个时间范围,哪些字段算有效信息,未知值和空值怎么区分。这些定义没有对齐,跑得再快也没有用。
下面是一个简化教学示例,用来说明单字段判断和多字段判断的区别。实际业务仍应根据口径决定哪些字段有效:
| |
这里也不能偷懒用“非空就有效”。例如一个字段有默认值,但默认值并没有提供信息,仍然可能应该算未知。反过来,合法的零值又不一定是缺失。
至少三分之一的时间,花在检查上
做商品理解交付时,我们至少三分之一的时间在检查。检查过程中也用 AI,但我通常会从不同角度提出假设,而不是只说“帮我检查一下”:
- 某些字段是不是系统性缺失?结果分布有没有突然变化?
- 错误是否集中在某些类目、图片类型或输入来源?
- 边界样本有没有被误判?解析修复后是否改变了原意?
- Hive 的实际结果和任务状态是否一致?失败样本有没有被抽样遗漏?
- 评测数字能否从原始样本复算,分母和过滤条件是否符合业务定义?
这和写单测很像。有测试,不代表能发现问题;有一张评测表,也不代表口径正确。真正有用的是设计出能戳中错误的检查。
这次拦住问题的起点,只是一句“我感觉不对”。不过后续不能一直靠感觉,应该把它落实到口径说明、样本和验证脚本里,下次就不用再赌有没有人留意到了。
给 AI 一个能执行的现场
前面这些问题,不是换一段更严厉的 Prompt 就能全部解决。AI 还需要一个稳定现场,可以读事实、改代码、运行、看结果,然后继续修。
我们逐渐形成的做法是:共用开发环境,加上 Repo as Truth。

共用开发环境,把“你可以试试”变成“我已经试过”
没有统一环境时,AI 很容易停在建议上:你可以运行这个命令,你可以检查那个日志。真正操作,还得人去找依赖、找样本、找路径,再把结果贴回来。
有了稳定的开发环境,代码、依赖、数据样本、脚本入口和日志位置都能被找到。AI 可以自己读材料、实现、试跑、看错误、修复、输出报告。人介入时,也能沿着同一份证据继续判断。
共用并不意味着所有任务写同一个目录。多人或多个 Agent 并行时,代码改动、输出路径和任务状态仍要隔离,否则一个任务覆盖另一个任务的结果,又会制造新的排障工作。
Repo as Truth,让下一轮不用重新问一遍
需求在聊天里,方案在在线文档里,运行命令在某个人的终端历史里,结论是一张群聊截图——人还可以靠记忆把它们接上,AI 每次接手却要重新冷启动。
所以仓库除了代码,还应该保存需求边界、方案决策、运行入口、评测口径、Badcase 和经验索引。

这里不是要求把所有原始数据和日志都提交进 Git。大文件和敏感材料留在受控存储,仓库保存入口、用途、版本和验证说明。关键是下一次接手的人或 AI,知道该去哪找,什么才是当前有效的事实。
在商品链路里,本地试跑、批量刷数、失败重跑、队列监控、三方对账、结果补传,都需要标准入口。静态说明告诉 AI 为什么这样设计,动态工具让它查询现在的分区、样本和状态。两者配合,它才不必每次重新猜一遍。
阶段要留下可追踪的产物
一个需求大致会经过:需求分析、最小方案、任务拆解、实现、测试、报告、问题归因、经验更新。
每一步应该留下可以继续使用的东西。分析留下边界和不确定问题,方案留下路径选择,测试留下命令和样本,报告留下结果位置和未解决项。
对复杂任务,我通常会先让 AI 复述目标、边界和验证方式,再动手。这个步骤看起来多花了点时间,却能把“写得很快但方向错了”的成本挡在前面。
权限和执行范围也需要落到工具上。对结果表、队列、状态的清理,必须知道作用范围和恢复方式,必要时先预演。口头提醒一句“谨慎操作”,不能替代这些约束。
每踩一个坑,给下一次留一点东西
修好问题以后,还可以再追问一步:这个问题为什么没有早点被发现?
如果是入口不清楚,就补导航;如果是状态语义混了,就补定义和验收;如果是评测口径不明确,就补样本和复算脚本;如果操作每次都一样,就把它做成可复用流程。
我们后来整理出了 13 个 Skill,覆盖的其实就是项目里反复做、反复踩坑的事情。下面按功能列出来,其中对象存储和 RPC 的名称做了泛化展示:
| 类别 | 沉淀的能力 |
|---|---|
| 批量推理 | batch-pipeline:阶段编排;async-rate-limiter:自适应限流;jsonl-checkpoint:断点续跑;multi-model-llm-client:多协议客户端 |
| 模型与评测 | multi-model-eval:多模型评测;embedding-match:语义匹配;image-process:多模态图片处理 |
| 数据与基础设施 | pyhive-guide:Hive 读写;object-storage:对象存储;es-query:商品检索;rpc-guide:服务发现与 RPC |
| 环境与导航 | env-setup-guide:环境初始化;item-attr-navigator:整条刷数链路的地图 |
Skill 的价值,不在于把聊天记录装进一个文件夹。它要说明什么时候用,输入输出是什么,从哪里执行,如何验证,哪些边界不能跨。否则下一个 Agent 还是要重新猜。

这是概念示意曲线,不是连续测量数据。其中周期 2~3 倍的比较来自我对实际推进节奏的估算,没有进行同条件的对照实验。
第一条链路里,我们花了不少时间补工具、踩坑、建立评测和对账。第二条链路再做类似工作时,批处理、限流、续跑、结构化输出这些东西已经有了,可以把时间更多花在新的业务问题上。
但复用也不是自动发生。说明过期、脚本跑不起来、Skill 不写边界,旧经验同样会变成负担。所以每次验证和修复,也要把事实源更新到可用状态。
最后的体感:执行被放大,判断更值钱了
如果按这次实际推进节奏估算,没有这套执行和验证环境,只靠常规的人肉推进,整体周期可能还要增加 2~3 倍。这是我的个人判断,不能当成对所有团队都成立的提效系数。
我更确定的变化有两件。第一,研发链路变短了,想法能更快变成可运行的代码、脚本和检查结果。第二,个人和团队可以走得更远,工程同学能参与数据、模型和评测,经验也能通过仓库、工具和 Skill 交给下一次任务。
不过,前三周这个结果,并不代表之后就只需要让 AI 自己跑了。伪完成、吞吐瓶颈和口径错误,都说明工程师仍然需要理解问题、质疑结果、设计检查。
以后团队里的代码、测试、流水线、文档和运行入口,会越来越适合交给 AI 执行;人的讨论、试错、方向选择,也会因此有更多空间。结构化的是可重复的执行,留下来的是需要判断的工作。
如果现在要开始,我觉得不用先做一个大平台。找一个真实任务,先写清上下文入口和完成条件,让 AI 实际运行并给出证据;遇到一个坑,就把它变成下一次可以执行的约束。
回到文章开头,GreenWake 让我感受到的是:以前不熟悉的领域,现在也敢动手做了。这次「智相」的经历让我更进一步看到:把执行环境和验证闭环搭好以后,一支小团队也能把更长、更复杂的链路交付出去。
AI 写代码已经很快了。接下来更值得投入的,是我们怎么让这些代码真正变成可靠的结果,以及这次付出的时间,能不能让下次少走一点弯路。