Harness Engineering 是什么
一句话:Harness Engineering 是把大模型放进一个可执行、可观察、可恢复、可扩展的工程外壳里,让模型不只是“会回答”,而是能在真实环境中持续完成任务。
这里的 harness 不是单个 prompt,也不是单个工具调用,而是模型外部的一整套运行时:上下文装配、工具系统、权限控制、状态管理、错误恢复、记忆、压缩、并发、会话、平台适配、测试和运维。
Note
Prompt Engineering 关心“怎么说清楚”。Context Engineering 关心“给模型什么信息”。Harness Engineering 关心“模型在真实执行链路中如何被约束、供能、校验和恢复”。
组建 Agent 的关注点
- 任务怎么拆
- 状态怎么管
- 关键步骤怎么校验
- 失败之后怎么恢复
- 工具怎么暴露给模型
- 权限和风险怎么拦截
- 长上下文怎么压缩
- 跨会话经验怎么沉淀
- 多平台入口怎么统一
- 运行成本和缓存怎么控制
这些问题加起来,就是 agent harness 的边界。
Harness 是怎么一步步演进出来的
AI 工程经历的三次重心偏移
flowchart LR A["Prompt Engineering<br/>模型有没有听懂"] --> B["Context Engineering<br/>模型有没有拿到正确事实"] B --> C["Harness Engineering<br/>模型能不能在真实环境中可靠做事"]
Prompt Engineering
实用意义:模型有没有听懂你在说什么?
在这个阶段,大模型是什么
- 大模型本质上是一个对上下文非常敏感的概率生成系统。
- 给什么身份,它更容易沿着那个身份回答。
- 给什么样例,它更容易沿着那个范式补全。
- 强调什么约束,它更容易把那部分当成重点。
本质
Prompt Engineering 不是“命令模型”,而是塑造一个局部概率空间。
这个阶段最重要的是语言设计:角色、目标、约束、格式、示例、反例、思维路径、输出协议。
天花板
很多任务不是“说清楚就行”,而是“模型真的得知道”。
Prompt 不能凭空提供真实仓库结构、当前文件内容、运行结果、用户历史偏好、权限状态、外部系统返回值。
擅长
- 澄清任务
- 约束输出
- 激发模型已有能力
- 让模型在短链路中遵守格式
不擅长
- 凭空补全缺失事实
- 管理大量动态信息
- 处理长链路状态变化
- 对真实副作用负责
- 从失败中自动恢复
Context Engineering
实用意义:模型有没有拿到足够且正确的信息?
Prompt Engineering 的问题是“怎么问”。Context Engineering 的问题是“问之前,应该把什么放进模型的上下文”。
Context 包括什么
- 系统提示:身份、规则、工具使用规范。
- 用户输入:当前任务和约束。
- 项目上下文:
AGENTS.md、架构文档、代码片段、配置文件。 - 会话历史:之前做过什么、为什么这么做、失败过什么。
- 外部事实:搜索、数据库、API、文件系统、运行日志。
- 长期记忆:用户偏好、稳定事实、常用环境。
- 工具 schema:模型此刻能调用哪些动作,以及参数格式。
核心问题
- 相关性:哪些信息值得进入上下文?
- 新鲜度:哪些信息必须实时读取?
- 可信度:哪些信息来自一手事实,哪些只是模型猜测?
- 优先级:系统规则、用户指令、项目约束冲突时谁更高?
- 成本:上下文越大,延迟和成本越高。
- 缓存:动态内容放错位置,会破坏 prompt cache。
天花板
Context Engineering 解决“知道什么”,但不自动解决“怎么行动”。
一个模型拿到了正确上下文,仍然可能:
- 调错工具。
- 忘记更新状态。
- 没有验证结果。
- 被危险命令诱导。
- 在长任务中丢失目标。
- 在失败后重复同一种错误。
这就进入 Harness Engineering。
Harness Engineering
实用意义:模型在真实执行里,能不能只需做对?
这里的“只需做对”不是指模型永不犯错,而是指系统把任务执行拆成一条可控链路:
- 模型负责判断、规划、选择动作。
- Harness 负责把动作变成真实调用。
- Harness 在调用前检查权限和风险。
- Harness 把结果以结构化方式返回给模型。
- Harness 记录状态、压缩历史、保存记忆。
- Harness 在失败、超时、中断、上下文溢出时提供恢复路径。
换句话说,Prompt 是“语言接口”,Context 是“信息接口”,Harness 是“执行接口”。
一个成熟的 Harness,到底包含哪些部分
1. Agent Loop
Agent loop 是 harness 的心脏。
最小循环是:
用户消息
-> 构造上下文和工具 schema
-> 调用模型
-> 如果模型返回 tool call,执行工具并追加 tool result
-> 再调用模型
-> 直到得到最终回答或达到预算成熟的 loop 还要处理:
- 最大迭代次数。
- 中断和取消。
- 并发工具调用。
- 工具结果顺序还原。
- provider fallback。
- token 预算。
- 上下文压缩。
- 会话持久化。
2. Tool Runtime
Tool runtime 决定模型能做什么。
关键设计不是“给模型很多工具”,而是:
- 工具 schema 是否清楚。
- 参数是否可校验。
- 工具是否按场景动态启用。
- 不可用工具是否从 schema 中移除。
- 工具错误是否统一包装成模型能读懂的结果。
- 危险动作是否需要审批。
- 工具结果是否保留足够上下文,但不撑爆窗口。
OpenAI 的 function/tool calling 文档把这个过程描述成多步链路:请求模型、模型产生 tool call、应用侧执行工具、把工具输出交回模型、再由模型继续回答或继续调用工具。参考:OpenAI Function Calling。
3. Context Assembly
Harness 需要把上下文当成工程资产管理,而不是把所有东西拼成一个大 prompt。
关键分层:
- 稳定层:身份、长期规则、工具使用规范。
- 项目层:
AGENTS.md、项目说明、当前工作目录约束。 - 记忆层:用户偏好、长期事实。
- 会话层:本轮历史、当前任务进展。
- 临时层:预算警告、上下文压力、平台特定提示。
成熟系统会区分“可缓存的稳定 prompt”和“每次调用才注入的临时信息”。这直接影响成本、延迟和缓存命中率。
Anthropic 的 prompt caching 文档也强调缓存内容的稳定性;内容变动会影响缓存复用。参考:Anthropic Prompt Caching。
4. State Management
Agent 不是一次性问答系统,它需要状态。
状态至少有四类:
- 当前任务状态:todo、已完成步骤、阻塞点。
- 会话状态:消息历史、工具调用、压缩 lineage。
- 用户状态:偏好、常用环境、长期事实。
- 运行状态:后台进程、审批状态、活跃 session、平台连接。
状态管理的难点在于:模型看到的是文本,但真实系统状态在数据库、文件、进程、队列、远端服务里。
Harness 的责任是把这些状态桥接起来,并确保模型不会靠猜。
5. Verification
一个可靠的 harness 会逼近“行动后必须有证据”。
常见验证方式:
- 运行测试。
- 读取文件确认修改。
- 检查命令退出码。
- 对 UI 做截图或 DOM 检查。
- 对 API 响应做 schema 校验。
- 对危险动作加人工审批。
没有验证的 agent 很容易变成“看起来完成了”的文本生成器。
6. Recovery
真实任务一定会失败。
Harness 需要处理:
- 工具异常。
- 网络失败。
- provider 429/5xx。
- 权限不足。
- 用户中断。
- 上下文超限。
- 会话恢复。
- 后台任务完成后的重新唤醒。
好的恢复设计不是“失败后道歉”,而是保留足够状态,让下一步可以继续。
7. Platform Adapter
成熟 agent 不只运行在 CLI。
它可能从 Telegram、Discord、Slack、Webhook、IDE、Cron、TUI、Dashboard 进入。不同入口的消息形态不同,但最终都应该归一到同一个 agent runtime。
Platform adapter 的作用是:
- 把平台事件归一成内部消息。
- 做用户授权。
- 维护 session key。
- 把响应发回正确通道。
- 支持中断、审批、队列、状态查询。
8. Extensibility
Harness 需要允许系统增长,而不是每加一个能力就改核心 loop。
扩展点包括:
- 新工具。
- 新 toolset。
- 新 provider。
- 新 memory backend。
- 新 context engine。
- 新 gateway platform。
- 新 slash command。
- 新 skill。
扩展点越清楚,核心越稳定。
Hermes Agent 的实现案例
Hermes Agent 是一个典型的 agent harness 项目。它的核心价值不是“有一个很长的 prompt”,而是把模型放进了一个完整运行时。
总体结构
flowchart TD A["入口<br/>CLI / Gateway / ACP / Cron / Batch"] --> B["AIAgent<br/>run_agent.py"] B --> C["Prompt Builder<br/>agent/prompt_builder.py"] B --> D["Provider Resolver<br/>hermes_cli/runtime_provider.py"] B --> E["Tool Dispatch<br/>model_tools.py"] E --> F["Tool Registry<br/>tools/registry.py"] E --> G["Terminal / Browser / Web / MCP / Files"] B --> H["Session DB<br/>hermes_state.py"] B --> I["Context Compressor<br/>agent/context_compressor.py"] B --> J["Memory Manager<br/>agent/memory_manager.py"] A --> K["Gateway Adapters<br/>gateway/platforms/*"]
1. AIAgent 是核心执行器
源码入口:run_agent.py 的 AIAgent。
它承担的职责:
- 构造系统 prompt。
- 解析 provider 和 API mode。
- 调用模型。
- 解析 tool calls。
- 执行工具。
- 维护 OpenAI-style message history。
- 处理并发工具调用。
- 处理中断。
- 触发上下文压缩。
- 保存会话。
- flush memory。
它支持两类入口:
agent.chat("Fix the bug")
agent.run_conversation(user_message="Fix the bug")chat() 是简单封装,run_conversation() 是完整 loop。
2. Provider 被抽象成 API mode
Hermes 不把自己绑死在单一模型服务上,而是把 provider 解析成不同 API mode:
chat_completions:OpenAI-compatible endpoint。codex_responses:OpenAI Responses/Codex 风格。anthropic_messages:Anthropic Messages API。
这说明 harness 需要把“模型供应商差异”隔离在 adapter 层,让内部消息格式保持统一。
3. Prompt 被拆成稳定层和临时层
Hermes 的 prompt assembly 有一个关键工程判断:稳定上下文和临时上下文分离。
稳定层包括:
- agent identity。
- tool-aware behavior guidance。
- memory snapshot。
- user profile snapshot。
- skills index。
- project context files。
- timestamp/platform hint。
临时层包括:
- budget warning。
- context pressure。
- gateway session overlay。
- prefill messages。
为什么重要:稳定层可以缓存;临时层每次变化。如果混在一起,会破坏 prompt caching。
4. Context 文件有优先级
Hermes 会读取项目上下文,但不是无脑全塞。
优先级:
.hermes.md/HERMES.mdAGENTS.mdCLAUDE.md.cursorrules/.cursor/rules/*.mdc
这些文件会被安全扫描、截断、去 frontmatter。这里体现的是 Context Engineering 到 Harness Engineering 的过渡:不仅要“给上下文”,还要“安全地给、按优先级给、可缓存地给”。
5. 工具系统是自注册架构
Hermes 的工具文件位于 tools/。
每个工具模块通过 registry.register(...) 声明:
- 工具名。
- 所属 toolset。
- schema。
- handler。
- availability check。
- required env。
- 是否 async。
model_tools.py 会触发工具发现,tools/registry.py 用 AST 找到顶层 registry.register(),再导入模块完成注册。
这解决了一个 agent harness 常见问题:工具越来越多时,核心 dispatch 不能变成手写大分支。
6. Toolset 让工具按平台和能力分组
Hermes 不把所有工具都直接暴露给模型,而是通过 toolset resolution 过滤。
过滤来源包括:
- 显式 enabled toolsets。
- disabled toolsets。
- 平台 preset。
- MCP 动态工具。
- availability check。
过滤后,schema 才进入模型请求。
重要点:不可用的工具不要出现在 schema 中。否则模型会调用不存在的能力。
7. 危险命令有审批流
Hermes 的 terminal tool 会检测危险命令,例如:
- 递归删除。
- 磁盘格式化。
- 破坏性 SQL。
- 系统配置覆盖。
- 服务停止。
- 远程脚本管道执行。
命中后进入 approval callback。CLI 和 Gateway 都能处理审批。
这说明 harness 不只是“帮模型执行”,还要承担权限边界。
8. 上下文压缩是双层机制
Hermes 有两层压缩:
- Agent 内部压缩:默认在上下文达到 50% 时触发。
- Gateway session hygiene:默认在 85% 时作为安全网触发。
压缩前会先 flush memory,避免信息在被摘要前丢失。
压缩算法大致是:
- 清理旧的大型工具输出。
- 保护开头消息和最近消息。
- 对中间消息做结构化摘要。
- 保持 tool call 和 tool result 成对。
- 生成新的 session lineage。
这体现了 harness 的一个核心原则:长任务不是一直塞完整历史,而是要维护“可继续工作的充分状态”。
Anthropic 也提供 server-side context editing,用来清理工具结果等上下文内容。参考:Claude Context Editing。
9. Gateway 把多平台消息归一
Hermes 的 gateway 支持 Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Mattermost、Email、SMS、DingTalk、Feishu、WeCom、Weixin、BlueBubbles、QQBot、Webhook、API Server、Home Assistant 等适配器。
平台消息流程:
platform raw event
-> adapter normalizes MessageEvent
-> GatewayRunner resolves session key
-> authorization
-> slash command or AIAgent
-> response delivery关键实现点:
- session key 编码平台、chat type、chat id。
- 有两层 running-agent guard。
/stop、/approve、/deny等控制命令必须绕过排队。- adapter 负责 connect/disconnect/send_message/on_message。
- token lock 防止多个 profile 使用同一 bot token。
这就是 platform harness:同一个 agent runtime 可以接多个入口,但入口差异被 adapter 吸收。
10. Slash command 有中心注册表
Hermes 把 slash command 统一定义在 hermes_cli/commands.py 的 COMMAND_REGISTRY。
下游消费者包括:
- CLI dispatch。
- Gateway known commands。
- Gateway help。
- Telegram command menu。
- Slack subcommand map。
- Autocomplete。
- CLI help。
这是一种很好的 harness 设计:命令定义只有一个事实来源,避免 CLI 和 Gateway 漂移。
11. 插件系统避免核心膨胀
Hermes 有几类扩展:
- General plugin:注册 hooks、tools、CLI commands。
- Memory provider plugin:替换或扩展长期记忆后端。
- Context engine plugin:替换上下文压缩策略。
- Gateway hooks:响应 gateway 生命周期事件。
- Skills:把可复用流程作为程序化记忆注入。
其中 memory provider 和 context engine 是 single-select、config-driven 的 provider plugin。
这说明成熟 harness 的核心不是“什么都内置”,而是提供稳定扩展面。
12. Profile-aware path 是运行时隔离
Hermes 支持多个 profile。每个 profile 有自己的 HERMES_HOME,包括 config、API keys、memory、sessions、skills、gateway 状态。
因此代码不能硬编码 ~/.hermes,而要使用:
get_hermes_home():真实读写路径。display_hermes_home():用户可见路径。
这属于 harness 的运维层:一个 agent 产品一旦多实例运行,路径隔离、token lock、状态隔离就变成核心能力。
从 Hermes Agent 抽象出的 Harness Engineering 设计清单
Agent Loop
- 是否有统一的内部消息格式?
- 是否支持工具调用循环?
- 是否有最大迭代预算?
- 是否能中断、取消、恢复?
- 是否能处理 provider fallback?
- 是否能保存完整会话轨迹?
Context
- 稳定上下文和临时上下文是否分离?
- 项目上下文是否有优先级?
- 长文件是否截断?
- 外部上下文是否安全扫描?
- memory snapshot 是否有明确生命周期?
- context compression 是否保持任务可继续?
Tools
- 工具是否有 schema?
- 工具是否按 toolset 分组?
- 不可用工具是否从 schema 移除?
- 工具错误是否返回结构化结果?
- 危险工具是否有审批?
- async 工具是否能桥接到 sync loop?
State
- session 是否持久化?
- memory 是否跨会话保存?
- background process 是否可追踪?
- approval state 是否按 session 管理?
- profile 是否隔离?
- 压缩后是否保留 lineage?
Platform
- CLI、Gateway、IDE、Cron 是否共享核心 runtime?
- 平台消息是否先归一成内部事件?
- 授权是否在 agent 执行前完成?
- 控制命令是否能绕过 active-session queue?
- 响应是否能发回正确平台和线程?
Extensibility
- 新工具是否不需要改 dispatch 核心?
- 新 provider 是否通过 adapter 接入?
- 新 memory backend 是否通过 provider plugin 接入?
- 新 context engine 是否可配置替换?
- 新平台是否只需要实现 adapter?
- 命令是否有单一注册表?
Verification
- 修改代码后是否运行测试?
- 工具调用后是否检查结果?
- UI 变更是否截图或浏览器验证?
- 文件写入后是否读取确认?
- 失败是否记录可恢复上下文?
OpenAI、Anthropic 这些公司,真实是怎么做的
从公开 API 和产品形态看,主流方向已经从“写好 prompt”转向“提供 agent runtime primitives”。
OpenAI 的方向
公开资料中能看到几个核心 primitive:
- Responses API:把模型响应、工具调用、多步执行统一成一个更适合 agent 的 API 面。
- Tools / Function Calling:模型输出结构化 tool call,由应用侧执行,再把结果交回模型。
- Structured Outputs:通过 JSON Schema 约束工具参数和结构化输出。
- Built-in tools:Web Search、File Search、Computer Use、Code Interpreter 等能力进入平台层。
- Tracing / Agents SDK:把 agent 执行过程变得可观察。
这对应 Harness Engineering 中的工具运行时、状态追踪和可观察性。
参考:
Anthropic 的方向
公开资料中能看到几个核心 primitive:
- Tool use:Claude 可以请求外部工具,由应用执行后返回结果。
- Prompt caching:鼓励把稳定的大上下文放在可缓存位置,降低成本和延迟。
- Context editing:服务端清理上下文中的工具结果等内容,避免上下文无限增长。
- Claude Code:把模型能力放进 CLI 编程工作流,包含文件、命令、编辑、验证等 harness 能力。
- MCP:把外部工具和上下文接入标准化。
这对应 Harness Engineering 中的上下文生命周期管理、工具协议和开发者运行时。
参考:
最关键的理解
Harness Engineering 的核心不是“让模型更聪明”,而是让模型的聪明被工程系统正确使用。
模型负责:
- 理解目标。
- 做判断。
- 选择下一步。
- 解释结果。
Harness 负责:
- 提供正确上下文。
- 暴露可控工具。
- 执行真实动作。
- 管住权限风险。
- 保存和恢复状态。
- 压缩和维护长任务。
- 验证结果。
- 把失败变成可继续的状态。
所以一个成熟 agent 的能力,往往不是单纯由模型决定,而是由“模型 + harness”的组合决定。
用一句工程判断收尾
如果一个系统只是在调用模型,它是在做 LLM integration。
如果它能让模型在真实环境中持续、可控、可恢复地完成任务,它才是在做 Harness Engineering。