> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Agent Harness完全指南
- URL: https://www.hubwiz.com/blog/the-complete-guide-to-agent-harnesses/
- Published: 2026-09-02T08:09:46.000Z
- Updated: 2026-10-13T15:19:01.000Z
- Description: 你的代理只有六行Python代码。Harness则是其余的一切。
- Author: admin
- Tags: AGENT, #Import 2026-10-14 14:41

两周前，我在配置文件中写了一条规则：永远不要应用未经人类批准的编辑。

这是一条好规则。措辞清晰，位于文件顶部，代理在每次会话开始时都会读取它。

有一次会话在下午直接违反了这条规则十一次，将大约七十六处未经批准的编辑放入了客户交付物中。没有任何东西阻止它。没有错误，没有标记，没有任何等待确认的东西。两小时后，我检查文件，标记了代理未经询问就重写的每一行。四十六行，在一份印有客户名字的文档中。

我把一条好规则写在了错误的地方。

我将其重写为钩子：一个脚本，在分派工具调用之前由harness运行，它读取待处理的调用并返回拒绝。代理仍然可以决定进行编辑。但编辑永远不会到达文件。

从那以后，钩子错了两次。两次都是我把规则写得太窄了，两次我都可以打开文件并指出错误的那一行。这才是值得拥有的部分。提示版本让我无从下手。

这种差异——你陈述的规则与你执行的规则之间的差距——就是harness。它存在于五个地方：

1. 代理被允许做什么
2. 它能触及什么
3. 什么在重启后存活
4. 谁检查它的工作
5. 你付多少钱向它展示任何东西

每个部分都附有代码，以及一个[仓库](https://github.com/paoloap-py/agent-harness-guide?ref=hubwiz.com)，你可以在生产环境中失败之前，在自己的机器上观察该层失败。

（如果代理对你来说仍然陌生，[从这里开始](https://medium.com/data-science-collective/the-complete-guide-to-building-your-first-ai-agent-its-easier-than-you-think-c87f376c84b2?ref=hubwiz.com)然后再回来。以下所有内容都假设你已经部署了一个并看着它行为异常。）

## 1、Harness真正的作用

让我从听起来太简单而不值得说的事情开始。

编码代理循环的核心是六行：

```python
while agent.turns < max_turns:
    call = agent.next_call(observations)
    if call is None:
        break
    result = execute(call)
    observations.append(result)

```

就这样。这就是代理。加上错误处理和重试，你大约有二十行。你评估过的每个框架都包装了这六行，有趣的部分是`execute`，这是你决定模型被允许做什么、能触及什么、记住什么、谁检查它以及它能看到什么的地方。

这就是harness。现在你的信息流里满是告诉你它比选择哪个模型更重要的人。

我花了一周时间验证这个说法。它站不住脚，而替代它的东西更有用。

**harness占主导的地方：** *成本*。一篇[2026年6月的预印本](https://arxiv.org/abs/2607.22585?ref=hubwiz.com)在Terminal-Bench Pro（Terminal-Bench代理基准的更难变体）的分层50任务子集上运行了三个harness（Goose、OpenCode、OpenHands-SDK）。它测量到**每个解决任务的token数差异高达40倍**。升级模型仅使相同数字移动1.0到1.3倍。

**它不占主导的地方：** *能力*。同一研究发现harness之间的通过率差异为**0到8个百分点**，除了最大的差距外，所有差距的引导置信区间都跨越零。作者称其准确性发现是描述性的，n=50。

因此，相同的任务，相同的模型，相同的成功率，在一个harness上的成本是另一个的四十倍。

以下所有内容使你的代理更便宜且更难被意外。但没有一个能使弱模型变聪明。

## 2、将规则移入代码

系统提示中的规则是一个赌注，赌模型会记住它、按你的意思阅读它，并在上下文压力下遵循它。模型大多数时候赢了这个赌注。这种可靠性正是危险所在，因为它是你停止检查的原因。

代码中的规则在每次调用时运行，包括模型已忘记其存在的调用。

在代理循环中有五个地方可以通过代码强制执行规则。以下各节逐一介绍：该层的用途、执行它的代码，以及缺少该层时相同代理的行为。你不需要在第一天就拥有所有五层。从你能想象本周发生在你身上的故障层开始。

![None](https://www.hubwiz.com/blog/content/images/2026/09/1-Fv8uI2dJpteYEYkkwESCAg.png)

## 3、第1层：执行边界

**问题：代理被允许做什么？**

边界是在模型决策和工具调用之间运行的函数。这是一个没有边界的相同代理。系统提示说永远不要未经询问就删除文件，它有三个工具调用排队。

```python
SYSTEM_PROMPT = "IMPORTANT: never delete a file without asking the user first."

def execute(call):
    if call.name == "delete_file":
        FILES.pop(call.args["path"], None)
        return f"deleted {call.args['path']}"

```

运行它，代理会删除`cache.tmp`（这是垃圾），然后删除`client-deliverable.md`（正如其名）。它从未询问。规则一直在提示中。

修复方法是在工具之前运行一个函数：

```python
def boundary(rules):
    def wrap(execute):
        def guarded(call):
            for name, deny_if, reason in rules:
                if deny_if(call):
                    return f"DENIED by {name}: {reason}"
            return execute(call)
        return guarded
    return wrap

NEEDS_APPROVAL = [(
    "delete-needs-approval",
    lambda c: c.name == "delete_file" and not c.args.get("approved_by_human"),
    "deletion requires an explicit human approval flag on the call",
)]

```

相同的代理，相同的脚本，相同的提示。

什么都不会被删除。

## 4、第2层：沙箱

**问题：代理能触及什么？**

沙箱是运行代理的进程可以触及的目录和主机集合。你从无开始，添加回工作所需的内容：路径允许列表、主机允许列表，以及一个看不到其外部任何内容的进程。

常见版本是拒绝列表，命名代理不能触及的路径：

```python
DENY = ["secrets/"]

def execute(call):
    path = call.args["path"]
    if any(path.startswith(d) for d in DENY):   # 检查拼写
        return "DENIED by deny-list"
    real = os.path.normpath(path)               # ../ 在此处折叠，在检查之后
    return DISK.get(real, "not found")

```

运行它，代理请求两次：

```
read('secrets/api_key')          -> DENIED by deny-list
read('work/../secrets/api_key')  -> sk-live-DO-NOT-LEAK

```

检查运行在代理输入的字符串上。查找运行在该字符串实际指向的位置。

两行之隔，两种拼写是同一个文件。

修复方法是在比较之前解析路径：

```python
ALLOW_ROOTS = ["work"]

def resolve(path):
    real = os.path.normpath(path)               # 首先解析
    if not any(real == r or real.startswith(r + os.sep) for r in ALLOW_ROOTS):
        return None
    return real

```

现在两种拼写都到达`secrets/api_key`，都不在`work`下，都被拒绝。允许列表替代拒绝列表的原因相同：拒绝列表只阻止你想到的拼写。

两个版本都在仓库中作为`failures/02_no_sandbox.py`和`layers/02_sandboxing.py`运行。它们是Python函数，因此你可以无需设置容器即可运行它们。在生产环境中，相同的检查是进程本身，一个在`work/`外部没有读权限的用户或容器，操作顺序相同。

这是第1层无法完成的工作。你自己程序中的检查只看到它识别的调用，同一个文件可以通过shell命令、符号链接或包含..的路径到达。这些都不是它正在监视的调用。

## 5、第3层：内存持久性

**问题：什么在上下文重置后存活？**

我今年改变的最有用的事情是学会毫不犹豫地丢弃对话。

上下文填满，质量下降，代理开始自相矛盾。本能是维持会话。更好的做法是自由重启，并确保重要的内容存在于重启无法触及的地方：提交的代码、配置、harness本身、提取的笔记。

这意味着有意识地决定代理产生的每个状态属于哪里：(1)在对话中，随会话消亡；(2)在磁盘上，存活且可检索；(3)在harness配置中，塑造每个后续会话。

三个目的地，一个调度：

```python
CONVERSATION, DISK, HARNESS_CONFIG = [], {}, {}

def remember(kind, key, value):
    """'chat' 随会话消亡，'disk' 存活，'config' 塑造每个后续会话。"""
    {"chat": lambda: CONVERSATION.append(value),
     "disk": lambda: DISK.__setitem__(key, value),
     "config": lambda: HARNESS_CONFIG.__setitem__(key, value)}[kind]()

```

调度很简单。价值在于你不再能在不明确说出三者之一的情况下存储任何东西。

大多数团队从未明确做出这个决定，这就是为什么他们的代理在某些方面感觉健忘，在其他方面顽固错误。（运行`layers/03_memory_persistence.py`：对话返回\[\]，而决策和规则仍然存在于磁盘和配置中。）

## 6、第4层：验证循环

**问题：谁检查工作，什么阻止他们自己修复？**

验证循环是某物对代理输出的第二次传递，该物可以报告问题但无法修复。

句子的后半部分是人们跳过的部分。给审查子代理写访问权限，它会默默地修复发现的问题。这听起来很高效。它破坏了信号，因为现在你无法区分原本正确的 work 和原本错误但被修补的 work。

使审查者只读。其输出是批评。对其采取行动是具有自己记录的单独步骤。这种分离保持了信号的可读性。

这里的第二个模式是模拟运行。对于任何不可逆的操作，工具接受一个标志，默认描述它会做什么，只有在标志明确关闭时才执行。默认是安全的，所以忘记是无害的。（在`layers/04_verification_loops.py`中，`apply_fix`打印`DRY RUN: would rewrite total.py, nothing written`，而差一错误仍然存在于末尾。）

两条规则都适合一个`execute`：

```python
def execute(call):
    if call.name == "review":
        snapshot = dict(CODE)               # 副本，审查者无法写入
        src = snapshot[call.args["path"]]
        return f"VERDICT: {'off-by-one' if '+ 1' in src else 'looks good'}"
    if call.name == "apply_fix":
        if call.args.get("dry_run", True):  # 默认开启，有意关闭
            return f"DRY RUN: would rewrite {call.args['path']}, nothing written"
        CODE[call.args["path"]] = call.args["new"]
        return f"wrote {call.args['path']}"

```

审查者发现了差一错误但无法触及它。

## 7、第5层：上下文管道

**问题：模型实际看到什么，这花费多少？**

上下文管道是信息到达模型上下文窗口的路径，以及关于多少信息到达的决定。

决定是昂贵的部分。你让进入窗口的每个token都在该轮和之后每轮付费，这就是为什么四十倍的差距出现在这里而不是第1层。

朴素的harness将所有内容放入窗口：当代理询问一个函数时整个文件，当需要一行时整个搜索输出，以及之前的每一轮。它有效。这也意味着第二十轮再次为第一到十九轮读取的所有内容付费。

修复它的方法是蒸馏委托。一个[子代理](https://medium.com/data-science-collective/ai-agents-in-5-levels-of-difficulty-with-full-code-implementation-15d794becfb8?ref=hubwiz.com)获得自己的上下文窗口，探索数万个token，返回一到两千。主线程永远不会看到搜索转储。它看到的是答案。

在代码中，整个技巧是哪个计数器递增：

```python
def subagent_search(query):
    """它自己的窗口。主线程永远不会为这次阅读付费。"""
    global subagent_tokens
    subagent_tokens += sum(len(v.split()) for v in CORPUS.values())
    return next(f"{n}: {b.split('ANSWER:')[1].strip()}"
                for n, b in CORPUS.items() if "ANSWER:" in b)
def execute(call):
    global main_tokens
    distilled = subagent_search(call.args["q"])
    main_tokens += len(distilled.split())    # 唯一计费的一行
    return distilled

```

[仓库](https://github.com/paoloap-py/agent-harness-guide?ref=hubwiz.com)中的演示是那个的最小诚实版本。相同的问题，相同的答案，两次。主线程在一个中读取9,602个token，在另一个中读取2个。

这也是为什么顺序子代理模式令人失望：每一步等待上一步的链为你购买了上下文隔离，但没有购买任何并行性。如果步骤真正独立，就那样运行它们。如果不是，诚实地说你在为隔离付费。（隔离通常值得花钱。只需知道发票上是什么。）

## 8、没人能告诉你的事情

限制，因为围绕这个话题的信心远超其下的证据。

1. **这里没有一项对照研究经过同行评审。** 截至2026年8月，每个harness隔离结果都是未经评审的arXiv预印本。最谨慎的实验性自称为 workshop 审稿中的初步工作。被引用最多的框架论文是一篇[自称立场论文](https://arxiv.org/abs/2605.23950?ref=hubwiz.com)，没有进行自己的实验。**这对你意味着什么：** 这里没有任何东西通过同行评审，包括我的。在根据任何内容做预算之前，在自己的任务上重新运行比较。
2. **Harness增益通常无法在新任务上存活。** 一篇[2026年7月的论文](https://arxiv.org/abs/2607.12227?ref=hubwiz.com)发现自动harness演化\*"不能一致地优于简单的测试时缩放方法，并表现出有限的泛化"\*，因为搜索和最终评估共享一个基准。Terminal-Bench 2.0上的[持续学习评估](https://arxiv.org/abs/2607.14004?ref=hubwiz.com)看着GEPA在调优任务上攀升至70.8%，然后在更广泛的集合上降至54.5%，低于起始的56.8%。优化使其变差。**这对你意味着什么：** 在未用于构建的任务上测量你的harness。仅出现在调优集上的增益是你在生产中失去的增益。
3. **一个著名的微调结果是用其自己的奖励信号评分的。** [Baseten](https://www.baseten.co/blog/fine-tuning-small-open-source-llms-to-outperform-large-closed-source-models-by-60/?ref=hubwiz.com)为临床笔记生成微调了Gemma 3 27B，从比Claude Sonnet 4差35%变为好60%。他们的文章明确表示\*"评估harness本身在训练期间用作奖励信号。"\* 合理的技术，结果是针对训练它的东西测量的。**这对你意味着什么：** 当供应商报告微调胜利时，询问训练期间的评估是什么。如果是同一个harness，该数字告诉你模型学会了评估。
4. **你的harness不可移植。** 一项[2026年6月的研究](https://arxiv.org/abs/2606.25447?ref=hubwiz.com)发现，仅在训练后应用harness只能恢复很少的训练时优势。在最小harness上后训练的模型在工具定义形状改变后返回\*"Invalid tool format"\*的比率为75.1%，一个Qwen2.5-7B变体比其起始基础模型低10.8分。适用于每个模型的harness对每个模型都稍差。**这对你意味着什么：** 在后训练之前选择你的harness。事后添加它只能恢复很少的训练时优势。如果你不训练自己的模型，预计项目中途的harness交换成本高于迁移看起来应有的成本。
5. **该领域尚未就该词的含义达成一致。** Hugging Face在2026年5月发布了一个[术语表](https://huggingface.co/blog/agent-glossary?ref=hubwiz.com)，将harness与scaffold分开，并指出Claude Code、Codex等无论如何都称整个为harness。五层是一种分解。在更窄的定义下，上述一半证据在测量其他东西。选择一个词汇表，在团队内保持一致，并谨慎对待跨研究比较。（这就是为什么五层作为你自己设置的检查表出现：检查表在词汇变化中存活，而分类法与之争论。）**这对你意味着什么：** 在相信两个代理结果之间的差距之前，询问每个包含哪些层。
6. **且harness效果似乎随着模型改进而缩小。** [Harness-Bench](https://arxiv.org/abs/2605.27922?ref=hubwiz.com)报告更强的模型后端显示\*"更高的平均分数同时表现出更低的跨harness方差"\*。脚手架可能正在补偿即将消失的弱点。**这对你意味着什么：** 将你的harness工作集中在成本控制上（持久），而不是准确性上（下一个模型版本可能免费吸收）。

## 9、仓库

十个脚本，在[github.com/paoloap-py/agent-harness-guide](https://github.com/paoloap-py/agent-harness-guide?ref=hubwiz.com)：每层都有其保护，以及没有它的相同代理。每个都在你的机器上运行，无需API密钥，因为模型被替换为发出固定工具调用序列的脚本替身。你可以运行的差异胜过你必须信任的声明。

```bash
git clone https://github.com/paoloap-py/agent-harness-guide
cd agent-harness-guide

python3 run_all.py        # 所有五层，保护关闭然后开启，并排
python3 test_harness.py   # 断言上述所有差异，10个检查

```

README将五层作为审计检查表。对于每一个，问题相同：这是由代码强制执行，还是由提示中的一句话强制执行？任何你无法用文件路径勾选的框都是你希望的规则。

## 10、这让你处于什么位置

我的规则很好。它在错误的地方待了两周，代价是十五次违规，其中十一次在那个下午，以及我想要回来的一天。

移动规则是执行它的方式。文字从未改变。

这就是全部想法。你购买模型。你构建它周围的一切，而那一半决定了你的代理成本以及它做你未批准的事情的频率。两者都是工程工作。只有一个是你的。

---

原文链接：[The Complete Guide to Agent Harnesses (With Code)](https://medium.com/data-science-collective/the-complete-guide-to-agent-harnesses-with-code-6fa11cecd004?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处

> **这是「Agent / MCP」专题的一部分。**完整地图见：[Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/)