☰
只改Harness不换模型:提升LLM编码能力的工程实践
2026/10/24 14:10:24 网站建设 项目流程

在实际的 LLM Coding 评测和 Agent 开发中,有一个很反直觉的现象:模型权重完全没动,提示词也没改,只是把模型外层的执行脚手架从“单次生成答案”改成“生成代码 -> 运行测试 -> 返回报错 -> 让模型修改”的闭环,许多不同模型的编码指标会同时上升。标题里的 “Only the harness changed” 说的就是这个场景。harness 在这里不是模型参数,也不是 prompt 模板,而是包裹在模型之外,负责输入构造、工具调用、代码执行、结果反馈和重试策略的一整套工程系统。

这篇文章会围绕 harness 展开:先说明它是什么,为什么能影响 15 个模型的编码表现,再一步步实现一个最小可复现的 coding harness,并用它批量评估多个 LLM。适合正在做模型评测、编码 Agent、AI 编程工具链的工程师阅读。读完以后,你可以把这套方案复制到自己的项目里,用同一套 harness 横向对比不同模型,也能理解为什么“换模型不如换 harness”。

1. Harness 到底是什么:模型之外的执行与评测系统

1.1 从一个“模型集体变强”的现象说起

如果只看结果,很多人会以为某个新版本模型“一夜之间变强了”。但标题里的现象并不是模型参数升级,而是推理时的运行方式变了。可以这样理解:同样是做一张数学卷子,之前要求直接写答案,现在允许在草稿纸上演算、检查、改正,最后再上交。草稿纸没有改变考生大脑里的知识,但确实提高了最终得分。

LLM 也一样。同一个模型在同样的权重下,如果外部系统允许它先执行代码、看到报错、再修改答案,它的 coding 指标就可能明显提升。这就是 harness 的作用:它不是一个更大的模型,也不是更长的 prompt,而是把“推理一次”扩展成“推理、执行、反馈、再推理”的完整循环。

这里要特别说明:不同评测集、不同模型供应商、不同任务难度下,harness 带来的提升幅度并不一样。不要把这个现象理解成“任何模型都能被 harness 救活”。模型本身的能力仍然是上限,harness 负责把已经存在但未被充分利用的能力释放出来。

1.2 Harness 在 LLM Coding 场景中的技术定位

在 LLM coding 场景里,harness 通常包含几个核心部分:

  • 输入构造:把题目、代码模板、历史错误、测试结果组装成模型能理解的上下文。
  • 动作执行:让模型有机会运行代码、执行命令、读取文件,而不是只输出静态文本。
  • 环境反馈:把执行后的 stdout、stderr、退出码、测试断言结果返回给模型。
  • 迭代控制:决定最大尝试次数、何时停止、何时切换策略。
  • 结果评估:用隐藏测试、静态检查、人工规则判断最终答案是否真正正确。

模型、prompt、harness 是三个不同层面的东西。用一张表可以看得更清楚:

层面决定因素典型改动影响范围
模型权重和架构换模型、微调基础语言理解、代码生成能力
Prompt输入文本和示例改任务描述、Few-shot 示例模型对当前任务的理解方式
Harness外部执行和反馈循环增加测试执行、工具调用、重试模型能否利用执行结果自我修正

很多团队在优化 coding 效果时,第一反应是换更大模型或写更多 prompt 示例。实际上,如果模型生成的代码经常只差一个边界条件,harness 里加一轮“跑测试再修复”通常更便宜、更稳定。

1.3 Harness 与 Agent 的关系

Agent 是 harness 的高级形态,但二者不是一回事。最小可用的 coding harness 可以只有四步:生成、执行、反馈、重试。Agent 则在此基础上增加了更复杂的规划、记忆、多工具选择和长期目标维护。

换句话说,Agent 框架本质上是一套加强版 harness。理解 harness 的底层逻辑,再去看 LangChain、自研 Agent、MCP 等工具时,会更容易抓住核心:它们都在解决“模型如何观察外部世界、如何行动、如何根据反馈调整”的问题。

所以这篇文章先从一个最小 harness 开始。它不需要复杂的 Agent 框架,也能实验出“只改 harness 就能提升多个模型”的效果。

2. 为什么只改 Harness 就能提升多个 LLM 的编码能力

2.1 单次生成的盲区:模型看不到运行结果

不接 harness 时,LLM 编码是单向过程:用户给题目,模型给代码,结束。如果代码有语法错误、边界条件错误、逻辑缺陷,模型完全没有机会看见。它只能依赖训练时见过的相似问题模式来推断。

这种模式适合“短答案生成”,但不适合“程序必须可运行”的场景。一个函数能否通过所有测试用例,不取决于生成时看起来是否合理,而取决于在真实 Python 环境中的执行结果。模型无法运行代码,就无法获得这一关键信息。

2.2 反馈回路让“试错”成为推理的一部分

接入 harness 后,模型的每次错误都不是终点,而是下一步推理的输入。举个例子:模型第一次生成:

def two_sum(nums, target): for i in range(len(nums)): for j in range(len(nums)): if i != j and nums[i] + nums[j] == target: return [i, j]

用样例测试[2, 7, 11, 15], 9可以通过,但用边界测试[3, 2, 4], 6会失败,因为正确答案是[1, 2],而双重循环可能先返回[0, 0]这种非法组合。如果不执行测试,模型永远不知道这个问题。加了 harness 后,反馈信息里会包含断言失败:

assert res == [1, 2] failed: expected [1, 2], got [0, 0]

模型看到这个反馈后,通常会在下一次生成时修正逻辑:让内层循环从i + 1开始。这个过程非常接近人类工程师调试代码。

2.3 工具调用扩展了模型的信息边界

更强的 harness 不只是运行代码,还可以让模型读取文件、执行 shell 命令、查询 API 文档、搜索代码仓库。模型训练数据里没有的依赖版本、内部 API 签名、最新框架用法,都可以通过工具调用即时获取。

这也解释了为什么 15 个模型都能从 harness 改动中受益:它们不需要共享同一套秘密技巧,只需要共用同一个能执行、能反馈、能搜索的外部系统。每个模型都会根据自己的能力从反馈中提取信息,最终整体通过率上升。

2.4 更合理的评判标准:交付前先验证

很多模型在 benchmark 上得分高,但实际交付时仍然需要工程师修改。原因之一是评测只检查“模型输出是否看起来对”,而不检查“代码是否真的能运行通过”。

harness 改变了评判方式:不是模型生成完就交卷,而是生成后先进入沙箱执行测试,测试通过后才算完成。如果失败,就继续反馈和重试。这样最终产物是经过执行验证的代码,而不是一段没有运行过的字符串。对于 Coding 类任务,这种“执行优先”的评判思路,比单纯比较生成文本相似度可靠得多。

3. 动手实现一个最小可复现的 Coding Harness

3.1 场景与工作机制

我们要实现一个最小但有完整闭环的 harness。它接收一个编程题目和若干测试用例,调用任意 OpenAI 兼容接口的 LLM 生成 Python 代码,在本地沙箱中执行测试。如果测试失败,就把错误信息返回给模型,让模型继续修复,最多重试若干次。

技术栈选择:

  • Python 3.10 以上
  • openaiPython SDK,用于调用 OpenAI 兼容接口
  • PyYAML,用于读取配置文件
  • subprocess+tempfile,用于隔离运行模型生成的代码

学习环境下使用subprocess加超时已经足够。生产环境要换成 Docker 容器或云沙箱,不要直接在本机执行不受信任的模型代码。

3.2 环境准备与依赖安装

先创建虚拟环境并安装依赖:

python -m venv .venv source .venv/bin/activate pip install openai pyyaml

OpenAI 兼容接口的模型可以通过环境变量配置:

export OPENAI_API_KEY="替换为你的 API Key" export OPENAI_BASE_URL="https://api.deepseek.com/v1"

这里使用OPENAI_BASE_URL的好处是,可以平滑切换到任意兼容 OpenAI 协议的网关。模型名在配置文件中单独指定,例如deepseek-chat、qwen-plus、gpt-4o-mini等。实际运行时,要把密钥和域名替换成自己环境对应的值。

3.3 数据模型与配置文件

为了让评测可重复,先用配置文件管理模型参数和 sandbox 参数:

# config.yaml model: deepseek-chat temperature: 0.2 max_tokens: 2048 max_attempts: 3 sandbox_timeout: 10 test_timeout: 5 truncate_feedback: 2000

字段含义:

参数默认值示例作用
modeldeepseek-chat指定要调用的模型名
temperature0.2控制生成随机性,越低越稳定
max_tokens2048限制单次输出长度
max_attempts3允许模型修复代码的最大轮数
sandbox_timeout10每次运行测试脚本的超时时间,单位秒
truncate_feedback2000截断返回给模型的错误信息长度,避免上下文膨胀

任务文件用 JSON 保存题目和测试用例:

{ "id": "two-sum", "instruction": "实现函数 two_sum(nums, target),返回两个下标组成的列表,使两个数之和等于 target。", "entry_point": "two_sum", "tests": [ {"args": [[2, 7, 11, 15], 9], "expected": [0, 1]}, {"args": [[3, 2, 4], 6], "expected": [1, 2]}, {"args": [[0, 4, 3, 0], 0], "expected": [0, 3]} ] }

这里测试用例不仅仅是样例。[0, 4, 3, 0], 0这种边界测试会暴露“返回相同下标”的错误,是评测 harness 必须覆盖的场景。

3.4 核心代码实现

先定义数据类:

# harness.py import argparse import json import os import subprocess import sys import tempfile import textwrap from dataclasses import dataclass from openai import OpenAI import yaml @dataclass class TestCase: args: list expected: object @dataclass class Task: id: str instruction: str entry_point: str tests: list[TestCase] @dataclass class HarnessConfig: model: str temperature: float = 0.2 max_tokens: int = 2048 max_attempts: int = 3 sandbox_timeout: int = 10 test_timeout: int = 5 truncate_feedback: int = 2000

加载任务和配置:

def load_task(path: str) -> Task: with open(path, "r", encoding="utf-8") as f: data = json.load(f) tests = [TestCase(args=t["args"], expected=t["expected"]) for t in data["tests"]] return Task( id=data["id"], instruction=data["instruction"], entry_point=data["entry_point"], tests=tests, ) def load_config(path: str) -> HarnessConfig: with open(path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) return HarnessConfig(**data)

构造测试脚本。这里把用户代码和断言拼成一个独立脚本,通过subprocess执行:

def build_test_script(user_code: str, task: Task) -> str: parts = [user_code, ""] for idx, t in enumerate(task.tests): args_repr = ", ".join(json.dumps(arg) for arg in t.args) expected_repr = json.dumps(t.expected, ensure_ascii=False) parts.append(f"res = {task.entry_point}({args_repr})") parts.append( f"assert res == {expected_repr}, " f"'case {idx} failed: expected {expected_repr}, got ' + repr(res)" ) parts.append("print('PASS')") parts.append("print('ALL_PASS')") return "\n".join(parts) def run_tests(user_code: str, task: Task, timeout: int) -> dict: test_script = build_test_script(user_code, task) with tempfile.TemporaryDirectory() as tmpdir: try: proc = subprocess.run( [sys.executable, "-I", "-c", test_script], capture_output=True, text=True, timeout=timeout, cwd=tmpdir, ) except subprocess.TimeoutExpired: return { "passed": False, "stdout": "", "stderr": "TimeoutExpired: 代码运行超时", } if proc.returncode == 0 and "ALL_PASS" in proc.stdout: return {"passed": True, "stdout": proc.stdout, "stderr": proc.stderr} stderr = proc.stderr or proc.stdout return {"passed": False, "stdout": proc.stdout, "stderr": stderr}

从模型输出中提取代码。为保证兼容性,允许模型输出 Markdown 代码块:

import re def extract_code(raw_output: str) -> str: pattern = r"```(?:python)?\s*(.*?)```" match = re.search(pattern, raw_output, re.S) if match: return match.group(1).strip() return raw_output.strip()

核心重试循环:

def solve_task(client: OpenAI, config: HarnessConfig, task: Task) -> dict: messages = [ { "role": "system", "content": "你是一个 Python 编程助手。只输出可运行的 Python 代码,不要添加多余解释。", }, {"role": "user", "content": task.instruction}, ] attempts = [] for attempt in range(1, config.max_attempts + 1): resp = client.chat.completions.create( model=config.model, messages=messages, temperature=config.temperature, max_tokens=config.max_tokens, ) raw_output = resp.choices[0].message.content or "" code = extract_code(raw_output) result = run_tests(code, task, config.sandbox_timeout) attempts.append( { "attempt": attempt, "code": code, "passed": result["passed"], "stderr": result["stderr"][-config.truncate_feedback :], } ) if result["passed"]: return {"status": "solved", "attempts": attempts, "final_code": code} feedback = f"第 {attempt} 次运行失败,请修复代码:\n{result['stderr'][-config.truncate_feedback:]}" messages.append({"role": "assistant", "content": code}) messages.append({"role": "user", "content": feedback}) return {"status": "failed", "attempts": attempts, "final_code": None}

最后是命令行入口:

def main(): parser = argparse.ArgumentParser() parser.add_argument("--task", required=True, help="任务 JSON 文件路径") parser.add_argument("--config", default="config.yaml", help="harness 配置文件路径") args = parser.parse_args() config = load_config(args.config) task = load_task(args.task) client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) result = solve_task(client, config, task) print(json.dumps(result, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()

这段代码有几个关键点需要解释:

  • 模型输出先经过extract_code,再进入沙箱执行,避免模型在代码块外添加解释导致运行失败。
  • 每次失败后,把模型自己的代码和报错一起追加到对话历史中,让模型在同一上下文里继续修改。
  • truncate_feedback防止巨型 traceback 撑爆上下文。
  • -I参数让 Python 以隔离模式运行,忽略当前 PYTHONPATH 影响,降低环境干扰。

3.5 运行与验证

保存好config.yaml、task.json、harness.py后,执行:

python harness.py --task task.json --config config.yaml

如果一切正常,会输出类似下面的 JSON:

{ "status": "solved", "attempts": [ { "attempt": 1, "passed": false, "stderr": "assert res == [1, 2] failed: expected [1, 2], got [0, 0]" }, { "attempt": 2, "passed": true, "stderr": "" } ], "final_code": "def two_sum(nums, target):\n seen = {}\n for i, n in enumerate(nums):\n if target - n in seen:\n return [seen[target - n], i]\n seen[n] = i\n" }

注意,status是solved只代表模型通过了任务里配置的测试用例。它不能证明代码在所有情况下都正确。这个区分很重要,后面专门讲。

4. 用同一套 Harness 评估 15 个 LLM 的流程设计

4.1 为什么必须使用同一套 Harness

如果你要横向对比多个模型,必须让 harness 完全一致。因为不同的重试次数、不同的测试用例、不同的沙箱超时,都会直接影响最终得分。有些模型在 1 次尝试内表现不佳,但如果在 3 次尝试中能通过,最终排名就会不一样。

“只改 harness 就提升 15 个 LLM”的场景里,提升幅度并不只属于某一个模型。所有模型共享同一个改进后的执行反馈机制。如果你在评测时给 A 模型 5 次重试,给 B 模型 1 次重试,那结果没有可比性。

统一内容至少包括:

  • 同一个任务集和测试用例。
  • 同一个max_attempts。
  • 同一个temperature。
  • 同一个沙箱超时时间。
  • 同一个反馈截断长度。
  • 同一个系统提示词。

任何一项不一致,都应视为不同 harness,而不是不同模型的真实对比。

4.2 评测集与判定规则

评测集不能只有公开样例,还要包含隐藏测试用例。最简单的方式是像前面task.json一样,把每个任务拆成“展示给模型的 instruction”和“模型看不到的 tests”两部分。模型自己不能提前看到全部测试,否则它可能生成专门针对测试用例的硬编码。

更完整的评测集应该覆盖:

  • 基础功能:正常输入。
  • 边界条件:空数组、最小值、最大值、重复元素。
  • 异常输入:不符合题目约束的数据是否被合理处理。
  • 性能约束:大数据量下是否超时。

不同难度的题目混合在一起,能看出模型在不同复杂度下的表现差异。如果只有一个简单样例,15 个模型可能全都满分,无法区分 harness 的作用。

4.3 批量评估与指标输出

批量评估脚本可以循环遍历任务文件和模型列表。为了控制成本,建议把配置参数单独抽取出来:

python evaluate.py \ --task-dir tasks/ \ --models deepseek-chat qwen-plus gpt-4o-mini \ --config config.yaml \ --concurrency 4 \ --output report.jsonl

评估脚本内部可以这样组织:

def evaluate_one(client, config, task, model): result = solve_task(client, config, task) return { "task_id": task.id, "model": model, "status": result["status"], "attempt_count": len(result["attempts"]), "token_usage_estimate": estimate_tokens(result["attempts"]), } def evaluate_all(client, config, tasks, models): records = [] for model in models: for task in tasks: cfg = HarnessConfig(**{**config.__dict__, "model": model}) records.append(evaluate_one(client, cfg, task, model)) return records

收集完记录后,可以统计每个模型的首次通过率、最终通过率和平均尝试次数:

指标计算方式
首次通过率第 1 次尝试即通过的任务数 / 总任务数
最终通过率在max_attempts内通过的任务数 / 总任务数
平均尝试次数所有已通过任务的尝试次数平均值
平均耗时单任务从开始到结束的墙钟时间平均值

注意,这里展示的表格结构是通用指标设计,不是任何特定模型的真实分数。你在自己的评测集上得到的结果,可以作为内部选型依据。

4.4 成本与并发控制

批量评估 15 个模型时,token 消耗会迅速增加。每个失败尝试都会额外消耗一次模型调用和一次反馈上下文。控制成本可以从几方面入手:

  • 限制max_attempts,一般 3 到 5 次足够。
  • 对相同请求做缓存,相同模型、相同任务、相同重试不必重复调用。
  • 使用并发时控制请求速率,避免触发限流。
  • 记录每次调用的 prompt token 和 completion token,便于核算成本。

并发不是越高越好。限流和超时会导致评测结果不稳定,最终浪费更多重试。更稳妥的做法是先单线程跑通一个小样本,确认流程稳定后再加大并发。

5. Harness 关键参数与调优

5.1 重试次数、温度与最大 token

这几个参数直接影响评测结果和成本。

参数取值范围参考调小的影响调大的影响
max_attempts1 - 5成本低,但模型没有自纠机会成本高,可能在简单错误上反复打转
temperature0 - 0.7输出稳定,但容易重复相同错误探索更多,但可能引入随机错误
max_tokens512 - 4096代码较长时容易被截断输出更完整,但可能生成多余内容
sandbox_timeout5 - 30 秒快速失败,但误杀复杂计算减少误杀,但死循环会拖慢评测

在实际调优中,不要一开始就追求高重试次数。先把max_attempts设为 1,看模型在不自纠时的基线水平;再逐步增加到 3 和 5,观察通过率提升和成本增长曲线。如果 3 次到 5 次的通过率提升很小,说明继续增加重试的意义不大。

5.2 测试用例生成策略

测试用例来源有三种:

  1. 人工编写:质量最高,但覆盖不全,成本高。
  2. 从题目约束自动生成:用枚举、随机、边界值生成,覆盖性好。
  3. 让另一个 LLM 生成:速度快,但可能出现错误断言。

更推荐先人工写题目和核心边界,再用脚本补充随机测试。模型生成的测试用例可以作为补充,但必须经过人工审查。绝不能把 LLM 生成的测试直接当成 ground truth,否则可能出现“错误测试认定正确代码”的情况。

5.3 反馈信息的长度控制

多轮重试时,上下文会越来越长。如果每次都把完整 traceback 塞给模型,几轮之后 prompt 会膨胀,模型容易丢失最早的任务描述。

推荐做法:

  • 只保留最后一次异常的最后 1000 到 2000 字符。
  • 把重复出现的相同错误去重,避免模型反复看同一个 traceback。
  • 在反馈中明确告诉模型当前是第几次尝试,避免它生成“这是第一次”的回答。
  • 如果上下文仍然过长,可以把历史反馈压缩成“之前尝试过的修改摘要”。

5.4 沙箱安全与资源限制

模型生成的代码不可信。学习环境里用subprocess和超时只是为了快速验证思路,生产环境必须做更强的隔离。

生产环境至少做到:

  • 使用 Docker 容器运行模型代码,限制 CPU、内存和网络。
  • 设置timeout和内存上限,避免死循环和内存耗尽。
  • 模型代码没有网络访问权限,除非任务明确需要。
  • 所有执行结果先落盘,再决定是否返回给模型。
  • 不要用宿主机的高权限账号运行任意生成代码。

注意:如果你的 harness 会执行模型生成的代码,安全性是第一优先级。宁可牺牲一点执行速度,也不要让不可信代码直接接触宿主机。

6. 常见问题与排错路径

6.1 模型输出不是合法代码

现象:run_tests报错 NameError 或者无法解析。

可能原因:

  • 模型没有按“只输出代码”的要求返回。
  • 模型把代码放在 Markdown 代码块中,但extract_code没匹配到。
  • 模型返回了 JSON 包装的字符串。

检查方式:打印raw_output,确认模型实际返回内容。

处理建议:增强解析函数。支持去掉```python标记;如果模型返回 JSON,先尝试解析code字段;如果仍然失败,可以让模型输出固定格式,例如:

BEGIN_CODE ... END_CODE

6.2 同一个错误反复重试

现象:max_attempts用完,但每次错误相同。

可能原因:

  • 反馈信息被截断到完全没有错误细节。
  • temperature=0导致模型每次都输出相同结果。
  • 模型没有真正“看到”反馈,因为消息顺序或角色设置错误。

检查方式:查看attempts中每一轮返回给模型的stderr。

处理建议:把temperature提高到 0.2 到 0.4;确认反馈中保留最后一行错误类型和断言信息;如果错误信息过长,只保留后半段。

6.3 公开测试通过,但隐藏测试失败

现象:harness 输出solved,但人工复测时发现代码不正确。

可能原因:

  • 测试用例太少,模型恰好通过了所有用例。
  • 模型学会了针对测试样例硬编码。
  • 测试用例只有正常输入,没有边界。

检查方式:把模型生成的final_code放到更大的隐藏测试集上执行。

处理建议:评测集必须分离公开样例和隐藏测试。最终指标以隐藏测试为准;一次评测同时加入边界、随机、性能三类用例。

6.4 API 调用超时或限流

现象:批量评估时大量请求报错。

可能原因:

  • 并发过高,触发 API 限流。
  • 部分模型响应慢,超过客户端默认超时。
  • 网络抖动。

检查方式:查看client.chat.completions.create抛出的异常类型和错误码。

处理建议:加入指数退避重试;降低并发数;为每次调用设置合理 timeout;对相同请求做缓存,避免重复调用。

6.5 沙箱卡死与资源耗尽

现象:单个任务执行时间特别长,甚至整机变卡。

可能原因:

  • 模型生成死循环代码。
  • 代码创建超大列表或递归无终止。
  • subprocess的timeout没有覆盖到子进程内部再创建的子进程。

检查方式:检查 sandbox 日志,确认是否出现TimeoutExpired。

处理建议:在subprocess.run中设置严格 timeout;生产环境用 Docker 的--memory和--pids-limit限制资源;不要相信模型生成的代码天然安全。

排错路径可以按这个顺序走:

  1. 先检查输入:任务文件、测试数据、配置格式。
  2. 再检查解析:模型输出是否正确提取。
  3. 再检查执行:沙箱能否运行最简单的脚本。
  4. 再检查反馈:模型是否收到了完整报错。
  5. 再检查限制:超时、token、并发是否导致假失败。
  6. 最后检查评测集:测试用例本身是否可靠。

7. 生产环境 Harness 的最佳实践与扩展方向

7.1 从评测脚本走向在线编码 Agent

最小 harness 是评测工具,但它的核心结构可以直接扩展成生产级的编码 Agent。区别在于生产系统需要更多工程能力:

  • 支持多文件仓库:模型可以读取目录、打开文件、修改文件。
  • 支持工具协议:模型能调用格式检查、静态分析、测试运行、git 操作。
  • 支持持久记忆:保存模型已经尝试过的方案,避免重复犯错。
  • 支持人工介入:在自动修复多次失败后,把任务转给工程师处理。
  • 支持版本回滚:模型对代码库的所有修改都记录 diff,异常时可以回滚。

这些能力本质上都是“在模型外面加 harness”。模型还是那个模型,但外部系统越完善,它能完成的任务越复杂。

7.2 评测前检查清单

把下面这份清单当作可复用模板,每次开始新的评测前过一遍:

  • 测试用例是否包含隐藏用例,而不是只给模型公开样例。
  • 所有模型是否使用完全相同的max_attempts、温度、超时和系统提示词。
  • 反馈信息是否保留异常类型和关键断言,而不是只返回“失败”。
  • 错误信息是否做了长度截断,防止上下文膨胀。
  • 沙箱是否设置了超时、内存和 CPU 限制。
  • 是否记录每轮 attempt 的原始输出、最终代码和错误日志。
  • 是否固定依赖版本和模型版本,保证结果可复现。
  • 是否使用pass@k多次采样而不是只跑一次。
  • 是否区分“通过公开测试”和“通过隐藏测试”两个指标。
  • 是否把随机种子、并发数、API 端点写入实验记录。

这十项做完,你的评测结果才有横向对比价值。

7.3 扩展方向:MCP、RAG 与更丰富的工具层

再往前走,harness 可以连接 MCP 协议、向量检索、内部文档库和外部 API。模型在编码时不再只依靠训练记忆,而是能实时获取依赖文档、历史代码、团队规范和安全策略。

例如,harness 里增加一个search_code工具,模型在写某个函数前先搜索项目里已有的相似实现,再生成代码。另一个read_docs工具,让模型在不确定某个 SDK 参数时先查官方文档。这些能力都会影响编码结果,而它们仍然属于“模型之外的系统”。

学习建议是:先把这个最小 harness 跑通,再逐步加入一个工具、一个记忆模块,观察每次改动对多个模型的影响。你会慢慢理解为什么“只改 harness”能提升 15 个模型——因为 harness 改变的不是参数,而是模型与外部世界互动的方式。这种工程能力,比单纯追新模型版本更可控,也更值得长期投入。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询