Harness Engineering 是什么

一句话:Harness Engineering 是把大模型放进一个可执行、可观察、可恢复、可扩展的工程外壳里,让模型不只是“会回答”,而是能在真实环境中持续完成任务。

这里的 harness 不是单个 prompt,也不是单个工具调用,而是模型外部的一整套运行时:上下文装配、工具系统、权限控制、状态管理、错误恢复、记忆、压缩、并发、会话、平台适配、测试和运维。

Note

Prompt Engineering 关心“怎么说清楚”。Context Engineering 关心“给模型什么信息”。Harness Engineering 关心“模型在真实执行链路中如何被约束、供能、校验和恢复”。


组建 Agent 的关注点

  1. 任务怎么拆
  2. 状态怎么管
  3. 关键步骤怎么校验
  4. 失败之后怎么恢复
  5. 工具怎么暴露给模型
  6. 权限和风险怎么拦截
  7. 长上下文怎么压缩
  8. 跨会话经验怎么沉淀
  9. 多平台入口怎么统一
  10. 运行成本和缓存怎么控制

这些问题加起来,就是 agent harness 的边界。


Harness 是怎么一步步演进出来的

AI 工程经历的三次重心偏移

flowchart LR
    A["Prompt Engineering<br/>模型有没有听懂"] --> B["Context Engineering<br/>模型有没有拿到正确事实"]
    B --> C["Harness Engineering<br/>模型能不能在真实环境中可靠做事"]

Prompt Engineering

实用意义:模型有没有听懂你在说什么?

在这个阶段,大模型是什么

  1. 大模型本质上是一个对上下文非常敏感的概率生成系统。
  2. 给什么身份,它更容易沿着那个身份回答。
  3. 给什么样例,它更容易沿着那个范式补全。
  4. 强调什么约束,它更容易把那部分当成重点。

本质

Prompt Engineering 不是“命令模型”,而是塑造一个局部概率空间。

这个阶段最重要的是语言设计:角色、目标、约束、格式、示例、反例、思维路径、输出协议。

天花板

很多任务不是“说清楚就行”,而是“模型真的得知道”。

Prompt 不能凭空提供真实仓库结构、当前文件内容、运行结果、用户历史偏好、权限状态、外部系统返回值。

擅长

  1. 澄清任务
  2. 约束输出
  3. 激发模型已有能力
  4. 让模型在短链路中遵守格式

不擅长

  1. 凭空补全缺失事实
  2. 管理大量动态信息
  3. 处理长链路状态变化
  4. 对真实副作用负责
  5. 从失败中自动恢复

Context Engineering

实用意义:模型有没有拿到足够且正确的信息?

Prompt Engineering 的问题是“怎么问”。Context Engineering 的问题是“问之前,应该把什么放进模型的上下文”。

Context 包括什么

  1. 系统提示:身份、规则、工具使用规范。
  2. 用户输入:当前任务和约束。
  3. 项目上下文:AGENTS.md、架构文档、代码片段、配置文件。
  4. 会话历史:之前做过什么、为什么这么做、失败过什么。
  5. 外部事实:搜索、数据库、API、文件系统、运行日志。
  6. 长期记忆:用户偏好、稳定事实、常用环境。
  7. 工具 schema:模型此刻能调用哪些动作,以及参数格式。

核心问题

  1. 相关性:哪些信息值得进入上下文?
  2. 新鲜度:哪些信息必须实时读取?
  3. 可信度:哪些信息来自一手事实,哪些只是模型猜测?
  4. 优先级:系统规则、用户指令、项目约束冲突时谁更高?
  5. 成本:上下文越大,延迟和成本越高。
  6. 缓存:动态内容放错位置,会破坏 prompt cache。

天花板

Context Engineering 解决“知道什么”,但不自动解决“怎么行动”。

一个模型拿到了正确上下文,仍然可能:

  1. 调错工具。
  2. 忘记更新状态。
  3. 没有验证结果。
  4. 被危险命令诱导。
  5. 在长任务中丢失目标。
  6. 在失败后重复同一种错误。

这就进入 Harness Engineering。

Harness Engineering

实用意义:模型在真实执行里,能不能只需做对?

这里的“只需做对”不是指模型永不犯错,而是指系统把任务执行拆成一条可控链路:

  1. 模型负责判断、规划、选择动作。
  2. Harness 负责把动作变成真实调用。
  3. Harness 在调用前检查权限和风险。
  4. Harness 把结果以结构化方式返回给模型。
  5. Harness 记录状态、压缩历史、保存记忆。
  6. Harness 在失败、超时、中断、上下文溢出时提供恢复路径。

换句话说,Prompt 是“语言接口”,Context 是“信息接口”,Harness 是“执行接口”。


一个成熟的 Harness,到底包含哪些部分

1. Agent Loop

Agent loop 是 harness 的心脏。

最小循环是:

用户消息
  -> 构造上下文和工具 schema
  -> 调用模型
  -> 如果模型返回 tool call,执行工具并追加 tool result
  -> 再调用模型
  -> 直到得到最终回答或达到预算

成熟的 loop 还要处理:

  1. 最大迭代次数。
  2. 中断和取消。
  3. 并发工具调用。
  4. 工具结果顺序还原。
  5. provider fallback。
  6. token 预算。
  7. 上下文压缩。
  8. 会话持久化。

2. Tool Runtime

Tool runtime 决定模型能做什么。

关键设计不是“给模型很多工具”,而是:

  1. 工具 schema 是否清楚。
  2. 参数是否可校验。
  3. 工具是否按场景动态启用。
  4. 不可用工具是否从 schema 中移除。
  5. 工具错误是否统一包装成模型能读懂的结果。
  6. 危险动作是否需要审批。
  7. 工具结果是否保留足够上下文,但不撑爆窗口。

OpenAI 的 function/tool calling 文档把这个过程描述成多步链路:请求模型、模型产生 tool call、应用侧执行工具、把工具输出交回模型、再由模型继续回答或继续调用工具。参考:OpenAI Function Calling

3. Context Assembly

Harness 需要把上下文当成工程资产管理,而不是把所有东西拼成一个大 prompt。

关键分层:

  1. 稳定层:身份、长期规则、工具使用规范。
  2. 项目层:AGENTS.md、项目说明、当前工作目录约束。
  3. 记忆层:用户偏好、长期事实。
  4. 会话层:本轮历史、当前任务进展。
  5. 临时层:预算警告、上下文压力、平台特定提示。

成熟系统会区分“可缓存的稳定 prompt”和“每次调用才注入的临时信息”。这直接影响成本、延迟和缓存命中率。

Anthropic 的 prompt caching 文档也强调缓存内容的稳定性;内容变动会影响缓存复用。参考:Anthropic Prompt Caching

4. State Management

Agent 不是一次性问答系统,它需要状态。

状态至少有四类:

  1. 当前任务状态:todo、已完成步骤、阻塞点。
  2. 会话状态:消息历史、工具调用、压缩 lineage。
  3. 用户状态:偏好、常用环境、长期事实。
  4. 运行状态:后台进程、审批状态、活跃 session、平台连接。

状态管理的难点在于:模型看到的是文本,但真实系统状态在数据库、文件、进程、队列、远端服务里。

Harness 的责任是把这些状态桥接起来,并确保模型不会靠猜。

5. Verification

一个可靠的 harness 会逼近“行动后必须有证据”。

常见验证方式:

  1. 运行测试。
  2. 读取文件确认修改。
  3. 检查命令退出码。
  4. 对 UI 做截图或 DOM 检查。
  5. 对 API 响应做 schema 校验。
  6. 对危险动作加人工审批。

没有验证的 agent 很容易变成“看起来完成了”的文本生成器。

6. Recovery

真实任务一定会失败。

Harness 需要处理:

  1. 工具异常。
  2. 网络失败。
  3. provider 429/5xx。
  4. 权限不足。
  5. 用户中断。
  6. 上下文超限。
  7. 会话恢复。
  8. 后台任务完成后的重新唤醒。

好的恢复设计不是“失败后道歉”,而是保留足够状态,让下一步可以继续。

7. Platform Adapter

成熟 agent 不只运行在 CLI。

它可能从 Telegram、Discord、Slack、Webhook、IDE、Cron、TUI、Dashboard 进入。不同入口的消息形态不同,但最终都应该归一到同一个 agent runtime。

Platform adapter 的作用是:

  1. 把平台事件归一成内部消息。
  2. 做用户授权。
  3. 维护 session key。
  4. 把响应发回正确通道。
  5. 支持中断、审批、队列、状态查询。

8. Extensibility

Harness 需要允许系统增长,而不是每加一个能力就改核心 loop。

扩展点包括:

  1. 新工具。
  2. 新 toolset。
  3. 新 provider。
  4. 新 memory backend。
  5. 新 context engine。
  6. 新 gateway platform。
  7. 新 slash command。
  8. 新 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.pyAIAgent

它承担的职责:

  1. 构造系统 prompt。
  2. 解析 provider 和 API mode。
  3. 调用模型。
  4. 解析 tool calls。
  5. 执行工具。
  6. 维护 OpenAI-style message history。
  7. 处理并发工具调用。
  8. 处理中断。
  9. 触发上下文压缩。
  10. 保存会话。
  11. 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:

  1. chat_completions:OpenAI-compatible endpoint。
  2. codex_responses:OpenAI Responses/Codex 风格。
  3. anthropic_messages:Anthropic Messages API。

这说明 harness 需要把“模型供应商差异”隔离在 adapter 层,让内部消息格式保持统一。

3. Prompt 被拆成稳定层和临时层

Hermes 的 prompt assembly 有一个关键工程判断:稳定上下文和临时上下文分离

稳定层包括:

  1. agent identity。
  2. tool-aware behavior guidance。
  3. memory snapshot。
  4. user profile snapshot。
  5. skills index。
  6. project context files。
  7. timestamp/platform hint。

临时层包括:

  1. budget warning。
  2. context pressure。
  3. gateway session overlay。
  4. prefill messages。

为什么重要:稳定层可以缓存;临时层每次变化。如果混在一起,会破坏 prompt caching。

4. Context 文件有优先级

Hermes 会读取项目上下文,但不是无脑全塞。

优先级:

  1. .hermes.md / HERMES.md
  2. AGENTS.md
  3. CLAUDE.md
  4. .cursorrules / .cursor/rules/*.mdc

这些文件会被安全扫描、截断、去 frontmatter。这里体现的是 Context Engineering 到 Harness Engineering 的过渡:不仅要“给上下文”,还要“安全地给、按优先级给、可缓存地给”。

5. 工具系统是自注册架构

Hermes 的工具文件位于 tools/

每个工具模块通过 registry.register(...) 声明:

  1. 工具名。
  2. 所属 toolset。
  3. schema。
  4. handler。
  5. availability check。
  6. required env。
  7. 是否 async。

model_tools.py 会触发工具发现,tools/registry.py 用 AST 找到顶层 registry.register(),再导入模块完成注册。

这解决了一个 agent harness 常见问题:工具越来越多时,核心 dispatch 不能变成手写大分支。

6. Toolset 让工具按平台和能力分组

Hermes 不把所有工具都直接暴露给模型,而是通过 toolset resolution 过滤。

过滤来源包括:

  1. 显式 enabled toolsets。
  2. disabled toolsets。
  3. 平台 preset。
  4. MCP 动态工具。
  5. availability check。

过滤后,schema 才进入模型请求。

重要点:不可用的工具不要出现在 schema 中。否则模型会调用不存在的能力。

7. 危险命令有审批流

Hermes 的 terminal tool 会检测危险命令,例如:

  1. 递归删除。
  2. 磁盘格式化。
  3. 破坏性 SQL。
  4. 系统配置覆盖。
  5. 服务停止。
  6. 远程脚本管道执行。

命中后进入 approval callback。CLI 和 Gateway 都能处理审批。

这说明 harness 不只是“帮模型执行”,还要承担权限边界。

8. 上下文压缩是双层机制

Hermes 有两层压缩:

  1. Agent 内部压缩:默认在上下文达到 50% 时触发。
  2. Gateway session hygiene:默认在 85% 时作为安全网触发。

压缩前会先 flush memory,避免信息在被摘要前丢失。

压缩算法大致是:

  1. 清理旧的大型工具输出。
  2. 保护开头消息和最近消息。
  3. 对中间消息做结构化摘要。
  4. 保持 tool call 和 tool result 成对。
  5. 生成新的 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

关键实现点:

  1. session key 编码平台、chat type、chat id。
  2. 有两层 running-agent guard。
  3. /stop/approve/deny 等控制命令必须绕过排队。
  4. adapter 负责 connect/disconnect/send_message/on_message。
  5. token lock 防止多个 profile 使用同一 bot token。

这就是 platform harness:同一个 agent runtime 可以接多个入口,但入口差异被 adapter 吸收。

10. Slash command 有中心注册表

Hermes 把 slash command 统一定义在 hermes_cli/commands.pyCOMMAND_REGISTRY

下游消费者包括:

  1. CLI dispatch。
  2. Gateway known commands。
  3. Gateway help。
  4. Telegram command menu。
  5. Slack subcommand map。
  6. Autocomplete。
  7. CLI help。

这是一种很好的 harness 设计:命令定义只有一个事实来源,避免 CLI 和 Gateway 漂移。

11. 插件系统避免核心膨胀

Hermes 有几类扩展:

  1. General plugin:注册 hooks、tools、CLI commands。
  2. Memory provider plugin:替换或扩展长期记忆后端。
  3. Context engine plugin:替换上下文压缩策略。
  4. Gateway hooks:响应 gateway 生命周期事件。
  5. 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,而要使用:

  1. get_hermes_home():真实读写路径。
  2. 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:

  1. Responses API:把模型响应、工具调用、多步执行统一成一个更适合 agent 的 API 面。
  2. Tools / Function Calling:模型输出结构化 tool call,由应用侧执行,再把结果交回模型。
  3. Structured Outputs:通过 JSON Schema 约束工具参数和结构化输出。
  4. Built-in tools:Web Search、File Search、Computer Use、Code Interpreter 等能力进入平台层。
  5. Tracing / Agents SDK:把 agent 执行过程变得可观察。

这对应 Harness Engineering 中的工具运行时、状态追踪和可观察性。

参考:

Anthropic 的方向

公开资料中能看到几个核心 primitive:

  1. Tool use:Claude 可以请求外部工具,由应用执行后返回结果。
  2. Prompt caching:鼓励把稳定的大上下文放在可缓存位置,降低成本和延迟。
  3. Context editing:服务端清理上下文中的工具结果等内容,避免上下文无限增长。
  4. Claude Code:把模型能力放进 CLI 编程工作流,包含文件、命令、编辑、验证等 harness 能力。
  5. MCP:把外部工具和上下文接入标准化。

这对应 Harness Engineering 中的上下文生命周期管理、工具协议和开发者运行时。

参考:


最关键的理解

Harness Engineering 的核心不是“让模型更聪明”,而是让模型的聪明被工程系统正确使用

模型负责:

  1. 理解目标。
  2. 做判断。
  3. 选择下一步。
  4. 解释结果。

Harness 负责:

  1. 提供正确上下文。
  2. 暴露可控工具。
  3. 执行真实动作。
  4. 管住权限风险。
  5. 保存和恢复状态。
  6. 压缩和维护长任务。
  7. 验证结果。
  8. 把失败变成可继续的状态。

所以一个成熟 agent 的能力,往往不是单纯由模型决定,而是由“模型 + harness”的组合决定。


用一句工程判断收尾

如果一个系统只是在调用模型,它是在做 LLM integration。

如果它能让模型在真实环境中持续、可控、可恢复地完成任务,它才是在做 Harness Engineering。