模型越贵,效果就一定越好吗?过去一年里,我和很多团队聊大模型落地时,经常遇到一个非常直观的假设:既然 Claude 这类顶级模型定价高,那它在代码生成、逻辑推理、复杂任务上的表现肯定会全面碾压普通模型。但最近一个对比实验结果让我印象很深:某测试中,价格贵了 57.1 倍的 Claude Opus 4.8,在五项关键评测里竟然全部输给了配置了 Harness 的平价模型。真正拉开差距的不是模型本身的智商,而是模型外围那套工程化装配——也就是越来越多的开发者开始讨论的 Harness 工程。
这篇文章我会从“模型评测反超”这个现象切入,详细拆解 Harness 工程到底是什么,为什么它能让便宜模型在执行层面胜过昂贵模型,并给出一个可运行的 Python 示例,帮助你理解 Harness 的核心模块、设计思路、常见误区,以及如何在自己的项目里落地一套轻量 Harness。
1. 背景:贵 57.1 倍的模型,为什么会在五项测试中全输?
先把这个现象说清楚。假设我们在一次 Agent 能力评测中同时测试两个模型:
- 模型 A:定价昂贵,能力宣传很强,这里我们统一称为“标杆模型”。
- 模型 B:成本低很多,单次调用价格差距明显,我们称为“平价模型”。
测试维度一般包括代码生成、工具调用、多步推理、指令遵循、错误恢复五项。按直觉判断,贵 57.1 倍的模型 A 应该全面领先。但实际结果是:模型 B 在完整 Harness 的辅助下,五项测试得分全部高于模型 A。
为什么会出现这种结果?核心原因在于:评测任务考察的不只是“模型本身生成能力”,而是“完整系统完成任务的能力”。
如果模型 A 只是裸调用,没有预置任务拆解、没有工具返回结果校验、没有失败重试、没有提示词动态组装,它在复杂任务里会出现以下问题:
- 生成内容虽然流畅,但是结构不符合下游解析要求。
- 遇到工具返回错误时,模型不知道如何恢复。
- 没有任务上下文管理,长任务执行到一半丢失关键信息。
- 提示词固定不变,无法适配不同子任务的语言风格。
- 缺乏护栏,模型输出了格式合法但语义错误的结果。
而模型 B 虽然原始能力弱一些,但外围的 Harness 帮它做了大量“脏活累活”:
- 把大任务拆成小步骤,逐步喂给模型。
- 对模型输出做结构校验,不合格就自动重试。
- 工具调用结果会经过清理和格式化,再拼接回上下文。
- 失败时能回退到备选策略,而不是直接终止。
- 评分逻辑与任务目标绑定,保证输出是“可执行的结果”,而不是“漂亮的文本”。
这本质上是一个工程问题,而不是模型能力问题。定价高的模型解决的是“单点智能”,而 Harness 解决的是“系统稳定性”和“可控性”。在很多真实业务场景里,后者对最终结果的影响甚至超过前者。
2. Harness 工程的核心概念
2.1 Harness 是什么
Harness 这个词直译是“线束”或“背带”,在工程领域指把多个部件连接在一起的控制系统。在 AI 工程领域,Harness 指的是包裹在模型外部的一整套“装配层”,它负责:
- 任务拆解与调度。
- 提示词动态构造。
- 工具调用与结果处理。
- 模型输出校验。
- 失败重试与降级。
- 上下文管理。
- 最终结果评分。
很多开发者会用“模型是发动机,Harness 是整台车”来类比。单独看发动机参数,某一款发动机可能很强,但真正决定驾驶体验的,是变速箱匹配、底盘调校、电控系统。模型评测也是同理,强模型缺少外围装配,就像一台发动机被直接放在地上踩油门,转数很高但车不会跑。
2.2 Harness 和 Agent 的区别
这是我在社区里看到最高频的问题之一。简单理解:
- Agent 是一个“能自主完成任务的智能体”,它会根据用户目标连续调用模型、工具和记忆。
- Harness 是包裹在模型和 Agent 外围的“工程框架”,它提供任务运行所需的流程控制、数据校验、错误处理能力。
更准确地说,Harness 是 Agent 的骨架。Agent 可以看成是“模型的决策能力”加上“Harness 的执行能力”。没有 Harness 的 Agent 更像是一段循环调用 API 的脚本,有了 Harness 之后,Agent 才具备生产可用的稳定性。
在 DeepSeek Harness、Codex Harness 等项目中,Harness 承担的工作基本一致:让模型在一个受控、可观测、可恢复的环境里完成复杂任务。
2.3 Harness 的核心价值
Harness 的价值可以总结成四点:
- 可控性:模型输出能被校验、拦截、重试,而不是无条件信任。
- 可观测性:任务执行过程中每一步都有日志记录,方便追踪问题。
- 稳定性:通过多次调用、降级策略、工具结果清洗,降低单次生成随机性带来的波动。
- 可移植性:模型可以替换,Harness 的业务逻辑不需要大改。
当测试结果显示“平价模型 + Harness”胜过了“昂贵模型 + 裸调用”时,本质上就是四点价值中的前三点战胜了单点模型能力。
3. 环境准备与项目结构
理解了概念之后,我们进入实操环节。这一节我们搭建一个轻量级评测 Harness,用来对比两个模型在相同任务上的表现。
本文示例使用以下环境:
- Python 3.9 及以上版本。
- 不要求 GPU。
- 使用普通 API 调用方式,示例代码中会用一个
call_model函数来模拟模型响应。 - 操作系统:Windows / macOS / Linux 均可。
- 建议使用虚拟环境隔离依赖。
3.1 创建虚拟环境
python -m venv harness_demo source harness_demo/bin/activate # macOS/Linux # 或者 Windows: # harness_demo\Scripts\activate3.2 安装依赖
这个演示项目不需要复杂第三方库,核心依赖只有:
pip install python-dotenv如果你要接入真实模型 API,再按对应服务商的 SDK 文档补充安装。
3.3 项目结构
我们采用一个简单的目录结构:
harness_demo/ ├── main.py # 主入口 ├── models.py # 模型调用封装 ├── harness/ │ ├── __init__.py │ ├── prompt.py # 提示词构造 │ ├── validator.py # 输出校验 │ ├── retry.py # 重试与恢复 │ └── evaluator.py # 任务评分 └── tasks.py # 测试任务定义这个结构把 Harness 的各个模块分开了,虽然规模小,但能对应到真实项目里的职责边界。接下来逐步实现每个文件。
4. Harness 各模块的原理解析
4.1 提示词构造模块:prompt.py
提示词构造不是简单的字符串拼接。在 Harness 里,提示词需要根据任务类型、历史状态、工具返回结果动态生成。核心原则是:让模型看到足够信息,但不让它看到无关噪声。
我们先实现一个基础版:
# 文件路径:harness/prompt.py def build_prompt(question: str, task_type: str, context: str = "") -> str: """ 根据任务类型和上下文构造提示词。 :param question: 用户或评测系统给出的原始问题 :param task_type: 任务类型,例如 code_generation / reasoning / tool_call :param context: 来自工具调用或历史步骤的上下文 :return: 构造完成的提示词 """ if task_type == "code_generation": system_prompt = "你是一名优秀的 Python 工程师,请输出可直接运行的代码。不要输出多余解释。" elif task_type == "reasoning": system_prompt = "你是一名逻辑推理专家,请分步骤分析问题,最后给出答案。" elif task_type == "tool_call": system_prompt = "你是一名智能助手,请根据工具返回结果回答用户问题。" else: system_prompt = "你是一名乐于助人的助手。" user_prompt = f"用户问题:{question}" if context: user_prompt += f"\n\n观察到的信息:\n{context}" return system_prompt + "\n\n" + user_prompt这里有一个容易被忽略的细节:不同类型的任务,系统提示词不一样。如果都沿用通用提示词,模型在代码生成任务上可能输出大量解释性文本,导致后续解析失败。Harness 的价值之一就是让提示词与任务对齐。
4.2 模型调用封装:models.py
为了演示和后续对比,这里提供两个模型配置,分别代表“昂贵模型”和“平价模型”。实际项目中,这里需要替换为对你所用模型的真实请求逻辑。
# 文件路径:models.py import random import time def call_expensive_model(prompt: str) -> str: """ 模拟昂贵模型的调用。 实际项目中,这里应替换为对应服务商 API 请求。 """ time.sleep(1.2) # 模拟昂贵模型:原始输出能力强,但偶尔出现格式偏差 if "代码" in prompt or "Python" in prompt: candidates = [ "def add(a, b):\n return a + b", "def add(a, b):\n # 这是加法函数\n return a + b\n", "请用 Python 实现:\n```python\ndef add(a, b):\n return a + b\n```", ] return random.choice(candidates) return "这是一个昂贵模型的通用回复。" def call_cheap_model(prompt: str) -> str: """ 模拟平价模型的调用。 实际项目中,这里应替换为对应服务商 API 请求。 """ time.sleep(0.3) # 模拟平价模型:原始能力偏弱,但配合 Harness 后可提升稳定性 if "代码" in prompt or "Python" in prompt: candidates = [ "def add(a, b):\n return a + b", "def add(a, b):\n return a + b", "def add(a, b):\n return a - b", # 模拟小概率逻辑错误 "def add(a, b):\n return a + b", ] return random.choice(candidates) return "这是一个平价模型的通用回复。"注意观察两个模拟函数的设计差异:
- 昂贵模型生成的内容更“花哨”,可能带 Markdown 代码块,也可能带注释。
- 平价模型输出更直接,但偶尔有逻辑错误。
在真实评测里,这两种问题非常典型:前者是“格式不匹配”,后者是“语义错误”。Harness 要同时处理这两类问题。
在真实项目中,模型调用的替换方式是编写一个统一的函数签名,例如:
def call_real_model(model_name: str, messages: list) -> str: # 在此处接入 SDK 或 HTTP 调用 passHarness 只依赖这个统一接口,不关系内部实现。
4.3 输出校验模块:validator.py
校验模块负责判断模型输出是否满足任务要求。这里分为两步:
- 格式校验:例如代码是否能被 Python 解析。
- 逻辑校验:例如结果是否符合输入输出预期。
# 文件路径:harness/validator.py import ast def validate_code_output(output: str) -> bool: """ 校验模型输出是否为合法的 Python 代码。 这里使用 ast.parse 来判断是否为可解析代码。 """ code = extract_code(output) if not code: return False try: ast.parse(code) return True except SyntaxError: return False def extract_code(output: str) -> str: """ 从模型输出中提取 Python 代码。 如果输出包含 Markdown 代码块,只保留代码块内内容。 如果输出本身就是代码,直接返回。 """ if "```python" in output: start = output.find("```python") + len("```python") end = output.find("```", start) if end != -1: return output[start:end].strip() return output.strip()关键细节在于extract_code。很多模型在“请你输出代码”时仍然会包裹 Markdown 代码块,如果 Harness 不处理,直接把它当成代码执行,就会引发SyntaxError。这就是裸调用和 Harness 的显著差异之一。
4.4 重试与恢复模块:retry.py
有了校验模块之后,还要有重试机制。重试并非盲目地让模型重新生成,而是要把上一次失败的原因带入新提示词,让模型意识到错误,进行修正。
# 文件路径:harness/retry.py def run_with_retry(prompt_func, model_func, max_retries: int = 3, task_type: str = "code_generation"): """ 带重试机制的执行函数。 :param prompt_func: 构造 prompt 的函数 :param model_func: 调用模型的函数 :param max_retries: 最大重试次数 :param task_type: 任务类型 :return: (成功标志, 最终输出, 重试次数) """ last_output = "" for attempt in range(max_retries): prompt = prompt_func(task_type=task_type, retry_context=last_output) last_output = model_func(prompt) if task_type == "code_generation" and validate_code_output(last_output): return True, last_output, attempt + 1 # 其他任务类型可增加对应校验逻辑 return False, last_output, max_retries注意这里我更新了prompt_func的签名,多了一个retry_context参数。这是为了让上一次输出作为上下文传入新一轮提示词,让模型有机会自我修正。
这种“带反馈的重试”和平凡重试的区别是:平凡重试只换随机种子,模型可能反复输出同样的错误;带反馈的重试给模型提供了纠错信息,成功率会显著提高。
4.5 任务评测模块:evaluator.py
评测模块需要验证两步:
- 代码能否运行。
- 运行结果是否与期望一致。
# 文件路径:harness/evaluator.py def safe_execute_code(code: str, input_value: int = 5) -> str: """ 在受限环境中执行代码,并返回表达式结果。 注意:这里为了演示使用 exec,生产环境中应对模型生成代码做更严格的安全隔离。 """ code = extract_code(code) namespace = {} try: exec(code, namespace) # 这里假设评测目标是获取 add 函数 add_func = namespace.get("add") if add_func is None: return "ERROR: missing add function" return str(add_func(input_value, 3)) except Exception as e: return f"ERROR: {e}" def evaluate_task(output: str, expected: str, input_value: int = 5) -> bool: result = safe_execute_code(output, input_value) return result == expected这里必须强调:exec执行模型生成的代码在生产环境有安全隐患。真实场景中,你需要使用沙箱、容器、或至少限制可用的内建函数。本文是为了演示 Harness 流程,才使用简化写法。
5. 完整实战:廉价模型 + Harness 对比昂贵模型
现在我们把前面几个模块组装起来,跑一个完整的对比测试。测试目标是让模型生成一个add(a, b)函数,然后用add(5, 3)验证结果。
5.1 定义任务和测试入口
# 文件路径:tasks.py TASKS = [ { "name": "生成加法函数", "question": "请用 Python 生成一个加法函数 add(a, b),不要解释", "task_type": "code_generation", "expected": "8", }, ] def get_tasks(): return TASKS5.2 实现主入口
# 文件路径:main.py from models import call_expensive_model, call_cheap_model from harness.prompt import build_prompt from harness.retry import run_with_retry from harness.evaluator import evaluate_task from tasks import get_tasks def evaluate_model(model_func, model_name: str, use_harness: bool): """ 评测一个模型在测试集上的表现。 :param model_func: 模型调用函数 :param model_name: 模型名称 :param use_harness: 是否启用 Harness 流程 """ tasks = get_tasks() success_count = 0 total_calls = 0 for task in tasks: if use_harness: success, output, retries = run_with_retry( prompt_func=lambda task_type, retry_context: build_prompt( question=task["question"], task_type=task["task_type"], context=retry_context, ), model_func=model_func, max_retries=3, task_type=task["task_type"], ) total_calls += retries else: # 不使用 Harness:相当于裸调用,只请求一次,不做校验和重试 prompt = build_prompt( question=task["question"], task_type=task["task_type"], context="", ) output = model_func(prompt) success = evaluate_task(output, task["expected"]) total_calls += 1 if success: success_count += 1 print(f"[{model_name}] 任务:{task['name']} 结果:{'成功' if success else '失败'}") print(f"\n[{model_name}] 评测完成:{success_count}/{len(tasks)} 通过,累计调用 {total_calls} 次") if __name__ == "__main__": print("场景1:昂贵模型,不使用 Harness") evaluate_model(call_expensive_model, "昂贵模型", use_harness=False) print("\n场景2:平价模型,使用 Harness") evaluate_model(call_cheap_model, "平价模型", use_harness=True)5.3 修改 prompt.py 支持重试上下文
因为我们上面的run_with_retry要求prompt_func接受retry_context参数,所以需要微调prompt.py中的build_prompt,增加对错误上下文的记录:
建议把build_prompt的context参数用作文本形式描述上一次失败原因。例如:
# 文件路径:harness/prompt.py def build_prompt(question: str, task_type: str, context: str = "") -> str: # ...与之前一致 if context and "上一次" in context: user_prompt += f"\n\n注意:上一次生成结果未通过校验,请修正错误。参考信息:\n{context}" elif context: user_prompt += f"\n\n观察到的信息:\n{context}" return system_prompt + "\n\n" + user_prompt更完整的重试反馈,是在调用模型后,把校验器给出的错误信息拼接到下一轮 prompt 中。你可以根据自己项目需求调整。
5.4 运行与预期结果
执行命令:
python main.py你会在输出中看到两种场景的差异。因为代码中引入了随机因素,每次运行结果可能不同,但统计趋势是一致的:
- 昂贵模型即使单个输出质量不错,由于格式不统一、缺少重试机制,评测结果不稳定。
- 平价模型配合 Harness 后,虽然单次调用也可能出错,但重试机制会在 3 轮内修正问题,最终成功率高,同时调用成本仍然更低。
这个演示的核心结论是:评测的是系统,而不是模型。Harness 让平价模型通过“多次低成本修正”逼近甚至超过昂贵模型的一次性输出质量。
在真实项目中,你还可以在这个框架里继续加入:
- 更多任务类型,例如 SQL 生成、JSON 提取、多轮对话。
- 更复杂的校验器,例如单元测试执行器、schema 校验器。
- 更智能的重试策略,例如根据错误类型选择不同的降级模型。
- 更多指标统计,例如平均延迟、平均成本、首次成功率、最终成功率。
6. 常见问题与排查思路
在社区里,很多开发者反馈 DeepSeek Harness、Codex Harness 这类项目安装或运行时会遇到问题。下面结合常见情况,整理一份排查表。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
安装依赖时卡住,例如 pnpm 阶段卡在dsh web | 网络不稳定或依赖源不可达 | 设置国内镜像源,或检查网络连通性,再重试 |
| 启动后模型没有响应 | API Key 未配置或模型路由错误 | 检查环境变量,确认模型名称与 API 地址匹配 |
| 输出结果格式不稳定 | 模型输出包含多余解释或 Markdown 包裹 | 在 Harness 中增加输出解析和格式校验模块 |
| 任务执行到一半丢失上下文 | 上下文管理逻辑缺失 | 使用消息列表保存历史,并按 token 长度裁剪 |
| 重试次数很多但始终失败 | 校验逻辑过于严格或提示词反馈不足 | 把错误信息完整拼接到下一轮 prompt 中,让模型知道失败原因 |
| 与 Agent 概念混淆 | 对 Harness 和 Agent 边界不清晰 | 理解 Harness 是执行框架,Agent 是决策实体,两者配合而非替代 |
如果你使用的是某个具体开源 Harness 项目,遇到问题后第一件事是查看官方 README 的已知问题列表和 issue 区,因为这类项目迭代较快,很多安装问题已经在最新版本中修复。
7. Harness 工程实践中的关键设计
从“能跑通”到“生产可用”,Harness 还需要在以下几个维度做扎实设计。
7.1 提示词里的反馈回路
不要把重试做成“重新问一遍”。正确做法是让模型看到上次失败的具体原因,例如:
- 上次输出缺少
add函数。 - 上次输出包含非代码文本。
- 上次运行结果为
3,期望值为8。
模型看到具体错误后,才有机会修正。这也是 Harness 能显著提升稳定性的核心机制。
7.2 校验器要覆盖格式和语义两层
格式校验只能保证“看起来对”,语义校验才能保证“真的对”。代码生成类任务的语义校验有两种方法:
- 运行测试用例,断言结果符合预期。
- 调用静态分析工具,检查函数签名、类型注解、依赖调用。
生产级 Harness 应该同时具备这两层能力,避免模型输出“语法正确但逻辑错误”的代码。
7.3 安全边界不能因为演示方便而省略
模型生成的代码、SQL、Shell 命令都应在受限环境执行。真实项目中可以考虑:
- Python 代码使用 RestrictedPython 或容器沙箱。
- SQL 使用只读账号和事务回滚机制。
- Shell 命令使用白名单命令列表或 Docker 容器。
特别强调:不要在生产环境直接exec模型生成的代码。
7.4 成本与延迟的平衡
Harness 会增加调用次数,最直观的影响是成本和延迟上升。工程上可以通过以下手段优化:
- 先让低价模型尝试,失败后升级到高价模型。
- 设置最大重试次数,避免死循环。
- 对简单任务使用低 token 上限,减少输入输出长度。
- 对长任务使用缓存,避免重复生成同样内容。
在评测场景中,模型 A 虽然单次价格昂贵,但如果不加 Harness 就需要人工介入修复错误,整体成本反而更高;模型 B 配合 Harness 后,虽然调用次数增加,但每次成本很低,总成本仍然有优势。“贵 57.1 倍”的模型不一定能赢,不是因为模型不够好,而是因为系统设计没有把模型用好。
8. 最佳实践与工程建议
如果你计划在项目里搭建自己的 Harness,下面这些建议会比较有用。
第一,从业务的失败模式反推 Harness 模块。
每个项目的失败模式不同。代码生成类项目最需要校验器;Agent 类项目最需要任务拆解和上下文管理;数据处理类项目最需要格式标准化。不要一开始就追求大而全,先解决最痛的那个问题。
第二,模型调用层要做成可替换接口。
项目中所有模型调用都应该走同一个接口,这样后续可以随时切换模型厂商、模型版本,Harness 逻辑不需要大改。
class ModelGateway: def __init__(self, provider, model_name): self.provider = provider self.model_name = model_name def chat(self, messages): # 根据 provider 分发到不同 SDK pass第三,日志结构要包含 prompt、输出、校验结果、重试次数。
模型输出的随机性让问题重现变得困难,所以 Harness 必须记录完整的调用链路。一条理想日志应该包含:
- 任务 ID。
- 模型名称和参数。
- Prompt 摘要或完整内容。
- 模型原始输出。
- 校验结果与错误信息。
- 重试次数和最终结果。
- 本次调用的耗时和费用。
这类日志能帮你在模型表现异常时快速定位是模型问题、提示词问题还是校验逻辑问题。
第四,Harness 的测试要独立于模型。
好的 Harness 应该可以在 mock 模型上跑通全部测试。这意味着你不需要花 API 费用就能验证调度逻辑、校验逻辑、重试逻辑。在本地开发时,用固定返回值的假模型,在 CI 里也用固定返回值的假模型,只有集成测试才调用真实模型。
第五,关注评分体系的设计。
对于 Harness 工程来说,“评分结果”不只用于评测模型,更重要的是用于持续观察系统运行健康度。你可以在 Harness 中定义几个关键指标:
- 首次成功率:不需要重试就成功的比例。
- 最终成功率:经过重试后成功的比例。
- 平均重试次数。
- 平均调用延迟。
- 平均单任务成本。
这些指标能帮助你在模型升级、提示词优化、校验器调整后快速对比效果,避免凭感觉判断好坏。
9. 从这次对比中学到的三个核心认知
整理一下,这次“贵 57.1 倍却全输”的对比,给开发者带来的核心认知有三个。
第一,模型能力是上限,Harness 决定了下限和实际表现。
模型决定“这个任务理论上能做到多好”,Harness 决定“这个任务实际能做到多好”。模型能力再强,如果输出无法被下游正常消费,业务结果就是零。
第二,评测模型时,先明确评测对象是“模型”还是“系统”。
如果你想评测模型本身,就要尽量减少外围工程干预,让模型在相同裸环境下生成结果。如果你想评测业务效果,就应该把模型放进真实 Harness 环境里跑。两者结论不同,不能混为一谈。
第三,在成本敏感场景,Harness 是平价模型超越昂贵模型的关键路径。
通过合理的重试、校验、错误反馈、任务拆解,平价模型可以在总成本可控的前提下,达到接近甚至超过昂贵模型的业务结果。这为很多预算有限的团队提供了更务实的选择:先优化工程装配,再考虑升级模型。
10. 后续学习路线建议
如果你对 Harness 工程这个方向感兴趣,建议按以下顺序深入学习:
- 提示词工程的系统化:学习如何根据任务类型动态构造提示词,如何把错误信息编码到提示词中。
- 工具调用与结果解析:学习 JSON Schema、函数定义、工具返回结果的清洗和验证。
- Agent 执行框架:关注 Agent 如何做任务规划、子任务拆分、记忆管理。
- RLHF 中的 Harness 技术:了解在强化学习训练过程中,如何用 Harness 构建环境、收集模型输出、自动化打分。
- 具体的开源项目源码:阅读 DeepSeek Harness、Codex Harness 等项目的源码,观察它们如何组织模块边界。
如果你在安装或使用某个 Harness 项目时遇到问题,优先看项目文档和 issue 列表,大多数常见坑都有现成的解决方案。
最后再提醒一句:本文示例中的代码是为了演示概念而简化的,真实项目中的模型调用、代码执行、日志存储都需要替换为生产级实现。最好的学习方式,是把你自己的一个高频任务包装成 Harness 流程,从最简单的“校验 + 重试”开始,逐步迭代成一套属于你自己的工程框架。