4 minute read

“Goals tell you where you want to go. Systems determine what you actually do every day.” — Terry J. Govier

别再用正则过滤代码了!带你彻底搞懂 AST 沙箱的必要性

从“野蛮生长”的 Python REPL 到工业级的 AST 沙箱:构建金融级 Agent 的安全底座。

「如果你在处理涉及金额的金融系统,当 LLM Agent 吐出一个包含货币符号、百分号且嵌套了多重括号的计算结果时,你敢直接把这个结果写入数据库吗?」

在这篇文章中,我将带你拆解从“入门级”的 Python REPL 调用到“工业级”的 AST 沙箱架构的演进路径,教你如何构建一个不仅能跑通、而且真正具备金融级安全性的代码执行环境。

在构建 AI Agent 时,尤其是处理金融数据时,你会发现 LLM 在处理算术逻辑时表现出一种极度不稳定的“智力断层”。虽然最前沿的模型在很多任务上表现惊人,但一旦涉及到复杂的括号嵌套、财务会计符号(例如用括号表示负数损失)或混合单位的计算,它们依然极易产生幻觉。

很多初级工程师在面对这个问题时的直觉是:“既然模型算不准,那就给它个 Python 解释器(REPL)不就行了?”

这在 Demo 视频里看起来是完美的解决方案,但在生产环境中,这种做法简直是灾难的代名词。原因有两个:

  1. 安全漏洞的温床:直接暴露 Python 执行工具给 LLM,等于给黑客开了一扇大门。Prompt Injection 并不是玩笑,一个恶意用户只需构造包含 __import__('os').system(...) 的输入,就能在毫秒内让你的 Agent 变成攻击工具。
  2. 极高的执行脆性:金融数据往往不是标准格式。当你面对类似 "( $100 - $80 ) / 20%" 这种带有货币符号和百分号的字符串时,原生 Python 的 eval() 函数会因为无法解析 $ 和 % 字符而直接崩溃。

真正的工业级解法:AST 沙箱执行

在 AI-FDE-Playbook 中,我实现了一套专门为金融问答优化的硬核 AST(抽象语法树)沙箱。它的核心逻辑不是试图去“修复”坏字符串,而是从语法解析层面重新定义规则:

  • 预处理清洗 (Pre-processing Sanitization):在进入任何解析逻辑之前,系统会自动剥离 $ 符号,并将 % 统一转化为 /100。这把不规则的“人类表达”强行对齐到标准的数学表达式上。
  • AST 节点白名单 (AST Node Whitelisting):我们利用 ast.parse 将表达式解析为树结构。随后,程序会遍历这棵树,仅允许执行极少数安全的二进制操作(例如 ast.Add, ast.Sub)。任何试图调用外部函数、访问对象属性或进行循环的操作,在解析阶段就会被彻底拦截。
# 核心逻辑片段:src/solver/tools.py
def safe_eval_expression(expr: str) -> float:
    # 1. 强制清洗脏数据
    expr = expr.replace("$", "").replace("%", "/100")
    
    # 2. 解析为 AST(抽象语法树)
    node = ast.parse(expr, mode='eval').body
    
    # 3. 仅允许白名单内的节点进行递归评估
    return _eval_node(node)

错误即价值的弹性设计 (Error-as-Value Resilience)

更高级的架构设计还包含一个关键细节:永远不要让工具的异常崩溃你的主 Agent 循环。

当用户输入的内容完全不符合语法时,我们不应该让程序抛出 Exception,而是捕获它,并将其转化为一个“软提示”反馈给模型。例如: error: invalid syntax. Hint: Use '*' for multiplication, not 'x'.

通过这种方式,LLM 可以感知到自己的逻辑错误,并在下一次迭代中根据这个提示进行自我修正。这就是高级 AI 工程师构建稳健架构的底层逻辑。在生产环境中,用正则去过滤代码逻辑,就像用筛子去过滤核武器——漏洞就在你意想不到的缝隙里。

如果你想深入了解完整的开源实现,可以参考 AI-FDE-Playbook 的工程实践。

Updated: