最近有一条消息在 AI 科研和工程圈子里传播得很快:OpenAI 在一项数学难题评测中连续解决了 10 道题,而社区项目 Fable 用 24 小时复现了其中 5 道。很多人看到这条消息的第一反应是“OpenAI 的模型又变强了”,但我觉得更值得讨论的是另一件事:当大模型从一个只输出文本的“聊天对象”,变成一个能自己编写代码、运行验证、观察报错并修正思路的“研究员”时,整个技术栈发生了什么变化。这才是数学难题类 Agent 应用真正迷人,也真正有工程价值的地方。
如果只是坐在对话框里问模型“这道题怎么解”,它给出的答案再漂亮,也很难直接信服。因为数学题,尤其是开放型数学难题,标准答案往往是唯一且可验证的;模型必须经受代码执行、反例搜索、推理链条校验的考验。OpenAI 这次能够连续解决 10 道,背后大概率不是简单的“记忆召回”,而是一套把推理、工具调用和结果验证串起来的 Agent 系统。相比“模型聪明了多少”,我更关心这套系统到底长什么样,以及普通开发者能不能复刻。
再看 Fable 在 24 小时内复现 5 道。这个速度乍一看不如 OpenAI,但在复现类工作中,能跑通 5 道已经很说明问题。复现一个 Agent 系统的难点通常不在模型本身,而在环境配置、提示词设计、评测对齐、预算控制和随机性管理上。Fable 能做到这一步,等于证明了:只要有了公开的 harness、合理的 API 配置和准确的评测标准,一个小团队完全可以在一天内复制出相当一部分结果。这篇文章就围绕这条信息展开:先讲 Codex harness 到底解决了什么问题,再拆解 Fable 的复现路径,接着给出一套可运行的 Agent 数学验证最小实现,最后把环境搭建、API Key 配置、常见问题和工程化建议一起讲透。
1. 这篇文章真正要解决的问题
很多读者对“AI 解数学题”的印象,还停留在 ChatGPT 能写证明步骤、能求积分、能解方程的阶段。但“能生成答案”和“能验证答案”之间隔着一条巨大的鸿沟。真正的数学难题,不是给一个发散题面让你写小作文,而是要求模型在若干步之内找到构造、反例或证明路径,并且最终结果必须可被机器或人工严格检验。无论答案写得多么像模像样,只要代码运行失败、逻辑链条断裂、边界条件漏掉一个,整个解题过程就是无效的。
所以这篇文章要解决的第一类痛点是:如果你也想做一个面向数学问题的 Agent,应该从哪里入手。很多开发者把精力放在“选个更强的模型”上,结果模型换了一个又一个,问题还是解不出来。原因往往不是模型能力不够,而是 Agent 缺少一个能安全执行代码、收集反馈、重新规划的外围系统。这个外围系统,就是标题中提到的 Codex harness 这类东西。它不负责“想”,它负责让模型“做完并验证”。
第二类痛点是复现成本。看到 OpenAI 的成果后,不少团队第一想法是“能不能把我们自己的数据也喂进去跑一遍”。但真正动手后才发现,复现一个大模型实验,时间往往不是按小时计算,而是按周计算。问题包括:官方环境怎么搭,API Key 怎么配,题目格式怎么统一,输出结果怎么判对错,跑到一半预算超了怎么办。Fable 能 24 小时复现 5 道,恰恰说明这些问题不是无解的。本文会把这些问题拆开,给出一套可落地的最小方案。
第三类痛点是安全与成本。使用 OpenAI API 或任何云模型接口,都涉及凭据管理、配额控制、沙箱隔离和日志审计。如果只追求“跑通”,很容易把 Key 写死在代码里,或者把 Agent 直接放在没有隔离的服务器上执行任意代码,这些都是把系统送进火坑的操作。文末会专门给出安全边界和最小权限建议,这部分内容在真实项目中往往比模型本身的 prompt 更重要。
2. Codex harness 到底是什么,解决什么问题
要理解 Codex harness,先要理解 Agent 执行数学题时的完整链路。模型本身是一个“推理核心”,它负责阅读题目、生成思路、写出代码或证明文本。但数学题的结果不能被“一句话”盖棺定论,它需要被放进一个真实执行环境里跑一遍。这个真实执行环境,就是 harness 的职责。
通俗地说,harness 是套在模型外面的一套“实验室设备”。它让模型能够操作文件、执行 Shell 命令、调用 Python 解释器、读取运行日志,并把这些日志作为下一轮推理的上下文。没有 harness 时,开发者只能手动把模型生成的代码复制到本地运行,再把报错信息粘回去,循环往复。这个过程不仅慢,而且很难自动化。有了 harness,Agent 就可以自己在环境中试错,直到代码通过、结果达到预期,或者达到预设的步数和成本上限。
2.1 没有 harness 时,数学题是怎么做的
假设你让模型证明“100 以内所有偶数都能写成两个素数之和”。传统做法是这样的:先把题面复制到聊天窗口,模型给出一个“思路”,然后你要求它写 Python 代码。代码生成后,你需要手动打开编辑器,粘贴代码,运行,得到一个输出。如果输出不符合预期,你还要把错误信息复制回聊天框,让模型继续改。这看起来只有几步,但实际迭代一次可能需要几分钟,而且需要你始终盯在中间。
如果题目难度上升,比如需要搜索一个特殊构造、需要运行数小时的计算、需要多轮反馈修正,这种人工搬运的方式几乎不可行。人不可能一直守在键盘前,把每次模型输出都手动喂回去。一旦中间出现乱码、缩进错误、环境依赖缺失,排错时间会成倍增加。更麻烦的是,这种手工流程很难被扩展成批量评测,你无法一次性跑 100 道题并统计成功率。
2.2 引入 harness 后,执行链路发生了什么变化
引入 harness 之后,模型看到的不是一个“聊天窗口”,而是一个带文件系统和命令行的工作环境。它可以写出solution.py,然后主动执行python solution.py,如果运行失败,它可以直接看到 traceback,并根据错误信息修改代码,再次运行。整个过程不需要人肉搬砖。从流程上看,相当于把“人读模型输出-人执行-人反馈”变成了“模型输出-工具执行-模型读取反馈”的闭环。
这意味着两件事。第一,模型的“智能”被工具放大。模型不需要一次性写出完美代码,它可以反复修正,这在现代大模型中更现实。第二,系统的行为边界更可控。harness 可以限制网络访问、限制文件目录、设置超时时间、控制进程权限,这些约束是普通聊天接口不具备的。对数学题这类需要严谨验证的任务来说,能控制执行环境,才有资格谈论“可信结果”。
从公开信息看,OpenAI 这次把 Codex harness 开放出来,等于把这套“实验室设备”的图纸公之于众。开发者不必从零开始设计整个执行框架,而是可以基于公开仓库,快速搭出一套类似的 Agent 环境。这也是为什么 Fable 能在 24 小时内复现 5 道题的底层原因:不是模型强得离谱,而是工程复现路径被大大缩短了。
3. Fable 24 小时复现的技术拆解
Fable 在标题中更像一个“复现者”角色。由于目前公开的第一手材料有限,我不能确定它内部是否使用了 Codex harness,也不能断言它的每一层技术选型。但从社区复现类项目的一般规律来看,Fable 能在 24 小时里复现 5 道题,说明它至少解决了四个关键问题:题目准备、环境搭建、评测对齐和成本控制。这四个问题,恰好是复现任何 Agent 数学任务的共同难点。
题目准备不是简单地把题目文本喂给模型。数学难题需要格式化成机器可读的验证脚本,例如把“证明某某性质”转成一段穷举或代数计算的代码。环境搭建则是要保证模型生成的代码能被安全执行,并且不会因为依赖缺失而意外失败。评测对齐是判断“模型输出算不算正确”的标尺,不同题目可能有不同的判定方式:有的是直接比较数值答案,有的是运行测试用例,有的则必须人工检查证明过程。成本控制则决定了你愿意为每道题投入多少步、多少 token。
3.1 Fable 复现了什么
从标题透露的信息看,Fable 复现的是“OpenAI 解决数学难题”这个实验的一部分结果,具体表现为成功复现了 5 道题。这里的“复现”并不是指模型一字不差地抄袭 OpenAI 的推理过程,而是指通过自己的 Agent 流程,在同样的或相近的题目上得到同样的正确答案。这种复现的价值在于:它验证了 OpenAI 公开的路线是可落地的,而不是只能出现在官方博客里的“演示”。
从工程角度说,复现 5 道题比复现 5 道题更容易失败的地方在于:一旦某个环节配置错误,正确率可能直接归零。例如,如果提示词没有要求模型输出 JSON,模型可能生成多余文本;如果执行环境缺少数学库,代码一运行就崩;如果评测函数写错,即使模型答对了你也会误判。Fable 能在一半以上的题目上成功,至少说明它的 Agent 框架在稳定性上已经通过了初步检验。
3.2 复现 5 道的成本与随机性问题
为什么不是 10 道全部复现?这不一定代表能力不足,更可能是成本和随机性问题。Agent 系统每一次运行都有相当高的自由度:模型 temperature 不是零时,输出可能不稳定;同样的题目重复跑 10 次,可能成功 6 次失败 4 次;API 调用费用会随着步数增加而快速累积。在 24 小时的时间窗口内,一个小团队能调用多少次模型、能执行多少轮代码,都是有上限的。
因此,复现 5 道而不是 10 道,是一个理性的资源分配结果。可能有些题需要的搜索空间更大,有些题需要更复杂的依赖,有些题反复失败后预算已经见底。对普通开发者来说,这个现象也是很好的提醒:做 Agent 评测时,不要只看“最后成功了几道”,还要看“单位成功率对应的成本”。能够稳定复现 5 道,比“撞运气”复现 10 道更有工程价值。
4. 环境准备与前置条件
要跑通一个 Agent 数学验证项目,环境准备是第一步。这里会涉及 OpenAPI 平台账号、API Key、Codex CLI 或 OpenAI Python SDK,以及一个可以执行 Python 代码的本地环境。由于不同时期的官方文档可能存在差异,下面给出的版本和命令以通用实践为准,具体以你实际使用的仓库 README 和官方文档为准。
4.1 OpenAI API Key 的获取与安全规范
先说 API Key 的获取方法。你需要先在 OpenAI 平台上注册账号,进入控制台的 API Keys 页面,创建一个新的 Secret Key。创建后,页面会展示一次完整的 Key 字符串,你需要立即复制并保存在安全位置,因为关闭页面后通常无法再次查看完整内容。这个 Key 就是你的 Agent 调用模型接口时的唯一凭证。
关于 Key 的使用,我必须强调三条底线。第一,不要把 Key 提交到 Git 仓库,尤其是公开仓库;一旦泄露,别人可以用它消耗你的 API 额度。第二,不要使用“共享 Key”或来源不明的转售 Key,这既违反平台服务条款,也可能导致账号被封禁。第三,生产环境中应该将 Key 放在环境变量、密钥管理服务或云厂商的 Secret Manager 中,并给 Key 设置最小权限和额度上限。你可以在 API Keys 页面为不同项目创建多个 Key,分别设置名称和用途,方便事后审计。
4.2 安装 Codex CLI 与运行时依赖
如果你的复现目标依赖 Codex CLI,那么首先需要确认本机已经安装了 Node.js 和 npm。安装方式根据你的操作系统而定,macOS 可以用 Homebrew,Ubuntu 可以用 apt,Windows 可以用官方安装包。Node.js 版本建议使用当前维护中的 LTS 版本,具体以 Codex 仓库要求为准。
安装 Codex CLI 的常见命令如下,但请注意:包名和参数可能随官方版本变化,使用前先查看目标仓库的 README。
# 以官方 Codex 仓库 README 为准,这里展示常见安装方式 npm install -g @openai/codex codex --version如果这一步因为网络、权限或包名变更导致失败,不要强行绕过去。最稳妥的做法是查看官方仓库的安装说明,或直接使用 Python 版本的 OpenAI SDK,因为后者相对稳定,且本文后面的最小示例也能继续跑通。
4.3 验证环境是否可用
安装完成后,需要先验证 API Key 和基础环境是否可用。设置环境变量:
export OPENAI_API_KEY="sk-你的实际Key" export OPENAI_MODEL="你的可用模型名"然后写一个最简单的 Python 调用,看看能否正常返回。如果这一步就报错,那么后边的 Agent 流程基本跑不起来,所以建议先在这里花一点时间排错。验证方式可以是一个十行以内的脚本,也可以直接使用官方 SDK 提供的示例。重点不是看模型回答得有多好,而是确认网络连接、鉴权、模型名填写都是正确的。
5. 从数学难题到自动化验证:核心流程拆解
环境准备好之后,我们来看核心流程。不管题目是“验证偶数能拆成素数之和”还是“证明某个数论难题的构造存在”,Agent 的解题链路都可以抽象成四个步骤:题目转断言、提示词约束、运行反馈循环、人工复核。下面我用一个具体题目来说明。
5.1 把题目转成可执行断言
这一步最容易被忽略。很多人直接把自然语言题目丢给模型,让模型“自由发挥”,结果模型写了 500 字证明,但根本没有可验证的程序。正确的做法是先把题目翻译成可执行断言。例如题目“验证 100 以内所有偶数都能写成两个素数之和”,我们先定义输入范围,再定义检查逻辑:对每个偶数 n,是否存在两个素数 p 和 q,使得 p + q = n。如果不能,就记录到失败列表。
这一步的作用是让“正确”变成机器可读的。如果断言写错了,后边无论模型多聪明,都会得到错误结论。所以我们通常把断言函数作为固定模板,放在系统提示词里,要求模型只补全解题代码,而不是重新设计整个验证规则。对更复杂的数学题,可能还需要引入符号计算库,比如 SymPy,或者定义多个评分函数。判断一个 Agent 是否可靠,首先看它的断言定义是否足够严格。
5.2 用提示词约束 Agent 输出
模型回答的自由度越高,解析代码的难度就越大。为了让 Agent 循环可运行,我们通常要求模型每一步输出一个固定 JSON 结构,里面包含思考过程和可执行的 Python 代码。例如:
{ "thought": "先枚举 4 到 100 之间的偶数,再用素数集合判断", "code": "import sympy as sp\nprimes = set(sp.primerange(2, 100))\nfailures = [n for n in range(4, 101, 2) if not any((n-p) in primes for p in primes if p < n)]\nprint('failures =', failures)" }JSON 格式的好处是:解析稳定、字段明确、日志可追踪。坏处是模型偶尔会输出 JSON 之外的文字,比如 Markdown 代码块包裹。因此在代码里,我们要做一层容错处理,把模型输出先按 JSON 解析,如果解析失败,就把错误信息作为新消息传给模型,要求它重新输出合法 JSON。这一步是 Agent 循环里最常见也最必要的防守。
5.3 运行-反馈-修正循环
把模型输出的代码保存到临时文件后,用subprocess执行,并捕获 stdout 和 stderr。如果代码运行成功,就把输出结果拼接到对话里,让模型判断结论是否成立;如果出现异常,就把 traceback 拼接到对话里,让模型修正代码。如此反复迭代,直到通过预设的最大步数,或者得到一个明确的“验证通过”信号。
这个循环的本质是让模型拥有“试错”能力。普通对话中的模型没有机会看见自己代码的真实运行结果,所以它可能自信地生成一段有 bug 的代码。而在 Agent 循环里,模型能够看到真实输出,这会让它的下一轮推理更加贴近实际。数学题越复杂,这个循环的重要性越明显,因为很少有模型能在第一轮就写出完全正确的证明程序。
6. 完整示例代码实现
下面给出一个可直接运行的最小 Agent 示例。它不使用复杂的 Codex harness,而是用 OpenAI Python SDK 实现一个“生成代码-执行-反馈-再生成”的循环。这个例子虽然简单,但足以让你理解 Fable 这类复现项目的核心骨架:模型负责推理,代码执行环境负责验证,历史消息负责传递反馈。
# agent_loop.py import json import os import subprocess from openai import OpenAI api_key = os.environ.get("OPENAI_API_KEY") model = os.environ.get("OPENAI_MODEL") if not api_key or not model: raise SystemExit("请先设置 OPENAI_API_KEY 和 OPENAI_MODEL 环境变量") client = OpenAI(api_key=api_key) messages = [ { "role": "system", "content": ( "你是一位数学验证助手。你可以生成 Python 代码来完成计算验证。" "每次回答必须是一个 JSON 对象,格式为:" "{\"thought\": \"你的推理过程\", \"code\": \"可执行的Python代码\"}。" ), }, { "role": "user", "content": "验证 4 到 100 之间的所有偶数都可以写成两个素数之和," "并在代码中输出 failures 列表。", }, ] max_steps = int(os.environ.get("MAX_STEPS", "5")) for step in range(max_steps): response = client.chat.completions.create( model=model, messages=messages, temperature=0, ) content = response.choices[0].message.content print(f"\n--- 第 {step + 1} 轮模型输出 ---") print(content[:500]) try: parsed = json.loads(content) except json.JSONDecodeError as e: messages.append({"role": "user", "content": f"JSON 解析失败:{e},请只输出合法 JSON。"}) continue code = parsed.get("code", "") thought = parsed.get("thought", "") print("思考:", thought) if not code.strip(): messages.append({"role": "user", "content": "代码为空,请补充可执行代码。"}) continue with open("_agent_temp.py", "w", encoding="utf-8") as f: f.write(code) result = subprocess.run( ["python", "_agent_temp.py"], capture_output=True, text=True, timeout=30, ) if result.returncode == 0: output = result.stdout.strip() print("运行输出:", output) if "failures" in output and "[]" in output: print("结论:在 4 到 100 范围内,验证通过。") break else: messages.append({ "role": "user", "content": f"运行成功,输出如下:{output}。请判断验证是否通过,如果失败请修正方案。" }) else: messages.append({ "role": "user", "content": f"运行失败:{result.stderr.strip()}。请修复代码后重试。" })这是一个完整脚本,不依赖额外的第三方数学库,只依赖openai包。它的关键点在消息设计:系统提示词强制模型输出 JSON,用户消息给出明确验证目标;每轮模型输出后,我们会把执行结果作为新的用户消息追加到对话中,让模型看到反馈。如果你要跑真实的数学难题,只需要替换用户消息里的题目,以及“输出 failures 列表”的判定逻辑。
运行方式如下:
export OPENAI_API_KEY="sk-你的实际Key" export OPENAI_MODEL="gpt-4o-mini" python agent_loop.py注意,gpt-4o-mini只是示例模型名,具体以你账号实际可用的模型为准。如果你需要使用更强的推理模型,请先在控制台确认模型名称和权限,再修改环境变量。
7. 运行结果与效果验证
正常运行后,你会在终端里看到多轮交互。第一轮模型通常会输出一个比较直接的 Python 代码,比如用sympy.primerange生成素数集合,然后循环判断每个偶数是否能拆成两个素数之和。如果第一轮代码运行成功,并且输出的failures列表为空,程序就会打印“验证通过”并退出,整个过程可能只需要十几秒。
如果第一轮代码出现了ModuleNotFoundError或语法错误,程序不会直接崩溃,而是会把错误信息拼进下一轮用户消息,让模型重新生成代码。你会看到终端里出现多轮“第 N 轮模型输出”,每一轮都针对上一轮的报错进行修正。这种“让模型自己 debug”的过程,才是 Agent 数学验证的核心体验。
判断成功有三个标准。第一,程序退出码为 0,没有抛未捕获异常。第二,终端中出现了“结论:在 4 到 100 范围内,验证通过”这类明确输出,说明模型生成的代码和人类预期一致。第三,查看_agent_temp.py临时文件,代码逻辑确实遍历了目标区间并统计了失败列表。如果第一轮就通过,说明模型能力足够;如果需要三到四轮才通过,说明提示词还需要优化。
如果运行失败,第一步不是改模型,而是检查三样东西:环境变量里的 API Key 是否正确、模型名是否真实存在、本地 Python 能否正常执行。这三个问题占掉了 Agent 项目初跑时的大部分异常。排掉它们之后,再进入 JSON 解析、代码超时、数学逻辑错误等后续问题。
8. 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 返回 401 或 invalid_api_key | API Key 错误或未设置 | 检查环境变量,确认 Key 是否复制完整 | 重新创建 Key,用export设置 |
| 返回 429 或 rate limit | 请求频率超过配额 | 查看账户用量和速率限制 | 降低请求频率,增加重试等待 |
| 模型名不存在 | OPENAI_MODEL 填错了 | 查看控制台可用的模型列表 | 换成账号实际可用的模型名 |
| JSON 解析失败 | 模型输出包含 Markdown 或说明文字 | 打印模型输出原文,看是否有额外字符 | 在系统提示词中强调只输出合法 JSON |
| 代码执行超时 | 脚本陷入死循环或计算量过大 | 缩小问题范围,检查timeout参数 | 给 subprocess 设置合理超时,降低数据规模 |
| 结果不稳定 | 模型随机性或评测标准不一致 | 固定 temperature=0,保存完整日志 | 多次运行取多数结果,统一判定脚本 |
这些排查点看起来简单,但在真实项目里非常消耗时间。尤其是“模型输出不按 JSON 来”这个问题,几乎每个做过 Agent 的人都遇到过。与其埋怨模型,不如在设计提示词时就把输出格式约束到最严格,并在解析层多做一层防御。如果模型仍然输出不规范,还可以在后处理阶段剥离代码块标记,再尝试解析。
9. 最佳实践与工程建议
先说安全边界。Agent 会执行模型生成的任意代码,这本身就是高风险行为。开发环境里可以跑临时脚本,但任何接近生产环境的系统,都应该把代码执行放到 Docker 容器或独立沙箱中,并限制网络访问、文件读写范围、内存和 CPU 配额。同时,设置每轮超时、最大步数和 API 预算上限。不要让 Agent 无限循环,更不要让它拥有宿主机上的所有权限。API Key 的权限要按项目隔离,日志里不要明文打印完整 Key。
其次是成本控制。数学难题的 Agent 流程通常需要多轮调用,每一次模型输出都要消耗 token,代码运行失败时还会叠加后续重新生成的费用。建议在进入正式评测前,先用 2 到 3 道难度较低的题跑通流程,估算出每道题的平均步数和平均成本,再决定要不要上全量难题。对于重复使用的固定验证逻辑,可以放在代码模板中,不必每次都让模型从零生成,这样既省钱又稳定。
第三是提示词与评测分离。把“解题”和“判题”分成两个模块:Agent 负责生成代码,评测模块负责执行断言、比较结果、生成结构化日志。这样做的好处是:只要评测模块稳定,Agent 怎么改都不会影响最终判定标准。在复现工作中尤其重要,因为复现的不仅是题目答案,还有可比较的日志和评估指标。Fable 能 24 小时复现 5 道题,背后一定有严格的结构化日志和评测脚本,否则很难在短时间内确认“这题真的成功了”。
第四是随机性管理。Agent 的每次运行都可能因模型采样、环境差异而不同。复现时建议固定temperature=0,并记录模型版本、系统提示词、题目原文、每轮输出、执行结果和最终结论。把这些信息完整保存下来,不同团队之间才能互相参照。否则你复现了 5 道题,别人复现不出来,很难定位是模型变了、提示词不同,还是评价标准不一致。
最后是团队协作。如果你和同事一起做数学 Agent,建议把题目集、评测脚本、Prompt 模板和运行记录放在同一个仓库里,用 Git 管理变更。任何一次“成功复现”,都应该能通过一条命令重跑完整流程。这样,24 小时复现 5 道题就不再是个别人的经验,而是整个团队的工程资产。
10. 总结与后续学习方向
OpenAI 能解开 10 道题,Fable 能在 24 小时内复现 5 道,这两个数字本身并不是重点。重点是:Agent 已经变成一套可以被工程复制的“科研装置”。以后数学难题的竞争,可能不只是谁的模型更聪明,而是谁的执行环境更稳定、评测集更干净、反馈循环更高效。对你个人来说,这篇文章的核心收获是一套可运行的最小闭环:把题目转成断言,让模型生成代码,用临时文件执行,再把结果反馈给模型,最终得到可验证的结论。
下一步,建议你先用上面的脚本跑通一个简单题目,比如哥德巴赫猜想验证;然后尝试替换成更难的数论或组合问题,观察模型在几轮内能修正自己;再往后,可以阅读 Codex harness 的源码,理解官方如何做沙箱隔离、工具调用和结果聚合。你会发现,真正值得复现的不是那 10 道题,而是 Agent 不断试错的那一整套“工作方式”。复现不是目的,把复现能力做成自己团队的工程能力,才是目的。