エージェントの動作をリアルタイムで追跡し、完了した作業や各ターンの詳細なトレースを確認できます。
- Platform ダッシュボードでセッションログを表示できます。
- イベントと保存済みの履歴からセッションの経過を追えます。
- ターンを調べて、委任されたコマンドをどのエージェントが実行したか特定できます。
- ルートエージェントとサブエージェントの各ターンに記録されたトークン使用量を確認できます。
platform.openai.com/logs?api=agents にアクセスし、エージェントタブを開きます。
セッション ID で検索すると、そのセッションのターン、ツール呼び出し、サブエージェントを確認できます。
ダッシュボードで記録済みのモデル応答、ツール呼び出し、サブエージェントの動作を確認する手順は、トレーシングガイドを参照してください。公開 API を使って、OTLP JSON 形式でセッショントレースをエクスポートすることもできます。
各セッションにはイベントストリームがあり、エージェントの動作をリアルタイムで確認できます。OPENAI_API_KEY を設定し、以下の例のセッション ID を保存しておいたセッション ID に置き換えてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 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))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 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));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 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
1
2
3
4
5curl -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 を押します。
セッションの実行中には、次のようなイベントが表示されます。
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
すでに行われた作業を確認するには、セッションに保存された項目を取得します。
1
2
3
4
5
6
7
8
9
10// 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);
1
2
3
4
5
6
7
8# 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())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 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());
1
2
3
4
5
6
7
8
9
10# 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
1
2
3
4curl \
-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 が必要です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// 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");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26# 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")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52// 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56// 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30# 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")
1
2
3
4
5
6
7curl "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'
手動でページを取得する場合は、レスポンスに含まれる last_id を次のページの after の値に指定します。次のページがあるのは、has_more が true の場合です。Go の例では、後続のページを自動的に取得します。
コマンド項目には turn_id が含まれます。そのターンを取得し、subagent_id を確認すると、委任されたコマンドを実行したエージェントを特定できます。サブエージェント ID が null の場合は、ルートエージェントが実行した作業です。コマンド出力が切り詰められたかどうかは報告されません。
Platform ダッシュボードでは、完了したターンと、そのターンでのエージェントの動作を確認できます。
公開 API から記録済みのトレースを取得するには、プロジェクトの API キーを使ってセッショントレースのエクスポート用エンドポイントを呼び出します。ダッシュボードのトレース用エンドポイントは、利用者向けにサポートされている API とは別です。
ターンリソースには、ベストエフォートで記録される usage と、作業の委任先を示す subagent_id が含まれます。使用量が不明な場合は null になることがあり、記録値は後から変わることがあります。サブエージェントのトークン使用量の確認を参照してください。
シェルコマンドの実行元を特定するには、コマンド項目の turn_id が示すターンを取得し、turn.subagent_id を確認します。利用者向け API では、コマンド出力が切り詰められたかどうかはわかりません。
エラーの内容を確認し、復旧方法を選んで安全に再試行する手順は、エラーと復旧を参照してください。
エージェントは、タスクを完了するまでにモデルを複数回呼び出すことがあります。Responses API と同様に、各呼び出しにはモデルのトークン料金とプロンプトキャッシュのルールが適用されます。コストを見積もる際は、タスクの完了に必要なすべての呼び出しを含めてください。
モデルの各呼び出しでは、次のトークンを消費することがあります。
- 入力トークン:エージェントへの指示、ツール定義、会話履歴、ユーザー入力、ファイルや画像、ツールの実行結果
- キャッシュ済み入力トークン:プロンプトの先頭部分が一致したことで再利用された入力。モデルのキャッシュ済み入力単価で課金
- 出力トークン:生成されたテキスト、ツール呼び出しの引数、推論
推論トークンには出力トークンの料金が適用されます。
サブエージェントもモデルを呼び出すことがあります。モデルのコストを調べる際は、ルートエージェントの作業とあわせて、サブエージェントのターンごとの使用量の記録を確認してください。
コストを見積もる際は、ルートエージェントとサブエージェントの作業にかかる費用を、再試行分も含めて計算してください。該当する場合は、ツール、サンドボックスのコンピューティングリソース、外部サービスの料金も加算します。キャッシュへの書き込みに料金が設定されているモデルでは、入力をキャッシュに書き込む際にも費用が発生します。以下に示す Agents API の使用量フィールドには、キャッシュに書き込んだトークン数が個別に表示されないため、書き込み料金が適用される場合は、これらのフィールドだけでモデルの正確な利用料金を算出することはできません。
エージェントはセッション内でコンテキストを引き継ぎます。連続するモデル呼び出しでプロンプトの先頭部分が一致すると、プロンプトキャッシュによって、その部分の処理結果を再利用できます。キャッシュを利用しても過去の回答が再生されることはなく、モデルは新しい応答を生成します。セッションを維持していても、必ずキャッシュにヒットするわけではありません。再利用できるかどうかは、プロンプトの先頭部分が一致するかどうかと、モデルごとのキャッシュの適用条件および有効期間によって決まります。
できる限り最初の指示とツール定義を変更せず、タスクに関する新しい情報は後続のメッセージに追加してください。ツール検索を使うと、見つかったツールの定義は会話の末尾に追加されるため、それまでの内容はキャッシュから再利用できます。モデルごとのルールは、プロンプトキャッシュを参照してください。
キャッシュ済み入力の割合が高くても、その割合からタスク全体のコスト削減額はわかりません。キャッシュ済み入力にも料金がかかり、呼び出しを繰り返すと、そのたびに長い会話履歴を処理することがあります。アプリケーションに必要な品質とレイテンシを満たす条件で、同じタスクを完了するまでのコストを比較してください。
セッションリソースとターンリソースでは、usage がベストエフォートで記録されます。使用量が不明な場合は null になることがあり、集計結果が反映されるにつれて記録値が変わることもあります。使用量の記録がなくても、使用量がゼロとは限りません。これらの数値は最終的な請求額を示すものではありません。
記録された使用量オブジェクトには、次のようにトークン数の内訳が含まれます。
1234567891011{
"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 はサブエージェントを識別する ID で、ルートエージェントのターンでは null です。has_more が true の場合は、last_id を after に指定し、同じ order を使って残りのターンを取得します。
使用量はベストエフォートで記録されます。不明な場合は null になることがあり、記録値は後から変わることがあります。各エージェントの使用量の記録は、トレーシングダッシュボードでも確認できます。