/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
调用 AI 的函数工具时,模型输出的是一条“调用建议”,真正执行代码的是你的应用。若把模型返回的函数名和参数直接交给 eval、动态导入或任意命令执行,模型输出就可能变成越权入口。
本文用 Python 标准库实现一个只读的订单查询分发器:只接受白名单工具、解析 JSON 参数、严格检查字段,再调用本地固定函数。代码不访问模型、网络、数据库或真实订单系统;运行结果是本地模拟,不代表任何 AI 服务的返回。
适用范围与环境
- 本地验证环境:Windows PowerShell、Python 3.14.7;脚本只依赖标准库 json。
- 演示目标:理解 Responses API 的 function_call 结果如何由应用接管并回传。
- 不适用于直接复制到生产。真实业务还需鉴权、用户级权限、审计、限流、超时和副作用确认。
- 样例订单 A-100、B-200 是脚本内的虚构数据。
先弄清调用边界
函数调用流程是:应用把工具定义发给模型 → 模型返回工具名和 JSON 参数 → 应用检查这条调用 → 应用执行本地函数 → 应用把结果作为 function_call_output 交回模型。
模型不会因为输出了 function_call 就替你的应用执行 Python 函数。工具定义主要描述可调用接口;是否执行、如何授权、如何保护数据都由应用负责。
示例:白名单加参数校验
将以下代码保存为 tool_dispatch_demo.py,运行:
python tool_dispatch_demo.py
import json
ORDERS = {
"A-100": {"order_id": "A-100", "status": "shipped"},
"B-200": {"order_id": "B-200", "status": "processing"},
}
def lookup_order(args):
if not isinstance(args, dict):
raise ValueError("arguments 必须是 JSON 对象")
if set(args) != {"order_id"}:
raise ValueError("arguments 必须且只能包含 order_id")
order_id = args["order_id"]
if not isinstance(order_id, str) or not order_id.strip():
raise ValueError("order_id 必须是非空字符串")
return ORDERS.get(order_id, {"error": "order_not_found"})
TOOLS = {"lookup_order": lookup_order}
def dispatch(call):
if not isinstance(call, dict):
raise ValueError("tool call 必须是对象")
name = call.get("name")
if not isinstance(name, str) or name not in TOOLS:
raise ValueError(f"unknown tool: {name}")
raw_arguments = call.get("arguments")
if not isinstance(raw_arguments, str):
raise ValueError("arguments 必须是 JSON 字符串")
try:
args = json.loads(raw_arguments)
except json.JSONDecodeError as exc:
raise ValueError(f"arguments JSON 无效: {exc.msg}") from exc
return TOOLS[name](args)
if __name__ == "__main__":
simulated_calls = [
("合法查询", {"name": "lookup_order", "arguments": '{"order_id":"A-100"}'}),
("未知工具", {"name": "delete_all", "arguments": "{}"}),
("多余参数", {"name": "lookup_order", "arguments": '{"order_id":"A-100","shell":"whoami"}'}),
("错误 JSON", {"name": "lookup_order", "arguments": '{"order_id":'}),
]
for label, call in simulated_calls:
try:
print(f"{label}: 执行结果 {json.dumps(dispatch(call), ensure_ascii=False)}")
except ValueError as exc:
print(f"{label}: 拒绝({exc})")
本地模拟输出
本地环境 Python 3.14.7 运行脚本,输出:
合法查询: 执行结果 {"order_id": "A-100", "status": "shipped"}
未知工具: 拒绝(unknown tool: delete_all)
多余参数: 拒绝(arguments 必须且只能包含 order_id)
错误 JSON: 拒绝(arguments JSON 无效: Expecting value)
这些调用对象是手工构造的测试输入,不是模型生成的结果。输出只说明本地分发器按预期拒绝了未登记工具、额外参数和无效 JSON。
接到真实 Responses API 时
OpenAI 官方函数调用指南中,Responses API 的工具调用以 type == function_call 的输出项出现,字段包括 name、JSON 字符串形式的 arguments 和 call_id。应用应逐项识别工具调用,经过自己的白名单与参数校验后再执行,并以 function_call_output 携带同一 call_id 返回结果。若使用严格模式,工具 JSON Schema 还需满足其约束;严格模式约束格式,不替代业务授权和数据校验。
核心伪代码如下,展示协议衔接,未在本文中调用真实模型或网络:
for item in response.output:
if item.type != "function_call":
continue
result = dispatch({
"name": item.name,
"arguments": item.arguments,
})
tool_outputs.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result, ensure_ascii=False),
})
真实 SDK 的响应对象和下一轮请求写法请按你正在使用的 SDK 版本核对官方示例;上面的代码只强调字段与信任边界。
排查与验证步骤
常见误区
- **把工具调用理解成模型替你运行代码。**模型只产生调用请求,应用需要自行判断和执行。
- **认为 strict schema 就是授权。**Schema 能限制结构,不能证明用户有权读取某个资源,也不能保证业务规则正确。
- **只校验工具名。**参数仍是不可信输入;检查 JSON 类型、必填字段、额外字段、长度和业务范围。
- **把异常原文回传模型或写入日志。**错误可能含内部路径、查询语句或个人信息;对外返回稳定错误码,对内做脱敏日志。
- **对副作用工具自动重试。**网络超时不能证明操作没发生。先设计幂等和结果查询,再决定能否重试。
官方资料
- OpenAI API:Function calling:工具定义、Responses API 的 function_call / function_call_output 流程、strict 模式。
- OpenAI API:Structured Outputs:结构化格式约束及其边界。
- Python 3.14:json — JSON encoder and decoder:json.loads 与解析错误。
结论:把模型输出当作外部输入。先白名单分发、解析并校验参数,再由业务代码按用户权限执行;格式正确只是第一道门。
网硕互联帮助中心



评论前必须登录!
注册