展开本页目录
开始前准备什么
- 独立 Python 3.10+ 虚拟环境;本次使用 Python 3.14.3。
- openai-agents 0.23.1、openai 3.26.0、httpx2 2.13.1,下载版本文件安装。
- 准备支持 Chat 与 native function calling 的准确型号;首次工具只读取固定虚构资料。
- 选择模型协议
- 注册只读工具
- 关闭 tracing 与重试
- 验收完整循环
- 回查全部费用
默认预览;离线完整循环;真实运行需要 --run。
用于独立 Agent 虚拟环境。
2 次模型请求、1 次只读工具执行;控制台扣费显示合计 USD 0.000542。
2026-10-25 资料复核与操作补充
继续操作前,核对当前配置与执行范围
此记录覆盖锁定版本中的Chat工具循环;OpenAI托管工具、Responses-only能力和多Agent运行不由这次成功推断。需要升级时在独立环境重跑同一工具与来源任务,保留原记录。
原记录范围:本页保留原有配置、离线检查和历史记录;本轮没有新增该客户端或协议的在线运行记录。 下列相邻实验各有自己的日期、模型和产物,不替代本篇客户端或协议的原生验收。
显式指定 Chat 模型,关闭外部 tracing
完整下载脚本额外设置 max_turns=3、parallel_tool_calls=False、输出记录和重复运行门禁。max_retries=0 关闭失败重试;正常工具循环需要再次请求模型,是另一个回合,仍可能产生费用。
模型与工具配置核心
from agents import Agent, OpenAIChatCompletionsModel, function_tool, set_tracing_disabled
from openai import AsyncOpenAI
import os
set_tracing_disabled(True)
client = AsyncOpenAI(api_key=os.environ["tokenba_API_KEY"],
base_url="https://api.apizt.com/v1", max_retries=0, timeout=45)
model = OpenAIChatCompletionsModel(
model=os.environ["tokenba_MODEL"], openai_client=client)
@function_tool
def lookup_stock(sku: str) -> str:
if sku != "QR-SKU-001":
raise ValueError("Unknown demo SKU")
return '{"sku":"QR-SKU-001","stock":7,"source":"DEMO-CATALOG-20261008"}'
agent = Agent(name="DemoStockReader", model=model, tools=[lookup_stock],
instructions="先查询库存,再依据工具实际结果回答。")固定工具只读取虚构库存
实际业务中,模型提出的是工具请求,不是执行授权。写数据库、发消息、退款等动作要由应用校验权限、参数和业务状态后决定。这个示例没有这些外部写操作。
| 对象 | 本例内容 | 验证目的 |
|---|---|---|
| 参数 | sku=QR-SKU-001 | 模型遵循工具 schema |
| 工具执行 | 本地返回 stock=7 | 函数真的执行,而非模型猜测 |
| 来源 | DEMO-CATALOG-20261008 | 最终回答引用真实工具返回 |
| 权限 | 无网络写入、Shell 或真实数据库 | 先验证协议和循环,再扩展业务工具 |
- 对象
- 参数
- 本例内容
- sku=QR-SKU-001
- 验证目的
- 模型遵循工具 schema
- 对象
- 工具执行
- 本例内容
- 本地返回 stock=7
- 验证目的
- 函数真的执行,而非模型猜测
- 对象
- 来源
- 本例内容
- DEMO-CATALOG-20261008
- 验证目的
- 最终回答引用真实工具返回
- 对象
- 权限
- 本例内容
- 无网络写入、Shell 或真实数据库
- 验证目的
- 先验证协议和循环,再扩展业务工具
实测:GLM 5.3 通过 SDK 查库存再回答
2026-10-08 使用 openai-agents 0.23.1 与 openai 3.26.0,真实请求 apizt 的 glm-5.3 Chat 通道。SDK 完成两次模型请求、本地只读工具执行一次,再将同一个 call ID 的库存结果送回模型。全过程约 17.369 秒;这是真实 SDK Runner,不是 CLI 或直接 HTTP 结果改名。
库存目录为固定虚构资料,只验证调用、执行、回传和最终回答。SDK 内部 history 使用统一 item 结构;其中 function_call/call_id 不能据此误判网络端点为 Responses,本次模型协议仍是 /v1/chat/completions。
当次 SDK usage 返回 input 506、output 141、total 706,前两项之和与 total 不同,记录保留原值。本人控制台两行扣费显示 0.000192 + 0.000350 = USD 0.000542,按六位小数显示值核对;首回合另列缓存 261。token 字段不归一化,这个费用只属于本次账号与通道,不是所有分组统一价格。
| 验收项 | 实际结果 | 范围 |
|---|---|---|
| SDK 模型请求 | 2 次 | glm-5.3、Chat Completions |
| 工具执行 | lookup_stock 1 次 | 只读固定 QR-SKU-001 |
| 回传事实 | stock=7,来源 DEMO-CATALOG-20261008 | 与本地实际工具返回一致 |
| 最终正文 | 库存 7 件,并列出来源 | 固定任务通过 |
| 本次两回合实扣显示 | USD 0.000542 | 本人控制台匹配;六位小数显示精度 |
- 验收项
- SDK 模型请求
- 实际结果
- 2 次
- 范围
- glm-5.3、Chat Completions
- 验收项
- 工具执行
- 实际结果
- lookup_stock 1 次
- 范围
- 只读固定 QR-SKU-001
- 验收项
- 回传事实
- 实际结果
- stock=7,来源 DEMO-CATALOG-20261008
- 范围
- 与本地实际工具返回一致
- 验收项
- 最终正文
- 实际结果
- 库存 7 件,并列出来源
- 范围
- 固定任务通过
- 验收项
- 本次两回合实扣显示
- 实际结果
- USD 0.000542
- 范围
- 本人控制台匹配;六位小数显示精度
本次实际最终回答
**QR-SKU-001 的库存为 7 件。**
来源:演示商品目录 `DEMO-CATALOG-20261008`(由库存查询工具 lookup_stock 返回的实际结果)。用同一份脚本分清三个模式
--offline 使用真实 SDK 和 HTTP mock,依次返回 tool_calls 与最终正文,检查第二次请求确实带入同 ID 的 tool 消息。没有向模型接口发请求。--run 会用自己的账号跑受限循环;输出文件已存在时拒绝再次提交。未知状态先查账户记录,不通过换文件名盲目重发。
创建独立 Agent 环境
python3 -m venv .venv-agents
.venv-agents/bin/python -m pip install -r agents-requirements.txt
.venv-agents/bin/python openai-agents-tool-loop.py
.venv-agents/bin/python openai-agents-tool-loop.py --offline --output fixture-record.json
# 由自己的安全环境提供 Key 与 Model ID 后
.venv-agents/bin/python openai-agents-tool-loop.py --run --output live-record-001.json验收完整循环,不用一条文字代替
SDK 离线循环和真实接口验收分别记录。Chat 兼容不自动提供 OpenAI 托管 File Search、Web Search、Responses 会话或多 Agent 功能;逐项核对当前通道支持。
| 阶段 | 保存内容 | 通过标准 |
|---|---|---|
| 模型提出工具 | function_call / tool_calls 事件 | 名称正确、参数能解析 |
| 应用执行工具 | toolExecutions | 只执行固定 SKU 查询 |
| 结果回传 | history 中的工具结果 | 原 call ID、真实输出、完整相邻顺序 |
| 最终正文 | finalOutput | 库存 7、来源编号准确 |
| 请求与费用 | usage.requests 与账单 | 全部模型回合可对应;billedUsd=null 表示未核实 |
- 阶段
- 模型提出工具
- 保存内容
- function_call / tool_calls 事件
- 通过标准
- 名称正确、参数能解析
- 阶段
- 应用执行工具
- 保存内容
- toolExecutions
- 通过标准
- 只执行固定 SKU 查询
- 阶段
- 结果回传
- 保存内容
- history 中的工具结果
- 通过标准
- 原 call ID、真实输出、完整相邻顺序
- 阶段
- 最终正文
- 保存内容
- finalOutput
- 通过标准
- 库存 7、来源编号准确
- 阶段
- 请求与费用
- 保存内容
- usage.requests 与账单
- 通过标准
- 全部模型回合可对应;billedUsd=null 表示未核实
400 错误常见在协议和工具历史
| 现象 | 优先检查 | 处理 |
|---|---|---|
| unexpected input / messages | 实际走 Chat 还是 Responses | 显式选模型实现 |
| tool_call_id 缺失或错配 | assistant 事件与 tool 回传 ID | 保留完整循环;不伪造工具结果 |
| 工具没被执行 | 该型号是否返回原生 tool_calls | 检查事件,不能把一句“已查询”当成功 |
| 模型请求超出预算 | max_turns 与每回合输出 | 限制轮次;超限后保留状态 |
- 现象
- unexpected input / messages
- 优先检查
- 实际走 Chat 还是 Responses
- 处理
- 显式选模型实现
- 现象
- tool_call_id 缺失或错配
- 优先检查
- assistant 事件与 tool 回传 ID
- 处理
- 保留完整循环;不伪造工具结果
- 现象
- 工具没被执行
- 优先检查
- 该型号是否返回原生 tool_calls
- 处理
- 检查事件,不能把一句“已查询”当成功
- 现象
- 模型请求超出预算
- 优先检查
- max_turns 与每回合输出
- 处理
- 限制轮次;超限后保留状态
SDK 环境分开,正式状态持久化
- 不要把所有 Agent 框架安装进同一个示例虚拟环境:本次 Agents 与 Semantic Kernel 的 MCP 依赖范围不同。
- 关闭 tracing 避免把提示词与工具结果发送到另一服务;需要观测时由你选择并配置数据范围。
- 本脚本只保护同一个输出文件;它不是多机去重、服务端幂等或完整会话恢复。
- 正式工具结果、业务 ID 与会话状态需要可靠持久化,失败时先判断工具是否已产生副作用。
怎样确认已经完成
- 准确记录 SDK 版本和模型协议。
- 工具事件、执行、同 ID 回传、最终回答全部存在。
- 循环有轮次上限,未知结果不自动重发。
- 按全部模型回合核对账单,不把 token usage 当实扣。
常见问题
只设置 OPENAI_BASE_URL 就够了吗?
还需明确模型实现和协议。本例显式使用 OpenAIChatCompletionsModel;默认 Responses 功能不能因为换地址就自动兼容。
为什么一个工具任务通常有两次请求?
第一次模型提出工具,应用执行并回传后,第二次模型生成最终回答。这是正常循环,费用应统计全部回合。
脚本记录 billedUsd=null 是免费吗?
不是。它表示未核实实际扣费;须用自己的控制台记录核对同一任务。