当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例

OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例

来源:17golang原创 2026-11-10 02:57:03 0浏览 收藏

工具调用最难排查的时刻,不是模型没有返回函数,而是参数校验失败后只剩一句“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 错误会在请求阶段暴露,而不是等到业务函数里才变成难追的异常。

Responses API function tool 的 strict schema、required 和 additionalProperties 静态边界结构图
图1:工具 schema 的静态边界说明图,展示 strict、required 与可空字段的关系,不是运行截图。
# 安装 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,以及截断并脱敏后的原始参数。原始参数不要直接写入生产日志,因为工具参数可能包含手机号、地址或内部编号。

Responses API 工具调用从 request_id、call_id 到参数校验错误回传的分层诊断关系图
图2:工具参数失败的分层诊断说明图,展示 request_id 到 function_call_output 的对应关系,不是运行截图。
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 关联其中某一个工具调用;批量工具调用时二者缺一不可。

校验失败后要不要把完整错误堆栈返回模型?

不建议。模型只需得到稳定的错误码和可修正提示,堆栈、字段路径和脱敏参数留在受控日志中。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Hugging Face Datasets streaming怎样用shuffle的buffer_size打散大语料并保持每轮可复现Hugging Face Datasets streaming怎样用shuffle的buffer_size打散大语料并保持每轮可复现
上一篇
Hugging Face Datasets streaming怎样用shuffle的buffer_size打散大语料并保持每轮可复现
Anthropic Claude tool use怎样把tool_use和tool_result配成一轮可继续的对话
下一篇
Anthropic Claude tool use怎样把tool_use和tool_result配成一轮可继续的对话
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    965次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    966次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1077次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1057次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    888次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码