관찰 가능성과 사용량

관찰 가능성과 사용량 (Observability and usage)

실시간 에이전트 활동을 추적하고, 완료된 작업을 검사하며, 상세한 턴 트레이스를 검토할 수 있어요:

  1. 플랫폼 대시보드에서 세션 로그를 볼 수 있어요.
  2. 이벤트와 저장된 기록으로 세션을 따라갈 수 있어요.
  3. 턴을 검사하고 위임된 명령 실행을 식별할 수 있어요.
  4. 루트 에이전트와 서브에이전트 턴의 기록된 토큰 사용량을 검사할 수 있어요.

출처: 문서

본문

대시보드에서 세션 보기 (View the session in the dashboard)

platform.openai.com/logs?api=agents로 가서 Agents 탭을 여세요.

ID로 세션을 검색해 턴, 도구 호출, 서브에이전트를 검사하세요.

대시보드에서 기록된 모델 응답, 도구 호출, 서브에이전트 활동을 검사하려면 Tracing 가이드를 사용하거나, 공용 API로 세션 트레이스를 OTLP JSON으로 내보낼 수 있어요.

이벤트 따라가고 세션 기록 검사하기 (Follow events and inspect session history)

모든 세션은 에이전트가 무엇을 하고 있는지 실시간으로 보여 주는 이벤트 스트림을 노출해요. OPENAI_API_KEY를 설정하고 이 예시의 예시용 세션 ID를 저장한 세션 ID로 바꾸세요:

실시간 세션 이벤트 따라가기

// 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();
}
# 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))
// 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)
}
// Replace the illustrative IDs and URLs below with your own resource values.
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.beta.agents.AgentSessionEvent;

OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var json = new 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));
  }
}
# 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
curl -N \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***" \
  -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

이미 일어난 작업을 검사하려면 세션의 저장된 아이템을 검색하세요:

저장된 세션 아이템 검사하기

// 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);
# 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())
// 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)
// 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());
# 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
curl \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***" \
  "https://api.openai.com/v1/agents/sessions/sess_123/items?order=asc&limit=100"

턴 검사하고 위임된 명령 식별하기 (Inspect turns and identify delegated commands)

세션 턴은 공용 API로 사용할 수 있어요. 명령 아이템의 turn_id를 저장한 세션 ID와 함께 사용하세요. cURL 예시는 jq가 필요해요:

위임된 명령 실행 식별하기

// 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 turns = await client.beta.agents.sessions.turns.list(sessionId, {
  limit: 20,
  order: "desc",
});
console.log(turns.data);
const turnId = "turn_123";
const turn = await client.beta.agents.sessions.turns.retrieve(turnId, {
  session_id: sessionId,
});
console.log(turn.subagent_id);
# Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI

client = OpenAI()

session_id = "sess_123"
turns = client.beta.agents.sessions.turns.list(session_id, limit=20, order="desc")
print(turns.to_json())
turn_id = "turn_123"
turn = client.beta.agents.sessions.turns.retrieve(turn_id, session_id=session_id)
print(turn.subagent_id)
// 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.Turns.List(ctx,
	"sess_123",
	openai.BetaAgentSessionTurnListParams{
		Limit: openai.Int(20),
		Order: "desc",
	})
if err != nil {
	panic(err)
}
fmt.Println(result.Data)
turn, err := client.Beta.Agents.Sessions.Turns.Get(ctx,
	"sess_123",
	"turn_123")
if err != nil {
	panic(err)
}
fmt.Println(turn.SubagentID)
// 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.turns.TurnListParams;
import com.openai.models.beta.agents.sessions.turns.TurnRetrieveParams;

OpenAIClient client = OpenAIOkHttpClient.fromEnv();
var result =
    client
        .beta()
        .agents()
        .sessions()
        .turns()
        .list(
            TurnListParams.builder()
                .sessionId("sess_123")
                .limit(20L)
                .order(TurnListParams.Order.of("desc"))
                .build());
System.out.println(result.items());
var turn =
    client
        .beta()
        .agents()
        .sessions()
        .turns()
        .retrieve(
            TurnRetrieveParams.builder().turnId("turn_123").sessionId("sess_123").build());
System.out.println(turn.subagentId());
# Replace the illustrative IDs and URLs below with your own resource values.
require "openai"

client = OpenAI::Client.new
result = client.beta.agents.sessions.turns.list(
  "sess_123",
  limit: 20,
  order: "desc"
)
puts result.data
turn = client.beta.agents.sessions.turns.retrieve(
  "turn_123",
  session_id: "sess_123"
)
puts turn.subagent_id
curl "https://api.openai.com/v1/agents/sessions/sess_123/turns?limit=20&order=desc" \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***"

curl "https://api.openai.com/v1/agents/sessions/sess_123/turns/turn_123" \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***" | jq '.subagent_id'

has_more가 true일 때 반환된 last_id를 다음 페이지의 after 값으로 사용하세요.

명령 아이템은 turn_id를 포함해요. 그 턴을 검색하고 subagent_id를 읽어 명령을 실행한 위임 에이전트를 식별하세요. null 서브에이전트 ID는 루트 에이전트 작업을 식별해요. 명령 출력 잘림은 보고되지 않아요.

턴 트레이스 검사하기 (Inspect a turn trace)

플랫폼 대시보드를 사용해 완료된 턴과 그 에이전트 활동을 검사하세요. 공용 API로 기록된 트레이스를 검색하려면 프로젝트 API 키로 세션 트레이스 내보내기 엔드포인트를 사용하세요. 대시보드 트레이스 엔드포인트는 지원되는 고객 API와 별개예요.

턴 리소스는 최선 노력(best-effort) usage와 위임된 작업을 식별하는 subagent_id를 포함해요. usage는 알 수 없을 때 null일 수 있고 바뀔 수 있어요. 서브에이전트 토큰 사용량 검사를 참고하세요.

셸 명령을 귀속시키려면 명령 아이템의 turn_id가 가리키는 턴을 검색한 뒤 turn.subagent_id를 검사하세요. 고객 API는 명령 출력이 잘렸는지 나타내지 않아요.

모델 사용량과 비용 (Model usage and cost)

에이전트는 과제를 완료하면서 여러 번 모델 호출을 할 수 있어요. 각 호출은 Responses API처럼 모델의 토큰 가격과 프롬프트 캐싱 규칙을 따라요. 과제 완료에 필요한 모든 호출에 걸쳐 비용을 추정하세요.

무엇이 비용에 기여하나요? (What contributes to cost?)

각 모델 호출이 소비할 수 있는 것:

  • 입력 토큰: 에이전트 지시사항, 도구 정의, 대화 기록, 사용자 입력, 파일 또는 이미지, 도구 결과.
  • 캐시된 입력 토큰: 일치하는 프롬프트 접두사에서 재사용된 입력. 모델의 캐시 입력 요율로 청구돼요.
  • 출력 토큰: 생성된 텍스트, 도구 호출 인자, 추론(reasoning).

추론 토큰은 출력 토큰으로 청구돼요.

서브에이전트도 모델 호출을 할 수 있어요. 모델 비용을 조사할 때 루트 에이전트 작업과 함께 그들의 기록된 턴 사용량을 검사하세요.

루트 에이전트와 서브에이전트 작업(재시도 포함)과 적용 가능한 도구, 샌드박스 컴퓨팅, 서드파티 서비스 요금을 모두 고려하세요. 캐시 쓰기 가격이 있는 모델에서는 캐시에 입력을 쓰는 것도 비용이 들어요. 아래 Agents API 사용량 필드는 별도의 캐시 쓰기 수를 노출하지 않아서, 그 가격이 적용될 때 정확한 모델 요금을 결정할 수 없어요.

프롬프트 캐싱 (Prompt caching)

에이전트는 세션 안에서 컨텍스트를 앞으로 이어 가져요. 연속된 모델 호출이 같은 프롬프트 접두사를 공유하면 프롬프트 캐싱이 이전 처리를 재사용할 수 있어요. 모델이 새 응답을 생성하지, 캐싱이 옛 답을 재생하는 건 아니에요. 세션을 유지한다고 캐시 히트가 보장되진 않아요. 재사용은 일치하는 접두사와 모델의 캐시 자격 및 수명 규칙에 달려 있어요.

가능한 한 초기 지시사항과 도구 정의를 안정적으로 유지하고, 새 작업 세부 정보는 후속 메시지에 넣으세요. tool search에서는 발견된 정의가 대화 끝에 추가되어 이전 내용이 캐시 재사용을 위해 보존돼요. 모델별 규칙은 Prompt caching을 참고하세요.

캐시 입력 비율이 높다고 해서 전체 과제 비용의 절감을 측정하는 건 아니에요. 캐시 입력도 청구되고, 반복된 호출이 큰 기록을 처리할 수 있어요. 애플리케이션이 필요로 하는 품질과 지연 시간으로 같은 과제를 완료하는 비용을 비교하세요.

토큰 사용량 이해하기 (Understand token usage)

세션과 턴 리소스는 최선 노력 usage를 노출해요. 알 수 없을 때 null일 수 있고, 회계가 도착하면 기록된 수치가 바뀔 수 있어요. 사용량이 없다고 해서 0 사용량을 뜻하지는 않아요. 이 수치는 최종 청구서가 아니에요.

기록된 사용량 객체는 이런 토큰 범주를 포함해요:

{
  "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에 포함돼요.

서브에이전트 토큰 사용량 검사 (Inspect subagent token usage)

세션 턴을 나열하거나 검색하고 각 턴의 usage를 검사하세요. subagent_id는 서브에이전트를 식별하며 루트 에이전트 턴에서는 null이에요. has_more가 true일 때 같은 order로 last_id를 after로 전달해 나머지 턴을 읽으세요.

사용량은 최선 노력이에요. 알 수 없을 때 null일 수 있고 기록된 값은 바뀔 수 있어요. 트레이싱 대시보드에서 각 에이전트의 기록된 사용량을 검사할 수도 있어요.

더 알아보기 (Learn more)

  • Tracing 가이드에서 모델 응답과 서브에이전트 활동 트레이스를 확인하세요.
  • 세션 관리에서 턴 검사 방법을 확인하세요.