프로덕션에서 모델 비교하기

프로덕션에서 모델 비교하기 (Compare Models in Production)

배포된 에이전트 하나에 서로 다른 모델을 붙이고, 모든 모델을 같은 지표로 채점한 뒤 대시보드에서 비교하는 방법을 설명하는 문서예요. 이 문서에서는 트레이싱 설정, 지표 생성, 대시보드 구성, 결과 해석, 그리고 어떤 파라미터든 비교하는 패턴까지 다뤄요.

출처: 문서

본문

개요 (Overview)

배포된 에이전트 하나를 여러 모델에 걸쳐, 모델 선택을 트레이스 메타데이터로 기록해서 비교할 수 있어요. 그러면 Confident AI가 모든 트레이스에 대해 같은 온라인 지표를 실행하고, 그 메타데이터로 필터·분할·추세를 보여주는 대시보드를 만들 수 있어요.

이 가이드는 confident-trace로 OpenAI Agents, LangGraph, Vercel AI SDK, Strands Agents에 걸친 패턴을 보여줘요. 핵심 아이디어는 항상 같아요: 모든 요청이 트레이스를 내보내고, 모든 트레이스가 안정적인 model_variant를 포함하며, 모든 모델 변형이 같은 지표 컬렉션으로 채점돼요.

model_variant는 여러분이 고르는 짧고 사람이 읽기 쉬운 라벨(gpt-4o, nova-pro, claude-sonnet)이에요. 모든 대시보드가 이 차원으로 분할되죠. model_id는 그 뒤에 있는 정확한 제공자 모델 문자열이에요. model_variant로 비교하고 model_id는 감사 가능성을 위해 유지해요.

핵심은 이거예요: LLM 스팬에 잡힌 모델 이름은 디버깅에 유용하지만, 제공자별로 너무 세분화되어 분석하기 어려운 경우가 많아요 — 그리고 스팬에 있지 트레이스에 있지 않아요. 안정적인 model_variant를 트레이스로 승격하면, 기본 제공자 모델 ID가 바뀌어도 모든 대시보드가 하나의 깔끔한 제품 수준 차원으로 분할·필터·추세를 볼 수 있어요.

이 같은 패턴은 모델보다 훨씬 많은 것을 비교해요. 트레이스에 라벨할 수 있는 것 — 프롬프트 버전, temperature, 검색기, 도구 집합 — 은 정확히 같은 방식으로 비교할 수 있어요. 다른 변수에 이 가이드를 반복하려면 아무 파라미터나 비교하기를 참고해요.

이 가이드는 배포된 트래픽에서 모델 변형을 관측하기 위한 문서예요. 같은 데이터셋을 여러 모델로 돌리는 통제된 비교가 필요하다면, 모델 변형당 하나의 AI Connection 설정으로 Experiments 또는 Arena를 사용해요.

무엇을 만들게 될까요?

끝나면 다음을 갖게 돼요:

  • 모든 트레이스에 model_variant와 model_id를 기록하는 트레이싱된 에이전트.
  • 각 모델 변형의 비교 트래픽을 생성하는 반복 가능한 명령.
  • 모든 변형을 같은 기준으로 채점하는 지표 컬렉션.
  • 변형 간 품질, 트레이스 볼륨, 지연 시간을 비교하는 대시보드.
  • 운 좋은 트래픽 조각 하나가 아니라 어떤 모델이 이기는지에 대한 명확한 판독.

최종 대시보드는 모델 품질, 트래픽, 지연 시간을 나란히 비교해요

사전 조건 (Prerequisites)

Confident AI 프로젝트, 프로젝트 API 키, 그리고 에이전트가 호출하는 모델 제공자의 자격 증명이 필요해요. OpenAI 기반 예시에는 OPENAI_API_KEY를 설정해요. Strands 예시에는 사용하는 Bedrock 모델 ID에 접근할 AWS 자격 증명을 구성해요.

사용하는 프레임워크와 함께 confident-trace를 설치해요:

OpenAI Agents

openai-agents extra는 프레임워크 자체가 아니라 프레임워크용 트레이싱 브리지를 설치하므로, openai-agents를 함께 설치해요.

python -m venv .venv
source .venv/bin/activate
pip install -U 'confident-trace[openai-agents]' openai-agents

LangGraph

python -m venv .venv
source .venv/bin/activate
pip install -U confident-trace 'langgraph>=1,<2' 'langchain-openai>=1,<2'

Vercel AI SDK

Node.js 22+와 AI SDK 7이 필요해요.

npm install confident-trace 'ai@>=7.0.93 <8' @ai-sdk/openai@4
npm install -D tsx

Strands Agents

Strands는 자체 OpenTelemetry 스팬을 내보내고, confident-trace가 그것을 내보내요 — 추가 exporter 패키지는 필요 없어요.

python -m venv .venv
source .venv/bin/activate
pip install -U confident-trace strands-agents

그런 다음 같은 통합에 프로젝트와 제공자 자격 증명을 구성해요:

OpenAI Agents

export CONFIDENT_API_KEY="confident_us..."
export OPENAI_API_KEY="sk-..."

LangGraph

export CONFIDENT_API_KEY="confident_us..."
export OPENAI_API_KEY="sk-..."

Vercel AI SDK

export CONFIDENT_API_KEY="confident_us..."
export OPENAI_API_KEY="sk-..."

Strands Agents

export CONFIDENT_API_KEY="confident_us..."
export AWS_REGION="us-east-1"
export AWS_PROFILE="your-aws-profile"

EU 프로젝트는 OpenTelemetry 내보내기를 EU 엔드포인트로 지정해요:

export CONFIDENT_OTEL_ENDPOINT="https://eu.otel.confident-ai.com/v1/traces"

같은 대시보드에 넣을 모든 모델 변형에는 같은 CONFIDENT_API_KEY 를 사용해요. 한 서비스 인스턴스가 다른 프로젝트에 쓰면, 그 트레이스와 온라인 평가 점수가 비교에 나타나지 않아요.

트레이싱 설정

트레이싱이 이 가이드의 모든 대시보드를 먹여 살려요. 세 단계로 에이전트를 계측해서 각 요청이 model_variant로 태그된 트레이스를 내보내게 하고, 모든 변형을 채점하는 지표 컬렉션을 붙이며, 위젯을 만들기 전에 데이터 형태를 검증해요.

에이전트 계측

가장 중요한 구현 세부 사항은 메타데이터를 어디에 붙이느냐예요. 대시보드는 보통 트레이스 수준에서 집계하므로(평균 트레이스 점수, 트레이스 수, 트레이스 지연 시간, 트레이스 수준 온라인 평가 결과) model_variant를 LLM 스팬이 아니라 트레이스에 추가해요.

confident-trace에서 패턴은 모든 프레임워크에서 같아요: 시작 시 init()을 한 번 호출하고(설치된 프레임워크를 감지해 계측해요), 에이전트 호출을 애플리케이션 span으로 감싸 요청에 명확한 진입점을 만들고, 그 안에서 update_trace / updateTrace를 호출해 트레이스 입력·출력·비교 메타데이터를 설정해요.

트레이싱된 에이전트 만들기

정규화된 모델 변형을 받아 제공자 모델 ID로 해석하고, 에이전트를 실행하며, 현재 트레이스에 두 이름을 모두 기록하는 작은 에이전트 모듈을 만들어요.

아래 각 통합은 같은 대시보드 키를 내보내요: model_variant, model_id, agent, agent_version, rollout. 배포 환경은 일급 트레이스 필드이므로 init()에 한 번 설정해요.

OpenAI Agents

init()이 OpenAI Agents 브리지를 자동 활성화하므로, 에이전트·모델·도구 스팬이 등록할 트레이스 프로세서 없이 support-agent 스팬 아래 중첩돼요.

import os
import sys

from agents import Agent, Runner
from confident_trace import init, span, update_trace, shutdown

MODEL_MAP = {"gpt-4o-mini": "gpt-4o-mini", "gpt-4o": "gpt-4o", "gpt-4.1": "gpt-4.1"}

init(environment=os.getenv("APP_ENV", "production"))


def run_agent(user_input: str, model_variant: str) -> str:
    model_id = MODEL_MAP[model_variant]
    agent = Agent(
        name="Support Agent",
        instructions="Answer support questions clearly and safely.",
        model=model_id,
    )

    with span("support-agent", type="agent"):
        output = Runner.run_sync(agent, user_input).final_output
        update_trace(
            metric_collection="Agent Quality",
            input=user_input,
            output=output,
            metadata={
                "agent": "support-agent",
                "agent_version": os.getenv("AGENT_VERSION", "v2"),
                "model_variant": model_variant,
                "model_id": model_id,
                "rollout": os.getenv("ROLLOUT_NAME", "model-comparison"),
            },
        )
        return output


if __name__ == "__main__":
    try:
        print(run_agent(sys.argv[2], sys.argv[1]))
    finally:
        shutdown()

LangGraph

init()이 LangGraph를 감지하고 그래프 실행을 계측해요 — 콜백 핸들러는 필요 없어요. 메타데이터가 노드가 아니라 트레이스에 닿도록 호출을 span으로 감싸요.

import os
import sys

from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessagesState, StateGraph
from confident_trace import init, span, update_trace, shutdown

MODEL_MAP = {"gpt-4o-mini": "gpt-4o-mini", "gpt-4o": "gpt-4o", "gpt-4.1": "gpt-4.1"}

init(environment=os.getenv("APP_ENV", "production"))


def run_agent(user_input: str, model_variant: str) -> str:
    model_id = MODEL_MAP[model_variant]
    model = ChatOpenAI(model=model_id)

    def assistant(state: MessagesState):
        return {"messages": [model.invoke(state["messages"])]}

    graph = (
        StateGraph(MessagesState)
        .add_node("assistant", assistant)
        .add_edge(START, "assistant")
        .add_edge("assistant", END)
        .compile()
    )

    with span("support-agent", type="agent"):
        result = graph.invoke({"messages": [{"role": "user", "content": user_input}]})
        output = result["messages"][-1].content
        update_trace(
            metric_collection="Agent Quality",
            input=user_input,
            output=output,
            metadata={
                "agent": "support-agent",
                "agent_version": os.getenv("AGENT_VERSION", "v2"),
                "model_variant": model_variant,
                "model_id": model_id,
                "rollout": os.getenv("ROLLOUT_NAME", "model-comparison"),
            },
        )
        return output


if __name__ == "__main__":
    try:
        print(run_agent(sys.argv[2], sys.argv[1]))
    finally:
        shutdown()

Vercel AI SDK

Vercel AI SDK에서 init()과 confident-trace/register 프리로드가 generateText를 자동 계측해요 — telemetry 옵션이나 트레이서는 필요 없어요. 각 생성문을 withSpan으로 감싸고 updateTrace로 비교 메타데이터를 설정해요.

import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { init, withSpan, updateTrace } from "confident-trace";

const runtime = init({ environment: process.env.APP_ENV ?? "production" });

const modelMap: Record<string, string> = {
  "gpt-4o-mini": "gpt-4o-mini",
  "gpt-4o": "gpt-4o",
  "gpt-4.1": "gpt-4.1",
};

export async function runAgent(input: string, modelVariant: string) {
  const modelId = modelMap[modelVariant];

  return withSpan({ name: "support-agent", type: "agent" }, async () => {
    updateTrace({
      metricCollection: "Agent Quality",
      input,
      metadata: {
        agent: "support-agent",
        agent_version: process.env.AGENT_VERSION ?? "v2",
        model_variant: modelVariant,
        model_id: modelId,
        rollout: process.env.ROLLOUT_NAME ?? "model-comparison",
      },
    });
    const { text } = await generateText({
      model: openai(modelId),
      prompt: input,
    });
    updateTrace({ output: text });
    return text;
  });
}

const [variant, ...rest] = process.argv.slice(2);
try {
  console.log(await runAgent(rest.join(" "), variant));
} finally {
  await runtime.shutdown();
}

Strands Agents

Strands는 에이전트·모델·도구 스팬을 자체적으로 캡처하고, init()이 그것을 Confident AI로 내보내요. 실행을 span으로 감싸고 update_trace로 정규화된 비교 메타데이터를 트레이스에 추가해요.

import os
import sys

from confident_trace import init, span, update_trace, shutdown
from strands import Agent

MODEL_MAP = {
    "nova-lite": "us.amazon.nova-lite-v1:0",
    "nova-pro": "us.amazon.nova-pro-v1:0",
    "claude-sonnet": "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
}

init(environment=os.getenv("APP_ENV", "production"))


def run_agent(user_input: str, model_variant: str) -> str:
    model_id = MODEL_MAP[model_variant]

    with span("support-agent", type="agent"):
        output = str(Agent(model=model_id, callback_handler=None)(user_input))
        update_trace(
            metric_collection="Agent Quality",
            input=user_input,
            output=output,
            metadata={
                "agent": "support-agent",
                "agent_version": os.getenv("AGENT_VERSION", "v2"),
                "model_variant": model_variant,
                "model_id": model_id,
                "rollout": os.getenv("ROLLOUT_NAME", "model-comparison"),
            },
        )
        return output


if __name__ == "__main__":
    try:
        print(run_agent(sys.argv[2], sys.argv[1]))
    finally:
        shutdown()

메타데이터 키는 원하는 어떤 문자열이든 될 수 있어요 — model_variant와 model_id는 그저 이 예시에서 쓰는 것들일 뿐이에요. 여기서 model_variant는 비교하는 짧고 사람이 읽기 쉬운 라벨이고, model_id는 시끄러워도 정확한 제공자 값을 감사 가능성을 위해 유지해요. 여러분에게 가장 유용한 이름을 쓰세요.

update_trace / updateTrace는 스팬 안에서 한 번 이상 호출할 수 있어요. 값이 병합되므로, Vercel AI SDK 예시처럼 모델이 실행되기 전에 input과 메타데이터를, 그 후에 output을 설정할 수 있어요. 모든 호출이 span 본문 안에서 일어나게만 하세요. 활성 스팬 밖에서는 헬퍼가 조용히 아무 일도 하지 않아서, model_variant가 트레이스에 나타나지 않아요. 여기서 output을 기록하게 해주는 건 스팬이에요. 비교 메타데이터만 필요하다면 실행 주위에 trace_context / traceContext를 열고 스팬을 건너뛸 수도 있어요 — 스팬 없이 트레이스 속성 설정 참조.

로컬 트레이스 실행

변형별로 요청 하나씩 보내 트레이스가 올바른 메타데이터와 함께 Confident AI에 도달하는지 확인해요. 각 스크립트는 변형과 입력을 위치 인자로 받아요.

OpenAI Agents

export AGENT_VERSION="v2"
export ROLLOUT_NAME="model-comparison-smoke-test"
export APP_ENV="production"

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  python openai_agents_model_compare.py "$model" \
    "A customer says their invoice doubled after upgrading. Explain what to check first."
done

LangGraph

export AGENT_VERSION="v2"
export ROLLOUT_NAME="model-comparison-smoke-test"
export APP_ENV="production"

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  python langgraph_model_compare.py "$model" \
    "A customer says their invoice doubled after upgrading. Explain what to check first."
done

Vercel AI SDK

TypeScript는 Node가 AI SDK를 로드할 때 훅을 걸 수 있도록 confident-trace/register 프리로드가 필요해요:

export AGENT_VERSION="v2"
export ROLLOUT_NAME="model-comparison-smoke-test"
export APP_ENV="production"

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  node --import tsx --import confident-trace/register vercel-ai-model-compare.ts "$model" \
    "A customer says their invoice doubled after upgrading. Explain what to check first."
done

Strands Agents

export AGENT_VERSION="v2"
export ROLLOUT_NAME="model-comparison-smoke-test"
export APP_ENV="production"

for model in nova-lite nova-pro claude-sonnet; do
  python strands_model_compare.py "$model" \
    "A customer says their invoice doubled after upgrading. Explain what to check first."
done

단명 스크립트는 트레이스 게시가 끝나기 전에 종료하는 경우가 많아요. 스팬은 백그라운드 워커에서 배치로 내보내지므로, 위 모든 예시는 프로세스가 끝나기 전에 큐를 비우도록 finally 블록에서 shutdown()을 호출해요. 장기 실행 서버에서는 시작 시 init()을 한 번, 종료 시 shutdown()을 한 번 호출해요 — 요청마다 하지 마세요. flush and shutdown 참조.

에이전트가 실행되는 즉시 트레이스가 Observatory에 나타나요

완료 ✅. 이제 모델 변형마다 트레이스가 하나 이상 있어요.

지표 만들기

각 모델 변형에 같은 지표 컬렉션을 사용해서 모두 동일한 기준으로 채점되게 해요. 프로젝트의 평가 모델 — LLM 심사자 — 은 모든 컬렉션에 공유되므로 심사자 자체는 이미 일관돼요. 함정은 gpt-4o-mini와 gpt-4o를 다른 컬렉션으로 채점하는 거예요. 그러면 두 개의 다른 루브릭에서 점수를 추세로 보게 되어 대시보드가 더 이상 사과 대 사과 비교가 아니에요.

지표 컬렉션 만들기

Project > Metrics > Collections를 열고 Agent Quality라는 컬렉션을 만든 뒤, 에이전트의 작업에 맞는 트레이스 수준 지표를 추가해요.

모든 모델 변형이 사용할 지표 컬렉션 만들기

지원 에이전트에 강력한 시작 컬렉션은:

  • 답변이 사용자 요청을 해결했는지에 대한 Task Completion.
  • 응답이 초점을 유지했는지에 대한 Answer Relevancy.
  • "지원 정책 준수"나 "에스컬레이션 품질" 같은 제품별 표준을 위한 커스텀 G-Eval 지표.

온라인 평가는 트레이싱 중 referenceless 지표만 실행돼요. expected_output, expected_tools 등 참조 데이터가 필요한 지표는 오프라인 테스트 실행, Arena, Experiments에 더 좋아요.

컬렉션 붙이기

각 계측 예시는 update_trace / updateTrace에 metric_collection="Agent Quality" 또는 metricCollection: "Agent Quality"를 전달해요. 이것이 모든 모델 변형에 같은 컬렉션을 직접 예약해요.

대안으로 코드에서 metric_collection / metricCollection을 제거하고 Workflows > Traces에서 트레이스 수준 Evaluation Rule을 만들어:

  1. metadata.rollout = model-comparison(또는 metadata.agent = support-agent) 같은 필터로 비교 트래픽과 일치시켜요.
  2. 일치하는 모든 트레이스에 Agent Quality 지표 컬렉션을 실행해요.

SDK가 설정한 명시적 컬렉션이 일치하는 UI 규칙보다 우선해요. 두 접근 모두 온라인 평가를 참고해요.

규칙 필터를 변형 간 안정적인 키(rollout 또는 agent)와 정렬하세요. model_variant 자체로는 모델마다 규칙 하나씩 필요해져서 하나를 잊기 쉬워요.

충분히 채점된 트레이스 생성

대시보드는 비교할 데이터가 충분할 때만 유용해요. 각 변형을 몇 개 프롬프트에 걸쳐 실행해서 지표 컬렉션이 트레이스 배치를 채점하게 해요.

OpenAI Agents

prompts=(
  "A customer cannot access invoices after changing teams. Help them troubleshoot."
  "Summarize why a trial user should upgrade, but do not mention unavailable features."
  "The integration failed with an OAuth callback error. Explain the likely cause."
  "A user asks for a refund after annual renewal. Give a careful support response."
)

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  for prompt in "${prompts[@]}"; do
    python openai_agents_model_compare.py "$model" "$prompt"
  done
done

LangGraph

prompts=(
  "A customer cannot access invoices after changing teams. Help them troubleshoot."
  "Summarize why a trial user should upgrade, but do not mention unavailable features."
  "The integration failed with an OAuth callback error. Explain the likely cause."
  "A user asks for a refund after annual renewal. Give a careful support response."
)

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  for prompt in "${prompts[@]}"; do
    python langgraph_model_compare.py "$model" "$prompt"
  done
done

Vercel AI SDK

prompts=(
  "A customer cannot access invoices after changing teams. Help them troubleshoot."
  "Summarize why a trial user should upgrade, but do not mention unavailable features."
  "The integration failed with an OAuth callback error. Explain the likely cause."
  "A user asks for a refund after annual renewal. Give a careful support response."
)

for model in gpt-4o-mini gpt-4o gpt-4.1; do
  for prompt in "${prompts[@]}"; do
    node --import tsx --import confident-trace/register vercel-ai-model-compare.ts "$model" "$prompt"
  done
done

Strands Agents

prompts=(
  "A customer cannot access invoices after changing teams. Help them troubleshoot."
  "Summarize why a trial user should upgrade, but do not mention unavailable features."
  "The integration failed with an OAuth callback error. Explain the likely cause."
  "A user asks for a refund after annual renewal. Give a careful support response."
)

for model in nova-lite nova-pro claude-sonnet; do
  for prompt in "${prompts[@]}"; do
    python strands_model_compare.py "$model" "$prompt"
  done
done

이 로컬 배치는 대시보드를 채우기에 충분해요. 실제 모델 결정을 내리려면 안정적인 시간 범위의 프로덕션 트래픽에 걸쳐 비교해요. 점수를 비교하기 전에 수집과 Evaluation Rule이 끝날 때까지 기다려요 — 트레이스는 최대 30초 걸려 나타날 수 있고, 평가 결과는 곧바로 따라와요.

트레이스 검증

대시보드를 만들기 전에 데이터 형태가 올바른지 검증해요. 다섯 개 위젯을 만들기 전에 메타데이터와 지표 컬렉션 이름을 고치는 게 훨씬 쉬워요.

Observatory 열기

Confident AI에서 Observatory로 가서 에이전트나 롤아웃으로 필터링해요:

  • metadata.agent = support-agent
  • metadata.rollout = model-comparison
  • OpenAI 기반 예시는 metadata.model_variant = gpt-4o, Strands는 metadata.model_variant = nova-pro

트레이스 하나 검사

트레이스를 열고 네 가지를 확인해요:

  • 트레이스 입력과 출력이 채워졌는지.
  • 트레이스 메타데이터에 model_variant, model_id, agent_version, rollout이 포함됐는지.
  • LLM 스팬이 통합에서 제공자 모델 상세를 캡처했는지.
  • 트레이스에 Agent Quality의 온라인 평가 결과가 있거나, 고칠 수 있는 명확한 지표 오류가 보이는지.

수집 후 온라인 평가 점수가 트레이스에 나타나요

대시보드 만들기

이전 단계의 트레이스와 점수가 대시보드를 먹여 살려요. 어느 쪽이든 만들어요 — Confident AI UI를 클릭하는 Platform, 또는 Dashboards API를 상대로 반복 가능한 스크립트를 실행하는 CLI. 선택은 아래 모든 단계에 그대로 유지되므로 한 번만 고르면 돼요.

예시는 OpenAI 변형 gpt-4o-mini, gpt-4o, gpt-4.1을 사용해요. Strands는 nova-lite, nova-pro, claude-sonnet으로 바꿔 넣어요.

대시보드 만들기

Platform

Confident AI에서 사이드바에서 대시보드를 만들어요:

  1. Dashboards를 열어요.
  2. New Dashboard를 클릭해요.
  3. Name을 Model Variant Comparison으로 설정해요.
  4. Description을 Compares support-agent quality, volume, and latency by metadata.model_variant로 설정해요.
  5. 프로젝트와 공유하려면 Private를 끄고, 개인 초안이면 켜요.
  6. Create를 클릭해요.

CLI

자격 증명을 설정하고, 빈 대시보드를 만들어 다음 단계를 위해 ID를 캡처해요:

export CONFIDENT_API_KEY="confident_us..."
export CONFIDENT_API_BASE="https://api.confident-ai.com"

export DASHBOARD_ID="$(
  curl -sS -X POST "$CONFIDENT_API_BASE/v1/dashboards" \
    -H "CONFIDENT_API_KEY: $CONFIDENT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Model Variant Comparison",
      "description": "Compares support-agent quality, volume, and latency by metadata.model_variant.",
      "private": false
    }' \
  | python -c 'import json,sys; print(json.load(sys.stdin)["data"]["id"])'
)"

echo "Created dashboard: $DASHBOARD_ID"

대시보드 만들기

모델별 품질 추가

Platform

Add widget을 클릭하고 model_variant로 품질을 분할하는 시계열 위젯을 만들어요:

설정 값
Widget name Average quality by model
Shape Time series
Display Line
Mode Breakdown
Data model Metric Data
Belongs to Trace
Metric collection Agent Quality
Aggregation Average score
Filter metadata.agent = support-agent
Dimension Metadata
Metadata key model_variant
Top K Top 10

이것이 주요 비교 차트예요: 같은 지표 컬렉션 아래에서 시간에 따라 어느 모델이 더 높게 점수를 받는가?

CLI

변형별로 라인 하나씩 추가해요. 각 라인은 support-agent와 하나의 model_variant로 필터링해요:

curl -sS -X POST "$CONFIDENT_API_BASE/v1/dashboards/$DASHBOARD_ID/widgets" \
  -H "CONFIDENT_API_KEY: $CONFIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Average quality by model",
    "type": "LINE",
    "unit": "SCORE",
    "mode": "TIME_SERIES",
    "lines": [
      { "name": "gpt-4o-mini", "color": "BLUE", "dataModel": "METRIC_DATA", "aggregation": "AVG_SCORE", "extraQueryParams": { "category": "TRACE", "metricName": "Task Completion" }, "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o-mini" } ] } ] } },
      { "name": "gpt-4o", "color": "EMERALD", "dataModel": "METRIC_DATA", "aggregation": "AVG_SCORE", "extraQueryParams": { "category": "TRACE", "metricName": "Task Completion" }, "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o" } ] } ] } },
      { "name": "gpt-4.1", "color": "VIOLET", "dataModel": "METRIC_DATA", "aggregation": "AVG_SCORE", "extraQueryParams": { "category": "TRACE", "metricName": "Task Completion" }, "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4.1" } ] } ] } }
    ]
  }'

Task Completion은 Agent Quality 안 트레이스 수준 지표의 자리 표시자예요. 실제로 그리려는 지표 — Answer Relevancy나 커스텀 G-Eval 지표 같은 — 로 바꾸세요.

트레이스 볼륨 추가

Platform

트래픽 볼륨용 두 번째 시계열 위젯을 추가해서, 몇 개 쉬운 요청만 처리한 모델을 과신하지 않게 해요:

설정 값
Widget name Trace volume by model
Shape Time series
Display Stacked bar
Mode Breakdown
Data model Trace
Aggregation Count
Filter metadata.agent = support-agent
Dimension Metadata
Metadata key model_variant
Top K Top 10

CLI

curl -sS -X POST "$CONFIDENT_API_BASE/v1/dashboards/$DASHBOARD_ID/widgets" \
  -H "CONFIDENT_API_KEY: $CONFIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Trace volume by model",
    "type": "STACKED_BAR",
    "unit": "COUNT",
    "mode": "TIME_SERIES",
    "lines": [
      { "name": "gpt-4o-mini", "color": "BLUE", "dataModel": "TRACE", "aggregation": "COUNT", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o-mini" } ] } ] } },
      { "name": "gpt-4o", "color": "EMERALD", "dataModel": "TRACE", "aggregation": "COUNT", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o" } ] } ] } },
      { "name": "gpt-4.1", "color": "VIOLET", "dataModel": "TRACE", "aggregation": "COUNT", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4.1" } ] } ] } }
    ]
  }'

P90 지연 시간 추가

Platform

품질 우승자가 품질만으로 판단되지 않도록 지연 시간 위젯을 추가해요:

설정 값
Widget name P90 latency by model
Shape Time series
Display Line
Mode Breakdown
Data model Trace
Aggregation P90 latency
Filter metadata.agent = support-agent
Dimension Metadata
Metadata key model_variant
Top K Top 10

전체 트레이스 지연 시간 대신 모델 호출 지연 시간을 원하면, 데이터 모델을 Span으로 바꾸고 LLM 스팬 유형을 고르며 같은 model_variant 브레이크다운을 유지해요.

CLI

curl -sS -X POST "$CONFIDENT_API_BASE/v1/dashboards/$DASHBOARD_ID/widgets" \
  -H "CONFIDENT_API_KEY: $CONFIDENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "P90 latency by model",
    "type": "LINE",
    "unit": "MILLISECONDS",
    "mode": "TIME_SERIES",
    "lines": [
      { "name": "gpt-4o-mini", "color": "BLUE", "dataModel": "TRACE", "aggregation": "P90_LATENCY", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o-mini" } ] } ] } },
      { "name": "gpt-4o", "color": "EMERALD", "dataModel": "TRACE", "aggregation": "P90_LATENCY", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4o" } ] } ] } },
      { "name": "gpt-4.1", "color": "VIOLET", "dataModel": "TRACE", "aggregation": "P90_LATENCY", "filters": { "operator": "AND", "groups": [ { "operator": "AND", "filters": [ { "category": "Metadata", "condition": "Is", "key": "agent", "value": "support-agent" }, { "category": "Metadata", "condition": "Is", "key": "model_variant", "value": "gpt-4.1" } ] } ] } }
    ]
  }'

대시보드 검증

Platform

공유 대시보드 날짜 범위를 Last 7 days 또는 Last 30 days로 설정한 뒤, 세 위젯 모두 model_variant로 분할되는지 확인해요.

완료 ✅. 이제 모델별로 품질, 볼륨, 지연 시간을 비교하는 대시보드가 생겼어요.

CLI

대시보드를 가져와 세 위젯이 모두 저장됐는지 확인해요:

curl -sS "$CONFIDENT_API_BASE/v1/dashboards/$DASHBOARD_ID" \
  -H "CONFIDENT_API_KEY: $CONFIDENT_API_KEY"

완료 ✅. 이제 모델별로 품질, 볼륨, 지연 시간을 비교하는 대시보드가 생겼어요.

대시보드 미리보기

결과 해석

더 높게 점수를 받는 모델이 더 나은 선택인 것은, 신뢰할 만큼 충분한 트래픽을 처리했고 지연 시간이 허용 가능한 수준을 유지했을 때뿐이에요. 세 위젯을 함께 읽어요:

  • 품질(Quality): 후보의 평균 점수가 의미 있는 날짜 범위에서 더 높은가요?
  • 볼륨(Volume): 각 변형이 결과를 신뢰할 만큼 트레이스가 충분한가요? 저볼륨 변형은 우연히 이길 수 있어요.
  • 지연 시간(Latency): 제품에 P90 지연 시간이 여전히 허용 가능한가요?

아무 파라미터나 비교하기

핵심 통찰은 이거예요: 이 가이드에서 모델 특화된 것은 아무것도 없어요. model_variant는 그저 모든 대시보드가 분할하는 메타데이터 키일 뿐이에요. A/B 테스트하려는 어떤 변수로든 바꾸면, 정확히 같은 워크플로 — 에이전트 하나, 지표 컬렉션 하나, 위젯 셋 — 가 그대로 적용돼요. 모델을 비교하는 게 아니라, 트레이스에 라벨하는 무엇이든 비교하는 거예요.

다른 것을 비교하려면 가이드를 반복하고 두 가지만 바꿔요:

  • 트레이스에 붙이는 메타데이터 키. model_variant 대신(또는 옆에) prompt_version(또는 temperature, retriever, ...)을 기록해요.
  • 각 위젯이 분할하는 차원. 같은 대시보드 필터를 새 키로 돌려요.

나머지는 모두 동일해요. 새 키는 model_variant와 정확히 같게 — 트레이스 메타데이터로 — 붙어요:

update_trace(
    metric_collection="Agent Quality",
    input=user_input,
    output=output,
    metadata={
        "agent": "support-agent",
        "prompt_version": prompt_variant,  # the dimension you're now comparing
    },
)

팀이 이렇게 비교하는 흔한 파라미터:

  • 프롬프트 버전 — prompt_version: v3 vs v4.
  • 디코딩 설정 — temperature: 0.2 vs 0.7.
  • 검색 전략 — retriever: bm25 vs hybrid, 또는 chunk_size: 512 vs 1024.
  • 도구 집합 — toolset: minimal vs full.
  • 에이전트 버전 — agent_version: v2 vs v3.

같은 트레이스에 여러 키를 붙여요(model_variant, prompt_version, temperature) 같은 트래픽에서 차원별로 대시보드 하나씩 만들어요 — 새 계측은 필요 없어요. 대시보드가 차이를 깔끔하게 귀속시키길 원하면 한 번에 변수 하나만 바꾸세요.

아무 측정값이든 차트로

브레이크다운 차원이 바뀔 수 있듯, 각 위젯이 그리는 측정값(measure) 도 바뀔 수 있어요. 이 가이드는 품질(AVG_SCORE), 트레이스 볼륨(COUNT), P90 지연 시간(P90_LATENCY)을 차트로 그렸지만, 그건 많은 것 중 셋일 뿐이에요. 다른 집계로 라인을 추가하면 정확히 같은 트래픽에서 새 비교가 돼요.

어떤 차원으로든 분할할 수 있는 측정값:

  • 품질 — 컬렉션의 어떤 지표든 AVG_SCORE, PASS_RATE, FAILURE_RATE, AVG_RATING.
  • 지연 시간 — AVG_LATENCY, P50_LATENCY, P90_LATENCY, P99_LATENCY.
  • 비용·토큰 — TOTAL_COST, AVG_COST, AVG_COST_PER_USER, INPUT_TOKENS, OUTPUT_TOKENS, TOTAL_TOKENS.
  • 볼륨·사용자 — COUNT, UNIQUE_USERS, UNIQUE_THREADS.
  • 신뢰성 — ERROR_COUNT, ERROR_RATE.

두 아이디어를 결합해요: 어떤 측정값이든 어떤 메타데이터 키로든 분할해요. "prompt_version별 평균 비용"이나 "model_variant별 P99 지연 시간"은 두 필드만 바꾼 같은 위젯이에요.

모범 사례

핵심 비교가 작동한 뒤 선택적으로 볼 수 있는 심화 내용이에요.

  • 한 번에 하나씩 비교해요. 프롬프트, 도구, 검색기, 모델이 모두 한 번에 바뀌면 대시보드가 차이의 원인을 알 수 없어요.
  • 메타데이터 이름을 일관되게 유지해요. 대시보드는 정확한 메타데이터 키에 의존하므로, model, model_name, model_variant를 번갈아 쓰지 마세요.
  • 제품 라벨과 제공자 ID를 분리해요. 결정에는 model_variant를, 정확한 재현성에는 model_id를 사용해요.
  • 결정 전에 충분한 트래픽을 사용해요. 저볼륨 변형은 우연히 더 좋아 보이거나 나빠 보일 수 있어요. 안정적인 시간 범위에 걸쳐 비교하세요.
  • 품질과 운영을 함께 봐요. 점수는 높지만 지연 시간이 훨씬 나쁜 모델이 더 나은 선택이 아닐 수 있어요.

무엇을 추적할까요?

대시보드는 일관된 메타데이터에 의존해요. 이 키들로 시작해요:

  • model_variant — nova-lite나 claude-sonnet 같은 비교 차원.
  • model_id — 요청에 사용된 정확한 제공자 모델 ID.
  • agent — support-agent 같은 안정적인 애플리케이션·에이전트 이름.
  • agent_version — 배포된 에이전트 버전.
  • rollout — 롤아웃, 카나리, A/B 테스트 이름.
  • environment — production, staging, development, testing. 이것은 메타데이터가 아니라 init(environment=...) / init({ environment })로 한 번 설정해요. Observatory를 필터링할 수 있는 일급 트레이스 필드예요. environment 참조.

메타데이터 값을 지루하고 예측 가능하게 유지해요. model_variant="nova-pro"는 model_variant="Nova Pro - July canary (fast)"보다 쿼리하기 쉬워요. 임시 롤아웃 컨텍스트는 모델 이름이 아니라 rollout에 넣어요.

온라인 평가는 각 트레이스가 사용한 모델을 관측해요. 에이전트가 직접 라우팅하지 않는 한 자동으로 모든 모델에 같은 요청을 보내지는 않아요. 하나의 입력을 모든 모델에 보내는 비교는 Experiments 또는 Arena를 사용해요.

롤아웃 패턴

모델을 비교하면서 트래픽을 라우팅하는 세 가지 흔한 방법:

  • 섀도우 비교(Shadow compare) — 프로덕션 트래픽을 현재 모델에 보내고, 후보 모델을 사용자 경로 밖에서 사본으로 돌려요. 섀도우 트레이스는 rollout=shadow-model-compare로 로깅해요. 신호가 높지만, 모든 요청이 여러 모델을 호출할 수 있어요.
  • 카나리 릴리스(Canary release) — 실제 트래픽의 작은 비율을 후보에 보내고 rollout=canary-v3으로 라벨해요. 가장 단순한 프로덕션 롤아웃이에요. 5% 카나리는 충분한 트래픽이 쌓이기 전에는 시끄러워 보이므로 트레이스 볼륨을 지켜봐요.
  • 세그먼트 라우팅(Segment routing) — 내부 사용자, 특정 테넌트, 특정 작업 유형 같은 세그먼트에 모델을 라우팅하고 그 세그먼트용 메타데이터를 추가해요. 최적 모델이 요청에 달려 있을 때 유용해요.

문제 해결

대시보드에 model_variant 브레이크다운이 없어요.

트레이스를 열고 metadata.model_variant가 트레이스에 존재하는지 확인해요. LLM 스팬에만 나타나면 값을 update_trace로 옮기고 — 그 호출이 span 본문 안에서 일어나는지 확인해요. 활성 스팬 밖에서는 조용히 아무 일도 하지 않으니까요. 호출할 스팬이 없다면 실행 주위에 trace_context / traceContext를 열면 돼요. 스팬 없이 트레이스 속성 설정 참조. 대시보드는 트레이스에 존재하는 메타데이터로만 트레이스 수준 데이터를 분할할 수 있어요.

온라인 평가 점수가 없어요.

Agent Quality가 정확히 그 이름으로 존재하는지, 그리고 update_trace / updateTrace가 스팬 안에서 실행되는지 확인해요. 코드에서 컬렉션을 제거했다면 Evaluation Rule이 트레이스와 일치하는지 확인해요. 그다음 트레이스에 지표가 요구하는 파라미터 — 보통 referenceless 트레이스 수준 지표의 input과 output — 가 있는지 확인해요.

한 모델이 훨씬 좋아 보이는데 트래픽이 아주 적어요.

품질 위젯 옆에 트레이스 수 위젯을 추가하고 더 긴 시간 범위에 걸쳐 비교해요. 저볼륨 변형은 우연히 이길 수 있어요. 특히 라우터가 더 쉬운 요청을 보냈다면요.

원시 제공자 모델 ID가 계속 바뀌어요.

model_variant를 안정적으로 유지하고 정확한 제공자 값을 model_id에 넣어요. 대시보드는 보통 model_variant로 분할해야 하고, model_id는 디버깅과 감사 추적을 위해 남겨요.

스크립트에서 트레이스가 전혀 나타나지 않아요.

프로세스가 내보내기 큐를 비우기 전에 종료됐을 가능성이 커요. shutdown()이 finally 블록에서 실행되는지(위 예시처럼) 그리고 첫 모델 호출 전에 init()이 실행됐는지 확인해요. troubleshooting 참조 — 일부 런타임에 필요한 프리로드 단계도 다뤄요.

다음 단계

이 설정으로 한 에이전트 아래에서 모델 변형을 비교한 뒤, 품질·볼륨·지연 시간이 모두 건강해 보이면 우승자를 롤아웃해요.

OpenAI Agents

에이전트, LLM, 도구, 핸드오프, 가드레일 스팬으로 OpenAI Agents 워크플로를 트레이싱해요.

LangGraph

init(), 트레이스 메타데이터, 온라인 평가로 LangGraph 에이전트를 자동 트레이싱해요.

Vercel AI SDK

AI SDK 생성문을 Confident AI 트레이싱과 트레이스 컨텍스트로 계측해요.

Strands Agents

OpenTelemetry, 온라인 평가, 트레이스 메타데이터로 Strands 에이전트를 계측해요.

대시보드

지표 데이터, 트레이스, 필터, 메타데이터 브레이크다운으로 위젯을 만들어요.

온라인 평가

프로덕션 트래픽이 수집될 때 트레이스와 스팬을 채점해요.

메타데이터

필터링과 분석을 위해 트레이스, 스팬, 스레드에 메타데이터를 추가해요.

더 알아보기