函数调用入参幻觉:参数校验怎么兜
模型让你调一个 refund(order_id, amount) 退款函数,结果它给的 order_id 是它现编的,amount 比订单金额还大。这种"入参幻觉"我吃过亏,下面用问答的形式把我趟过的几个问题理一遍。
Q:模型真的会编造函数入参吗?有多严重?
会,而且比想象中频繁。我做过一个退款 Agent,统计了一周,大概每 100 次函数调用里有 6~8 次入参有问题。最常见三类:
-
编造不存在的 ID(用户没提,模型自己造一个看起来很像的)
-
数值越界(退款金额 > 订单金额、数量为负)
-
枚举值乱填(状态字段它填了个枚举里根本没有的值)
光靠 prompt 里写"不要编造参数"压不住,能降但压不到零。
Q:那靠模型自己约束不行,怎么办?
核心就一句:把函数入参当成完全不可信的外部输入来校验。跟你校验前端表单提交是一个道理,谁信前端谁倒霉。
我在工具被真正执行之前,强制加一道校验关:
def validate_refund(order_id, amount):
order = db.get_order(order_id)
if order is None:
return Err("order_id 不存在,请向用户确认订单号")
if amount <= 0 or amount > order.paid_amount:
return Err(f"退款金额非法,应在 0~{order.paid_amount} 之间")
return Ok()
校验不过,不执行,把错误原因回吐给模型,让它重新组织。
Q:校验失败后,是直接报错还是让模型重试?
我倾向"带着错误信息让它重试一次"。把 order_id 不存在 这种具体原因塞回去,模型很多时候会反应过来"哦我没问用户要订单号",转头去问用户,而不是再编一个。
但重试要设上限,我设的是最多 2 次。见过模型连编三次不同的假 ID 死活不改的,再循环下去就是烧钱,到上限直接转人工。
Q:schema 层面能不能提前堵一部分?
能,而且性价比很高。在函数定义里就把约束写死:
{
"name": "refund",
"parameters": {
"amount": {"type": "number", "minimum": 0},
"reason": {"type": "string", "enum": ["质量", "物流", "其他"]}
}
}
枚举和类型约束写进 schema 后,枚举值乱填这类直接少了一大半。不过 schema 管不了"业务级"约束(比如金额不能超订单),那种还得靠运行时校验。两层都得有。
Q:这套自己写很麻烦吗?
说实话校验逻辑本身不复杂,麻烦的是编排——什么时候校验、失败怎么回流给模型、重试几次。我后来是在一个零代码搭智能体的平台上配的:函数节点后面挂一个校验节点,校验节点的失败分支连回模型节点形成重试环,到次数上限走转人工分支。这套流程图拉出来一目了然,比埋在代码里的 if-else 好维护多了。
Q:有没有什么反直觉的坑?
有一个。我一开始把校验错误信息写得特别"机器",比如 ERR_AMOUNT_001,结果模型看不懂,重试还是错。后来改成人话——退款金额 200 超过了订单实付 150——模型一下就改对了。给模型看的错误信息要写成人话,别写错误码。 这跟给人看的日志正好相反。
最后补一句不相关的:模型那端我走的讯飞 MaaS,现成 API,没自己部署,省了不少运维心思。
你们 function calling 的入参校验做到哪一层了?只靠 schema 还是上了运行时兜底?评论区说说你们的幻觉率。
更多推荐




所有评论(0)