尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

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

拆解RLM Harness:从ARC-AGI-3高分看自我改进的Agent工程实践

拆解RLM Harness:从ARC-AGI-3高分看自我改进的Agent工程实践 开源 Agent 框架在 ARC-AGI-3 上刷出高分是最近一段时间 AI 工程社区最热闹的事之一。围绕这次成绩真正引发争论的不是模型参数量也不是提示词模板而是一类把语言模型放进循环执行环境中、通过执行反馈让模型反复自我改进的框架。这类框架通常被统称为 RLM harness核心思路很直接语言模型不只生成一次答案而是生成候选程序、运行程序、观察运行结果、反思错误、再生成下一个候选直到程序通过全部测试。社区里 Codex Harness、DeepSeek Harness 这类工具的讨论热度把 harness 这个原本偏底层的概念推到了台前。这篇文章不替某个开源项目站台而是从工程视角拆解 RLM harness 的工作机制、最小实现、验证方法和争议来源帮助你判断这类框架到底是在提升通用推理能力还是在通过重试和反馈做一种高成本的搜索。1. ARC-AGI-3 与 RLM Harness 是什么关系1.1 ARC-AGI-3 到底在测什么ARC-AGI 是 Abstraction and Reasoning Corpus 的缩写设计目标是评估模型完成抽象推理任务的能力而不是考察知识记忆。每道题通常给出一组输入输出网格示例模型需要推断出网格变换规则并把规则应用到新的测试输入上。这类题目与普通编程题最大的区别在于任务空间不是预先定义的每次给的都是新规则模型不能靠背诵解题。ARC-AGI-3 延续了这一设计思路并进一步提高了任务难度。它不公开标准答案也不允许参赛者通过反复提交答案来反推数据因为这种 试错逼近 的方式会把推理问题变成搜索问题。这也是 ARC-AGI-3 的评测标准被社区反复讨论的原因如果评测流程允许在测试集上多次尝试那么模型本身是否真正学会规则就很难说清楚。我们不需要知道每一道题的答案但必须理解 ARC-AGI-3 的数据结构任务输入是二维数组输出也是二维数组中间是一个隐藏的变换规则。这个结构天然适合程序生成——把输入输出对作为测试用例让模型写一个 Python 函数再由测试用例验证函数是否正确。一个能把二维数组正确变换的函数就是模型给出的 解题答案。1.2 为什么单一模型打不过 Harness单纯让模型直接输出答案等于要求模型在一次前向推理内完成「读题、猜测规则、生成结果」三个步骤。对简单规则可以但对多步骤变换、条件分支、对象计数和几何旋转类任务一次生成往往不够稳定。Harness 改变的是执行方式而不是模型参数。它允许模型先生成一个候选函数然后在本地或者隔离环境里运行这个函数把测试结果和报错信息返回给模型。模型看到反馈后可以修正函数、验证新假设再跑一次。这个过程把 一次推理 扩展成 多轮推理把 模型必须对 变成 模型可以错但能从错误里恢复。这也是 ARC-AGI-3 上 RLM harness 表现突出的原因。RLM 可以理解为推理语言模型Reasoning Language Model它是 harness 的决策核心harness 则是包围在模型外层的工程系统负责执行代码、捕获异常、整理反馈、控制循环时间。模型负责认知harness 负责验证和试错。1.3 RLM 是模型Harness 是系统很多讨论把 RLM 和 harness 混在一起导致争论各说各话。需要拆分清楚RLM 是模型提供生成能力。它负责根据任务描述和历史反馈写出新函数。Harness 是系统提供验证能力。它负责运行函数、判断输出、构造反馈信息、管理尝试次数。自我改进不是模型单独完成的而是模型和 harness 协作的结果。模型输出假设harness 验证假设模型基于验证结果再输出新假设。所以讨论 开源 Agent 框架刷爆 ARC-AGI-3 时真正刷分的可能是整个 harness而不是某一个模型的原始能力。如果把模型单独拿出去做评测分数大概率不如带 harness 的完整框架。这也是争议的起点同一个模型加上不同 harness得分可能差很多那么成绩到底应该归功于谁2. 自我改进在 RLM Harness 里是怎么发生的2.1 主循环生成、执行、反馈、反思一个最小化的 RLM harness 通常包含四个步骤循环执行生成把任务描述、历史尝试记录和当前反馈拼成 prompt让模型生成一个候选函数。执行在可隔离的环境中运行候选函数传入测试输入得到输出。判断把实际输出与期望输出对比如果一致则通过否则记录失败原因。反思把失败原因、错误堆栈、实际输出和期望输出作为新上下文追加到 prompt 中进入下一轮生成。这个循环可以用伪代码表达for attempt in range(max_attempts): code model.generate(prompt) output execute(code, test_input) ok is_equal(output, expected_output) if ok: return code prompt append_feedback(prompt, code, output, error)循环终止条件有两个要么得到通过验证的候选函数要么消耗完最大尝试次数。很多开源框架为了稳定还会在每轮生成时保存完整轨迹方便事后重放和分析失败原因。2.2 反馈信号从哪来ARC-AGI-3 任务里最自然的反馈信号是测试用例的匹配结果。模型生成的函数运行后harness 比较输出矩阵是否与期望矩阵逐元素相等。如果不等harness 可以给出输出矩阵的前几行期望矩阵的前几行差异位置运行时抛出的异常堆栈这些信息进入下一轮 prompt模型就能知道自己的函数执行结果长什么样。反馈不一定需要模型自己写。harness 可以直接把矩阵差异打印成文本比如 第 0 行第 1 列期望是 5实际是 0模型读到这条结构化信息后更容易修正。2.3 反思模板不是魔法是结构化上下文反思 听起来很高级但工程上就是把反馈拼进 prompt。真正影响效果的是反馈的格式。如果只告诉模型 你错了模型很难知道怎么改。如果给出差异位置、异常堆栈和正确输出样例模型就能基于具体信号调整。一个典型的反思 prompt 片段可以这样组织上一轮你生成了以下函数 def transform(grid): return grid 执行结果测试用例失败。 实际输出[[0, 5], [5, 2]] 期望输出[[0, 0], [0, 2]] 差异位置(0,1)期望0实际5位置(1,0)期望0实际5。 请分析失败原因并生成一个修正后的 transform 函数。关键是让模型看到 期望 和 实际 的对比而不是模糊的错误描述。社区里很多 harness 会在这个环节加入分类把错误分成 函数逻辑错误边界条件错误类型错误规则不完整 等帮助模型聚焦。2.4 轨迹记录让每次改进可追溯自我改进如果不可复现就失去工程价值。生产级 harness 会把每一轮的 prompt、生成代码、执行日志、输出结果、错误堆栈、耗时、重试次数完整保存下来。这样当模型在某次任务上连续失败时可以直接从日志中看到是哪一轮引入的错误假设。轨迹也是评估框架质量的重要材料。通过分析轨迹可以判断模型是在真正收敛还是从一个错误跳到了另一个错误。记录的内容建议包含任务编号模型版本和采样参数尝试轮次生成的函数源码执行返回码和输出是否通过失败分类这些数据会直接用于后续调参和问题复盘。3. 自己实现一个最小 RLM Harness3.1 环境准备与项目结构为了完整理解 RLM harness动手写一个最小可运行版本比读十篇分析更有效。这里用 Python 实现一个不依赖任何第三方 API 的演示版本模型部分用本地 Mock Agent 模拟重点是跑通 harness 的循环流程。环境要求项目版本或工具Python3.10依赖库无额外依赖使用标准库操作系统Windows / macOS / Linux 均可推荐编辑器VS Code 或任意 Python IDE项目结构如下rlm_harness/ ├── tasks.py # 任务定义和样例数据 ├── agents.py # 模型代理可换成真实 LLM API ├── harness.py # 主循环、执行器、数据记录 ├── run.py # 入口脚本 └── results/ # 运行结果输出目录3.2 实现任务定义ARC-AGI 风格的任务核心是二维数组。为便于演示定义一个颜色替换规则把所有值为 5 的格子改成 0其他值保持不变。任务定义使用 dataclass 保存输入输出。# tasks.py from dataclasses import dataclass from typing import Any dataclass class Task: task_id: str train_inputs: list[list[list[int]]] train_outputs: list[list[list[int]]] description: str def build_sample_task() - Task: train_input [[0, 5], [5, 2]] train_output [[0, 0], [0, 2]] return Task( task_idcolor_5_to_0, train_inputs[train_input], train_outputs[train_output], descriptionReplace every 5 in the grid with 0., )这里只放了一组训练样例真实 ARC 任务通常会有多组样例但最小演示足够。后续接入真实任务时只需要把 ARC-AGI-3 的 JSON 结构解析成这个 Task 对象即可。3.3 实现代码执行器执行器的职责是运行模型生成的函数并返回输出或异常信息。出于演示目的直接在一个新的命名空间里执行代码然后取出 transform 函数。生产环境必须用子进程或容器隔离避免任意代码执行风险。# harness.py import traceback class CodeRunner: def __init__(self, timeout: int 5): self.timeout timeout def execute(self, code: str, input_data: Any) - Any: namespace {} exec(code, namespace) transform namespace[transform] return transform(input_data)这里把代码字符串交给 exec 执行并约定模型必须输出一个名为 transform 的函数。真实场景中还需要限制可导入的模块、执行时间、内存大小并放进沙箱运行。3.4 实现模型代理模型代理负责生成候选函数。演示版本使用 MockAgent第一次返回原样返回输入的错误函数第二次返回正确函数。这能模拟模型收到反馈后修正行为的过程。接入真实模型时在这里替换成 API 调用。# agents.py class MockAgent: def __init__(self): self.calls 0 def generate(self, prompt: str, attempts: int 1) - list[str]: code self._next_code() return [code] def _next_code(self) - str: self.calls 1 if self.calls 1: return def transform(grid):\n return grid return ( def transform(grid):\n return [[0 if cell 5 else cell for cell in row] for row in grid] )这样第一次生成会失败第二次根据反馈修正。理解这个跳过逻辑后你会明白真实模型只是把 根据反馈修正 这一步从硬编码变成了概率生成。3.5 实现 Harness 主循环主循环把任务、代理、执行器连接起来。每一轮生成候选函数执行比较结果。如果失败就把反馈追加到 prompt 中进入下一轮。# harness.py from dataclasses import dataclass, field dataclass class AttemptRecord: code: str ok: bool output: Any None error: str dataclass class HarnessResult: task_id: str solved: bool attempts: list[AttemptRecord] field(default_factorylist) class RLMHarness: def __init__(self, agent, runner, max_attempts: int 5): self.agent agent self.runner runner self.max_attempts max_attempts def solve(self, task: Task) - HarnessResult: prompt self._build_prompt(task) result HarnessResult(task_idtask.task_id) for i in range(self.max_attempts): code self.agent.generate(prompt)[0] try: actual self.runner.execute(code, task.train_inputs[0]) ok actual task.train_outputs[0] record AttemptRecord(codecode, okok, outputactual) except Exception as exc: record AttemptRecord(codecode, okFalse, errortraceback.format_exc()) result.attempts.append(record) if record.ok: result.solved True break prompt prompt \n self._build_feedback(record, i 1) return result def _build_prompt(self, task: Task) - str: return ( fSolve task {task.task_id}: {task.description}\n fTrain input: {task.train_inputs[0]}\n fTrain output: {task.train_outputs[0]}\n Write a Python function named transform(grid) that maps input to output.\n ) def _build_feedback(self, record: AttemptRecord, attempt_num: int) - str: if record.error: detail record.error.splitlines()[-1] else: detail str(record.output) return ( fAttempt {attempt_num} failed. Error or mismatch: {detail}\n Please analyze why and provide a revised transform function. )3.6 运行与预期结果入口脚本把依赖组装起来并输出结果。# run.py from tasks import build_sample_task from agents import MockAgent from harness import CodeRunner, RLMHarness if __name__ __main__: task build_sample_task() agent MockAgent() runner CodeRunner(timeout5) harness RLMHarness(agent, runner, max_attempts5) result harness.solve(task) print(ftask_id: {result.task_id}) print(fsolved: {result.solved}) print(fattempts: {len(result.attempts)}) for i, record in enumerate(result.attempts, 1): print(f--- attempt {i} ---) print(record.code) print(fok: {record.ok}, output: {record.output}, error: {record.error[:80]})运行python run.py预期输出是task_id: color_5_to_0 solved: True attempts: 2 --- attempt 1 --- def transform(grid): return grid ok: False, output: [[0, 5], [5, 2]], error: --- attempt 2 --- def transform(grid): return [[0 if cell 5 else cell for cell in row] for row in grid] ok: True, output: [[0, 0], [0, 2]], error:这个最小闭环证明了一件事当模型第一次失败时harness 没有直接宣布失败而是把失败信息带回到 prompt驱动模型生成第二次候选。这就是自我改进的最低层实现。4. 关键参数与配置详解4.1 模型参数温度、采样、最大尝试次数真实 RLM harness 接入大模型 API 后最先要调的是采样参数。参数默认参考值作用调整影响temperature0.7控制生成随机性越高越容易探索新解法越低越稳定但可能重复失败top_p0.9nucleus sampling 阈值影响候选多样性max_tokens512单次生成最大长度太短会截断函数太长会增加耗时max_attempts5每任务最多尝试轮数越大越可能成功但评测成本线性增加timeout10 秒单次代码执行超时过短会误杀合法程序过长会拖慢整批任务max_attempts 是最关键的资源参数。在 ARC-AGI-3 风格任务上如果只允许 1 次尝试那么 harness 退化成普通单次生成如果允许 20 次尝试得分会明显提升但评测时间和 API 费用也翻倍。评测规则必须明确声明这个数字否则分数没有可比性。4.2 执行环境参数超时、并发、沙箱harness 要运行模型生成的任意代码安全是第一位。生产环境不要直接在主进程里exec需要至少做到使用子进程执行设置 CPU 时间和内存上限。使用 Docker 或容器隔离限制文件系统访问。禁用网络访问避免模型生成的代码请求外部资源。设置单次执行超时避免死循环。并发参数也要控制。API 有速率限制本地执行也有 CPU 资源限制。常用做法是使用线程池控制并发数例如 8 到 16 个 worker每个任务独立运行。如果一次评测几千个任务还应该支持断点续跑把已完成结果写到磁盘避免中途失败全部重来。4.3 日志与结果保存每次运行建议保存一个 JSON 结果文件包含任务编号、模型参数、每轮生成的函数、执行输出、错误、耗时和最终是否通过。一种推荐结构如下{ task_id: color_5_to_0, solved: true, model: mock-agent, temperature: 0.7, max_attempts: 5, attempts: [ { code: def transform(grid):\n return grid, ok: false, error: , output: [[0, 5], [5, 2]] }, { code: def transform(grid):\n return [[0 if cell 5 else cell for cell in row] for row in grid], ok: true, error: , output: [[0, 0], [0, 2]] } ] }这个文件既用于复盘也用于后续统计指标。如果一轮评测跑了一千个任务可以从这些 JSON 文件聚合出通过率、平均尝试次数、失败分布等指标。4.4 参数调整速查表现象调整方向原因结果不稳定降低 temperature减少随机探索一直重复同样错误调高 temperature 或 top_p增加候选多样性生成长函数被截断调大 max_tokens输出被截断无法执行频繁超时调小 max_attempts 或 timeout单任务资源消耗过高API 限流降低并发数、增加重试请求频率超过额度5. 验证框架是真的在改进还是只跑通了一次5.1 使用多组任务而不是单个用例单任务跑通只能说明代码流程正确不能说明框架有效。要验证自我改进是否真实有效至少要在 20 个以上任务上运行并且任务要覆盖不同类型颜色替换、形状复制、对称变换、计数、对象删除等。把每个任务的最终结果汇总得到总任务数20 通过任务数14 整体通过率70% 平均尝试轮数3.1 首次尝试通过6 后期改进通过8如果绝大多数成功任务都发生在第 1 次尝试说明模型本身能力足够harness 的贡献不大。如果大量任务在后续轮次才通过说明反馈机制确实在起作用。5.2 指标设计通过率、平均尝试次数、passk评估 RLM harness 不能只看最终通过率。需要设计更精细的指标指标含义使用场景整体通过率成功任务数 / 总任务数衡量最终效果首次尝试通过率第一轮就成功的任务占比衡量模型原始能力改进成功率首次失败但后续轮次成功衡量 harness 自我改进能力pass1 / pass5模型在 1 次或 5 次候选中成功的概率对比不同采样策略平均尝试轮数所有任务尝试轮次的平均值衡量评测成本失败分布尝试 5 次仍失败的任务占比判断是不是存在不可解分支如果一份评测报告只给最终通过率而不公开 pass1、平均尝试轮数和完整轨迹那么第三方很难验证成绩的含金量。5.3 避免伪提升的检查清单自建 harness 后很容易在调参过程中得到看似不错的分数但这些分数可能不可靠。验证前要检查是否使用了与开发集不同的评测集每轮生成时模型是否能访问到标准答案是否固定了随机种子多次运行结果是否一致是否对同一个任务运行多遍并报告平均结果是否记录了每次尝试的完整日志而不是只记录最终成功是否控制了 max_attempts 上限避免无限重试如果这些问题有一个没有落实分数就只能当作内部参考不适合对外发布。6. 常见问题与排查链路6.1 任务一直失败反馈信息太弱现象模型每轮都在生成类似代码错误不收敛。常见原因反馈里只有 失败 字样没有差异位置和期望输出。模型缺少修改依据。检查方式查看最近几轮 prompt 的追加内容确认反馈是否包含具体错误信息。解决方案在反馈中增加期望输出、实际输出、第一个差异位置。如果函数抛异常至少带上最后一行堆栈信息。6.2 自我改进越改越差现象第一轮生成的函数能通过部分用例第二轮反而完全错误。常见原因temperature 过高导致探索过度模型在错误方向上反复跳跃。另一种情况是反馈 prompt 过长模型丢失了原始任务语义。检查方式降低 temperature 到 0.2 再跑同一批任务看是否稳定。同时检查 prompt 是否因为追加过多历史而超过上下文窗口。解决方案只保留最近 2 到 3 轮反馈而不是把全部历史都拼进 prompt。对失败轮次做去重避免同一错误重复出现。6.3 代码执行结果不匹配现象逻辑看起来正确但程序判定失败。常见原因空心列表比较时模型把元组和列表混淆或者回溯时修改了原始输入对象。检查方式对比实际输出和期望输出的类型。ARC 任务通常要求矩阵是list[list[int]]模型可能返回tuple或 numpy 数组。解决方案在 harness 判断前统一转换类型例如先转成嵌套 list 再比较。同时对输入做深拷贝防止模型函数修改原始网格。6.4 调试顺序遇到问题按以下顺序排查确认任务数据格式是否正确grid 是不是二维数组。确认模型生成代码是否满足函数签名约定。确认执行器是否能正确捕获异常和超时。确认反馈信息是否写入了下一轮 prompt。确认日志文件是否记录了完整轨迹。确认资源限制并发、timeout是否合理。7. 争议背后的三个技术问题7.1 数据污染训练集和评测集是否隔离ARC-AGI-3 的争议焦点之一是数据隔离。如果一个开源 Agent 框架在训练阶段已经接触过评测任务或类似规则那么高分可能来自记忆而不是推理。harness 无法解决这个问题它只能放大人模型已经学到的能力。实际项目里要做的是把开发集和评测集严格分开并且在文档中声明模型的训练数据截止时间。如果评测时发现某道题与训练样本高度相似应该单独标注不能混入最终通过率。7.2 评测时多次重试算推理还是算搜索模型每多试一次就多一次从测试输出中获取信息的机会。最极端情况是 harness 可以穷举所有可能的变换规则然后在测试用例上逐个尝试直到一个规则匹配。这种方式在技术上也能刷高 ARC-AGI-3 分数但它更像是搜索算法而不是智能。RLM harness 的边界在于它到底是在 根据反馈理解规则还是仅仅在 暴力搜索可用函数。判断标准是轨迹。如果模型在第二轮就能根据差异位置修正逻辑说明它理解力强。如果它只是不断生成随机函数然后碰运气那么分数再高也不会让社区信服。7.3 Harness 是否应该算作模型成绩的一部分讨论刷榜争议时经常出现一个问题成绩应该记在模型头上还是 harness 头上。答案是看你想衡量什么。如果目标是比较模型能力就应该固定 harness只改模型。如果目标是比较 Agent 框架就应该固定模型只改 harness。两者混在一起比较分数没有意义。社区里 Codex Harness、DeepSeek Harness 等工具流行的原因恰恰是它们把 代码生成、执行、反馈、重试 做成通用基础设施让模型可以更容易地利用测试信号。这类工具本身是工程能力的体现不应被否定。只是对外发布成绩时必须说明 score 是在哪个模型、哪个 harness、多少次尝试下得到的否则很容易引起误读。8. 生产环境落地建议与扩展方向8.1 学习环境与生产环境的差异学习时最小 harness 可以像上文那样直接在主进程exec。生产环境就完全不同必须考虑稳定性、成本和风险。维度学习环境生产环境代码执行直接在测试进程 execDocker 容器或子进程隔离网络限制不限默认关闭需要时白名单并发单任务单线程线程池 队列 限流日志stdout 打印结构化 JSON 落盘 监控告警重试最多 5 次可配置且记录成本统计安全审计不关注记录生成代码和运行痕迹结果回复手动查看写入数据库或消息队列8.2 落地前可复用清单把一个 RLM harness 用于真实项目前建议逐项确认模型能稳定输出符合函数签名的代码。单次执行有超时和资源限制。模型生成的代码没有访问外部网络的能力。反馈信息包含具体差异而不是简单报错。每轮尝试都落盘包含 prompt、代码、输出、耗时。评测集和开发集完全隔离。max_attempts、temperature 等参数有统一配置。支持断点续跑重启后能跳过已完成任务。有通过率、平均尝试轮数、失败任务分布等统计脚本。对模型版本和 harness 版本做双记录保证可复现。8.3 扩展方向多智能体、沙箱、失败分类完成最小闭环后可以继续扩展失败分类把失败原因分成语法错误、运行时异常、逻辑错误、输出不匹配针对不同错误生成不同反馈模板。多智能体协作让一个模型生成方案另一个模型做代码审查审查意见再回到生成模型减少无效重试。沙箱服务化把代码执行器做成独立服务支持多种语言运行时统一接收候选代码和测试用例。参数优化用少量样本搜索 temperature、max_attempts 的组合降低单任务平均成本。回到 ARC-AGI-3 的争议你会发现它不只是一道模型竞赛题更是一道评估工程题。RLM harness 把模型能力、执行环境、反馈设计和资源控制变成了一个整体系统。真正有价值的不是某个框架刷到多高分而是你能不能复现这套循环并且用清晰的轨迹说明它为什么有效、在哪些任务上有效、又隐藏了哪些成本。对新手来说最好的练习方式就是从本文的最小 harness 开始换几组不同的 ARC 风格任务逐步加入日志、并发和沙箱再回头看社区里那些高分成绩自然会得出自己的技术判断。
返回列表