我给 Agent 写了个清理 N 天前数据的工具,模型某次把 N 填成了 0,工具没校验就照单执行,把全部数据都删了:一次 Agent 工具参数未校验、把模型输出当可信输入的深度复盘

我给 AI Agent 写了个工具 cleanup_old_data(days) 清理 days 天前的数据,描述写得清清楚楚、平时用得也好。可某次 Agent 处理一个模糊的清理请求时把 days 填成了 0,而我的工具拿到 days=0 没做任何校验,直接执行 DELETE WHERE created_at < 此刻——删除了全部数据。查清才明白:我把模型填进工具的参数当成了绝对可信合法的输入直接执行,可 LLM 是概率性的、完全可能填出 0/负数/超大值/不存在的 ID 等非法甚至危险的参数,模型填的参数本质上是不可信输入,必须像校验用户输入一样校验。这篇复盘从故障现场讲到为什么模型填的参数是不可信输入、不校验的后果,再到工具内严格校验(范围/合法性/白名单)、危险操作算影响范围加人工确认、校验不过返回错误给模型、把工具当面向不可信调用者的 API 加固的完整正解,以及把 LLM 纳入不可信输入范畴一律校验、信任不该代替防范、越强大的执行者越需要相称的约束防范别被聪明麻痹的认知。

我给 Agent 写了个"清理 N 天前数据"的工具,模型某次把 N 填成了 0,工具没校验就照单执行,把全部数据都删了:一次 Agent 工具参数未校验的深度复盘

那次数据被误删是触目惊心的:我给 AI Agent 写了个工具 cleanup_old_data(days)——"清理 days 天前的数据"。我把工具描述写得清清楚楚,Agent 平时用得也挺好。可某次,Agent 在处理一个模糊的清理请求时,把参数 days 填成了 0;而我的工具拿到 days=0 后没做任何校验,直接执行了 DELETE WHERE created_at < now() - 0 天——也就是删除了"0 天前"(即此刻)之前的全部数据。我盯着这个由"一个被填错的参数"引发的删库,后背发凉:问题出在我把"模型填进工具的参数"当成了"绝对可信、绝对合法"的输入,直接拿来执行,没有做任何校验我犯的根本错误是:我潜意识里把 LLM 当成了一个"聪明、可靠、不会填错参数"的调用者;可 LLM 是概率性的——它完全可能填出不合理、非法、甚至危险的参数(把 N 填成 0、负数、超大值,把 ID 填成不存在的,把范围填错);而我的工具,对这个"不可信来源"填来的参数,毫无防备地照单全收、直接执行,于是一个 days=0 就酿成了删库。根本原因是:工具没有校验模型填来的参数——而模型填的参数,本质上是一种"不可信的输入",必须像对待任何外部输入(用户输入、API 入参)一样严格校验。问题的根,是把 LLM 填的工具参数当成可信合法输入直接执行、没校验;而 LLM 是概率性的、会填出非法/危险参数。这篇就把这次"Agent 工具参数未校验"的坑,从头到尾复盘一遍。

故障现场:模型把参数填成 0,工具直接删库

问题在于工具对模型填来的参数不做校验、直接执行:

# ✗ 出问题的工具: 对模型填的参数不校验, 直接执行
def cleanup_old_data(days: int):
    # ✗ 直接拿模型填的 days 去删, 没校验它合不合理!
    cutoff = now() - timedelta(days=days)
    db.execute("DELETE FROM records WHERE created_at < ?", cutoff)
    return f"已清理 {days} 天前的数据"

# Agent调用这个工具时, 模型(概率性地)把 days 填成了 0:
#   cleanup_old_data(days=0)
#   → cutoff = now() - 0天 = 此刻
#   → DELETE WHERE created_at < 此刻  → 删除了【全部历史数据】! (删库)

# 为什么会这样? 因为我把"模型填的参数"当成了可信、合法的输入:
# 1. 我潜意识假设"模型很聪明, 不会填出 days=0 这种离谱的值";
# 2. 但LLM是【概率性】的, 它完全可能:
#    - 把N填成0、负数、超大值; 把ID填成不存在的/猜的; 把枚举值填成没定义的;
#    - 在请求模糊、上下文误导、或纯粹"抽风"时, 填出各种非法/边界/危险的参数;
# 3. 而我的工具对这些参数【毫无校验】, 照单全收、直接执行 → 一个days=0就删库。

# 类比: 这就像写一个Web接口, 直接拿用户传的参数去执行危险操作而不做任何校验——
#   只不过这里的"用户", 换成了"会犯错的、概率性的LLM"。
# 而且LLM填参数, 比人填表单更不可控(人填错有限, LLM可能填出任何东西)。

# 关键: 模型填给工具的参数, 是"不可信的输入"(LLM概率性、会填非法/危险值); 工具若不校验就直接执行,
#       一个被填错的参数(如days=0)就可能酿成删库等灾难 —— 工具必须校验模型填的参数。

第一次想明白"是我把模型当成了不会出错的调用者、对它填的参数照单全收"时,我又懊恼又后怕:"我费心把工具描述写清楚,以为模型就会乖乖填对参数;完全没想到它会填出 days=0,更没想到我的工具对此毫无防备、直接就把库删了。"这个坑最危险的地方在于:它把"模型偶尔填错一个参数"这种本该被工具挡下的小概率失误,直接放大成了删库这样的灾难;而且 days 参数平时填得都对(给了你工具很可靠的错觉),偶发一次填错就出大事下面就来拆解,Agent 的工具该怎么对待模型填的参数。

第一件事:搞懂"模型填的参数是不可信输入"

我顺着这次事故,把 Agent 工具与参数校验的关系彻底理清了。

为什么"模型填给工具的参数"必须当成不可信输入来校验?

【核心: LLM是概率性的、会填出非法/边界/危险参数; 模型填的参数=不可信输入, 工具(尤其有副作用的)必须像校验用户输入一样校验它, 危险操作加确认】

1. 谁在"调用"你的工具? —— 一个概率性的、会犯错的 LLM
   - Agent工具的参数, 是LLM根据上下文"生成"的(本质是预测token);
   - LLM不是确定性程序, 它【可能】填出: 0/负数/超大值、不存在的ID、非法枚举、格式错的、甚至危险的内容;
   - → 它是一个"大概率填对、但小概率填错"的调用者。

2. 关键认知: 模型填的参数 = 不可信输入
   - 就像"用户通过表单/API传来的参数"是不可信的(可能恶意/错误)一样;
   - "LLM填给工具的参数"也是不可信的(LLM会犯错、会被误导、会抽风);
   - → 凡是处理不可信输入的铁律(校验、边界检查、白名单), 都适用于工具参数。

3. 不校验的后果(尤其有副作用的工具):
   - 删除类: days=0/负数 → 删了不该删的(本文);
   - 数量类: size=100万 → 拖垮系统/OOM;
   - ID类: 不存在的/别人的ID → 操作错对象/越权;
   - 金额/范围类: 负数/超大 → 业务错乱;
   - → 一个非法参数 + 一个有副作用的工具 = 真实灾难。

4. 正确做法: 工具内部严格校验参数
   - 范围校验: days必须 >= 1(且 <= 合理上限); size有上限; 金额非负;
   - 合法性校验: ID必须存在、属于当前用户; 枚举值在白名单内;
   - 边界/危险拦截: 对"会影响大量数据/不可逆"的操作, 额外严格(甚至要人工确认, 同541篇);
   - 校验不过: 返回清晰的错误给模型(同553篇), 让它知道参数错了、能改, 而非默默执行。

5. 更深: 把LLM放在"不可信"的位置, 在它和"真实副作用"之间设防护
   - LLM负责"决策/填参数", 但它的输出要经过"校验层"才能触达真实操作;
   - 这层校验, 是"不可信的LLM"和"真实的、有副作用的执行"之间的安全闸门。

一句话: LLM是概率性的、会填非法/危险参数, 模型填给工具的参数是"不可信输入"; 工具(尤其有副作用的)必须
   像校验用户输入一样严格校验它(范围/合法性/白名单)、危险操作加确认、校验不过返回错误给模型, 别照单执行。

这套认知,是整个坑的根。谁在调用你的工具:一个概率性、会犯错的 LLM——工具参数是 LLM 根据上下文生成的,它可能填出 0/负数/超大值、不存在的 ID、非法枚举、危险内容,是"大概率填对、小概率填错"的调用者关键认知:模型填的参数=不可信输入——就像用户/API 传来的参数不可信一样,LLM 填的参数也不可信(会犯错、被误导、抽风);处理不可信输入的铁律(校验、边界检查、白名单)都适用不校验的后果(尤其有副作用工具):删除类(days=0 删库)、数量类(size 百万拖垮)、ID 类(操作错对象/越权)、金额类(业务错乱)。正确做法:工具内部严格校验(范围、合法性、白名单)、危险/不可逆操作额外严格甚至人工确认、校验不过返回清晰错误给模型让它能改。更深:把 LLM 放在"不可信"位置,在它和真实副作用之间设"校验层"安全闸门。一句话:LLM 是概率性的、会填非法/危险参数,模型填给工具的参数是"不可信输入";工具(尤其有副作用的)必须像校验用户输入一样严格校验它(范围/合法性/白名单)、危险操作加确认、校验不过返回错误给模型,别照单执行。

第二件事:正解——工具内严格校验模型填的参数、危险操作加确认

搞懂了原理,正解就清晰了:工具内部把模型填的参数当不可信输入严格校验(范围/合法性/白名单),校验不过返回清晰错误给模型;有副作用/不可逆的操作再加人工确认

# ====== 正解: 工具内严格校验参数 + 危险操作加确认 ======
def cleanup_old_data(days):
    # ★ 校验1: 类型和范围(days必须是合理的正整数)
    if not isinstance(days, int) or days < 1:
        return {"success": False,
                "error": f"days 必须是 >= 1 的整数, 收到的是 {days!r}",
                "hint": "请确认要清理多少天前的数据, 至少 1 天"}   # 返回错误给模型(同553篇)
    if days > 3650:   # ★ 校验2: 合理上限(防超大值)
        return {"success": False, "error": "days 不能超过 3650(10年)"}

    # ★ 校验3: 危险操作先算影响范围, 太大就要人工确认
    cutoff = now() - timedelta(days=days)
    affected = db.count("SELECT count(*) FROM records WHERE created_at < ?", cutoff)
    if affected > 10000:   # 影响过多, 不让Agent自己拍板
        if not request_human_confirmation(f"将删除 {affected} 条数据(days={days}), 确认?").approved:
            return {"success": False, "error": "影响数据量大, 用户未确认, 已取消"}

    db.execute("DELETE FROM records WHERE created_at < ?", cutoff)
    return {"success": True, "message": f"已清理 {days} 天前的数据, 共 {affected} 条"}
# → days=0 进来: 校验1直接拦下, 返回错误给模型(模型会知道填错了、可改), 不会再删库。
# ====== 校验工具参数的要点 ======
# 1. 把模型填的参数当"不可信输入": 像校验用户输入/API入参一样校验, 别假设模型填得对;
# 2. 范围/类型校验: 数字的上下界(days>=1、size<=上限)、类型对不对、非负等;
# 3. 合法性校验: ID存在且属于当前用户(防越权)、枚举值在白名单、格式合法;
# 4. 危险/不可逆操作额外防护: 先算影响范围, 过大/不可逆就要人工确认(同541篇), 或做软删除可回滚;
# 5. 校验不过返回清晰错误(同553篇): 让模型知道参数错了、错在哪、怎么改, 而非默默执行或崩;
# 6. 用schema约束工具参数: 很多框架支持给工具参数定义JSON Schema(类型/范围/枚举), 让框架先校验一道。

# ====== 一个心智模型 ======
# - 把Agent的工具, 当成一个【对外暴露的、会被不可信调用者(LLM)调用的API】来设计;
# - API怎么防御不可信调用(校验、限权、限流、确认), 工具就怎么防御LLM;
# - LLM比普通用户更不可预测(可能填出任何东西), 所以工具的防御要【更严】, 不能更松。

# 核心: 工具把模型填的参数当不可信输入严格校验(范围/合法性/白名单)、危险操作加人工确认、
#   校验不过返回错误给模型; 像设计"对不可信调用者开放的API"那样设计Agent工具, 别照单执行。

修复的核心,是"工具把模型填的参数当不可信输入严格校验,危险操作加确认"正解:工具内严格校验 + 危险操作加确认——校验类型范围(days>=1 且有上限)、合法性、危险操作先算影响范围过大就人工确认;days=0 进来被校验直接拦下、返回错误给模型(它会知道填错可改),不再删库校验要点:把参数当不可信输入、范围/类型校验、合法性校验(ID 存在且属当前用户防越权、白名单)、危险/不可逆操作额外防护(算影响范围+人工确认+软删除)、校验不过返回清晰错误、用 JSON Schema 约束参数让框架先校验一道心智模型:把 Agent 工具当成"对外暴露的、会被不可信调用者(LLM)调用的 API"来设计;API 怎么防御不可信调用,工具就怎么防御 LLM;LLM 比普通用户更不可预测,防御要更严归根结底:工具把模型填的参数当不可信输入严格校验(范围/合法性/白名单)、危险操作加人工确认、校验不过返回错误给模型;像设计"对不可信调用者开放的 API"那样设计 Agent 工具,别照单执行。

第三件事:AI Agent 工具安全与健壮性的其他要点

排查后我把 Agent 工具的安全、健壮性相关的其他要点也系统梳理了一遍。

AI Agent 工具安全与健壮性的其他要点

# 1. 参数未校验(本文): 模型填非法参数照单执行。→ 当不可信输入严格校验。

# 2. 工具错误没回传(同553篇): 失败模型不知情谎报成功。→ 如实回传错误。

# 3. 有副作用工具没幂等(同541篇): 重试/重复调重复执行。→ 幂等键+确认。

# 4. 工具权限过大: 给工具的权限超出任务所需(能删能改一切)。→ 最小权限。

# 5. 危险/不可逆操作Agent自己拍板: 删库/转账没人把关。→ human-in-the-loop确认。

# 6. 没有审计: Agent调了什么工具、传了什么参数没记录。→ 全程审计可追溯。

# 7. 没有沙箱/限制: 让Agent执行任意代码/命令而不隔离。→ 沙箱、白名单、资源限制。

# 8. 工具返回过大: 海量结果塞爆context。→ 分页/摘要/截断/存引用按需取。

# 共同根源: Agent是"用一个不可信、概率性的LLM, 去驱动一组能操作真实世界的工具"; 这个组合的安全性,
#   不能寄望于"LLM总是做对的事", 而要靠"工具这一侧的严格防御"——把每个工具都当成"面对一个会犯错、
#   甚至可能被诱导做坏事的调用者"来加固(校验、限权、确认、审计、沙箱)。

# 核心: Agent的安全防线在"工具侧"而非"模型侧"——把模型当不可信调用者, 工具做严格校验、最小权限、
#   危险操作确认、全程审计、必要时沙箱; 别指望LLM永远填对、做对, 要让工具挡住它的错误和风险。

排查让我把 Agent 工具安全与健壮性的其他要点也梳理清了。一、参数未校验(本文)。二、工具错误没回传三、有副作用工具没幂等四、工具权限过大五、危险操作 Agent 自己拍板六、没有审计七、没有沙箱/限制八、工具返回过大它们的共同根源是:Agent 是"用一个不可信、概率性的 LLM,去驱动一组能操作真实世界的工具";这个组合的安全性,不能寄望于"LLM 总是做对的事",而要靠"工具这一侧的严格防御"——把每个工具都当成"面对一个会犯错、甚至可能被诱导做坏事的调用者"来加固核心是:Agent 的安全防线在"工具侧"而非"模型侧"——把模型当不可信调用者,工具做严格校验、最小权限、危险操作确认、全程审计、必要时沙箱;别指望 LLM 永远填对、做对,要让工具挡住它的错误和风险下面这张图,是这次参数未校验坑的成因与解法:

第四件事:不可信输入来源对比表

这次踩坑后,我把几种"不可信输入来源"对比成一张表,把 LLM 也明确列了进去。

输入来源 为什么不可信 可预测性 防御
用户表单/API 入参 可能错误/恶意 较可预测(范围有限) 校验/限权
外部系统返回 可能异常/变更 较可预测 校验/容错
LLM 填的工具参数(本文) 概率性、会犯错/被诱导 更不可预测(可能填任何东西) 更严格校验+确认
配置/环境 可能配错 较可预测 校验/默认值

这张表把 LLM 在"不可信输入"里的位置钉清了。核心是:我们早就习惯了"用户输入不可信、要校验"这条铁律;而 LLM 填的参数,是一种"新的、甚至更不可信的"输入来源——用户填错有限(受表单约束、有常识),而 LLM 是概率性的、可能填出任何东西(包括人类绝不会填的离谱值),它的不可预测性更高;可我们却容易因为"它很聪明"而不自觉地信任它、放松了对它输出的校验它给我的最大启发是:随着 LLM 进入系统,我们的"不可信输入"清单上,多了一个新成员——LLM 的输出;"用户输入不可信"这条老智慧,要扩展成"包括 LLM 在内的、一切'系统边界之外/由不确定来源产生'的输入都不可信";而且因为 LLM "看起来聪明",它的不可信反而更容易被忽视——这让它比传统不可信输入更危险这给了我一种构建 AI 系统时的根本边界意识:在引入 LLM 的系统里,要清醒地划出"信任边界"——把 LLM(及其输出)放在边界的"不可信"一侧,它的任何输出(填的参数、给的内容、做的决策)在被系统"当真"执行前,都要经过校验这道关;"把 LLM 明确纳入'不可信输入'的范畴、对其输出一律校验、别因它'聪明'就给它特权",是构建安全 AI 系统的基本边界纪律认清 LLM 是更不可信的输入来源、把它纳入不可信范畴一律校验——是这个坑带给我的认知。

第五件事:这次事故暴露的"信任带来的疏于防范"

这次让我反思更深一层:我之所以不校验,根上是因为我"信任"了那个聪明的模型。我把"信任它"和"防范它"对比成表。

维度 信任模型(我当时的心态) 防范模型(正确的)
对模型填的参数 觉得它会填对, 照单执行 当不可信输入, 一律校验
对危险操作 让它自己拍板 加确认/限权
出错时 毫无防备, 直接酿祸 工具挡下, 不出事
心态根源 "它很聪明, 应该没问题" "它会犯错, 我要兜住"
本质 用信任代替了防范 不因信任而放弃防范

这张表道出了最深的教训。核心是:我栽跟头的心态根源,是"因为觉得它聪明可靠, 就用'信任'代替了'防范'"——我想"模型这么聪明, 应该不会填出 days=0 这种离谱值吧", 于是省掉了本该有的校验;可"聪明"不等于"不会犯错",尤其是概率性的 LLM;我的信任, 恰恰让我在它犯错时毫无防备它给我的深刻启发是:"信任"是一种宝贵的东西,但在系统安全/健壮性的语境里,"信任不该代替防范"——哪怕你信任一个组件/人/模型很可靠,也不该因此省掉"万一它出错"的防护;真正健壮的系统, 不是"建立在'各部分都可靠'的信任之上", 而是"即使某部分不可靠/出错, 也能兜住";"用信任省掉防范", 是脆弱系统的常见根源这给了我一种设计健壮系统的根本原则:设计系统时,要把"信任"和"防范"分开——可以在'正常协作'上信任各方, 但在'关键的、有严重后果的'环节, 必须保留"不信任的防范"(校验、确认、限权、兜底),假设"任何一方都可能出错";"不因信任而放弃防范、为'万一出错'守住关键防线",是构建一个不会被'某一方的失误'轻易击垮的健壮系统的核心原则——对 LLM 尤其如此认清信任不该代替防范、对关键环节保留不信任的防护——是这个参数校验坑带给我的工程态度。

第六件事:给 Agent 写工具时,我现在的自检习惯

现在每当我给 AI Agent 写一个工具,我都会先按这张图问自己:

这张图的精髓,是"把模型参数当不可信输入校验,危险操作加确认,校验不过回传错误"模型参数当不可信输入校验、有副作用算影响+人工确认、校验不过返回错误给模型、再加最小权限+审计这套习惯,让我从"信任模型填的参数照单执行"变成了"把工具当面向不可信调用者的 API 加固"——核心始终是:把模型填的工具参数当不可信输入严格校验(范围/合法性/白名单),危险操作加人工确认,校验不过返回错误给模型,别照单执行。

我立下的几条规矩

这场"模型把 days 填成 0、工具直接删库"的事故,换来了我给 Agent 写工具时,刻进骨子里的几条铁律:

  1. LLM 是概率性的,会填出非法/边界/危险的参数。别假设它永远填对。
  2. 模型填给工具的参数 = 不可信输入,必须像校验用户输入一样校验。
  3. 校验范围/类型/合法性:数字有上下界、ID 存在且属当前用户、枚举在白名单。
  4. 有副作用/不可逆的操作额外防护:算影响范围、过大要人工确认、软删除可回滚。
  5. 校验不过返回清晰错误给模型,让它知道错在哪、能改,而非默默执行。
  6. 把 Agent 工具当成"面向不可信调用者的 API"来加固:校验、限权、确认、审计、沙箱。
  7. Agent 的安全防线在工具侧,别因为模型"聪明"就用信任代替防范。

写在最后

回头看,这场由"没校验模型填的参数"引发的、一个 days=0 删库的事故,真正教给我的,远不止"工具要校验参数"这一个技巧。它让我对"我们越是觉得某个'执行者'聪明、可靠, 就越容易放松对它的防范; 而恰恰是这份'因信任而放下的防备', 让它偶尔的一次失误, 变成了无人能挡的灾难",有了一次刻骨的体会。我栽跟头,最深的根源,是我被 LLM 的"聪明"麻痹了——它平时表现得那么智能、那么"善解人意",以至于我下意识地把它当成了一个'比普通程序/用户更可靠'的存在,对它格外地放心、格外地不设防;我给普通用户的输入都会老老实实校验,却偏偏对这个"聪明的 LLM"填的参数, 免去了校验——仿佛它的"聪明"给了它一张"免检通行证";可这张通行证是我臆想出来的, LLM 从未保证过它"不会填错", 它本质上比普通用户更不可预测; 我对它越信任、防备越松, 它那偶发的 days=0 也就越畅通无阻地酿成了删库这让我领悟到一个关于"能力、信任与防范"的深刻认知:一个执行者"看起来越能干、越聪明",我们就越倾向于信任它、给它更大的权限、放松对它的防范——而这恰恰是危险的:能力强 ≠ 不会犯错;给一个"能力强但仍会犯错"的执行者过度的信任和不设防的权限, 意味着它一旦犯错, 破坏力也更大;"越是强大的执行者, 越需要配套强大的约束和防范", 而非相反这给了我一种对待"强大执行者(尤其 AI)"的根本清醒:面对一个强大、智能的执行者(LLM、自动化系统、乃至能干的人),不要让"它很能干"成为"放松防范、给予无约束权限"的理由——反而要意识到"它越能干, 它的失误/失控后果越严重, 越需要校验、限权、确认、审计这些约束来兜住它";"不被能力和聪明麻痹、对强大的执行者配套以相称的约束与防范",是安全地驾驭强大能力(尤其是 AI)的根本原则认清能力强不等于不会错、越强大的执行者越需要相称的约束防范、别被聪明麻痹而放松防备——这,是我用一次 days=0 删库的事故,换来的、关于 AI Agent、也关于如何安全驾驭强大执行者的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次给 Agent 写工具时,把模型填的每个参数都老老实实校验一遍、给删除这类操作加道确认,那我对着那被 days=0 删空的数据复盘的这段时间,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我用数字枚举定义订单状态,以为类型安全了,结果一个不存在的数字 99 也能传进去、遍历枚举时还莫名多出一倍的项:一次 TypeScript 数字枚举两个坑的深度复盘

2026-6-2 21:56:13

技术教程

我用乘法快速初始化了一个二维列表,给其中一个格子赋值,结果发现每一行的那个格子都跟着变了:一次 Python 列表乘法复制的是引用而非副本的深度复盘

2026-6-2 22:06:58

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索