OpenAI 可观测性与用量
可通过以下方式跟踪活动、检查已完成工作和轮次追踪:
- 在平台控制台查看会话日志。
- 跟随事件及已保存历史。
- 检查轮次,归属委派命令执行。
- 检查主智能体与子智能体的已记录词元用量。
在控制台查看会话
打开 平台日志 的 Agents 选项卡,按会话ID查找轮次、工具调用及子智能体。
按追踪指南在控制台查看模型响应、工具和子智能体活动,或通过公共API将会话追踪导出为OTLP JSON。
跟踪事件及检查会话历史
每个会话都有实时事件流。设置 OPENAI_API_KEY,将示意ID替换成已保存会话ID:
来源示例 1(javascript):
// [中文] 示例 1:跟踪事件及检查会话历史。
// [中文] 订阅会话事件流;在提交后续任务前建立订阅,避免遗漏早期事件。
// Replace the illustrative IDs and URLs below with your own resource values.
import OpenAI from "openai";
const client = new OpenAI();
const events = await client.beta.agents.sessions.events.stream("sess_123");
try {
for await (const event of events) {
if (
[
"agent.session.turn.failed",
"agent.session.turn.cancelled",
"agent.session.failed",
"agent.session.environment.failed",
"error",
].includes(event.type)
) {
throw new Error(`Agent lifecycle failure: ${event.type}`);
}
console.log(JSON.stringify(event));
}
} finally {
events.controller.abort();
}
来源示例 2(python):
## [中文] 示例 2:跟踪事件及检查会话历史。
## [中文] 订阅会话事件流;在提交后续任务前建立订阅,避免遗漏早期事件。
## Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
client = OpenAI()
session_id = "sess_123"
with client.beta.agents.sessions.events.stream(session_id) as events:
for event in events:
if event.type in {
"agent.session.turn.failed",
"agent.session.turn.cancelled",
"agent.session.failed",
"agent.session.environment.failed",
"error",
}:
raise RuntimeError(f"Agent lifecycle failure: {event.type}")
print(event.to_json(indent=None))
来源示例 3(go):
// [中文] 示例 3:跟踪事件及检查会话历史。
// [中文] 订阅会话事件流;在提交后续任务前建立订阅,避免遗漏早期事件。
// Replace the illustrative IDs and URLs below with your own resource values.
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
ctx := context.Background()
client := openai.NewClient()
events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, "sess_123")
defer events.Close()
if events.Err() != nil {
panic(events.Err())
}
for events.Next() {
event := events.Current()
switch event.Type {
case "agent.session.turn.failed", "agent.session.turn.cancelled", "agent.session.failed", "agent.session.environment.failed", "error":
panic(event.RawJSON())
}
fmt.Println(event.RawJSON())
}
if err := events.Err(); err != nil {
panic(err)
}
来源示例 4(java):
// [中文] 示例 4:跟踪事件及检查会话历史。
// [中文] 订阅会话事件流;在提交后续任务前建立订阅,避免遗漏早期事件。
// Replace the illustrative IDs and URLs below with your own resource values.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.ObjectMappers;
import com.openai.core.http.StreamResponse;
import com.openai.models.beta.agents.AgentSessionEvent;
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var json = ObjectMappers.jsonMapper();
try (StreamResponse<AgentSessionEvent> events =
client.beta().agents().sessions().events().streamStreaming("sess_123")) {
var iterator = events.stream().iterator();
while (iterator.hasNext()) {
var event = iterator.next();
if (event.turnFailed().isPresent()
|| event.turnCancelled().isPresent()
|| event.failed().isPresent()
|| event.environmentFailed().isPresent()
|| event.error().isPresent()) {
throw new IllegalStateException("Agent failed: " + event);
}
System.out.println(json.writeValueAsString(event));
}
}
来源示例 5(ruby):
## [中文] 示例 5:跟踪事件及检查会话历史。
## [中文] 订阅会话事件流;在提交后续任务前建立订阅,避免遗漏早期事件。
## Replace the illustrative IDs and URLs below with your own resource values.
require "openai"
require "json"
client = OpenAI::Client.new
events = client.beta.agents.sessions.events.stream_streaming("sess_123")
begin
events.each do |event|
case event.type.to_s
when "agent.session.turn.failed", "agent.session.turn.cancelled", "agent.session.failed", "agent.session.environment.failed", "error"
raise "Agent failed: #{event.to_h}"
end
puts JSON.generate(event.to_h)
end
ensure
events.close
end
来源示例 6(bash):
## [中文] 示例 6:跟踪事件及检查会话历史。
## [中文] 本段涉及的请求或服务地址:https://api.openai.com/v1/agents/sessions/sess_123/events?stream=true;示例占位地址需替换为实际资源。
curl -N \
-H "OpenAI-Beta: agents=v1" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Accept: text/event-stream" \
"https://api.openai.com/v1/agents/sessions/sess_123/events?stream=true"
流在空闲事件之后保持打开,以免遗漏排队工作。按 Ctrl+C 停止观察。
运行时可见类似事件:
来源示例 7(text):
agent.session.environment.connected
agent.session.turn.created
agent.session.turn.in_progress
agent.session.turn.item.added
agent.session.turn.output_text.delta
agent.session.turn.completed
agent.session.idle
检查已经发生的工作,可读取会话已保存项目:
来源示例 8(javascript):
// [中文] 示例 8:跟踪事件及检查会话历史。
// [中文] 读取已保存会话项目;它用于恢复历史,不是实时事件重放。
// Replace the illustrative IDs and URLs below with your own resource values.
import OpenAI from "openai";
const client = new OpenAI();
const sessionId = "sess_123";
const items = await client.beta.agents.sessions.items.list(sessionId, {
order: "asc",
limit: 100,
});
console.log(items.data);
来源示例 9(python):
## [中文] 示例 9:跟踪事件及检查会话历史。
## [中文] 读取已保存会话项目;它用于恢复历史,不是实时事件重放。
## Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
client = OpenAI()
session_id = "sess_123"
items = client.beta.agents.sessions.items.list(session_id, order="asc", limit=100)
print(items.to_json())
来源示例 10(go):
// [中文] 示例 10:跟踪事件及检查会话历史。
// [中文] 读取已保存会话项目;它用于恢复历史,不是实时事件重放。
// Replace the illustrative IDs and URLs below with your own resource values.
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
ctx := context.Background()
client := openai.NewClient()
result, err := client.Beta.Agents.Sessions.Items.List(ctx,
"sess_123",
openai.BetaAgentSessionItemListParams{
Order: "asc",
Limit: openai.Int(100),
})
if err != nil {
panic(err)
}
fmt.Println(result.Data)
来源示例 11(java):
// [中文] 示例 11:跟踪事件及检查会话历史。
// [中文] 读取已保存会话项目;它用于恢复历史,不是实时事件重放。
// Replace the illustrative IDs and URLs below with your own resource values.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.beta.agents.sessions.items.ItemListParams;
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var result =
client
.beta()
.agents()
.sessions()
.items()
.list(
ItemListParams.builder()
.sessionId("sess_123")
.order(ItemListParams.Order.of("asc"))
.limit(100L)
.build());
System.out.println(result.items());
来源示例 12(ruby):
## [中文] 示例 12:跟踪事件及检查会话历史。
## [中文] 读取已保存会话项目;它用于恢复历史,不是实时事件重放。
## Replace the illustrative IDs and URLs below with your own resource values.
require "openai"
client = OpenAI::Client.new
result = client.beta.agents.sessions.items.list(
"sess_123",
order: "asc",
limit: 100
)
puts result.data
来源示例 13(bash):
## [中文] 示例 13:跟踪事件及检查会话历史。
## [中文] 本段涉及的请求或服务地址:https://api.openai.com/v1/agents/sessions/sess_123/items?order=asc&limit=100;示例占位地址需替换为实际资源。
curl \
-H "OpenAI-Beta: agents=v1" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
"https://api.openai.com/v1/agents/sessions/sess_123/items?order=asc&limit=100"
检查轮次并识别委派命令
会话轮次可通过公共API读取。用命令项目的 turn_id 和会话ID查询。Go示例通过SDK原生分页搜索主智能体与子智能体轮次,再向所属智能体读取匹配轮次。将示意ID换成实际ID;cURL示例依赖 jq。
来源示例 14(javascript):
// [中文] 示例 14:检查轮次并识别委派命令。
// [中文] 通过轮次 ID 读取状态及 subagent_id;null 表示主智能体。
// [中文] 先搜索主智能体轮次,再遍历子智能体及其轮次;找到匹配 turn_id 后向所属智能体读取。
// Replace the illustrative session and turn IDs with values from your command item.
import OpenAI from "openai";
const client = new OpenAI();
async function findTurn(sessionId, turnId) {
for await (const turn of client.beta.agents.sessions.turns.list(sessionId, {
limit: 100,
})) {
if (turn.id === turnId) {
return client.beta.agents.sessions.turns.retrieve(turnId, {
session_id: sessionId,
});
}
}
for await (const subagent of client.beta.agents.sessions.subagents.list(
sessionId,
{ limit: 100 }
)) {
for await (const turn of client.beta.agents.sessions.subagents.turns.list(
subagent.id,
{ session_id: sessionId, limit: 100 }
)) {
if (turn.id === turnId) {
return client.beta.agents.sessions.subagents.turns.retrieve(turnId, {
session_id: sessionId,
subagent_id: subagent.id,
});
}
}
}
throw new Error(`Turn ${turnId} not found in session ${sessionId}`);
}
const turn = await findTurn("sess_123", "turn_123");
console.log(turn.subagent_id || "main agent");
来源示例 15(python):
## [中文] 通过SDK原生分页先搜索主智能体轮次,再搜索各子智能体轮次;找到归属后读取,未找到则报错。
## Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
def find_turn(client, session_id, turn_id):
# Native SDK pagination searches every page for the owning agent.
for turn in client.beta.agents.sessions.turns.list(session_id, limit=100):
if turn.id == turn_id:
return client.beta.agents.sessions.turns.retrieve(
turn_id, session_id=session_id
)
for subagent in client.beta.agents.sessions.subagents.list(session_id, limit=100):
turns = client.beta.agents.sessions.subagents.turns.list(
subagent.id, session_id=session_id, limit=100
)
for turn in turns:
if turn.id == turn_id:
return client.beta.agents.sessions.subagents.turns.retrieve(
turn_id, session_id=session_id, subagent_id=subagent.id
)
raise ValueError(f"Turn {turn_id} not found in session {session_id}")
turn = find_turn(OpenAI(), "sess_123", "turn_123")
print(turn.subagent_id or "main agent")
来源示例 16(go):
// [中文] 示例 16:检查轮次并识别委派命令。
// [中文] 通过轮次 ID 读取状态及 subagent_id;null 表示主智能体。
// [中文] 自动分页遍历主智能体与各子智能体轮次,找到指定 turn_id 后读取其 subagent_id,避免漏查委派命令。
// Replace the illustrative session and turn IDs with values from your command item.
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
ctx := context.Background()
client := openai.NewClient()
turn, err := findTurn(ctx, &client, "sess_123", "turn_123")
if err != nil {
panic(err)
}
if turn.SubagentID == "" {
fmt.Println("main agent")
} else {
fmt.Println(turn.SubagentID)
}
// findTurn discovers the owning agent using native SDK pagination.
func findTurn(ctx context.Context, client *openai.Client, sessionID, turnID string) (*openai.Turn, error) {
turns := client.Beta.Agents.Sessions.Turns.ListAutoPaging(ctx, sessionID,
openai.BetaAgentSessionTurnListParams{Limit: openai.Int(100)})
for turns.Next() {
if turns.Current().ID == turnID {
return client.Beta.Agents.Sessions.Turns.Get(ctx, sessionID, turnID)
}
}
if err := turns.Err(); err != nil {
return nil, err
}
subagents := client.Beta.Agents.Sessions.Subagents.ListAutoPaging(ctx, sessionID,
openai.BetaAgentSessionSubagentListParams{Limit: openai.Int(100)})
for subagents.Next() {
subagent := subagents.Current()
childTurns := client.Beta.Agents.Sessions.Subagents.Turns.ListAutoPaging(ctx, sessionID, subagent.ID,
openai.BetaAgentSessionSubagentTurnListParams{Limit: openai.Int(100)})
for childTurns.Next() {
if childTurns.Current().ID == turnID {
return client.Beta.Agents.Sessions.Subagents.Turns.Get(ctx, sessionID, subagent.ID, turnID)
}
}
if err := childTurns.Err(); err != nil {
return nil, err
}
}
if err := subagents.Err(); err != nil {
return nil, err
}
return nil, fmt.Errorf("turn %s not found in session %s", turnID, sessionID)
}
来源示例 17(java):
// [中文] 通过autoPager搜索主智能体和子智能体,找到目标轮次后从所属智能体读取;示意ID须替换。
// Replace the illustrative session and turn IDs with values from your command item.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.beta.agents.sessions.subagents.SubagentListParams;
import com.openai.models.beta.agents.sessions.turns.Turn;
import com.openai.models.beta.agents.sessions.turns.TurnListParams;
import com.openai.models.beta.agents.sessions.turns.TurnRetrieveParams;
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var turn = findTurn(client, "sess_123", "turn_123");
System.out.println(turn.subagentId().orElse("main agent"));
// Discover the owning agent using native SDK pagination before retrieving the turn.
static Turn findTurn(OpenAIClient client, String sessionId, String turnId) {
var sessions = client.beta().agents().sessions();
var turns =
sessions.turns().list(TurnListParams.builder().sessionId(sessionId).limit(100L).build());
for (var turn : turns.autoPager()) {
if (turn.id().equals(turnId)) {
return sessions
.turns()
.retrieve(TurnRetrieveParams.builder().sessionId(sessionId).turnId(turnId).build());
}
}
var subagents =
sessions
.subagents()
.list(SubagentListParams.builder().sessionId(sessionId).limit(100L).build());
for (var subagent : subagents.autoPager()) {
var childTurns =
sessions
.subagents()
.turns()
.list(
com.openai.models.beta.agents.sessions.subagents.turns.TurnListParams.builder()
.sessionId(sessionId)
.subagentId(subagent.id())
.limit(100L)
.build());
for (var turn : childTurns.autoPager()) {
if (turn.id().equals(turnId)) {
return sessions
.subagents()
.turns()
.retrieve(
com.openai.models.beta.agents.sessions.subagents.turns.TurnRetrieveParams
.builder()
.sessionId(sessionId)
.subagentId(subagent.id())
.turnId(turnId)
.build());
}
}
}
throw new IllegalArgumentException("Turn " + turnId + " not found in session " + sessionId);
}
来源示例 18(ruby):
## [中文] 使用auto_paging_each搜索主智能体和子智能体轮次;找到后读取,未找到则抛出错误。
## Replace the illustrative IDs below with values from your command item.
require "openai"
## Discover the owning agent before retrieving the turn.
def find_turn(client, session_id, turn_id)
turns = client.beta.agents.sessions.turns.list(session_id, limit: 100)
turns.auto_paging_each do |turn|
if turn.id == turn_id
return client.beta.agents.sessions.turns.retrieve(turn_id, session_id: session_id)
end
end
subagents = client.beta.agents.sessions.subagents.list(session_id, limit: 100)
subagents.auto_paging_each do |subagent|
turns = client.beta.agents.sessions.subagents.turns.list(
subagent.id, session_id: session_id, limit: 100
)
turns.auto_paging_each do |turn|
if turn.id == turn_id
return client.beta.agents.sessions.subagents.turns.retrieve(
turn_id, session_id: session_id, subagent_id: subagent.id
)
end
end
end
raise "Turn #{turn_id} not found in session #{session_id}"
end
turn = find_turn(OpenAI::Client.new, "sess_123", "turn_123")
puts(turn.subagent_id || "main agent")
来源示例 19(bash):
## [中文] 示例 19:检查轮次并识别委派命令。
## [中文] 本段涉及的请求或服务地址:https://api.openai.com/v1/agents/sessions/sess_123/turns?limit=20&order=desc、https://api.openai.com/v1/agents/sessions/sess_123/turns/turn_123;示例占位地址需替换为实际资源。
curl "https://api.openai.com/v1/agents/sessions/sess_123/turns?limit=20&order=desc" \
-H "OpenAI-Beta: agents=v1" \
-H "Authorization: Bearer $OPENAI_API_KEY"
curl "https://api.openai.com/v1/agents/sessions/sess_123/turns/turn_123" \
-H "OpenAI-Beta: agents=v1" \
-H "Authorization: Bearer $OPENAI_API_KEY" | jq '.subagent_id'
手工分页时,has_more 为真就用返回的 last_id 作为下一页 after。Python、Go、Java和Ruby示例使用SDK原生分页搜索主智能体与子智能体轮次;JavaScript示例也会搜索主智能体及各子智能体轮次。
命令项目含 turn_id。读取轮次的 subagent_id 可识别执行命令的子智能体,null 表示主智能体。API不报告命令输出是否被截断。
检查轮次追踪
控制台可查看已完成轮次及智能体活动。公共API通过项目密钥调用会话追踪导出端点。控制台内部追踪端点与受支持客户API分开。
轮次包含尽力记录的 usage 和表示委派工作的 subagent_id。未知用量可为 null,计数可能变化,见子智能体用量。
归属shell命令时,先按命令项目 turn_id 读取轮次,再检查 turn.subagent_id。客户API不指示命令输出是否截断。
错误与恢复
失败检查、恢复选择与安全重试见错误与恢复。
模型用量与费用
完成一个任务可能调用模型多次,每次沿用模型词元定价和提示词缓存规则,与Responses API一致。估算需覆盖完成任务所需全部调用。
费用来自哪些部分
- 输入词元:指令、工具定义、对话历史、用户输入、文件或图像、工具结果。
- 缓存输入词元:复用匹配前缀的输入,按模型缓存输入费率收费。
- 输出词元:生成文字、工具参数和推理。推理词元作为输出计费。
子智能体也调用模型,排查费用时把其轮次用量与主智能体一起检查。
合计主、子智能体工作与重试,再加入适用的工具、沙箱计算和第三方服务费。采用缓存写入计费的模型还需计算写入费用;下方Agents API用量字段没有单独缓存写入计数,不能据此精确计算此类模型账单。
提示词缓存
会话内上下文会向后传递,相邻模型调用共享前缀时可复用早先处理结果。模型仍生成新响应,缓存不会重放旧答案。维持会话不保证命中,取决于匹配前缀、模型缓存资格与存活规则。
尽量保持初始指令和工具定义稳定,把新任务细节放在后续消息。工具搜索发现的定义追加在对话末尾,保留较早内容以供复用。模型规则见提示词缓存。
较高缓存输入比例不等于整个任务省钱。缓存输入仍计费,反复调用也可能处理大量历史,应比较在所需质量和延迟下完成同一任务的总成本。
理解词元用量
会话和轮次资源提供尽力记录的 usage,未知时可为 null,会计数据到达后计数可能更新。缺失不代表零,这不是最终账单。
用量对象示例:
来源示例 20(json):
{
"input_tokens": 5000,
"input_tokens_details": {
"cached_tokens": 1500
},
"output_tokens": 900,
"output_tokens_details": {
"reasoning_tokens": 200
},
"total_tokens": 5900
}
本例输入5,000词元,输出900;输入中1,500来自缓存,输出中200用于推理。缓存已包含在 input_tokens,推理已包含在 output_tokens,不要再次相加。
检查子智能体词元用量
列出或读取会话轮次,检查各轮 usage。subagent_id 标识子智能体,主智能体为 null。has_more 为真时,保持相同 order,将 last_id 作为 after 继续分页。
用量仍是尽力记录,未知时为 null,计数可能变化。也可在追踪控制台检查每个智能体用量。

TRAE-AI编程


更多建议: