上周在复现一个多步数学推理任务时,我遇到了一个典型问题:Agent 在解 (3x+5)/2 = 7 这种方程时,竟然直接输出了 x=3,跳过了关键的移项、化简步骤。检查日志才发现,模型在思维链中生成了正确步骤,却在最终输出时“偷懒”跳步了。这让我意识到,构建数学推理 Agent 远不是简单拼接 Prompt 和调用 API 那么简单——它本质上是在构建一个具备严格逻辑执行能力的符号计算流程

今天我们就来拆解这个问题,手把手搭一个能真正“一步步思考”的数学推理 Agent。


二、设计核心:把“思维过程”变成可检查的中间状态

直接让大模型输出最终答案,等于把可靠性交给了黑盒。我们的设计原则是:让推理过程成为一系列可验证的中间状态。这里借鉴了程序语言里的“解释器”思想:每一步操作都是对表达式树的合法变换。

先看一个反模式(别这样写):

# 糟糕的Prompt设计
prompt = "请解方程 (3x+5)/2=7,直接输出x的值。"
# 模型很可能跳过步骤直接猜答案

改成这样:

# 把任务拆成可执行的步骤
steps = [
    "将方程两边乘以2",
    "移项得到3x = ...",
    "两边除以3",
    "得到x的值"
]

但这样还是靠模型自觉。我们需要更强的约束。


三、实现关键:用结构化输出锁死推理链

我最终采用的方案是 JSON + 形式化描述。让 Agent 每步输出一个包含三个字段的结构:

{
  "step": 1,
  "operation": "multiply_both_sides",
  "expression": "2*(3x+5)/2 = 7*2",
  "result": "3x+5 = 14"  # 这是必须填的,逼它写出变换结果
}

这里踩过坑:最初没写 result 字段,模型经常在 expression 里写个形式变换就结束,实际计算还是错的。加上 result 等于强制它执行计算。

核心解析函数大概长这样:

def parse_step(response_text):
    # 正则或json解析提取操作和结果
    # 关键:立即用sympy验证结果的数学正确性
    # 如果验证失败,让Agent重试这一步
    # 这个验证循环是可靠性的基石

四、操作库设计:有限动作集比自由发挥更可靠

早期版本我让模型“自由选择任何数学操作”,结果什么奇葩步骤都有。后来学乖了:预定义操作集,像给计算器设计按钮。

ALLOWED_OPERATIONS = {
    "multiply_both_sides": "两边同乘某数",
    "add_to_both_sides": "两边同加某数",
    "simplify": "化简表达式",
    "substitute": "代入已知值",
    # 不超过10个基本操作
}

每个操作对应一个验证函数。比如 multiply_both_sides 会检查:

  1. 乘数不能为零(虽然明显,但模型真干过)
  2. 乘法分配律是否应用正确
  3. 是否两边都乘了

这实际上构建了一个数学推理的有限状态机


五、错误处理:把“跑偏”拉回正轨

模型在复杂问题中常犯两类错误:

  1. 符号错误:比如把 -(x+3) 写成 -x+3
  2. 逻辑跳跃:比如从 x^2=9 直接到 x=3(漏了负根)

我的处理策略是分层拦截:

def validate_step(step):
    if syntax_error(step):          # 语法层
        return "表达式格式错误,请检查括号"
    if math_error(step):            # 数学规则层
        return "分配律应用错误,注意负号"
    if logic_jump(step):            # 逻辑完整层
        return "缺少必要步骤,请写出展开过程"
    # 验证通过才继续

关键技巧:错误提示不直接给答案,而是给“错误类型+修正方向”。比如发现漏了负根,不说“还有x=-3”,而说“平方根需要考虑正负两种情况”。这保留了推理的连续性。


六、多步问题的流程控制

解复杂方程时,步骤数不确定。我用了目标驱动+超时回溯机制:

while not is_solved(current_expr):
    goal = generate_subgoal(current_expr, target_form)
    # 例如当前是分式,目标就是消去分母
    
    for attempt in range(MAX_RETRY):
        step = agent_execute(current_expr, goal)
        if validate(step):
            current_expr = step.result
            break
    else:
        # 重试失败,回溯到上一步尝试其他路径
        backtrack()

这里有个经验:记录完整状态转移图。不仅为了回溯,更是宝贵的调试数据。我经常发现模型在特定表达式结构(比如多层分式)下容易重复犯错,这些数据后来成了优化操作库的依据。


七、效果对比与局限性

实现这个框架后,在公开数据集 GSM8K 上的测试显示:

  • 基础 Prompt 方法:单次正确率 ~72%
  • 我们的结构化 Agent:单次正确率 ~89%(5步内问题)

但仍有明显局限:

  1. 符号计算能力边界:超过三元方程组或高次方程,正确率骤降
  2. 几何问题处理弱:需要图像转文本的额外模块
  3. 证明类问题吃力:归纳法、反证法这类需要“构思”而不仅是“计算”的任务

这不是模型能力问题,而是我们的“操作库”设计问题——把几何证明步骤形式化,是另一个层面的挑战。


八、给实践者的几点经验

  1. 从简单操作库开始:先实现加减乘除、移项、合并同类项这5个核心操作,覆盖80%初中数学题。别一开始就想搞微积分。

  2. 验证比生成更重要:花60%精力在验证逻辑上。我甚至写了个“错误模式检测器”,专门识别模型常犯的7类数学错误(如符号错误、漏解、循环论证等)。

  3. 让Agent自己写注释:要求每一步输出简短理由,如“这里乘以2是为了消去分母”。这些理由文本是后续优化的重要数据。

  4. 保留人工介入接口:复杂问题做到一半卡住时,允许人工指定下一步操作。这些干预数据是训练改进的黄金样本。

  5. 性能不是首要目标:一个需要10步但正确率95%的Agent,比3步但正确率70%的有用得多。数学推理,正确性压倒一切。


最后的话

构建数学推理Agent的过程,很像在教一个聪明的孩子学数学:你不能只给答案,也不能完全放任。关键是在“固定规则”和“灵活思考”之间找到平衡点。这个平衡点随着模型能力进化在移动——GPT-3时代需要严格约束,GPT-4时代可以适当放宽。

但核心思想不变:把模糊的“思考”变成可检查的“状态转移”。这套方法论不止用于数学,任何需要逻辑链的任务(代码调试、法律分析、实验设计)都可以借鉴这个框架。

下次遇到模型“跳步”或“犯低级错误”时,别只抱怨模型能力——想想你的框架给了它多少犯错的自由空间。控制与放开的艺术,就是我们设计者的价值所在。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐