来源:互联网 更新时间:2026-06-05 07:22
Harness Engineering这个概念在圈子里热闹有一阵子了。比较宽泛的说法把它定义为Harness = Agent - Model——模型之外附加的所有能力都算Harness,像是MCP、Skill、提示词什么的。不过今天聊的是个更具体的场景:在Claude Plugins里的Harness实践。跟广义定义可能有些出入,但本质都一样——都是为了保障模型能稳定地输出结果。
关于Harness Engineering的具体概念,这里就不展开了。简单说说Prompt、Context和Harness这三种范式的区别:
| 维度 | Prompt Engineering | Context Engineering | Harness Engineering |
|---|---|---|---|
| 核心定义 | 该怎么问 | 能让模型知道到什么 | 给模型提供一个运行环境 |
| 实现方式 | 自然语言描述 | 滑动窗口、向量库 | 搭流水线、管状态、校验、接外部API |
| 层级关系 | 最内层,使用模型的基础方式 | 中间层,管理上下文信息 | 最外层,包含上下文工程和提示词工程 |
先说说背景。团队做的是一个针对服务端代码改造的影响面分析工具Claude Plugins。之所以做这个工具,是因为Q2开始我们逐渐向AI全栈转型,挑战是用AI交付Ja va项目。现在很多模型在新项目里写代码已经不在话下,但对那些历史包袱比较重的项目,模型写代码时还是会有Bug——即使用了Sdd/SuperPowers这些增强开发范式。
目前交付的两个已上线的服务端项目中,就有一个出了线上问题。根因是AI把一个不该改的业务入口改了。所以为了更大程度保证质量,法宝箱里确实需要一件能分析改动影响面的工具。
分析代码影响面对AI来说是个复杂任务。需要全量查代码引用(已经有GodeGraph/GitNexus做了深度优化)、梳理业务链路、判断风险、整理输出等等。相信都有过类似经历:一个复杂任务直接通过提示词丢给模型,执行过程中模型可能会偷懒、偏移。无论怎么调整提示词,最后结果总跟预期有偏差——这通常是模型的认知和上下文窗口限制导致的。而Harness的作用之一,就是为了减少这种情况发生。
关于这个分析代码改动影响面的Plugin,做了不少工程手段上的设计。分享一下怎么做的,以及为什么要这么做。
从流程图可以看到,Plugin主要通过暴露Skill作为用户交互入口,Skill中用脚本驱动流程推进(Step1 → Step2 → ...)。每个Step下的创造性任务还是交给模型,模型执行完后,脚本记录结果到yaml(状态机)、校验成果、继续下一个Step,直到结束。
这样做的好处有三点:
yaml)。以分析改动影响面的Skill为例,让模型直接调py脚本,在脚本中生成需要处理的所有任务,给每个任务标记status: WAITING/DONE/FAILED。脚本派发这些任务给模型并标记状态,执行过程中就能知道哪些任务完成了、哪些没有。最后执行结束时用脚本校验这个.yaml,针对没完成的任务让模型回去继续执行,确保处理了所有任务。---name: xxdescription: xx---
## 作用
xx...
## 执行步骤
### Step1 - 生成改动任务
执行
```bash
python3 ${PLUGIN_ROOT}/script/generate.py" --create
```
### Step2 - xxxxxx
...
def main():
# ...
print(f"脚本输出的内容会被模型读到,可以输出你想让模型知道的上下文")
if __name__ == "__main__":
main()
模型能拿到什么上下文,完全由脚本中print输出了什么内容决定。利用这一点,可以在脚本里做文章——比如告诉模型当前执行结果、下一步该做什么、下一步任务需要的参数等。
状态机主要解决两个问题。
面对复杂且任务多的需求时,模型可能因为上下文窗口限制、追踪失效等问题提前收工。具体表现是:模型在对话框里宣布所有任务已完成,但手动review时发现有的地方并没有改到。反问“这块是不是没有改到”,模型才意识到还有任务没完成。
src/main/ja va/com/example/OrderService.ja va:
analysis_steps:
- step1_分析影响面: WAITING/PROCESSING/DONE/FAILED
- step2_分析风险点: WAITING/PROCESSING/DONE/FAILED
解决办法是给每一个任务加上状态标记,任务执行完后通过验证门校验状态,校验通过才放行,否则打回重新执行。工作流如下:
(图片占位)
将关键步骤的执行结果记录到.yaml文件中,其他Agent/Skill执行时也能读到这个记录,从而实现跨Agent/Skill协作。试想一个全流程需求交付的Plugin,从PRD → 系分 → 开发 → 测试每个阶段都对应不同的Skill,下一步一定依赖前一步的执行结果,状态记录就显得很有必要。
这一层的核心思路是:拆分出模型容易产生幻觉的任务,用脚本辅助完成,或者直接全部用脚本完成,减少模型的不确定性。
比如在这个Plugin中,获取“当前分支相比origin/master改动了哪些文件、哪些代码块”对模型来说是容易产生幻觉的任务。所以直接选择用脚本梳理好再喂给模型。当每一个模型有概率出错的小任务都加一层保障,整个工作流自然就稳定了。
执行复杂任务时,把所有任务都交给一个Agent执行容易触发上下文窗口限制、注意力分散的问题。Anthropic也遇到过类似情况:上下文达到一定数量时(40%),模型会变得犹豫,甚至倾向于提前收工,即使任务还没完成。
一些AI IDE支持开启subAgent,比如Claude Code、Cursor。所以可以换一种思路:用一个mainAgent控制主流程,复杂任务则让mainAgent派发subAgent执行。这样subAgent执行任务时有干净的上下文,不会出现提前收工的问题,整个工作流也不会上下文溢出。
这里可能有人疑惑mainAgent如何拿到subAgent的运行结果。其实IDE会自动把subAgent的结果返回给mainAgent,这点无需担心。但从稳定性和管理状态统一性的角度来说,通过回写状态机的方式会更合适。工作流如下:
(图片占位)
验证机制是Harness理念中关键的一层。一般的Prompt工程里如果想验证结果,可能会写这样一些提示词:“Review你的成果,识别有没有问题”。但仔细想想,让本就带有不确定性的模型来Review自己的成果,这跟让人用同一双眼睛检查自己的作业没什么区别。曾经也做过一些尝试——让模型生成一份方案文档,然后新建会话保持干净上下文,继续让模型Review这份方案有没有问题,重复这个动作。最终结论是每次Review模型都会提出新的问题。让模型自己Review自己,不是一个趋于稳定的方案。
这次实践中关于验证的部分,做法是将一个大任务进行拆解。比如在一次代码改动分析任务中,diff里的每一个文件都可以拆成一个小任务,而每一个文件里的改动又有不同维度需要分析(调用链、影响面、逻辑错误、性能风险等),每个维度也可以拆成一个小任务,依次类推。任务也分不同类型,有的任务只需要执行过就好,有的任务有好几个方面需要模型执行得全面。最终验证门要做两件事:
.yaml文件校验模型是否执行到位校验不通过不允许放行,让模型重新执行任务。具体实现也是通过脚本来控制,只需要在脚本执行完后print结论,模型就会拿到这个结论做后续处理。不过建议针对整个工程设计一套结构化的输出范式,加强模型关注脚本执行结果的心智。工作流和状态机类似,只是更细粒度的校验,这里不再贴图。
虽然有了不少工程化手段,但Prompt设计依然很重要。这方面踩了一些坑,总结了一些实践经验。
尝试了两种风格的Prompt。一种是写得复杂,把各个场景下正确的case、错误的case都列出来,用全面的自然语言举例什么是对的什么是错的,附加一些边界情况。另一种是写得简单,整个Prompt只包含需要执行的Step。最后发现Prompt越精简,模型执行过程越稳定——过多的Prompt会导致模型注意力分散。不过这一点可能与模型有关,各家对此观点也不一,比如OpenAI倾向详细的Prompt,Anthropic则倾向于定期精简Prompt。
这点很好理解。某些模型内部编码、预训练数据、分词逻辑天生偏英文,在解析中文上可能有偏差。测试发现即使很短的描述也有概率产生幻觉,而代码是通用的,不会有歧义。
起初在代码中放了一份风险点列表的md,一共34个风险点,让模型逐一分析。测试次数多了发现有时模型最后输出的风险点不一致,甚至偏差很大。排查下来是风险点太分散了。后来把34个具体的风险点整合到10个,并且从具体的点转变成给模型引导方向。
以下是几个主要Skill的最终提示词设计:
(Skill Prompt模板占位)
Plugin和单个Skill不同,Plugin可以是一个Skill集合,所以目录设计要考虑通用性。
├── .claude-plugin/plugin.json # 插件元信息
├── .cc-version # 版本号 (1.0.0)
├── README.md # 项目文档
├── constants/
│ ├── analysis_diff_risk/
│ │ └── filter-rules.yaml # 文件过滤规则
│ └── common/
│ ├── runtime-constants.yaml # 公用运行时配置
│ └── output-messages.yaml # 输出消息模板
├── docs/
│ └── analysis_diff_risk_point.md # 风险检查文档
│ └── analysis-diff-risk-step2.md # 风险检查文档
├── script/analysis_diff_risk/
│ ├── analyze_diff.py # Git diff 解析 + YAML 生成
│ ├── execute_analysis.py # 状态机引擎(列表/收集/汇总)
│ └── cleanup_state.py # 清理临时状态
├── state/analysis_diff_risk/ # 运行时状态机
│ └── impact-analysis-${hash_id}.yaml # 待执行的任务列表
│ └── results_${hash_id}_batch.yaml # subAgent执行任务的结果
├── skills/fe-analysis-diff-risk/
│ └── SKILL.md # Skill 入口定义
└── template/
└── analysis_diff_risk_result.md # 输出报告模板
└── subagent_analyze_prompt.md # subagent提示词
使用脚本输出执行结果时,注意print里的文字描述需要精确,否则也会造成模型偏移。最好能附带下一步要做什么。
能力一般的模型在统计diff行数时会有偏差。这里选择直接用脚本算好每一段diff的start_line、end_line再喂给模型。
改动量比较大时,模型的检索时间会比较久。这是因为模型的常用检索方式是grep / ripgrep(rg)命令,这些命令的本质是用正则模糊匹配项目里的代码字符,匹配完成后还要依赖模型判断是否是正确的场景。
举个例子:告诉模型“把项目中所有的start换成star”,模型开始执行grep命令。因为只是正则模糊匹配,它分不清语境,可能会匹配出各种情况。然后模型再挨个分析上下文,判断哪些才是预期的场景,循环下来就拖慢了检索速度。
好消息是目前社区已有开源库能解决这些问题,比如codegraph。它会先把代码解析成结构图(AST + 符号关系图),记录“谁是什么角色”。开始检索时,它可以直接查索引,知道哪些是变量定义、哪些是函数引用,甚至能知道代码在哪些地方被真正使用了。实测下来能减少30%的耗时,而且比模型更稳定,后续也打算集成到Plugin中。
Sonnet 4.6、Opus 4.8、deepseek-v4-pro、Qwen3.6 Plus,都能按预期执行。Skill读取不到会造成上下文不够完整。简单的解法是把上下游项目放在同一个文件目录下。下饭影视APP下载安装指南
灵宝派对手游下载安装地址推荐
和平精英如何做到压枪稳-和平精英怎样才能压枪稳
下载浏览器app下载安装选择推荐
初中英语同步课文跟读APP推荐|免费下载高口碑跟读软件排行榜
4D采矿者官网在哪下载 最新官方下载安装地址
阅读app安卓版下载推荐
免费影视剧APP推荐
碎片人偶Vragmeet官网在哪下载 最新官方下载安装地址
儿子穿新中式现身大会堂 马斯克罕见用中文回应:他正在学习普通话
Elysium Above 履云录官网在哪下载 最新官方下载安装地址
好用的手环阅读app下载安装
名单曝光!库克、马斯克等将随团到访中国 黄仁勋不在其中
人声接近真人!OpenAI一口气更新三款超强语音AI
短视频软件推荐
短剧《情绪超市》剧情介绍
苹果macOS 27将优化界面设计并测试AI驱动的Safari标签页自动分组功能
《梦幻西游》出道人金价走势解析-云游道人影响解析
免费看电影的软件推荐
官姓可爱谐音网名女生(精选100个)
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc