Function Calling参数校验实战:用JSON Schema拦截模型编造参数
1. 模型为什么会“编造”参数从一次线上事故说起先说一个我亲身踩过的坑。去年做一个智能客服工单系统用户输入“帮我查一下上周北京到上海的高铁票”我把这句话连同工具定义一起丢给模型工具定义里有个search_train_tickets函数参数是from_city、to_city、date、passenger_count。测试环境跑得好好的上线第二天运营就来找我有用户查“明天广州到深圳的票”结果系统返回了“2023年11月32日”的查询结果——对11月32日一个根本不存在的日期。更离谱的是有用户问“帮我订两张票”模型把passenger_count填成了two字符串而我的函数签名要的是整数。这就是Function Calling 参数被模型编造的典型现场。模型不是故意骗你它是在做概率生成——它根据上下文“猜”下一个 token 最可能是什么而不是在“计算”一个合法值。你给它一个 JSON Schema它大概率会遵守结构但值的合法性它管不了。日期格式对不对、枚举值在不在范围内、数字有没有超界、必填字段有没有漏这些它都可能“顺手编一个看起来像那么回事的值”。所以我的结论很直接不要把模型当成参数校验器它只是个参数生成器。校验这件事必须由你自己的代码在入口处挡下来。这篇文章就围绕这个思路展开讲清楚 schema 校验到底该怎么做、用什么工具、踩过哪些坑以及怎么把校验层设计得既严格又不影响正常调用。2. 先搞清楚Function Calling 的参数到底长什么样2.1 模型返回的参数本质是一段 JSON不管你用的是哪家的模型Function Calling 的返回结构基本都长这样模型决定调用哪个函数然后给你一段 JSON 字符串作为参数。以常见的结构为例{ name: search_train_tickets, arguments: { from_city: 广州, to_city: 深圳, date: 2023-11-32, passenger_count: two } }注意两个细节第一arguments里的值类型完全由模型决定它可能给你字符串、数字、布尔甚至嵌套对象第二这段 JSON 在传输过程中可能是字符串形式需要你先JSON.parse一次。很多新手直接把这个对象透传给业务函数结果就是各种TypeError和脏数据入库。2.2 模型编造参数的四种典型模式我把实际遇到过的编造行为归了类基本逃不出这四种格式幻觉日期给你2023-11-32、2023/13/01手机号给你138-0000-0000带横杠邮箱给你abc。模型知道“这里应该是个日期”但它不检查日历。类型漂移该给数字给了字符串two、2张该给布尔给了true字符串该给数组给了单个对象。枚举越界你定义了status只能是pending、paid、cancelled它给你返回processing或者已完成。字段捏造你 schema 里根本没定义remark字段它自作主张加了一个或者必填的user_id它直接省略因为它“觉得”上下文里有。这四种里格式幻觉和类型漂移最常见字段捏造最危险——因为多出来的字段如果直接透传给下游可能触发意料之外的逻辑。2.3 为什么必须在“入口”校验有人会问我在业务函数内部做校验不行吗行但代价大。业务函数一旦被调用可能已经产生了副作用——写日志、发消息、扣库存。等你在函数内部发现参数非法再抛异常副作用已经发生了。入口校验的核心价值是“零副作用拦截”在参数还没碰到任何业务逻辑之前就把它挡在门外返回一个明确的错误让模型重新生成。提示入口校验的另一个好处是错误信息可以结构化返回给模型让它有机会自我修正。这比在业务层抛一个ValueError然后整个链路崩掉要优雅得多。3. 选型jsonschema、zod 还是手写校验3.1 三种主流方案的对比校验工具的选择直接决定了你后面维护的成本。我把常见的几种方案拉出来对比一下方案语言生态优点缺点适用场景jsonschema跨语言标准统一模型侧工具定义可直接复用错误信息偏底层嵌套校验写起来啰嗦多语言混合、需要和模型工具定义保持一致zodTypeScript/JS类型推导强链式 API 好写错误信息友好仅限 JS 生态和 JSON Schema 需要转换Node/前端为主的团队手写校验任意完全可控无依赖重复劳动容易漏字段难维护参数极简、临时脚本我的实际选择是如果工具定义本身就是 JSON Schema大多数模型平台都要求这样那就直接用 jsonschema 库校验一份定义两处用模型侧和校验侧完全对齐不会出现“模型以为的 schema”和“你校验的 schema”不一致的问题。如果是 TypeScript 项目zod 写起来更爽但要注意把 zod schema 转成 JSON Schema 给模型否则两边定义会漂移。3.2 为什么我最终选了 jsonschema说个具体的理由。我之前的项目里工具定义是用 zod 写的然后手动维护了一份 JSON Schema 给模型。结果有一次改字段zod 改了但 JSON Schema 忘了改模型按旧 schema 生成参数校验按新 schema 拦截两边打架排查了半天。后来我统一成JSON Schema 作为唯一事实来源模型侧用它校验侧也用它zod 只在需要类型推导的地方做一层薄封装。这样改一处两边同步再没出过不一致的问题。3.3 校验库的版本坑jsonschema 这个库Python 生态里叫jsonschemaNode 生态里叫ajv有个坑要注意不同版本对 JSON Schema draft 的支持不一样。模型平台给的 schema 通常是 draft-07 或 2020-12如果你用的校验库默认走的是老 draft某些关键字比如const、if/then可能不生效。我建议显式指定 draftfrom jsonschema import Draft7Validator, Draft202012Validator # 明确用哪个 draft别让它自己猜 validator Draft202012Validator(schema)Node 侧用 ajv 的话要显式new Ajv({ strict: false })否则它会对 schema 里一些“非标准但模型平台在用”的写法报错。4. 核心实操把 schema 校验挡在入口的完整实现4.1 第一步定义一份“严格模式”的 schema模型平台给的 schema 往往是“宽松”的——它只告诉模型有哪些字段但不强制约束。你要在校验侧把它收紧。关键是在 schema 里加上这些约束{ type: object, properties: { from_city: { type: string, minLength: 1, maxLength: 50 }, to_city: { type: string, minLength: 1, maxLength: 50 }, date: { type: string, pattern: ^\\d{4}-\\d{2}-\\d{2}$ }, passenger_count: { type: integer, minimum: 1, maximum: 5 } }, required: [from_city, to_city, date], additionalProperties: false }几个关键点解释一下pattern只能保证格式是YYYY-MM-DD但保证不了2023-11-32这种“格式对但日期不存在”的情况。所以正则之后还要加一层语义校验这个后面讲。additionalProperties: false是拦截“字段捏造”的关键。模型多给的字段会直接导致校验失败而不是被静默忽略。required里没放passenger_count因为它是可选的但一旦给了就必须是 1 到 5 的整数。4.2 第二步封装一个统一的校验入口不要在每个工具函数里各写一遍校验逻辑抽一个统一的入口函数import json from jsonschema import Draft202012Validator, ValidationError from datetime import datetime def validate_tool_args(tool_name: str, raw_args: str, schema: dict) - dict: # 1. 先解析 JSON模型可能返回字符串 try: args json.loads(raw_args) if isinstance(raw_args, str) else raw_args except json.JSONDecodeError as e: raise ToolArgError(tool_name, JSON_PARSE_FAILED, str(e)) # 2. schema 结构校验 validator Draft202012Validator(schema) errors sorted(validator.iter_errors(args), keylambda e: e.path) if errors: raise ToolArgError(tool_name, SCHEMA_VALIDATION_FAILED, format_errors(errors)) # 3. 语义校验schema 管不到的部分 semantic_check(tool_name, args) return args这个函数做了三件事解析、结构校验、语义校验。顺序不能反因为语义校验依赖结构已经正确。4.3 第三步补上 schema 管不了的语义校验这是最容易被忽略的一环。JSON Schema 能校验格式但校验不了“这个日期是否真实存在”“这个城市名是否在服务范围内”。我一般会针对每个工具写一个语义校验函数def semantic_check(tool_name: str, args: dict): if tool_name search_train_tickets: # 日期真实性校验 try: datetime.strptime(args[date], %Y-%m-%d) except ValueError: raise ToolArgError(tool_name, INVALID_DATE, f日期不存在: {args[date]}) # 城市白名单校验 valid_cities load_city_whitelist() for key in (from_city, to_city): if args[key] not in valid_cities: raise ToolArgError(tool_name, CITY_NOT_SUPPORTED, f暂不支持: {args[key]})语义校验的粒度要把握好太松等于没校验太严会把正常请求也拦掉。比如城市白名单我一开始只放了几个大城市结果用户查“佛山到东莞”直接被拒体验很差。后来改成“先查白名单不在白名单的走模糊匹配匹配不到再拒”拦截率降下来了脏数据也没进来。4.4 第四步把校验失败的信息结构化返回给模型校验失败不要直接抛异常让链路崩掉而是返回一个结构化的错误让模型有机会重新生成参数def build_retry_message(tool_name: str, error: ToolArgError) - dict: return { role: tool, tool_call_id: error.call_id, content: json.dumps({ status: error, error_code: error.code, message: error.message, hint: 请根据错误信息修正参数后重新调用 }, ensure_asciiFalse) }这样模型收到错误后大概率会重新生成一版参数。我实测下来格式类错误日期、类型模型一次修正成功率在 80% 以上枚举类错误大概 60%。所以重试机制要设上限一般 2 次就够了超过就降级到人工或默认值。5. 参数校验的进阶技巧与性能考量5.1 用oneOf处理多形态参数有些工具的参数支持多种形态比如“查询条件”既可以是城市名也可以是城市 ID。这时候用oneOf{ query: { oneOf: [ { type: string, pattern: ^[\\u4e00-\\u9fa5]{2,10}$ }, { type: integer, minimum: 1 } ] } }但oneOf有个坑如果两个分支都能匹配校验会失败。比如字符串123既匹配 string 分支如果 pattern 允许数字又可能被尝试匹配 integer 分支。所以分支之间要互斥或者用anyOf放宽。5.2 校验性能别让校验成为瓶颈jsonschema 的校验本身很快但有两个地方容易拖慢每次调用都重新编译 validator。Draft202012Validator(schema)这个动作有开销应该把编译好的 validator 缓存起来按工具名做 key。语义校验里的远程调用。比如城市白名单如果每次都查数据库QPS 一高就顶不住。我一般用本地缓存 定时刷新白名单这种数据变更频率低缓存 5 分钟完全够用。实测数据一个包含 8 个字段、3 层嵌套的 schema编译一次约 0.5ms校验一次约 0.1ms。缓存 validator 后单次校验总耗时稳定在 0.2ms 以内对整体链路基本无感。5.3 校验规则的版本管理schema 是会变的。今天加个字段明天改个枚举值。如果 schema 直接硬编码在代码里每次改都要发版。我的做法是把 schema 抽成独立的配置文件JSON 或 YAML代码启动时加载配合一个版本号。这样改 schema 只需要更新配置重启服务即可不用改代码逻辑。注意schema 变更要向后兼容。加字段可以删字段和改类型要谨慎因为模型侧的工具定义可能还没同步更新贸然收紧会导致大量正常请求被拦。6. 常见问题与排查技巧实录6.1 校验失败排查速查表现象可能原因排查方向所有请求都校验失败schema 本身写错了或 draft 不匹配打印 schema用在线校验器验证偶发失败重试就好模型生成不稳定看失败样本补语义校验或加提示词约束报additionalProperties错误模型捏造了字段确认是否要放开或在校验前剔除多余字段日期格式对但业务报错语义校验缺失补strptime之类的真实性校验数字被当成字符串模型类型漂移schema 里加type约束或做类型转换6.2 三个我踩过的坑坑一additionalProperties: false太激进。有次模型返回的参数里多了一个_reason字段它自己加的“理由”结果被拦了。后来我改成校验前先按 schema 的properties过滤一遍只保留合法字段多余的直接丢弃而不是报错。这样既防了脏数据又不会因为模型的“好心”而误伤。坑二枚举值大小写敏感。我定义status枚举是pending、paid模型返回Pending校验失败。后来在语义校验里加了一层lower()归一化问题解决。枚举校验前先做归一化是个低成本高收益的操作。坑三嵌套对象的校验信息不友好。深层嵌套的 schema 校验失败时jsonschema给的错误路径是[items, 0, price]这种直接返回给模型它看不懂。我写了个format_errors把路径转成items[0].price这种人类可读格式模型修正成功率明显提升。6.3 一个反直觉的经验校验不是越严越好。我一开始追求“零脏数据”把所有能加的约束都加上了结果拦截率飙升到 15%大量正常请求被误伤。后来我调整策略结构校验从严类型、必填、枚举语义校验从宽能归一化的就归一化能兜底的就兜底。拦截率降到 3% 左右脏数据依然没进来。这个平衡点需要根据你的业务容忍度来调没有标准答案。7. 把校验层做成可复用的基础设施7.1 抽象成独立的校验模块如果你的系统里有多个工具、多个模型调用点校验逻辑一定要抽成独立模块而不是散落在各处。我的模块结构大概是这样validator/ __init__.py core.py # validate_tool_args 主入口 schemas/ # 各工具的 schema 配置 search_train.json book_hotel.json semantic.py # 语义校验函数 errors.py # 错误类型定义 cache.py # validator 缓存这样新增一个工具只需要加一份 schema 配置和一个语义校验函数主流程完全不用动。7.2 监控与告警校验层是观察模型行为的最佳窗口。我一般会埋几个指标校验失败率按工具、按错误类型分组。失败率突然升高说明模型侧可能变了或者 schema 改出问题了。重试成功率模型收到错误后重新生成的成功率。这个指标低说明错误信息不够清晰或者模型能力不够。字段捏造频率additionalProperties触发的次数。频率高说明提示词需要加强约束。这些指标我一般接到现有的监控系统里设个阈值告警。有一次模型平台悄悄更新了版本某个工具的失败率从 2% 涨到 20%告警及时触发我们当天就定位到了问题。7.3 和提示词约束的配合schema 校验是“事后拦截”提示词约束是“事前引导”。两者要配合用。我在工具定义的 description 里会明确写清楚约束比如“date 必须是 YYYY-MM-DD 格式且为真实存在的日期”“passenger_count 必须是 1 到 5 的整数”。实测下来提示词写清楚约束能把格式类错误降低一半以上剩下的再靠 schema 兜底。但提示词不能替代校验。模型再听话也有概率“发挥”。所以我的原则始终是提示词负责降低错误率schema 校验负责保证零脏数据两者缺一不可。8. 关于这套方案的一些个人体会这套“schema 校验挡在入口”的方案我在三个项目里落地过从最初的纯 jsonschema 到后来加上语义校验、缓存、监控前后迭代了大概半年。最大的体会是Function Calling 的可靠性不取决于模型多聪明而取决于你的工程约束多严密。模型编造参数是它的本性你没法改变但你可以决定这些编造出来的参数能不能进入你的系统。另一个体会是校验层的错误信息质量直接决定了整个链路的自愈能力。错误信息写得越清楚、越结构化模型自我修正的成功率越高人工介入的次数就越少。我在这上面花的功夫比写校验逻辑本身还多。最后分享一个小技巧如果你不确定某个 schema 约束会不会误伤正常请求可以先把它设成“只记录不拦截”模式跑一周看看命中率再决定要不要真正开启拦截。这个灰度过程能帮你找到那个“严而不误伤”的平衡点。

相关新闻

HER算法实战:稀疏奖励下DDPG目标重标记与Fetch环境实现

HER算法实战:稀疏奖励下DDPG目标重标记与Fetch环境实现

做机器人控制、自动驾驶或者游戏AI的朋友,大概率都撞上过同一个坎:稀疏奖励。环境给的反馈要么是0,要么就是终点处那一个1,中间漫长的探索过程完全靠瞎猜。传统强化学习在这种场景下基本是废的,而Hindsight Experience…

2026/10/2 20:12:09 阅读更多 →
JavaWeb学生管理系统:三层架构与JDBC事务实战

JavaWeb学生管理系统:三层架构与JDBC事务实战

简介:本资源是一套高分JavaWeb期末大作业项目——学生信息管理系统,面向计算机及相关专业本科生,专为课程设计、期末综合实践及Web开发入门实战打造。项目已通过实际教学检验,获98分优异成绩,涵盖完整MVC架构实现&…

2026/10/2 20:44:13 阅读更多 →
华为产品开发项目计划模板拆解:从PDT组织到TR评审的研发管理实战

华为产品开发项目计划模板拆解:从PDT组织到TR评审的研发管理实战

简介:面向产品开发项目管理者、项目经理及团队成员,这份华为产品开发项目计划模板以单文件PDF形式提供,用于规范产品开发各环节的规划与管理,减少关键路径和风险遗漏。内容涵盖项目概况、项目组织结构、依赖关系分析、技术方法与工…

2026/10/2 20:01:58 阅读更多 →

最新新闻

剖析阅读Sigma源码架构:RuleAnalyzer规则解析流水线与 Room 数据库设计

剖析阅读Sigma源码架构:RuleAnalyzer规则解析流水线与 Room 数据库设计

剖析阅读Sigma源码架构:RuleAnalyzer规则解析流水线与 Room 数据库设计 【免费下载链接】legado-E 阅读Sigma是legado的继承,保持开源免费,延续开源精神。 项目地址: https://gitcode.com/gh_mirrors/legado2/legado-E 阅读Sigma&…

2026/10/2 20:47:12 阅读更多 →
Kimi Work v3.2.15 并发 Agent 调度机制:基于虚拟线程的任务隔离与资源竞争分析

Kimi Work v3.2.15 并发 Agent 调度机制:基于虚拟线程的任务隔离与资源竞争分析

Kimi Work v3.2.15 并发 Agent 调度机制:基于虚拟线程的任务隔离与资源竞争分析上周在重构内部数据清洗流水线时,团队决定将原本基于 Akka Actor 模型的异步任务系统迁移到 Kimi Work v3.2.15 提供的并行智能体环境。初衷是看中其宣称的「300 个智能体并…

2026/10/2 20:47:12 阅读更多 →
计算机毕设选题推荐:基于Hadoop+Django的贷款审批决策数据挖掘分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习

计算机毕设选题推荐:基于Hadoop+Django的贷款审批决策数据挖掘分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习

✍✍计算机毕设指导师** ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!也可以在主页上或文…

2026/10/2 20:47:12 阅读更多 →
论文AI率太高怎么降?有保障承诺的降AI率网站推荐,降AI率没达标全额退回

论文AI率太高怎么降?有保障承诺的降AI率网站推荐,降AI率没达标全额退回

最近毕业季身边不少同学都踩了AI痕迹的坑,尤其在论文查重和AIGC检测上吃了大亏。根据教育部2025年发布的《高等学位论文质量监测年报》,全国本科毕业论文中疑似存在AI痕迹的比例高达29.7%,而硕士论文更是攀升至34.2%。随着政策不断收紧&#…

2026/10/2 20:47:12 阅读更多 →
产业资本运作之读懂资本底色

产业资本运作之读懂资本底色

产业资本运作之读懂资本底色 何伏 融通资管 投资合伙人(手搓原创,拒绝AI编写)资本运作这门手艺,门槛不在知识,在清醒。看清自己的位置,看清钱的脾气,看清周期站在哪一边。看不清的时候,少签一个字,多睡一晚。慢一步的代价,从来不是错过&…

2026/10/2 20:47:12 阅读更多 →
023_帧间隔期间总线活动引发的协议违例

023_帧间隔期间总线活动引发的协议违例

023、帧间隔期间总线活动引发的协议违例 那个凌晨三点的波形 前年做一套分布式采集系统,主站和从站之间用差分总线跑自定义轮询协议。实验室调试一切正常,拉到现场跑了不到四小时,主站开始间歇性报协议违例,从站离线。抓波形一看,帧间隔里出现了不该有的窄脉冲,宽度大概…

2026/10/2 20:46:11 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →