前言

这里说的 Harness,不是某个模型评测框架。它更接近英文里“挽具”的本意:把一个力量很大的东西套住,让它能够被驾驭、被导向、被限制,而不是到处乱冲。放到 Agent 语境里,Harness 就是一套用来驾驭大模型的工程结构。

大模型本身像一股很强的推理和生成能力,但它没有天然的边界感。它可能把任务做偏,也可能调用不该调用的工具,还可能在没有证据时编造结论。Harness 的作用不是削弱模型,而是把模型放进一个可执行、可观察、可回滚的轨道里。

如果说 Agent 是“能行动的模型系统”,那 Harness 就是让这个行动系统稳定工作的挽具。它规定任务入口是什么,模型能看到什么上下文,可以调用哪些工具,每一步如何记录,什么时候停止,怎么验证结果,以及失败后如何恢复。

为什么 Agent 需要 Harness

很多 Agent Demo 看起来很聪明,但一接近真实任务就容易失控。原因通常不是模型太差,而是系统没有 Harness。用户说“帮我修一下这个问题”,模型拿到一句模糊目标,就开始读文件、改代码、运行命令。中间没有预算、没有权限边界、没有状态记录、没有验收标准,最后结果只能靠感觉判断。

Harness 要解决的正是这些问题。它把自然语言目标变成任务契约,把工具调用放进权限闸门,把长任务拆成状态机,把每一步观察写入 trace,把最终结果交给验证器。模型仍然负责理解、计划和生成,但它不再裸奔。

一个没有 Harness 的 Agent 像是在直接拉一辆车;一个有 Harness 的 Agent 则像是有缰绳、车辕、刹车和仪表盘。力量还是同一个力量,但可控性完全不同。

Harness 的组成

我会把 Agent Harness 拆成七个部分:

  • Task Contract:任务契约,定义目标、输入、输出、验收标准和禁止事项。
  • Context Builder:上下文装配器,只把当前步骤需要的信息交给模型。
  • Planner:计划器,把目标拆成可执行步骤。
  • Tool Gate:工具闸门,决定某个工具调用是否允许执行。
  • Executor:执行器,真正调用文件、网络、数据库、浏览器等外部能力。
  • Observer:观察器,把工具结果压缩成模型可读的反馈。
  • Verifier:验证器,用测试、规则、截图、人工检查等方式判断任务是否完成。

这七个部分不一定都要做得很重,但概念上最好分开。否则所有逻辑都塞进一个大 prompt,后面一定难调。

任务契约

Harness 的第一步是把用户目标变成任务契约。契约不是为了增加形式感,而是为了让 Agent 知道什么叫完成,什么叫越界。

task_id: fix-category-page
objective: 修复分类和标签页面的展示问题
inputs:
  - screenshots
  - repo_path
allowed_actions:
  - read_file
  - search_text
  - edit_static_html
  - run_git_diff_check
  - push_to_remote
forbidden_actions:
  - delete_repository
  - reset_unrelated_changes
acceptance:
  - AI开发分类页标题不再出现“分类:”前缀
  - 分类页不再显示个人信息侧栏
  - 标签页采用同样规则
  - 修改必须提交并推送

有了契约,模型就不只是“尽量帮忙”,而是在一组边界里工作。后续每一次工具调用,都可以拿契约做判断:这个动作是否被允许,是否和目标相关,是否需要用户确认。

工具闸门

Agent 最容易出事的地方是工具调用。读文件通常风险较低,写文件风险更高,删除、部署、支付、发邮件则需要更严格的确认。Harness 要把工具分级,而不是把所有工具都裸露给模型。

下面是一个极简工具闸门示例:

class ToolGate:
    def __init__(self, contract):
        self.contract = contract

    def allow(self, tool_name, args):
        if tool_name not in self.contract["allowed_actions"]:
            return False, "tool is not allowed by task contract"

        if tool_name.startswith("delete_"):
            return False, "destructive action requires human approval"

        if tool_name == "edit_static_html":
            path = args.get("path", "")
            if not path.endswith("index.html"):
                return False, "this task only allows editing generated html pages"

        return True, "ok"

这段代码很简单,但思想重要:模型提出 action,Harness 决定 action 是否能落地。模型负责想,Harness 负责管。

执行循环

一个 Harness 驱动的 Agent 循环通常不是“模型一次性回答完”,而是多轮推进。每一轮包含:构造上下文、让模型选择下一步、通过工具闸门、执行工具、观察结果、记录 trace、判断是否完成。

class AgentHarness:
    def __init__(self, model, tools, verifier, max_steps=12):
        self.model = model
        self.tools = tools
        self.verifier = verifier
        self.max_steps = max_steps
        self.trace = []

    def run(self, contract):
        state = {"contract": contract, "observations": []}
        gate = ToolGate(contract)

        for step in range(self.max_steps):
            action = self.model.next_action(state)
            allowed, reason = gate.allow(action.name, action.args)

            if not allowed:
                self.trace.append({"step": step, "blocked": action.name, "reason": reason})
                state["observations"].append(f"blocked: {reason}")
                continue

            result = self.tools[action.name](**action.args)
            self.trace.append({"step": step, "action": action.name, "result": result.summary})
            state["observations"].append(result.summary)

            if self.verifier.passed(state):
                return {"status": "done", "trace": self.trace}

        return {"status": "need_review", "trace": self.trace}

这里最关键的是 max_steps 和 verifier。没有步数上限,Agent 可能陷入循环;没有验证器,Agent 会把“我觉得完成了”当成完成。Harness 要求系统用外部证据收尾,比如测试通过、页面检查通过、接口返回正确、用户确认通过。

上下文装配

很多人做 Agent 时喜欢把所有信息塞进上下文:用户需求、历史对话、全部文件、全部工具说明、全部规则。这样模型看起来知道得很多,但实际更容易混乱。Harness 里的 Context Builder 应该按步骤装配上下文。

比如当前步骤是“判断分类页为什么显示错标题”,上下文只需要相关 HTML 片段、页面路径、用户截图里的现象、已知约束。它不需要整个仓库,也不需要所有历史任务。上下文越干净,模型越容易做出稳定决策。

一个实用做法是把上下文分成四层:任务契约永远保留,最近观察保留,相关文件片段按检索加入,长期记忆只在命中时加入。这样 Harness 像一个过滤器,决定模型此刻应该看什么。

验证器

Verifier 是 Harness 的刹车和仪表盘。它不是为了否定模型,而是为了告诉系统“这一步是否真的有效”。不同任务需要不同验证器。

  • 代码任务:单元测试、构建、lint、类型检查。
  • 前端任务:截图对比、DOM 查询、响应式检查。
  • 数据任务:行数、schema、统计分布、异常值检查。
  • 文档任务:标题、链接、摘要、关键词和格式检查。
  • 自动化任务:dry-run、审计日志、幂等性检查。

如果没有验证器,Agent 很容易停在“看起来对了”。Harness 要让“看起来”变成“证据显示”。例如页面侧栏问题,不应该只凭肉眼说修好了,而应该查询主内容里是否还有 aside-content、检查页面是否启用了 hide-aside、再线上请求确认部署结果。

Harness 和 Skill

Harness 和 Hermes 里的 Skill 是互补关系。Skill 告诉 Agent “类似任务通常怎么做”,Harness 则告诉 Agent “这次任务允许怎么做、做到什么程度、如何验证”。一个偏经验,一个偏约束。

没有 Skill,Agent 每次都要从零规划,效率低;没有 Harness,Agent 拿到 Skill 也可能用错地方。比如一个 Skill 建议“批量替换 HTML 标题”,Harness 需要检查这个批量替换是否只作用在目标页面,是否会改坏其它分类,是否需要同步搜索索引和订阅文件。

所以比较理想的 Agent 架构是:Harness 管住任务生命周期,Skill 提供可复用方法。模型在两者之间做推理和选择。

落地建议

如果要从零实现一个 Harness,不建议一开始就做成大平台。可以先做一个窄场景,例如“静态站点维护 Agent”或“仓库测试修复 Agent”。先定义任务契约,再接入少量工具,然后加上 trace 和验证器。

第一版只需要做到:每一步 action 都有记录,每个工具调用都有权限检查,每个任务结束都有验收项。等这个闭环稳定后,再加入自动规划、上下文检索、Skill 召回和长期记忆。

Harness 的价值不是让 Agent 显得复杂,而是让它可靠。一个能被追踪、能被限制、能被验证的 Agent,才适合真正接入工作流。

小结

Harness 是驾驭大模型的挽具。它用任务契约定义目标和边界,用上下文装配器控制模型看到什么,用工具闸门限制模型能做什么,用执行器和观察器推进任务,用验证器判断结果是否可信。

做 Agent 时,prompt 很重要,但 prompt 不是全部。真正让 Agent 从 Demo 走向可用的,是 Harness 这种工程结构。它让模型的能力不再是散开的冲劲,而是被导向一个可控、可复盘、可持续改进的执行系统。