从LLM到数学Agent:用工具与验证构建可靠解题系统
数学题做完不会验算算到一半把题目条件记岔这是大语言模型做数学题最常见的两个毛病。标题里带“AI for Math速成”老读者应该明白这个系列前面几篇把数学数据、提示词、微调都已经过了一遍这一期专门聊聊怎么把单个模型升级成一个能规划、能调工具、能自我检查的数学Agent。一句话概括就是从LLM到Agent本质上是让模型从“凭记忆答题”变成“用工具解题”。这套思路不仅适合想给数学解题系统做落地的人任何在处理复杂推理任务的场景——金融数据解读、代码报错排查、物理题推导——都可以借用同一个框架。这篇文章会沿着设计动机、模块拆解、落地代码、评测收敛、常见坑的顺序往下走全程给可复现的做法。1. 为什么单靠LLM做数学总是差一口气先别急着堆Agent得先把问题的病根看清楚。我最早做数学解题的时候用的是最直接的路子把题目扔给大模型让它一步步写推理过程。效果看着还行小学应用题可以拿高分但一上到竞赛题和大学数学就开始露馅。不是不会是“会但不严谨”——经常思路完全正确中间某一步代数变换错了或者多位数乘法口算出问题整道题全盘皆输。这其实是语言模型的先天缺陷它擅长的是“词的组合”而不是“符号的精确运算”。1.1 数学推理对模型的三个苛刻要求数学和其他生成任务最大的不同是它对“每一步”都有硬约束。写作文错一个字不影响整体但数学里错一个符号结论就废了。具体来说有三重考验。第一重是逻辑链条要闭链。模型在生成时是一个词一个词往外蹦的它的注意力窗口有限一旦推导超过十步前面的条件会产生“注意力衰减”写到后面把前面某个前提忘了这在数学上就是致命的。第二重是算术和代数要零容错。8位以内的加减乘除、符号展开、因式分解这些对人类来说机械但可靠的步骤对语言模型反而是最不擅长的地方。它没有内置的运算器全靠token概率“猜”猜错一个数字是常有的事。第三重是验证能力。人类做完题会回头检查把解代回原方程看两边相等不相等。但纯用LLM做这个自查它会倾向于“自己怎么算的就怎么说对的”这是模型固有的确认偏误——它不是在验算而是在续写一个呼应前文的答案。1.2 能力边界测试模型擅长什么不擅长什么我在早期做了一个小实验把同一批数学题分成三类跑纯LLM概念解释、计算求解、证明推导。结果很有意思。概念解释类比如“解释什么是特征向量”表现最好因为这类问题在训练数据里出现过成千上万次模型本质是在做“语义回忆”。计算求解类尤其涉及多位乘除法、复合函数求导后化简的错误率明显上升因为它是真在“猜数”。证明推导类最难推理链一长经常出现逻辑断层甚至自己发明一个不存在的定理。所以结论很清晰LLM在数学上真正的价值不是“计算器”而是“解题策略生成器”。它能理解题意、给出一个大致可行的思路框架、判断哪条路可能走得通但具体的计算和严密的推导应该交给确定性的工具完成。这也是做数学Agent最重要的心智转变——别把模型当全能选手把它当队长真正的“苦力活”让队友干。1.3 解题的本质切换从“背诵答案”到“调用能力”这个切换很关键。传统的LLM数学方案本质是在让模型“想起答案”而Agent方案是让模型“组织解题过程”。前者依赖预训练阶段见过多少类似题后者依赖工具调用的可靠性和循环验证的严密性。举个直观的例子“求两个矩阵的乘积并验证是否可逆”。纯LLM的做法是凭空生成矩阵乘法的结果。Agent的做法是调用符号计算库完成乘法再通过计算行列式判断可逆性。后者每一步都有据可查错在哪一步一目了然还能把中间结果打印出来给用户看。所以Agent不是智商升级是工作方式升级——从“独立答题”变成“监督解题生产线”。认识到这一点后面所有的模块设计就有了逻辑起点。2. Agent整体架构拆解大脑、手脚和检查员既然定位变成了“队长队友”模式架构上就要明确谁是大脑谁动手谁验收。我在搭建自己的数学Agent原型系统时参考了常见的ReAct模式做了本地化调整核心是五个模块规划器、执行器、工具集、短期记忆、验证器。它们的配合关系不复杂但每一环都需要仔细设计。2.1 模块职责为什么这样分工规划器Planner就是那个LLM大脑负责读题、定策略、分解步骤。它输出的是一个“行动序列”比如“先计算导数然后求根判断单调性”。执行器Executor负责接住规划器下发的指令去调度具体工具——调用符号计算库、执行Python代码、查公式表。工具集Tools是一组确定性的程序包括代码解释器、符号计算内核、图形绘制、数值计算等它们不接受模糊指令天然精确。短期记忆负责保存中间结果。很多Agent跑着跑着忘记前面算到哪就是因为没有把“子目标完成情况”显式记录下来。验证器Verifier独立于规划器存在它的任务是对最终结果做反代测试、单位检查、边界验证。把验证和生成分开才不会被模型的“自我感觉良好”带偏。2.2 核心循环流程思考、行动、观察的闭环整个Agent的运行是一个循环逻辑上和人类的做题过程很一致规划器读取题目生成当前子目标例如“化简方程左边”。执行器把子目标翻译成具体工具调用交给工具集。工具集返回精确结果写入短期记忆。观察器把结果格式化成上下文反馈给规划器。规划器判断是否完成子目标决定是继续下一步还是终止。全部步骤完成后验证器对最终答案做独立校验。这个循环听起来简单但实际工程里“格式化反馈”这一步最容易被忽视。工具返回的结果是一堆矩阵数字直接塞回给规划器模型会被噪音干扰忘记自己在干嘛。正确做法是把结果提炼成一句话摘要加关键数值——事件过程要完整信息要有节奏。我在实际中踩过这个坑后面会展开讲。2.3 为什么选择确定性工具而非“再问一次模型”有一个很容易被新手绕进去的问题有些计算比如那个矩阵乘法我再让模型算一遍、给两个答案取相同值不就完了吗多模型投票确实能降低错误率但解决不了系统性问题——如果模型因为某个概念理解错了它的所有输出会在同一个地方错。投票只能过滤随机错误不能过滤系统性错误。而确定性工具比如符号计算库遵循的是严格的算法规则不存在“状态不好算错”的情况。只要你的调用参数传对了结果就是正确的。这就把“随机性”从最关键的运算环节中彻底剥离了只在“策略选择”层面保留LLM的灵活性。这个架构选型的底层逻辑说穿了就一句话把容易出错但需要创造力的部分交给模型把必须精确但机械的部分交给程序。3. 核心落地实操从零搭一个可运行的数学Agent理论说太多容易飘真正动手你会发现另一堆麻烦。我在这里给出一套我从本地原型系统里积累的具体实践方案尽量做到拿来就能改。3.1 环境准备与最小依赖搭建一个数学Agent不需要重型框架我的建议是用最轻的Python环境起步。核心依赖就三块一个能调用大模型的HTTP库或者官方SDK、一个符号计算库用于精确代数运算、一个代码解释器用于数值计算和图线绘制。调试阶段我推荐直接在Notebook里跑循环不用一上来就上工程框架降低出问题的范围。一个可以快速起步的最小目录结构大概是math_agent/ agent.py # 主循环逻辑规划器执行器 tools.py # 工具集符号计算、数值计算、绘图 llm_api.py # 统一的大模型调用封装 memory.py # 短期记忆存储维护子目标状态 prompt.py # 规划器和验证器的提示词模板每一块代码都控制在几百行以内重点是跑通“思考→行动→观察”的最小闭环再逐个加模块。3.2 关键代码示例Agent主循环的骨架实现下面这一段是我实际调试中用的简化版主循环。它没有做精巧的状态管理但足够展示Agent的本质逻辑。核心是用一个while循环控制迭代次数防止Agent跑飞然后每一步根据规划器的输出决定调用哪个工具。# agent.py — 最小Agent循环骨架 import json from tools import symbolic_simplify, numeric_eval, plot_function def run_agent(question, max_steps10): memory {question: question, steps: [], result: None} for step in range(max_steps): # 1. 规划器根据当前记忆生成下一步行动 planner_output llm_call( system你是数学解题规划器。只输出JSON动作不要解释。, promptformat_planner_prompt(memory) ) action json.loads(planner_output) # 2. 执行器分发到具体工具 if action[tool] symbolic_simplify: output symbolic_simplify(action[expr]) elif action[tool] numeric_eval: output numeric_eval(action[expr]) elif action[tool] plot: output plot_function(action[expr]) elif action[tool] finish: memory[result] action[answer] break else: output f未知工具: {action[tool]} # 3. 把观察结果压缩后写回记忆 memory[steps].append({ tool: action[tool], input: action.get(expr, ), output: summarize_output(output) }) # 4. 验证器独立检查 verification verify_solution(memory) return memory[result], verification这个骨架有三个设计点值得注意。第一个是规划器必须“只输出JSON”不要自由文本。否则模型容易在“思考”里夹带计算过程又退化成硬算模式。我通过提示词强制约束并且在解析时用异常捕获兜底——模型偶尔会输出不规范JSON要能容错重试。第二个是每次工具返回结果后要对输出做“摘要化”summarize_output。这一步在骨架代码里只有一行但非常关键。比如符号计算返回一个巨长的表达式我会在摘要里保留“化简结果2x1”这种精炼信息同时截断过长的中间表达式避免上下文爆炸。第三个是验算要在循环外单独做。主循环跑完拿到答案再调用验证器反代检查两者逻辑不复用从设计上避免“又当运动员又当裁判员”。3.3 工具集的设计符号计算与数值计算的分工工具集是整个Agent可信度的基石。我的经验是分两层符号计算层处理代数推导问题数值计算层处理具体数值和绘图问题两者不要混用。符号计算层我推荐用符号计算库它能处理因式分解、求导积分、方程求解等代数任务。调用的时候有一点需要注意表达式解析要容错。模型生成的表达式字符串经常有多余空格、中文括号甚至绘制符残留需要先做清洗。我习惯写一个safe_parse函数把常见异常全部捕获返回给规划器一个明确错误信息而不是让整个Agent崩溃。数值计算层则负责代值计算、逼近求解和画函数图像。比如题目要求“在某个区间内找零点”符号方法求不出解析解的时候就用数值方法迭代逼近。实现上就是常规Python库没有特别多坑但要注意数值误差的展示格式——保留六位有效数字就好多了噪音反而干扰模型判断。有一类特殊工具公式查询器。有些竞赛题会用到冷门定理模型可能记错。我会在工具集里放一个结构化的公式库Agent需要时按名称检索。实测下来这比让模型死背公式靠谱得多属于“把外部知识做成可检索工具”的思路。3.4 验证器的设计与反代测试验证器是我认为整个Agent里性价比最高的模块一个简单的验证函数能堵住一半以上的低级错误。它做的事情很直接把最终答案代回原始条件检查等号是否成立、不等式是否满足、定义域边界是否正确。具体到一个“解方程”的场景验证器的逻辑是这样的拿到Agent给出的解x2把2代回原始方程两边算差值绝对值如果低于阈值就判定通过。如果在定义域内还需要逐个检查边界值。验证器本身不用LLM全部是确定性代码因此它的结论是可信任的——这就是“独立验证”的价值所在。但验证器能做的事情有限它只能验证“已知答案是否正确”没法验证“推导过程是否严谨”。所以我的Agent在输出最终答案时会同时给出“过程摘要验证结果”让用户自己判断。这有点像做实验写报告结论背后要有支持证据读者可以复核。这也是Agent相比纯LLM输出的最大优势——过程透明、结论可验证。3.5 一步到位完整运行的一个解题实例光说代码骨架可能还不够直观我来走一遍完整实例。假设题目是“解方程 2^(x1) 3^x并判断有几个解”。这个题涉及到指数方程和对数纯LLM容易在取对数化简时出错非常适合Agent来做。第1步规划器读题后决定先对等式两边取自然对数转换为线性方程。输出动作调用符号计算工具表达式为“log(2**(x1)) - log(3**x)”意图是化简。第2步符号计算库返回结果“log(2)·x log(2) - log(3)·x”。这个结果显示化简后的形式虽然不是传统的最简式但已经包含核心信息。第3步规划器观察结果后决定下一步解这个关于x的线性方程。它让工具库调用solve函数输入表达式“log(2) x(log(2) - log(3)) 0”。第4步工具库返回“x log(2)/(log(3) - log(2))”约等于1.7095。第5步验证器上场。它把x代回原方程两边分别算出2^(x1)和3^x的值计算相对误差判定通过。第6步Agent输出“一个解x≈1.7095验证通过”。这个例子你拿纯模型跑一遍大概率也能得到正确答案但如果数值复杂一点、特判多一点纯模型的翻车率马上上来。Agent方案的价值在复杂任务上会体现得指数级明显——因为它把每一步都放在了可检验的基础上。4. 提升数学Agent能力的三个进阶方向跑通最小闭环之后你会发现效果已经很不错了但要让它稳定地处理更难的问题还有三个方向需要投入。这三个方向不是相互独立的按照性价比排序先做提示工程再做验证器升级最后才是模型微调。4.1 提示工程进阶给Agent立一套“解题规范”很多Agent发挥不稳定卡在提示词的设计上。规划器和验证器需要的是不同风格的提示词不能一套走天下。规划器的提示词核心是“决策而不是计算”。我会明确告诉它你不需要执行计算你的任务是决定下一步做什么把计算交给工具。还有一条重要的约束如果判断某一工具返回的结果不对劲可以要求重新调用或换一种策略不要硬着头皮继续。验证器的提示词核心是“怀疑而不是确认”。如果让验证器也用“检查答案并返回通过”它就会站在Agent那边找借口。我的做法是明确告诉验证器“如果发现任何异常立刻报告”并且给它独立的工具权限它可以自己重算一遍关键数值。这样拆分后Agent的判断链路更清晰规划器是进攻手验证器是防守手两者在提示词层面就形成了正交。这是最简单、见效最快的优化手段新Agent到手先调提示词比换模型划算多了。4.2 过程监督从只查结果到监控每一步默认验证器只看最终答案但很多难解的题错因不在最后一步而在中间的某一步推导。一旦中间融入了错误的假设后面全对也是白搭。所以要引入过程监督——给Agent的每个核心步骤都打一个“可信度分数”。做法可以是这样的每完成一个子任务把该子任务的输入、输出、使用工具、执行耗时记成一条日志。验证模块不只看最终答案还抽查若干中间步骤对代数变换做数值代入检查。例如化简后的表达式我会用随机取几个数分别代入化简前后两侧如果不等立刻标注“该步骤有疑点”。这样做的代价是计算量增加了一些但换来的是错误定位能力。当一个Agent跑错了你能精确到“第三步化简有问题”调试效率完全不同。对于竞赛数学题这种多步骤推理场景过程监督是最值得投入的一项能力。4.3 换个更强的大脑数学模型的选型与微调取舍Agent的效果上限最终还是受制于规划器的聪明程度。如果规划器连思路方向都判断不了再好的工具也是白搭。所以选型很重要通用的模型在“语义理解”上强但“数学推理”能力中等尤其是涉及多步推导时指令遵循能力会下降。我自己的调研结论是优先选择在数学评测基准上表现较好的推理增强模型它们接受了更多数学数据训练更习惯符号和推导表达。如果预算允许还可以在垂直场景做小规模微调——用一批带“动作注释”的解题轨迹微调规划器让它学会“见到哪种题型应该优先尝试哪种工具序列”。但要泼一盆冷水微调投资大、周期长而且如果提示词体系和工具设计没理清微调并不能根治“策略混乱”的问题。我的建议是先花两周把提示词和验证器打磨到位再考虑微调。绝大多数场景前者提升幅度已经非常可观。5. 常见问题与排查技巧实录任何Agent系统运行起来都会有一堆幺蛾子。这一节把我实测中遇到的高频问题、成因和解决方案整理出来可以当速查表用。5.1 问题速查表现象常见原因排查思路与解决方案Agent反复调用同一个工具停不下来规划器陷入局部循环观察结果没有改变它下一步决策在提示词里加“如果某工具重复调用三次结果仍未变化换一种策略或直接结束”同时给最大步数硬限制工具调用时格式出错模型输出的JSON不规范或表达式字符串无法解析在调用工具前加解析层捕获所有异常提示词里给一个“正例反例”格式并开启重试机制符号计算返回结果与预期差太远表达式语义和模型理解不一致比如变量名混淆检查提示词里是否定义了变量的规范写法在工具层做变量名自动统一验证器误判该过不过、不该过过了阈值设置不合理或没检查定义域把判据分成多条独立规则代回测试、边界检查、量纲检查每条规则单独报告不要合成一个模糊分数上下文太长导致Agent“失忆”中间结果堆叠太多模型注意力被稀释增加摘要机制把每一步输出提炼成短句超过长度时启动“滚动压缩”把历史步骤压成一行总结输出答案虽然对但过程不可信模型反向编造步骤也就是“答案对了但思路是假的”在最终报告里强制列出“工具调用记录”贴出每一步的入参和出参对用户透明AI造的假过程一眼就能被识别5.2 我的调试顺序建议如果你刚搭好Agent开始跑测试我建议按这个顺序做问题排查第一步只跑题目观察循环是否正常走完检查有没有死循环和工具调用崩溃第二步加入验证器看最终结果准确率提升了多少同时收集验证失败案例这些案例是提示词优化的最佳素材第三步针对验证失败的题目做“行为轨迹回放”从日志里找Agent在哪个环节跑偏了对症下药。在这个阶段我一再提醒自己不要把Agent当黑盒。每次运行都保存完整日志包括每一步的规划器原始输出、工具入参、出参和耗时。这些日志是调试的命根子跑了一百道题之后你会发现自己对Agent行为模式的理解完全不一样了。5.3 稳定运行的一些小技巧最后分享几个我用真金白银换来的小技巧不高深但能让系统稳一个档次。第一给所有外部工具调用加超时控制。符号计算库偶尔会遇到超复杂表达式导致卡死给每个函数调用套上超时保护超时返回错误信息不要让Agent卡在一步上整个挂起。第二正确使用随机采样参数。规划器如果总选同一条路可以适当调高采样温度增加探索但验算环节必须把参数设为确定性否则逐字输出的小抖动会影响最终答案比对的可靠性。第三建立“结果置信度”概念。Agent跑完一道题除了给答案还应该输出验证通过与否、调试日志条数、总步数这些元信息。用户看到“验证失败”比看到“错误答案”体验好得多——至少Agent知道自己不行而不是硬撑。这个诚实机制也是Agent走向可信赖的第一步。6. 一些没有写进代码里的体会从LLM到Agent这一步我最大的感触是一定要接受“模型会犯错”这个前提。很多人做Agent潜意识里还是想找一颗“什么都会的聪明大脑”可真正的工程恰恰相反——它假定大脑一定会犯错然后用流程、工具、验证把所有错误隔绝在最终结果之外。这和写复杂软件时要加单元测试是同一个道理没有人指望编译器不报错而是靠测试把错误拦截在交付之前。在做这个数学Agent原型的过程中我反复体会到“工具调用记录”是一种非常有价值的副产品。它让AI系统的推理过程变得可以复盘、可以审计、可以追溯。不管是给学生讲题还是给领导汇报方案这个透明性都比“一个神奇的神秘答案”有价值得多。如果后面你想更进一步可以试着把Agent的日志结构化沉淀下来变成一套解题过程的知识库——这又是另一个值得展开的话题了。