大模型工具调用的安全与稳定性实战
在前面的文章中,我们让 AI 拥有了“双手”(Function Calling)并统一了“接口”(MCP 协议)。看着 AI Copilot 行云流水地帮我们查天气、搜文档,确实令人兴奋。
但是,给 AI 赋予行动能力,就像给一个聪明的五岁小孩一把上了膛的枪。大模型会产生幻觉,用户会进行 Prompt 注入攻击(Prompt Injection),下游的 API 也会因为并发过高而宕机。
在将 AI 助手推向生产环境之前,我们必须构建一套坚固的防御体系。结合我们的项目代码,今天我们就来盘点工具调用中不可或缺的安全与稳定性控制。
一、 永远不要信任大模型的输出:输入验证与清洗
在传统的 Web 开发中,我们有一条铁律:“永远不要信任用户的输入”。在 AI 开发中,这条规则要加上半句:“也永远不要信任大模型的输出”。
大模型在提取参数时,可能会带上多余的 Markdown 标记、恶意的 SQL 注入代码,或者奇怪的系统路径。在我们的项目中,所有的工具输入除了要经过 Zod 的类型校验,还必须经过专门的 input-sanitizers.ts 进行清洗。
实战代码:地点与搜索词的净化
1 | |
防御逻辑:在 lookupWeather 和 searchReactDocs 工具的执行层,第一步永远是调用这些清洗函数。宁可让查询失败,也不能让脏数据进入下游。
二、 保护你的钱包与下游系统:调用频率限制 (Rate Limit)
大模型在处理复杂任务时,可能会陷入死循环(比如工具 A 报错,AI 不断重试调用工具 A),这不仅会迅速烧光你的 LLM Token 余额,还会对下游服务造成 DdoS 级别的打击。
因此,我们需要在请求上下文中引入限流机制(Rate Limiting)。
实战代码:给工具加一把“锁”
1 | |
防御逻辑:当 AI 试图以每秒 5 次的速度疯狂调用 search_react_docs 时,限流器会直接返回类似 "当前工具调用频率过高,请稍后再试" 的文本结果,强行打断 AI 的幻觉循环。
三、 雁过留声,查“内鬼”必备:审计日志 (Audit Log)
当你的 AI Copilot 在生产环境运行了一周后,你可能会收到用户的反馈:“AI 给我的财报数据是错的!” 这时候如果你没有日志,根本无法排查是大模型胡说八道,还是你的 API 返回了脏数据。
我们需要对每一次工具的调用、输入、输出和耗时进行无死角的记录。
实战代码:基于上下文的日志追踪
1 | |
防御逻辑:在 mcp/protect-tools.ts 或执行中间件中拦截调用,记录 Trace ID。一旦出现故障,通过 Trace ID 可以完整回溯用户的提问、大模型生成的 JSON,以及工具的实际输出。
四、 最小权限原则:工具的读写隔离
在我们的代码设计中,你可能会注意到 index.ts 里的工具注册包含了 annotations 属性:
1 | |
这是一个极其优秀的实践。我们在给 AI 分配工具时,应该严格遵循最小权限原则 (Least Privilege):
只读操作 (Read-Only):如查天气、搜文档,可以自动放行。
破坏性操作 (Destructive):如删除数据库记录、发邮件、执行服务器脚本。这类工具在本地执行前,必须强制拦截,通过 UI 弹窗要求人类用户进行二次确认(Human-in-the-loop)。
结语:让 AI 成为助手,而不是隐患
构建一个 AI Copilot,写对 Prompt 只是第一步,调通 MCP 只是第二步。真正的挑战在于:如何在一个充满不确定性(幻觉、超时、脏数据)的 LLM 生态中,构建一个确定且稳定的工程系统。
通过输入清洗、频率限制、审计日志和权限隔离这四道防线,我们就成功地为 AI 戴上了“紧箍咒”。它依然可以七十二变,但再也无法大闹天宫。
源码已托管至🫱 GitHub,期待你的 Star🌟。