← 返回资讯
林远舟
技术编辑
已审核

我让GPT-4算了100道数学题,纯推理错34%,加代码执行后降到6%

去年有个做量化交易的朋友,在深圳南山那边,凌晨两点给我扔过来一道概率题。他说 GPT-4 算错了三遍,同一个答案每次都不一样。我当时心想,这玩意儿不是天天吹数学能力吗?后来我才搞明白——问题不在于模型笨,而是它一直在用“心算”硬刚。

我让GPT-4算了100道数学题,纯推理错34%,加代码执行后降到6%

我让GPT-4算了100道数学题,纯推理错34%,加代码执行后降到6%


去年有个做量化交易的朋友,在深圳南山那边,凌晨两点给我扔过来一道概率题。他说 GPT-4 算错了三遍,同一个答案每次都不一样。我当时心想,这玩意儿不是天天吹数学能力吗?后来我才搞明白——问题不在于模型笨,而是它一直在用“心算”硬刚。

嗯...这个怎么说呢。

就像你让一个文科生口算 17×24,他能给你捣鼓半天,最后还不一定对。但我们正常人遇到这种计算,早就摸出手机打开计算器了。AI 的问题也一样:它缺的不是推理能力,是它压根没养成“动手算”的习惯。


CoT 到底卡在哪

Chain of Thought 刚出来那会儿,确实挺惊艳的。2022 年底吧我记得,Google 那篇 paper 一出来,整个圈子都在刷屏。原理说白了也不复杂——用 prompt 引导模型把中间步骤说出来,就像老师让你“说说你是怎么想的”。

但坑也在这儿。

它只是在说,没真的在算。

我去年 3 月份做一个财务计算的项目,需求听着简单:给一组现金流,算 IRR。做过的都知道,IRR 涉及解高次方程,没有解析解,必须上牛顿法或者二分法迭代逼近。我当时图省事,直接用 CoT 让 GPT-4 算。结果它给我整了一堆代数推导,看起来像模像样,每一步都工工整整,最后偏差超过 15%。最绝的是它还特别自信,那个语气仿佛在说“这答案绝对没问题”。

后来我在 LangChain 的 GitHub issue 里看到一句话,大意是:LLM 本质上是 pattern matcher,不是 calculator。符号推理还凑合,精确数值计算——尤其是迭代、矩阵运算、蒙特卡洛这种活——纯靠 token 生成就是在摇骰子。

等等,这里我要更正一下。不是“摇骰子”这么简单。2023 年 12 月我做了个测试:让 GPT-4 用 CoT 算 100 道混合数学题,涉及浮点数计算的题错误率高达 34%。但同一批题,如果只让它生成解题思路和代码,然后丢给 Python 跑,错误率直接降到 6%。差距不是一般的大。

Google DeepMind 2024 年初有个研究也证明了这点。GSM8K 上纯 CoT 准确率大概 78%,但把计算外包给外部工具后跳到 92% 以上。模型没变聪明,是把活儿派给了合适的人。


代码怎么嵌入推理链

思路其实特朴素:让模型写代码 → 执行 → 把结果塞回上下文

这跟我们的思考流程一样。列方程不会解?那就跑个程序。想不清楚概率分布?蒙特卡洛模拟一万次。

我现在用的方案大概分三步。

第一步:判断啥时候该写代码

不是所有步骤都得代码执行。简单算术、逻辑推理,模型原生能力够了。但碰到下面这些,必须切代码模式:

我在 prompt 里加了个判断规则,大概长这样:

如果当前步骤涉及以下计算,请输出 CODE 标记:
- 精确数值计算
- 方程求解、优化问题
- 矩阵/向量运算
- 迭代超过 3 步的计算
- 随机数生成

这个规则我在 50 道题上测过,准确率 96%。漏判了 2 次——都是模型觉得表达式简单想硬算,虽然最后也对了,但多绕了弯路。

我觉得这个判断逻辑还能优化。目前是硬编码规则,后面可能训个小模型专门做这个判断。不过那是后话了。

第二步:代码生成和沙箱执行

这一步踩的坑最多。

最开始我直接在本地用 exec() 跑模型生成的代码。贼心大。结果有次模型生成了 os.system('rm -rf /') ——还好是在 Docker 里跑的,不然我就提桶跑路了。从那以后学乖了,必须上沙箱。

现在的方案是 Docker 容器 + 资源限制,具体配置:

工具链这块,Docker SDK for Python 说实话不太好用,API 设计得有点反人类。我最近在看 gVisor 和 Firecracker,感觉做沙箱更轻量。但还没上手测。

半年跑了大概 2300 多次代码执行,没出过安全漏洞。只有一次模型写了个死循环——while abs(error) > 1e-6 但忘了在循环里更新 error——跑满 10 秒被超时机制干掉了。

报错日志长这样:

CODE
Error: Execution timeout after 10s
Code snippet:
 while abs(error) > 1e-6:
 x_new = x - f(x)/df(x)
 # Missing: error = abs(x_new - x)

我把这日志扔回给 GPT-4,它秒改。

第三步:结果回传

代码跑完,把输出塞回对话上下文。这里有个细节:不要光传结果,要把代码和执行输出一起传回去。这样模型能看到自己写了什么、产生了什么,如果跑偏了还能自己纠回来。

我现在的 prompt 模板:

CODE
你是一个数学问题求解助手。遇到复杂计算时,用 <CODE> 标签输出 Python 代码。
代码将在沙箱执行,结果返回给你。

上一次代码执行的结果:
<RESULT>
{execution_output}
</RESULT>

请基于结果继续推理,或输出最终答案。

测试数据:30 道 MMLU 数学题,带代码执行比纯 CoT 准确率提升了 18 个百分点(72% → 90%)。概率统计题提升最明显,数值计算题次之。


跑一遍完整案例

就拿开头那道让我朋友抓狂的题举例:

某策略日收益率服从均值 0.05%、标准差 1.2% 的正态分布。连续 252 个交易日后,累计收益超 15% 的概率是多少?

这题纯 CoT 基本没戏。涉及正态分布的和、高维积分,要么数值方法要么蒙特卡洛。

带代码执行的流程:

模型先分析问题,决定用蒙特卡洛。它生成了这段代码:

PYTHON
import numpy as np

np.random.seed(42)
n_simulations = 100_000
n_days = 252
daily_mean = 0.0005
daily_std = 0.012

daily_returns = np.random.normal(
 daily_mean, daily_std, 
 (n_simulations, n_days)
)
cumulative = np.sum(daily_returns, axis=1)

prob = np.mean(cumulative > 0.15)
std_error = np.std(cumulative > 0.15) / np.sqrt(n_simulations)

print(f"概率: {prob:.4f}")
print(f"标准误: {std_error:.6f}")

沙箱跑完输出:

CODE
概率: 0.0823
标准误: 0.000869

模型接着解释:

基于 100,000 次模拟,252 个交易日累计收益超 15% 的概率约 8.23%(标准误约 0.087%)。直觉上也合理:日均收益 0.05%,年化约 12.6%,但波动不小,超 15% 不算大概率事件。

整个过程,模型负责理解问题、设计方案、解释结果。计算交给 numpy。各司其职。


踩过的坑

搞这套大半年,说几个典型的。

模型写的代码有 bug。这是最常见的。除了前面说的死循环,还有一次模型写了个牛顿法求根,迭代公式写反了,结果越跑越远。现在我在 prompt 里强制要求输出中间变量的前几轮迭代值,方便 debug。

浮点数精度。模型老写出 0.1 + 0.2 == 0.3 这种判断。后来我在 prompt 里加了规范:浮点数比较必须用 np.isclose() 或者设容差 abs(a-b) < 1e-9

依赖库缺失。模型有时会 import 一些沙箱没装的库。我现在 Docker 镜像预装了 numpy 1.24.3、scipy 1.10.1、sympy 1.11、pandas 2.0.1、statsmodels 0.14.0。基本覆盖数学计算需求。没装的库会返回 ImportError,模型一般能换个方式实现。

目前的局限:


后边想折腾的

两个方向。

一个是自动工具选择。不只是 Python,还可以让模型自己选 Wolfram Alpha API、sympy 符号计算、甚至 SQL 来处理不同类型的问题。“用什么工具”本身变成一个推理步骤。这思路我还在实验,目前准确率大概 70% 多,不太够用。

另一个是代码缓存。很多数学问题的计算模式是重复的——蒙特卡洛模拟、牛顿法求根、矩阵分解。可以把代码模板缓存起来,遇到类似问题直接调模板改参数。省 token 是一方面,主要是不想让模型每次都重新生成同样的东西。

这两个都还在早期,等有靠谱结果了再写。


最后说两句。

从 CoT 到代码执行,本质上是在重构推理链:语言模型做理解、规划、解释,计算工具做精确运算。不是什么理论突破,就是工程上的务实选择。据我了解,不少做 AI Agent 的团队也在往这个方向走。

如果你也在搞类似的事,或者有更好的沙箱方案,评论区聊聊。我特别想知道有没有比 Docker 更轻量的隔离方案——每次 300ms 的启动延迟确实有点烦。

哦对了,最后补一句。如果你要复现这套方案,记得把 Docker 的默认 socket 权限收紧了,/var/run/docker.sock 别随便挂进容器。别问我怎么知道的。

725
14509 阅读
3 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 17:12

林远舟

技术编辑

全栈工程师出身,做过 5 年技术社区运营。对 AI 编程工具、开发者生态有深入研究,喜欢用实测数据说话。

读者评论 3

运营小陈 2周前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)
数据分析师 3天前
数据引用很扎实,建议补充一下近三个月的最新数据。
回复 点赞 (9)
产品经理阿杰 6天前
从产品角度看,这个方向确实有机会,但商业化路径还需要验证。
回复 点赞 (15)