别再让大模型做数学了
他们做得不好。下面是解决办法。
你知道语言模型有什么奇怪之处吗?它们可以解释量子力学、写诗、调试你的 TypeScript……但要它们把 18472 乘以 9347,往往会自信地给出相差数千的答案。
这曾让我困惑,直到我意识到我们到底在让它们做什么。我们在让一个模式匹配引擎充当计算器。这就好比让体操选手去平衡你的账本,因为他们懂得“平衡”这个概念。
问题在于,LLM 并不进行任何计算。当你问 GPT 或 Claude “2 + 2 等于多少”时,它们并没有在加法,而是预测在 “2 + 2 =” 之后最可能出现的 token 是 “4”。大多数情况下这能很好地工作,因为这些模式在它们的训练数据中出现过。但一旦超出简单算术,进入多步计算或涉及训练中不常见的数字时,你基本上就是在掷骰子。
我最近就在审查一段使用顶级模型计算房贷付款的代码时撞到了这个问题。模型自信满满地给出答案,却偏差了每月 400 美元。这种错误才是真正会产生影响的。
即使模型在推理方面变得更强(据说 GPT‑5 已经有所提升),它们仍然是在进行高级模式匹配,而不是符号计算。对于创意工作和自然语言任务,这种概率性质正是它们的魔力所在。对于数学?就没那么神奇了。
实际解决方案是什么?
答案不是等更聪明的模型出现,而是给模型配上合适的工具。
想想如果你在构建一个非 AI 系统时会怎么解决这个问题。你不会自己写数学逻辑,而是直接使用库。同样的原则也适用于这里,只是我们现在要教 LLM 何时以及如何使用那个库。
现代 AI SDK 的工具调用功能让我们可以向模型提供可调用的结构化函数。我们不再强迫 LLM 假装会数学,而是给它真正能做数学的东西:一个符号数学引擎。
我一直在使用 AI SDK v5 和 v6 搭配 CortexJS Compute Engine。SDK 负责编排和工具路由,而 CortexJS 则处理从基础算术到微积分的所有计算。这是一种出乎意料的干净的职责分离。
bun add ai @ai-sdk/anthropic @cortex-js/compute-engine zod构建数学工具
实现比你想象的更直接。我们要做的是在 LLM 的自然语言理解和真实的数学计算之间搭建一座桥梁。
import { generateText, stepCountIs, tool } from 'ai';import { ComputeEngine } from '@cortex-js/compute-engine';import { z } from 'zod';
// 只初始化一次引擎const ce = new ComputeEngine();
const mathTool = tool({ description: '评估数学表达式并求解方程,保证结果准确。必须用于所有数学操作以验证正确性——不要尝试心算。支持算术、代数、微积分以及复合运算。可一次处理多个表达式。', parameters: z.object({ expressions: z.array(z.string()).describe( '数学表达式数组,使用 LaTeX 或普通记法,例如 ["2 + 2", "\\frac{x^2 + 1}{x - 1}", "\\int x^2 dx"]' ), }), execute: async ({ expressions }) => { // 并行(或批量)处理所有表达式 return expressions.map(expression => { try { const result = ce.parse(expression).evaluate(); return { expression, result: result.toString(), latex: result.latex, }; } catch (error) { return { expression, error: (error as Error).message }; } }); },});需要注意的几点:
- 描述承担了大量工作。那句 “必须用于” 看似强硬,但我的经验是,明确告知模型何时使用工具,是从偶尔成功到可靠运行的关键。这相当于在工具层面的提示工程。
- 通过
expressions数组进行批处理的意义往往被低估。每一次模型调用都有延迟。如果你在求解方程组或进行多步计算,逐个处理会导致糟糕的用户体验。批处理只需一次往返就能解决十个问题。 - 使用符号引擎而不是直接
eval()(请勿使用eval())让我们拥有真正的数学理解。引擎会解析意图、处理 LaTeX,并能处理导数和积分。我们做的不只是计算,而是完整的数学运算。 - 错误处理是按表达式粒度进行的。某个计算失败时,我们返回该错误但继续处理其余表达式。这让模型能够看到哪些成功、哪些失败,从而在后续步骤中自行纠正。
实际使用
挑选一个通常会让原始模型产生幻觉的例子:
import { anthropic } from '@ai-sdk/anthropic';
const { text } = await generateText({ model: anthropic('claude-sonnet-4-5'), prompt: '计算 18472 × 9347,除以 127,然后取结果的平方根。', tools: { mathTool }, stopWhen: stepCountIs(5), // 最多允许五次模型/工具交互});
console.log(text);模型识别到数学需求,知道需要精确计算,调用工具获取准确结果,随后用自然语言解释。每个组件都在各自擅长的领域发挥作用。
超出基础算术
因为我们使用的是符号引擎,这种方式能够处理普通计算器工具根本碰不到的情形。
想求代数方程?直接写 “Solve these equations: 3x + 7 = 22 and 2y - 5 = 13” 就能得到答案。
需要微积分? “Find the derivative of x^3 + 2x^2 and evaluate it at x = 2” 只会触发一次工具调用。
如果你在做教育类应用,LaTeX 支持尤为便利。引擎本身就能识别 LaTeX 输入,并返回可直接渲染的结果,无需额外的解析步骤。
更大的视角
我认为这种模式的意义超出单纯的数学。我们真正做的,是在承认 LLM 局限的同时,充分利用它的优势——对意图的理解、自然语言的解析以及工作流的编排。它们并不是计算器、数据库或文件系统。
每次让 LLM 去完成确定性任务,都是在与其本性作对。可是当我们把这种自然语言理解与专门处理确定性部分的工具结合起来时,才会出现有趣的效果。
数学工具只是一个例子。同样的原则同样适用于日期处理、金融计算、图像处理、数据库查询……只要精度比创意更重要的场景。让模型先弄清用户需求,再把实际运算交给专门为此构建的组件。
这是一种构建 AI 应用的思维转变。不是在问 “模型能做到吗?” 而是问 “模型能把这些工作编排起来吗?” 表述的细微差别,却决定了可靠性的天壤之别。