LLM 트레이싱 퀵스타트

LLM 트레이싱 퀵스타트 (LLM Tracing Quickstart)

5분 안에 LLM 애플리케이션을 계측해 관측성을 확보하는 방법을 다루는 페이지예요. Confident AI의 OpenTelemetry 네이티브 트레이싱 SDK인 confident-trace로 앱을 계측하고, 패키지를 설치하고 init()을 한 번 호출하면 Observatory에서 첫 트레이스를 볼 수 있답니다 — 보통 5분 안에요.

출처: 문서

본문

개요 (Overview)

이 가이드는 confident-trace로 LLM 앱을 계측하는 방법을 보여줘요. 이는 Python·TypeScript용 Confident AI의 OpenTelemetry 네이티브 트레이싱 SDK예요. 패키지를 설치하고 init()을 한 번 호출하면 Observatory에서 첫 트레이스를 볼 수 있어요 — 보통 5분 안에요.

이미 프레임워크나 OpenTelemetry를 쓰고 있나요? confident-trace는 OpenAI, LangChain 등이 설치되는 순간 자동 계측해요. 그리고 어떤 OpenTelemetry(OTEL) 앱이든 어떤 언어로든 코드 변경 없이 Confident AI로 내보낼 수 있어요.

동작 방식 (How it works)

트레이싱은 계측을 통해 동작하는데, 이는 자동(confident-trace의 내장 통합) 또는 수동(span 데코레이터/래퍼)이에요:

  1. 앱이 시작될 때 init()을 한 번 호출해요 — 설치된 모든 지원 SDK·프레임워크를 감지해 계측해요
  2. 각 LLM 호출, 검색, 툴 실행, 또는 span으로 감싼 함수가 스팬(span) 이 돼요
  3. 가장 바깥쪽 스팬이 트레이스(trace) 가 돼요 — 모든 중첩 스팬이 그 안으로 합쳐져요 (스팬이 중첩 대신 별도 트레이스를 만들면 트러블슈팅(troubleshooting) 참고)
  4. 스팬은 지연 영향 없이 배치로 비동기 내보내져요
  5. 유입되면 트레이스는 구성한 메트릭으로 자동 평가될 수 있어요

트레이싱 용어도 이해해야 해요:

Trace

LLM 앱의 단일 종단간 실행 — 관측성의 최상위 단위.

Span

트레이스 안의 개별 컴포넌트 — LLM 호출, 검색, 툴 실행 같은 것.

Thread

공유 스레드 ID로 연결된, 멀티턴 대화를 나타내는 트레이스 그룹.

트레이싱을 바이브 코드로 (Vibe Code Your Tracing)

코딩 에이전트가 앱을 계측하게 해보세요 — 프로젝트가 쓰는 것과 요청에 따라 올바른 경로(confident-trace 자동 계측, 커스텀 span, 또는 OpenTelemetry)를 고릅니다. 아래에서 에이전트용 설치 방법을 선택하세요.

Claude Code (plugin)

Claude Code에서 이 네 개 명령을 실행하세요:

/plugin marketplace add confident-ai/confident-trace
/plugin install confident-trace@confident-trace-plugins
/reload-plugins
/plugins

/plugins 명령은 설치된 플러그인 아래 Confident Trace를 나열해야 해요.

Cursor, Codex, Windsurf 등 (Skills CLI)

아무 Skills 호환 설치자로 confident-tracing Agent Skill을 설치하세요. Cursor, Claude Code, Codex, Windsurf, OpenCode, 그리고 Skills 표준을 지원하는 어떤 어시스턴트에서도 동작해요:

npx skills add confident-ai/confident-trace --skill confident-tracing

이 스킬은 에이전트가 네이티브 통합과 수동 스팬 중에서 고르고, 스팬 타입/태그/메타데이터를 설정하고, 트레이스를 Confident AI의 Observatory로 보내는 법을 가르쳐요. 아래 같은 프롬프트에서 자동으로 트리거돼요.

설치 후, 추적하려는 프로젝트를 열고 필요한 것을 에이전트에게 말하세요. 예시 프롬프트:

  • "Instrument this app with confident-trace and send traces to Confident AI."
  • "Add tracing so my OpenAI calls show up on Confident AI."
  • "I'm using LangGraph — wire up Confident AI tracing for it."

에이전트가 코드베이스를 읽고 네이티브 통합과 수동 스팬 중에서 고른 뒤 트레이스가 Observatory에 도착하는지 확인할 거예요.

정확하고 최신의 계측을 위해 에이전트를 LLM 친화적 문서로 안내하세요: llms.txt가 모든 페이지를 인덱싱해요 (아무 문서 URL에 .md를 붙이면 그 페이지의 원시 Markdown이 나와요). 에이전트를 docs MCP server에 직접 연결할 수도 있어요.

AI 앱 계측하기 (Instrument Your AI App)

계속하기 전에 setup and installation에 나온 대로 API 키를 준비해야 해요.

confident-trace 설치 (Install confident-trace)

계측은 코드에서 이루어지므로 먼저 confident-trace를 설치해요. 아래 예시는 OpenAI SDK도 쓰므로, 함께 따라 하고 싶다면 그것도 설치하세요:

Python

pip install confident-trace

TypeScript

npm install confident-trace
npm install -D tsx
yarn add confident-trace
yarn add -D tsx

API 키 설정 (Set Your API Key)

Confident AI Project API 키를 얻고 환경 변수로 설정하세요:

export CONFIDENT_API_KEY=YOUR-API-KEY

API 키만으로 트레이스가 어디로 가는지 결정되지는 않아요. EU 지역이나 자체 호스팅 배포(self-hosted deployment)라면 OTEL 엔드포인트도 설정해야 해요. 그렇지 않으면 트레이스가 US 서버로 보내지거든요:

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

자체 호스팅 배포의 경우 이것은 자신의 otel. 호스트를 가리켜요 — setting the base URL to your deployment를 참고하세요. CONFIDENT_BASE_URL은 confident-trace가 트레이스를 보내는 곳에 영향을 주지 않는다는 점을 유의하세요.

앱 계측하기 (Instrument Your App)

애플리케이션의 진입점에서 init()을 한 번 호출해요:

Python

from openai import OpenAI
from confident_trace import init, shutdown

init()
client = OpenAI()

def llm_app(query: str) -> str:
    return client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": query}]
    ).choices[0].message.content

# Call app to send trace to Confident AI
try:
    llm_app("Write me a poem.")
finally:
    shutdown()

TypeScript

import OpenAI from "openai";
import { init } from "confident-trace";

const runtime = init();
const openai = new OpenAI();

const llmApp = async (query: string) => {
  const res = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: [{ role: "user", content: query }],
  });
  return res.choices[0].message.content;
};

// Call app to send trace to Confident AI
try {
  await llmApp("Write me a poem.");
} finally {
  await runtime.shutdown();
}

마지막으로, SDK가 Node가 패키지를 로드할 때 훅할 수 있도록 confident-trace/register preload로 진입점을 실행하세요. 이 작업은 반드시 해야 해요.

# Running TypeScript source directly
node --import tsx --import confident-trace/register src/index.ts

# Running compiled JavaScript
node --import confident-trace/register dist/index.js

이것을 평소 시작 명령으로 만들려면 package.json 스크립트에 추가하세요:

{
  "scripts": {
    "start": "node --import confident-trace/register dist/index.js",
    "dev": "node --import tsx --import confident-trace/register src/index.ts"
  }
}

preload 없이 init()을 호출하면 설정 경고가 뜨고 스팬이 생기지 않아요. init() 호출 없이 preload를 추가하면 스팬은 만들어지지만 아무것도 내보내지지 않아요.

완료 ✅. 방금 안에 LLM 스팬이 있는 트레이스를 만들었어요. Observatory로 가서 트레이스를 확인하세요.

init()은 인식하는 모든 것을 자동 계측해요. OpenAI, Anthropic, Google GenAI, Bedrock 같은 프로바이더와 LangChain, LangGraph, OpenAI Agents, Pydantic AI, CrewAI, LlamaIndex, Strands, Google ADK, Mastra, Vercel AI SDK 같은 프레임워크가 모두 confident-trace와 함께 설치되는 순간 추적돼요. 각 통합 페이지에 정확히 무엇이 캡처되는지 나열돼 있어요.

Video

Tracing Quickstart

트레이스가 보이지 않는다면, 99.99% 프로그램이 트레이스를 올릴 기회를 갖기 전에 종료됐기 때문이에요. 종료 전에 shutdown()(또는 장기 실행 프로세스에서는 flush())을 호출하고 있는지 확인하세요:

from confident_trace import flush

flush()  # blocks until the local queue is drained

자세한 내용은 트러블슈팅 페이지를 참고하세요.

init() 구성하기 (Configure init())

한 번 초기화, 한 번 종료. 장기 실행 서버에서 시작 시 init(), 프로세스 종료 시 shutdown()을 호출하세요 — 요청마다 하지 마세요. span과 업데이트 헬퍼는 재초기화 없이 앱 어디에서나 쓸 수 있어요.

init()은 트레이싱이 구성되는 단일 지점이에요. 인자 없이 호출하면 환경에서 모든 것을 읽어요. 그래서 위 예시가 CONFIDENT_API_KEY만 필요했던 거예요. 각 핵심 설정은 인자로 제공하거나 CONFIDENT_* 환경 변수로 제공할 수 있어요:

Setting Python init() TypeScript init() Environment variable Default
API key api_key apiKey CONFIDENT_API_KEY —
Endpoint endpoint endpoint CONFIDENT_OTEL_ENDPOINT https://otel.confident-ai.com/v1/traces
Sample rate sample_rate sampleRate CONFIDENT_SAMPLE_RATE 1.0 (export every trace)
Environment environment environment CONFIDENT_ENVIRONMENT —

명시적 인자는 항상 환경 변수를 이겨요. 배포의 경우 환경 변수가 보통 더 좋아요:

export CONFIDENT_API_KEY="<your-confident-project-key>"
export CONFIDENT_OTEL_ENDPOINT="https://eu.otel.confident-ai.com/v1/traces"  # EU or self-hosted only
export CONFIDENT_SAMPLE_RATE="0.5"
export CONFIDENT_ENVIRONMENT="production"

또는 값이 자신의 설정 시스템에서 오면 명시적으로 전달해요:

Python

from confident_trace import init

init(
    api_key=settings.confident_api_key,
    endpoint="https://eu.otel.confident-ai.com/v1/traces",
    sample_rate=0.5,
    environment="production",
)

TypeScript

import { init } from "confident-trace";

const runtime = init({
  apiKey: settings.confidentApiKey,
  endpoint: "https://eu.otel.confident-ai.com/v1/traces",
  sampleRate: 0.5,
  environment: "production",
});
  • Sample rate는 각 트레이스의 루트에서 한 번 결정되는, 0과 1 사이의 헤드 샘플링 비율이에요. 자식 스팬은 부모를 따르므로 트레이스는 완전히 내보내지거나 전혀 안 되죠 — 반쪽짜리 트레이스를 볼 일은 없어요. sampling을 참고하세요.
  • Environment는 이 프로세스의 모든 트레이스에 스탬프를 찍어서(예: production, staging, development) 배포별로 Observatory를 필터링할 수 있게 해요. 단일 트레이스에 대해 update_trace(environment=...) / updateTrace({ environment })로 덮어써요. environment를 참고하세요.
  • Endpoint는 EU 지역, 자체 호스팅 배포, 또는 자신의 OpenTelemetry 컬렉터에 대해서만 바꾸면 돼요. 기본 US 엔드포인트가 기본으로 동작해요.

콘텐츠 컨트롤(capture_content, redact, max_content_bytes)도 init() 인자예요 — 프로세스를 떠나기 전에 PII를 제거하거나 페이로드 크기를 제한하려면 masking을 참고하세요.

Flush와 shutdown

스팬은 백그라운드 워커에서 배치로 내보내지므로, 프로세스가 큐가 비워지기 전에 종료되면 트레이스의 끝부분을 잃을 수 있어요. 두 헬퍼가 이걸 처리해요:

Python

from confident_trace import flush, shutdown

flush(timeout_millis=30000)   # drain the queue, keep running
shutdown(timeout_millis=5000) # at process exit

TypeScript

await runtime.flush(30000);   // drain the queue, keep running
await runtime.shutdown(5000); // at process exit
  • **flush()**는 장기 실행 프로세스와 서버리스 함수를 위한 거예요 — 요청 핸들러 끝(스트림이 끝난 후)에 호출해서 환경이 얼기 전에 트레이스를 보내요. 앱은 그 후 계속 실행돼요.
  • **shutdown()**은 프로세스 종료를 위해 있어요 — flush한 다음 내보내기를 정리해요. 요청마다가 아니라 한 번 호출해요.

두 타임아웃 모두 밀리초 단위예요. 성공적인 flush()는 로컬 큐가 비워졌다는 뜻이지 Confident AI가 유입을 끝냈다는 뜻은 아니에요 — 트레이스는 보낸 후 Observatory에 나타나기까지 최대 30초 걸릴 수 있어요.

멀티턴 앱 계측 (Instrument Multi-Turn Apps)

앱이 대화나 멀티턴 상호작용을 다룬다면, 스레드 ID를 제공해 트레이스를 스레드로 그룹화할 수 있어요. 각 턴은 자신의 트레이스를 만들고, 같은 스레드 ID를 가진 트레이스가 대화로 함께 그룹화돼요.

서버에 대한 각 요청이 대화형 에이전트의 턴이라고 가정해볼게요. 핸들러의 작업을 요청의 스레드 ID로 turn()에 감싸요 — 그 턴에 대해 새 트레이스를 시작하고 스레드 ID를 스탬프하며, 안에서 호출하는 것(자동 계측 LLM 호출 포함)이 그 아래 중첩돼요:

Python

from fastapi import FastAPI
from openai import OpenAI
from confident_trace import init, turn, update_trace

init()
app = FastAPI()
client = OpenAI()

@app.post("/chat")
def chat(thread_id: str, query: str):
    with turn("chat", thread_id=thread_id):
        res = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": query}],
        ).choices[0].message.content
        update_trace(input=query, output=res)
        return {"output": res}

TypeScript

import express from "express";
import OpenAI from "openai";
import { init, turn, updateTrace } from "confident-trace";

init();
const app = express().use(express.json());
const openai = new OpenAI();

app.post("/chat", async (req, res) => {
  const { threadId, query } = req.body;
  const output = await turn({ name: "chat", threadId }, async () => {
    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [{ role: "user", content: query }],
    });
    const output = completion.choices[0].message.content;
    updateTrace({ input: query, output });
    return output;
  });
  res.json({ output });
});

app.listen(3000);

자동 계측된 OpenAI 호출이 턴 안에 중첩되도록 Node preload로 서버를 실행하는 걸 잊지 마세요.

같은 thread_id / threadId로 들어오는 모든 요청은 같은 스레드에 들어가므로, "What's the weather in SF?"와 "What about tomorrow?"에 대한 /chat 호출 두 개가 두 턴을 가진 하나의 대화로 나타나요. 스레드 ID는 어떤 문자열이든 될 수 있어요 — 보통 앱이 이미 가진 세션·대화 ID죠.

스레드 I/O 규칙, turn() 헬퍼, 호출된 툴, 검색 컨텍스트, 스레드에서 평가 실행에 대한 자세한 내용은 전체 Threads 페이지를 참고하세요.

다음 단계 (Next Steps)

AI 앱 계측의 아주 기초를 배웠으니, 더 깊게 들어가보세요:

Manage Trace Context

스팬을 만들고 트레이스·스팬을 업데이트하며 스팬 없이 트레이스 기본값을 설정해요 — 모든 트레이스 뒤에 있는 네 개의 헬퍼.

Online Evals

트레이스·스팬·스레드가 Confident AI에 유입되는 대로 실시간 평가를 실행해 AI 품질을 모니터링해요.

더 알아보기