Tool calling校验工具参数并区分模型与业务错误的实现方法
我在第一次把工单查询接入 tool calling 时,最容易混淆的是“模型给错参数”和“工具执行失败”。前者属于调用契约问题,应该拒绝或让模型修正;后者属于业务结果,应该把可解释的失败回传给模型,而不是让模型继续猜参数。比较稳妥的实现是:工具声明负责约束形状,应用服务负责二次校验和白名单路由,业务函数只返回稳定的成功或失败结果。
官方文档:https://developers.openai.com/api/docs/guides/function-calling
strict: true能收紧函数参数契约,但不替代库存、权限、状态等业务校验。- 模型侧错误包括未知工具、JSON 解码失败和 schema 不匹配;业务侧错误包括订单不存在、无权访问和状态不允许。
- 回传
function_call_output时保留call_id,让日志、重试和最终回答能对应同一次调用。
先把工具调用拆成两条责任边界
OpenAI 的 function calling 本质上是一个多轮交互:应用带着工具定义请求模型,模型返回工具调用,应用执行函数,再把工具结果发回模型。对我来说,最重要的设计决定不是“要不要加重试”,而是先给这条数据流划线:模型产生的调用对象到达业务函数之前,全部按模型错误处理;业务函数已经拿到合法参数后发生的失败,才算业务错误。

工具声明也要尽量具体。官方文档把函数输入定义为 JSON Schema,并建议开启严格模式;严格模式下,对象需要设置 additionalProperties: false,属性通常都应列入 required,可选值可以用可空类型表达。这些规则解决的是“字段长什么样”,不是“这个订单此刻能不能查”。
用 schema 拦截形状错误,再做一次应用层判断
下面这个示例把两层错误显式分开。示例使用 Python 标准库解析 JSON;生产项目可以把同一份契约交给 JSON Schema 校验器,但不要因为模型开启了 strict 就跳过应用侧校验。模型输出仍需要经过工具名白名单、解码、字段范围和权限上下文检查。
import json
def classify_tool_call(tool_call):
# 先限制工具名,防止模型请求未注册的业务能力。
if tool_call.get("name") != "lookup_order":
return {"kind": "model_error", "code": "unknown_tool"}
try:
args = json.loads(tool_call.get("arguments", "{}"))
except json.JSONDecodeError:
# JSON 无法解析时,业务函数还没有被调用。
return {"kind": "model_error", "code": "invalid_arguments_json"}
if not isinstance(args, dict) or not isinstance(args.get("order_id"), str):
# 这是参数形状错误,不应进入订单查询重试。
return {"kind": "model_error", "code": "schema_mismatch"}
if not args["order_id"].startswith("ORD-"):
# 形状合法但业务字段格式不接受,仍在边界层拦截。
return {"kind": "business_error", "code": "invalid_order_id"}
return {"kind": "validated", "args": args}
这里的 invalid_order_id 也可以归入领域校验,关键是团队要固定分类并写进日志。不要有时把它当模型错误,有时又当订单服务错误,否则重试器很难判断是否应该再次请求模型。
| 现象 | 归属 | 处理动作 |
|---|---|---|
| 工具名不在注册表 | 模型错误 | 拒绝调用并记录原始 call_id |
| arguments 不是合法 JSON | 模型错误 | 让模型修正调用,避免触发业务重试 |
| 订单不存在或无权限 | 业务错误 | 返回稳定错误码,由模型组织用户可读答复 |
| 订单已关闭,不能执行动作 | 业务错误 | 回传状态和下一步建议,不伪装成成功 |
业务函数只接收已验证参数,结果也要可回传
验证通过后再走白名单路由。业务函数不要把 Python 异常、数据库堆栈或内部字段直接塞进模型上下文;可观测信息写日志,给模型的结果保持短小、结构稳定。成功返回订单摘要,失败返回 ok=false、错误码和可行动的提示即可。
def execute_lookup(validated, current_user):
# 通过验证后才读取业务数据,权限仍以当前用户为准。
order_id = validated["args"]["order_id"]
order = find_order(order_id)
if order is None:
return {"ok": False, "error_code": "ORDER_NOT_FOUND",
"message": "订单不存在,请核对订单号"}
if order["owner_id"] != current_user["id"]:
# 不泄露订单是否存在,权限失败统一返回可解释结果。
return {"ok": False, "error_code": "ORDER_FORBIDDEN",
"message": "当前账号无权查看该订单"}
return {"ok": True, "order_id": order_id,
"status": order["status"]}
def to_function_output(call_id, result):
# call_id 必须原样保留,便于把结果对应回这次工具调用。
return {"type": "function_call_output", "call_id": call_id,
"output": json.dumps(result, ensure_ascii=False)}

官方文档允许把工具结果作为字符串传回,字符串内部可以是 JSON、错误码或普通文本。我的取舍是统一使用 JSON,因为监控可以按 ok 和 error_code 聚合,模型也能稳定理解;但内部异常细节不放进去,避免把实现信息暴露给下一轮上下文。
重试只对症下药,别把业务失败变成参数修复
模型错误可以有限次重试,例如只针对 JSON 解码或 schema 不匹配重新请求;未知工具应直接报警,因为它更像注册表漂移。业务错误通常不该自动重试:订单不存在不是换个参数就一定能解决,无权限也不能靠模型多调用几次绕过。若同时开启并行工具调用,日志还要按每个 call_id 独立记录结果,不能把一条失败污染同轮其他合法调用。
我会把以下字段放进结构化日志:调用名、call_id、schema 校验结果、业务错误码、用户上下文摘要、耗时和最终是否重试。这样排查时先看错误层级,再决定修提示词、修工具契约还是修业务服务,不会从一条模糊的“tool failed”开始猜。
常见问题
开启 strict 后还需要应用层校验吗?
需要。strict 主要约束模型生成的参数形状,订单存在性、权限、状态和数据一致性仍必须由应用服务判断。
模型错误要不要把原始异常回传?
不建议。给模型稳定的错误码和修正方向即可;原始 JSON、堆栈和内部字段留在受控日志中。
业务错误应该让模型重新调用工具吗?
只有错误信息明确提示可修正输入时才考虑一次重试,例如订单号格式不对;不存在、无权限和状态冲突通常直接生成解释性答复。
Go HTTP 超时回收空闲连接避免资源占满的排查指南
- 上一篇
- Go HTTP 超时回收空闲连接避免资源占满的排查指南
- 下一篇
- Go io合并多个输入流并处理错误的实践方案
-
- 科技周边 · 人工智能 | 2小时前 | openai api · 结构化输出 · AI工程 · Pydantic JSON Schema Structured Outputs
- Structured Outputs让模型结果贴合 JSON Schema的实现方法
- 191浏览 收藏
-
- 科技周边 · 人工智能 | 4小时前 | 人工智能 · 结构化输出 · JSON解析 · 模型输出 · 接口容错 · 大模型结构化输出 模型输出JSON缺字段 JSON兜底解析 JSON Schema校验 AI接口异常处理
- 模型输出 JSON 缺字段时如何设计兜底解析
- 272浏览 收藏
-
- 科技周边 · 人工智能 | 5小时前 | 人工智能 · 显存管理 · 推理优化 · 本地推理 KV Cache batch size
- 本地推理 KV cache 和 batch size 如何做取舍
- 251浏览 收藏
-
- 科技周边 · 人工智能 | 7小时前 | API · 性能优化 · ai · AI 提示词缓存 Responses API Prompt Caching
- AI 提示词缓存如何按稳定前缀组织请求
- 357浏览 收藏
-
- 科技周边 · 人工智能 | 8小时前 |
- 分类评测集不均衡时如何比较 macro 与 micro 指标
- 473浏览 收藏
-
- 科技周边 · 人工智能 | 9小时前 |
- Agent 工具返回文件路径时如何限制工作区范围
- 381浏览 收藏
-
- 科技周边 · 人工智能 | 10小时前 |
- AI 流式响应中的 finish_reason 如何决定持久化时机
- 195浏览 收藏
-
- 科技周边 · 人工智能 | 12小时前 |
- LoRA adapter 合并后 tokenizer 配置如何核对
- 162浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 43次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 138次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 74次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 39次使用
-
- CMMLU
- 深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
- 26次使用
-
- Go代码规范错误处理示例经验总结
- 2022-12-23 278浏览
-
- 分析Go错误处理优化go recover机制缺陷
- 2023-01-01 483浏览
-
- Go 错误处理实践总结示例
- 2023-01-07 291浏览
-
- Go程序员踩过的defer坑错误处理
- 2023-01-19 195浏览
-
- golangvalidator库参数校验实用技巧干货
- 2023-01-07 111浏览

