ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

评测的隐形枷锁:Harness如何限制模型推理能力?

评测的隐形枷锁:Harness如何限制模型推理能力? 当 Claude Opus 5 在 ARC-AGI-3 上通关的消息传出后AI 社区又掀起一轮关于“模型能力是否已经接近通用推理”的讨论。但真正跑过评估、做过模型评测的人会清楚Benchmark 分数从来不是模型独力得出的它是由“模型”和“测试脚手架”共同产出的。这里的“测试脚手架”就是 Harness。Harness 的原意是线束或测试夹具在 AI 工程里它指包裹在模型外层负责输入输出、工具调用、状态管理、评分记录的整套代码和配置。Harness 本应像安全带一样保护模型测试和部署过程但在越来越多将模型封装进复杂环境的项目中它正悄然变成一根“捆住模型的绳子”。尤其当模型本身变得更强以后固定的 prompt 模板、先验的工具白名单、刚性的停止条件和脆弱的判分器反而成为模型能力释放的天花板。这里不争论 Opus 5 的分数是否真实也不去对比各家模型而是围绕 ARC-AGI-3 场景拆解 Harness 的工作原理、典型设计决策以及它为什么会反过来限制模型。1. ARC-AGI-3 和 Harness先厘清这两个容易混淆的概念1.1 ARC-AGI-3 到底在测什么ARC-AGIAbstraction and Reasoning Corpus for AGI系列基准的目标是评估模型的抽象推理能力。它不像传统的问答或代码生成任务那样依赖大量知识记忆而是通过一组组可拼图式的小任务考察模型能否从少量示例中提取规则并推广到新形状、新空间关系和新变换上。ARC-AGI-3 是该系列的进阶版本。从公开的信息来看它增加了更多未见过的任务类型减少示例数量并要求模型在更复杂的环境中完成组合式推理。这类任务恰好是“查表式模型”最难应对的类型因此也成为衡量模型是否具有真正泛化能力的重要试金石。在实际评估中模型不会直接看到一张“任务图”而是通过 JSON 字符串接收任务描述、输入输出示例和当前测试输入。模型需要输出一个答案网格然后由判分程序比较模型输出与标准答案是否一致。整个过程中模型如何理解任务描述、如何组织推理、是否能够多步尝试都由 Harness 的程序逻辑决定。1.2 Harness 在 AI 工程里扮演什么角色Harness 这个词在 AI 领域有几种常见用法评估型 Harness负责把一个 benchmark 数据集分批发送给模型收集输出计算准确率或匹配度。代理型 Harness负责管理模型与外部工具之间的循环例如调用代码解释器、搜索接口、文件系统等。训练型 Harness在强化学习或评估过程中通过环境交互驱动策略更新。在 ARC-AGI-3 这类评测场景中Harness 通常指“评估型 Harness”。它可以很简单也可以非常复杂。简单版本是循环遍历任务拼接 prompt调用模型得到输出对比答案。复杂版本是模型可以在多个步骤中调用工具访问一个模拟世界修改一个状态网格再继续推理。正因为 Harness 在“模型”和“结果”之间担任了中间层它本身就携带了大量设计选择。比如同样是 Opus 5不同评估团队可能用不同的工具集、不同的停止条件、不同的错误处理方式最终分数可能相差很大。这不是模型能力变了而是 Harness 变了。1.3 为什么说“分数是模型和 Harness 共同决定的”如果把模型比作一位棋手Harness 就是棋手面前的棋盘、计时器和规则手册。棋手水平再高如果规则手册不允许他使用某种合法的战术或者计时器在关键时刻强制停止他的比赛成绩就会低于真实水平。更麻烦的是Harness 中的很多选择不会写进论文或报告。比如同样一个任务有的 Harness 允许模型输出失败后自动重试五次有的 Harness 只允许一次有的 Harness 会保留模型完整的多轮推理历史有的则在每轮之间强制截断。这些差异对结果影响很大但读者在排行榜上看到的只是一个最终分数。因此当讨论“Opus 5 通关 ARC-AGI-3”时真正值得讨论的不仅是模型本身会不会推理还包括它是在哪种 Harness 中推理的。如果 Harness 设计得好模型的能力能够得到充分发挥如果设计得不好Harness 就会从测试工具变成限制模型发挥的“绳子”。2. 一个典型评估 Harness 的解剖从请求到评分经历了什么2.1 Harness 的核心模块一个可工作的评估 Harness 至少包含以下模块。这里的“模块”是指代码层面的组件而非固定目录结构。任务加载器读取数据集的 JSON 文件将任务拆分为单个 episode。Prompt 构建器把任务描述、示例输入输出、当前输入、系统提示说明组合成模型输入。模型客户端负责调用模型接口处理重试、超时、token 统计和异常。输出解析器将模型生成的文本或结构化输出转换为可比较的答案对象。状态管理器如果是多步推理需要保存当前世界状态、工具调用结果、消息历史。判分器比较模型答案和期望答案输出正确或错误同时记录错误信息。日志记录器保存每个 episode 的 prompt、输出、中间步骤、耗时、token 消耗和评分结果。下面是一份简化到只剩核心逻辑的 Python 示例。它展示的是单轮推理不包含工具调用但足够说明 Harness 的基本骨架。import json from dataclasses import dataclass dataclass class EpisodeResult: task_id: str correct: bool model_output: str expected: str reason: str class SimpleHarness: def __init__(self, model_client, prompt_builder, parser, judge): self.model_client model_client self.prompt_builder prompt_builder self.parser parser self.judge judge def run(self, dataset_path): with open(dataset_path, r, encodingutf-8) as f: tasks json.load(f) results [] for task in tasks: prompt self.prompt_builder.build(task) raw_output self.model_client.generate(prompt) parsed_answer self.parser.parse(raw_output) correct, reason self.judge(parsed_answer, task[answer]) results.append(EpisodeResult( task_idtask[id], correctcorrect, model_outputraw_output, expectedtask[answer], reasonreason )) return results这段代码展示了评估 Harness 的最小闭环。prompt_builder负责把任务信息映射成模型输入model_client负责外部通信parser负责把自然语言转为结构化答案judge负责判定正误。任何一步都可能引入偏差后面会重点展开。如果加入多步推理Harness 还需要一个循环通常长这样while step max_steps: output model_client.generate(current_prompt) action parser.parse_action(output) if action.type final_answer: break observation self.env.step(action) current_prompt self.prompt_builder.append_observation( current_prompt, action, observation ) step 1这里的max_steps、env.step的封装逻辑以及append_observation的上下文策略都直接决定模型能否完成复杂推理。2.2 参数说明表在配置一个评估 Harness 时常见的参数及其影响如下表所示。参数含义典型值调大的影响调小的影响max_steps允许模型执行的最大推理步数5-20模型有更多机会修正错误但可能导致循环和成本上升推理不够充分容易提前失败max_tokens单次生成的最大 token 数2048-8192能容纳更长推理但可能被无意义内容填满长推理会被截断答案不完整temperature采样温度0-0.2增加探索结果不稳定输出稳定但可能缺乏多样性context_window 截断策略保留历史消息的方式最近 N 条保留更多上下文但容易超过窗口上下文被裁剪模型丢失关键信息retry_times模型调用失败后重试次数2-3减少网络抖动影响但会拉长运行时间偶发失败直接浪费正确样本timeout单次调用超时时间60-300 秒更宽容但整体耗时增加长思考过程被强制中断parse_strict是否严格要求结构化输出true/false解析成功率高但可能要求模型严格遵循格式允许自由格式但解析失败率上升实际项目里可以建一个harness_config.yaml来管理这些参数。不要把参数硬编码在代码中否则换一个数据集或换一个模型时无法快速对比。harness: max_steps: 10 max_tokens: 4096 temperature: 0.0 retry_times: 3 timeout: 120 context_policy: keep_all judge_mode: semantic tools: - python_repl - file_search - calculator2.3 运行验证和预期输出一个 Harness 完成后不能直接拿大模型跑整个 benchmark。应该先用一个假模型验证全链路。假模型可以是一个固定返回特定 JSON 的服务也可以是一个本地小模型。目的是确认任务加载正确prompt 构建符合预期解析器能处理格式判分器能给出正确对比结果。预期输出类似下面这样Task abc123: correct Task abc124: wrong model_output: [[1,2],[3,4]] expected: [[1,2],[3,5]] reason: answer grid value mismatch at row 1, col 1 Accuracy: 0.50如果跑到这里发现parse抛异常或者日志里缺少关键字段说明 Harness 本身还没有达到可评估状态。此时不要急着上正式模型先修 Harness。3. 当 Harness 从支撑变成绳子七种常见的“捆绑模式”3.1 固定 Prompt 模板剥夺了模型的表达自由度很多评估 Harness 会使用同一个 prompt 模板处理所有任务。模板内部只变动任务数据不变化提示策略。这在早期模型能力较弱时是合理的因为固定模板能减少方差让比较更有意义。但 ARC-AGI-3 这类任务本身就是高度多样化的。有的任务需要模型先识别网格对称性有的需要理解颜色映射有的需要按行列变换规则生成输出。如果 Harness 强行要求模型“必须逐步思考”对某些任务而言反而会把模型引导到错误的思维路径。更好的做法是支持分层 promptHarness 提供一个默认模板同时允许为不同任务类别加载不同模板或者允许模型在第一次尝试后基于反馈重新形成自己的提示。把 prompt 设计的主导权从“代码”慢慢移交给“模型”。3.2 白名单工具集限制了模型调用新能力在多步推理 Harness 中工具列表往往被写死。模型只能调用python_repl、calculator、web_search这些预设工具。如果模型在学习过程中产生了调用“表格可视化工具”或“形状变换推演器”的意图Harness 会直接报错模型只能被迫放弃。这个问题的本质是工具集是 Harness 定义的能力边界不是模型定义的能力边界。当模型已经具备在更广工具集中选择策略的潜力但 harness 不给它暴露工具入口分数自然上不去。一种缓解方案是工具注册表。Harness 通过注册表暴露工具并支持在特定策略文件里显式启用或禁用。这样不同实验可以快速对比“有无某个工具”对结果的影响。同时最好的 Harness 应该允许模型在受限环境中“申请”新工具由人工审核后动态加入。3.3 停止条件设置不当模型没机会自我修正多数 Harness 用max_steps或“输出中出现 final_answer 标记”作为停止条件。问题在于这两者都可能过早切断推理。例如模型在第五步发现前面的推理有误知道需要重新推导但 Harness 的最大步数是 5。它只能要么给出一个错误答案要么因为时间耗尽而返回空结果。另一个常见现象是模型在final_answerJSON 中写错了字段名Harness 解析不到答案于是直接判为失败而不是给模型一次重新格式化的机会。建议在设计 Harness 时区分“正常停止”和“异常终止”。正常停止由模型主动发出 final 信号异常终止则应该触发修复循环比如要求模型重新输出结构化内容而不是直接判负。对 ARC-AGI-3 这类推理任务给模型 10 步以上的推理预算比 2-3 步更有价值但要注意防止死循环。3.4 判分器只认标准答案不接受等价结果ARC-AGI 的标准答案是一张输出网格。大多数情况下网格必须精确匹配。但是部分任务可能允许多个等价答案例如旋转后的对称网格、颜色映射的不同解释、多个合理补全。如果判分器把所有“不一致”一律归为错误模型的推理能力就被低估了。好的判分器应当支持多层判断先做严格匹配若不匹配再用规则判断是否等价仍然不确定时可以引入一个独立的 LLM judge 或人工抽样复核。同时也要记录 fail 原因便于后续统计哪些任务存在多解歧义。3.5 上下文裁剪把关键推理过程提前丢掉了长任务中模型可能已经在前面的步骤里推导出一个重要结论并写在了对话历史中。如果 Harness 为了控制 token 成本把旧消息截断模型后续就无法访问先前结论。很多框架默认只保留最近 N 轮消息。这在普通对话里没问题但在需要多步推理、环境状态反馈的任务里历史消息本身就是模型的“工作记忆”。建议不要简单截断而是做摘要每轮结束后将已经完成的关键操作和结论压缩成一段摘要代替删除历史。这样既节省 token又不丢失信息。3.6 固定采样参数让模型在“稳”和“活”之间被迫选择为了评估的可复现性多数 Harness 会把 temperature 设为 0。这对需要确定性输出的 benchmark 是合理的。但 ARC-AGI-3 中的抽象推理任务往往需要一定的探索和假设验证temperature0 可能让模型陷入高概率但不正确的解释中。更合理的方法是对简单任务使用低温度对复杂任务或重试轮次使用稍高的温度并对比多次采样的稳定性。Harness 可以记录每次采样的 seed 和 temperature让下游分析知道哪些分数来自低温度、哪些来自高温度。3.7 环境与并发机制导致评估结果不可复现团队协作评估时不同成员的开发机可能使用不同版本的依赖、不同模型接口环境甚至不同初始化种子。同一个 Harness 在同一模型上可能得到不同的准确率这不是模型变了而是 Harness 的环境没有锁定。这里的“绳子”更多来自工程层面未冻结依赖、未记录模型版本、未统一随机种子、未持久化日志、并发运行时抢占资源导致超时。一个不稳定的 Harness 会让优秀模型背上“分数波动大”的标签。因此评估结果必须附带完整的运行时指纹。4. 从 DeepSeek Harness、Codex Harness 看 Harness 工程化趋势4.1 为什么深度推理模型更需要 HarnessOpus 5 这类模型开始展现出更强的深度推理能力后单次问答的评估方式已经不能反映真实水平。模型需要经历“观察-思考-工具调用-再观察”的长循环。DeepSeek Harness、Codex Harness 这类社区工具的出现说明了同一个趋势把模型从“单次输出器”改造成“多步决策器”需要一个独立的、可编程的执行环境。这个执行环境不仅要能调用模型还要能处理代码执行结果、文件读写、环境状态变化和批量任务调度。换句话说Harness 已经从“测试工具”升级为“模型的外围操作系统”。模型负责思考Harness 负责落地。如果落地的接口设计得不好思考再强也无处发挥。4.2 评估型 Harness 与生产型 Harness 的差异理解 Harness 的用途边界非常重要。同一个 Harness 不能同时适配评估和生产因为目标不同。维度评估型 Harness生产型 Harness目标获得可复现的模型能力度量稳定服务真实业务请求容错策略失败重试、记录错误、继续下一个样本失败熔断、降级、人工转接判分离线判分可接受歧义分析在线反馈必须定义明确成功指标日志完整记录每个样本的所有信息只保留必要业务日志和追踪链路上下文策略可支持长历史和多轮摘要严格控制延迟和 token 成本工具权限允许沙箱内任意工具调用严格白名单和审计可复现性依赖版本和 seed 必须锁定更关注高可用和滚动发布如果在生产系统里照搬评估型 Harness 的随机重试逻辑可能会导致延迟和成本不可控反之如果评估时使用生产型的超时和熔断策略又可能低估模型能力。落地时应先确定 Harness 的用途再选择对应的参数。4.3 现有 Harness 的共同短板从社区中常见的 Harness 实现来看主要短板集中在四个方面对模型输出格式假设过强。很多 Harness 只解析{action: ..., args: {...}}这类严格 JSON。如果模型输出带有自然语言解释解析器就会失败。对工具的封装不透明。工具失败的原因被 Harness 吞掉模型只看到通用报错找不到修复线索。对并发评估的资源隔离不足。多个进程同时调用模型接口容易触发限流或超时。缺少对任务难度的动态识别。所有任务使用相同步数和相同工具导致简单任务浪费计算复杂任务不够算。这些短板叠加起来就是“Harness 正在变成捆住模型的绳子”的工程注脚。不是模型不想发挥而是外围框架没有给模型足够的空间。5. 设计一套“不捆住模型”的 Harness关键原则与示例配置5.1 原则一Harness 只负责“提供可能性”不要替模型做决定Harness 的职责是让模型的每一个合理动作都能被执行而不是预设“模型应该怎么思考”。评估 Harness 时可以用一个简单的判断标准如果某条推理路径是模型自己想出来的但它因为 harness 不支持而无法执行这就是 Harness 在捆绑模型。具体实现上Harness 应该提供开放的动作空间而不是固定枚举的动作。可配置的上下文保留和摘要策略。对模型输出解析失败时的自动修复机制。在每个失败点输出足够的信息让开发者能判断问题在模型还是 Harness。例如当模型输出一个 JSON 但字段名写错时不要直接返回失败而是把错误信息回传给模型请它修正后再重试。此类循环称为“格式修复循环”。5.2 原则二每一步都要可观测、可回放、可比较不可观测的 Harness 无法排查问题。每个 episode 必须保存完整轨迹包括原始 prompt、模型原始输出、解析后的动作、环境返回的 observation、每一步耗时、token 数、最终判分依据。建议日志格式采用 JSONL每行一个事件方便使用 jq、Python 脚本或日志平台分析。示例{event: prompt_built, task_id: abc123, prompt_length: 2048, template_version: v3} {event: model_output, task_id: abc123, step: 1, raw_output: ..., tokens: 1500} {event: tool_call, task_id: abc123, step: 1, tool: python_repl, args: ...} {event: judge_finish, task_id: abc123, correct: true, reason: grid_match}有了这样的事件流才能回答最核心的问题某个任务失败是模型想错了还是 Harness 没接住。5.3 一个更灵活的 Harness 配置示例下面给出一个更符合“不要捆住模型”思路的配置片段。它支持分层 prompt、动态工具注册、格式修复循环和语义判分harness: name: flexible-arc-eval model: client: anthropic model_name: claude-opus-5 temperature: initial: 0.0 retry: 0.7 max_tokens: 8192 prompt: default_template: prompts/default.txt per_task_prompt_script: prompts/adapt.py include_examples: true max_message_history: 100 steps: max_steps: 12 stop_on_final_answer: true invalid_json_retries: 2 on_timeout: regenerate tools: registry_file: tools/registry.yaml allow_dynamic_tools: true judge: mode: hybrid exact_match: true semantic_judge: enabled: true model: claude-opus-5 threshold: 0.8 human_sample_ratio: 0.1 logging: path: logs/run_{timestamp}.jsonl record_raw_prompt: true record_tool_outputs: true这里的关键在于per_task_prompt_script允许每个任务在默认模板基础上做个性化调整。invalid_json_retries给模型修复格式问题的机会。on_timeout: regenerate让超时后的重试成为一种可选策略而不是立即判负。semantic_judge.enabled表示在严格匹配失败后还会通过语义判断确认是否存在等价答案。human_sample_ratio引入人工抽检防止自动判分器系统性误判。5.4 如何验证 Harness 本身没有引入偏差一个 Harness 在正式用于评估前需要做三组验证。第一组是“空跑验证”用 mock 模型跑 20-50 个样本确认加载、解析、判分、日志链路没有异常。第二组是“回归对比”用同一模型、同一数据集跑两次 Harness结果应完全一致。如果两次结果不同说明 Harness 在并发、随机种子或依赖版本上有不稳定因素。第三组是“能力下限对照”用一个人畜无害的 baseline 模型跑同一 Harness。如果 baseline 模型分数低说明 Harness 没有过度奖励某种输出格式如果 baseline 分数异常高说明 Harness 可能存在评分漏洞。这三组验证的价值在于把“模型能力”和“Harness 质量”分开看避免把 Harness 的 bug 当成模型的缺陷。6. 实际使用中的常见故障检查清单6.1 现象模型输出被截断分数偏低很多模型在生成长篇推理时会触发max_tokens截断尤其当 Harness 设置了过小的单次生成上限。检查方式是在日志中查finish_reason。如果大量length而不是stop说明 token 预算不足。解决办法提高max_tokens引导模型先输出结论、后给出解释或者使用“结论前置”的 prompt对长任务启用“分段输出”协议让模型分多次提交结果而不是一次写完全部内容。
返回列表