OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例
来源:17golang原创
时间:2026-11-10 02:57:03 106浏览 收藏
工具调用最难排查的时刻,不是模型没有返回函数,而是参数校验失败后只剩一句“invalid arguments”。我的处理方式是把失败拆成三层:请求 schema 被拒绝、arguments 不是合法 JSON、JSON 合法但不符合业务 schema。每层保存不同字段,最终给模型的 function_call_output 只返回短错误码,完整诊断留在受控日志里。
官方文档:https://platform.openai.com/docs/guides/function-calling
- Responses API 的工具调用参数是 JSON 字符串,必须用
call_id对应回传结果。 strict=true不是业务校验的替代品;对象要关闭额外属性,所有属性要列入required。- 日志保存 request_id、call_id、错误路径和脱敏参数,工具回传只保留可重试的错误码。
一、先把严格 schema 写成稳定边界
先定义工具能接受的最小输入。严格模式下,不能把“可选”理解成省略字段;更稳妥的写法是把字段放进 required,再用 ["string", "null"] 表示它允许为空。这样 schema 错误会在请求阶段暴露,而不是等到业务函数里才变成难追的异常。

# 安装 SDK 和本地 JSON Schema 校验器 python -m pip install openai jsonschema
import json
import os
# 每个字段都 required;允许缺省语义时,用 null 表达而不是省略字段
ORDER_SCHEMA = {
"type": "object",
"properties": {
"order_id": {"type": "string", "minLength": 1},
"locale": {"type": ["string", "null"]},
},
"required": ["order_id", "locale"],
"additionalProperties": False,
}
TOOLS = [{
"type": "function",
"name": "lookup_order",
"description": "读取订单的非敏感状态",
"strict": True,
"parameters": ORDER_SCHEMA,
}]
# 密钥只从环境变量读取,不把它拼进日志或文章输出
assert os.environ.get("OPENAI_API_KEY"), "请先配置 OPENAI_API_KEY"
print(json.dumps(TOOLS, ensure_ascii=False))
这里的 additionalProperties=false 会阻止未声明字段混入;locale 虽然允许为空,仍然必须出现在 required 中。若直接把严格 schema 写成“只要求 order_id、其他字段随便来”,请求本身就可能被拒绝,日志应该把这类问题归为 schema_request,不要误判为模型生成错误。
二、把失败分层并保留可诊断字段
下面的最小示例故意保留三份信息:请求异常的 request_id,每次函数调用的 call_id,以及截断并脱敏后的原始参数。原始参数不要直接写入生产日志,因为工具参数可能包含手机号、地址或内部编号。

import hashlib
import json
import logging
import os
from jsonschema import Draft202012Validator
from openai import OpenAI
logger = logging.getLogger("tool_call")
client = OpenAI()
def lookup_order(order_id, locale):
# 这里替换成真实查询;示例只返回可供模型继续理解的 JSON 字符串
return json.dumps({"ok": True, "order_id": order_id, "locale": locale}, ensure_ascii=False)
def redact(raw: str, limit: int = 240) -> str:
# 只保留有限长度,并用摘要帮助关联同一次失败
clipped = raw[:limit]
digest = hashlib.sha256(raw.encode("utf-8")).hexdigest()[:12]
return f"{clipped}... sha256={digest}" if len(raw) > limit else f"{clipped} sha256={digest}"
def diagnostic(stage: str, *, request_id=None, call_id=None, raw=None, errors=None):
# 完整诊断进日志,敏感参数经过截断;对模型只返回 error_code
item = {"stage": stage, "request_id": request_id, "call_id": call_id}
if raw is not None:
item["raw_arguments"] = redact(raw)
if errors:
item["validation_errors"] = errors[:5]
logger.warning("tool_call_diagnostic=%s", json.dumps(item, ensure_ascii=False))
return item
try:
response = client.responses.create(
model=os.environ.get("OPENAI_MODEL", "gpt-4.1-mini"),
input="查询订单 A-100 的状态",
tools=TOOLS,
)
except Exception as exc:
# 请求阶段失败:记录 SDK 能提供的 request_id 和异常类型
diagnostic("schema_request", request_id=getattr(exc, "request_id", None))
raise
for item in response.output:
if item.type != "function_call":
continue
raw_args = item.arguments
try:
args = json.loads(raw_args)
except json.JSONDecodeError as exc:
diagnostic("arguments_json", request_id=response.id, call_id=item.call_id,
raw=raw_args, errors=[str(exc)])
continue
problems = sorted(Draft202012Validator(ORDER_SCHEMA).iter_errors(args),
key=lambda error: list(error.path))
if problems:
paths = [".".join(map(str, error.path)) or "$" for error in problems]
diagnostic("arguments_schema", request_id=response.id,
call_id=item.call_id, raw=raw_args, errors=paths)
# 用 call_id 对回去;模型只看到短错误,不看到内部字段值
print(json.dumps({"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps({"ok": False,
"error_code": "invalid_tool_arguments"})},
ensure_ascii=False))
continue
# 只有通过本地校验才进入实际业务函数
print(lookup_order(args["order_id"], args["locale"]))
示例里的 response.id 用来关联本轮 Responses 请求,item.call_id 用来关联具体工具调用;一次响应可能有多个 function_call,不能只用“最后一个调用”做日志键。生产系统还应把诊断写到有访问控制的日志流,避免把完整参数回传给模型。
三、校验通过后再回传工具结果
校验失败时,业务函数没有被调用,模型收到的是可识别的 invalid_tool_arguments。校验通过时才执行查询,并把结果作为字符串放进 function_call_output。这个结果可以是 JSON、错误码或普通文本,但必须带原来的 call_id;否则多工具并行时容易把结果串错。
| 失败层 | 应保存 | 给模型的内容 |
|---|---|---|
| 请求 schema | 异常类型、request_id、schema 版本 | 不重试原请求,先修 schema |
| JSON 解析 | call_id、参数摘要、解析错误 | arguments_json_error |
| 业务 schema | 字段路径、参数摘要、规则版本 | invalid_tool_arguments |
这里“保留可诊断信息”不等于把所有上下文都塞给模型。对外回传短错误码,给人工和监控保留关联字段,既方便重放,也降低敏感数据扩散。
四、发布前的工具调用检查清单
- 确认严格 schema 的对象都设置了
additionalProperties=false,每个 property 都在required中。 - 确认日志能关联
request_id、call_id和 schema 版本,并对原始参数截断、脱敏。 - 确认 JSON 解析、schema 校验、业务执行分别有独立错误码,成功结果不会覆盖失败诊断。
- 确认重试只针对可重试的网络或服务错误;参数错误先修调用方或 schema,不要盲目重发。
相关问题
strict=true 能替代本地参数校验吗?
不能。它约束模型输出是否贴合工具 schema,本地校验仍负责业务范围、敏感字段和版本兼容。
为什么日志里必须同时保存 request_id 和 call_id?
request_id 关联整次 Responses 请求,call_id 关联其中某一个工具调用;批量工具调用时二者缺一不可。
校验失败后要不要把完整错误堆栈返回模型?
不建议。模型只需得到稳定的错误码和可修正提示,堆栈、字段路径和脱敏参数留在受控日志中。
-
346 收藏
-
235 收藏
-
387 收藏
-
447 收藏
-
360 收藏
-
240 收藏
-
354 收藏
-
158 收藏
-
113 收藏
-
306 收藏
-
139 收藏
-
242 收藏
-
374 收藏
-
442 收藏
-
383 收藏
-
289 收藏
-
158 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习