트레이스 컨텍스트 관리
트레이스 컨텍스트 관리 (Manage Trace Context)
스팬을 만들고 트레이스·스팬을 업데이트하며 트레이스 속성을 수동으로 설정하는 방법을 다루는 페이지예요. 대부분의 앱에서는 init()만으로 충분해요. 지원되는 프로바이더·프레임워크를 자동으로 추적하고 그 호출들 사이의 관계까지 잡아주거든요. 더 세밀한 제어가 필요할 때 이 페이지의 API를 쓰면 된답니다.
출처: 문서
본문
개요 (Overview)
대부분의 앱에서 init()만으로 충분해요. 지원되는 프로바이더·프레임워크를 자동으로 추적하고, 그 호출들 사이의 관계까지 포함해요.
이 페이지의 API는 그 트레이스에 대해 더 많은 제어가 필요할 때 사용해요:
- 자신의 함수·툴·애플리케이션 단계에 스팬을 추가한다.
- 입력·출력·사용자·고객·스레드 같은 트레이스 레벨 세부 정보를 설정한다.
- 특정 스팬에 데이터를 추가한다.
- 자동 계측된 프레임워크 호출을 올바른 트레이스에 붙여 둔다.
- 첫 스팬이 시작되기 전에 트레이스 세부 정보를 공급한다.
아래 단원은 init()이 이미 제공하는 계측을 대체하지 않고 이런 변경을 하는 방법을 보여줘요.
스팬 만들기 (Create Spans)
자동 계측은 프레임워크 통합이 아는 모든 것을 잡지만, 요청이 어디서 시작하는지나 어떤 함수가 툴로 간주되는지는 알 수 없어요. 그 경계를 표시하려면 전체 함수 주위나 특정 코드 블록 주위에 직접 스팬을 만들어요.
함수 주위 (Around a function)
Python
from confident_trace import init, span
init()
@span(type="retriever")
def retriever(query: str):
return retrieve(query)
@span("llm_app", type="agent")
def llm_app(query: str):
context = retriever(query)
return generate(query, context)
@span은 선택 사항인 위치 인자 이름(기본값은 함수 이름), type, 그리고 앞의 어떤 타입별·공유 필드도 받아요 — 예를 들어 @span(type="llm", model="gpt-4o"). 동기·비동기 함수 모두에서 동작해요.
TypeScript
import { init, span } from "confident-trace";
const runtime = init();
const retriever = span({ name: "retriever", type: "retriever" }, (query: string) => {
return retrieve(query);
});
const llmApp = span({ name: "llm_app", type: "agent" }, async (query: string) => {
const context = await retriever(query);
return generate(query, context);
});
span(options, fn)은 같은 시그니처를 가진 래핑된 함수를 반환해요. name은 기본적으로 함수 이름이고, type과 함께 타입별·공유 필드를 나란히 전달할 수 있어요 — 예를 들어 { name: "generate", type: "llm", model: "gpt-4o" }. 동기·비동기 함수 모두에서 동작해요.
자동 계측된 스팬이 여기서 만든 것 아래 중첩되도록 Node preload로 진입점을 실행하는 걸 잊지 마세요.
코드 블록 주위 (Around a block of code)
전체 함수를 래핑할 필요는 없어요. 함수 정의를 바꿀 수 없거나 일부만 표시되길 원할 때는 같은 인자로 그 블록만 추적해요:
Python
from confident_trace import span
def generate(prompt: str) -> str:
with span("generate", type="llm", model="gpt-4o"):
return call_llm(prompt)
span()은 동기·비동기 컨텍스트 매니저(with / async with)와 데코레이터로 모두 동작해요. @span에 적용되는 모든 것이 여기에도 적용돼요.
TypeScript
import { withSpan } from "confident-trace";
const generate = async (prompt: string) => {
return withSpan({ name: "generate", type: "llm", model: "gpt-4o" }, async () => {
return callLlm(prompt);
});
};
withSpan(options, callback)은 콜백을 새 스팬 안에서 실행하고 콜백이 끝나면 스팬을 종료해요. span()과 같은 옵션을 받아요 — 유일한 차이는 span()이 재사용 가능한 래핑 함수를 주는 반면 withSpan()은 인라인 블록을 추적한다는 점이에요.
각 스팬은 현재 활성인 것 아래 중첩돼요. 활성인 것이 없으면 새 트레이스의 루트가 되므로, 요청 핸들러에서 만든 가장 바깥쪽 스팬이 보통 트레이스 그 자체예요. type은 Confident AI가 스팬을 어떻게 렌더링할지와 어떤 타입별 필드를 받아들이는지 알려줘요 — 전체 목록은 스팬 타입(span types)을 참고하세요.
스팬은 기본적으로 함수의 인자를
input으로, 반환값을output으로 캡처하므로 대부분의 단계에서 I/O를 수동으로 설정할 필요가 없어요. 페이로드를 전혀 내보내고 싶지 않다면capture_content=False/captureContent: false를 전달하거나, 마스킹(masking)을 참고하세요.
스팬 속성 업데이트 (Update Span Properties)
update_span() / updateSpan()은 호출하는 순간 현재인 스팬에 스팬 레벨 필드를 써요. 단계의 기본 I/O가 보고 싶은 것과 다를 때, 또는 검색된 청크·토큰 개수 같은 단계별 데이터가 있을 때 사용해요:
Python
from confident_trace import span, update_span
@span(type="retriever")
def retriever(query: str):
chunks = retrieve(query)
update_span(input=query, output=chunks, retrieval_context=chunks)
return chunks
TypeScript
import { span, updateSpan } from "confident-trace";
const retriever = span({ name: "retriever", type: "retriever" }, async (query: string) => {
const chunks = await retrieve(query);
updateSpan({ input: query, output: chunks, retrievalContext: chunks });
return chunks;
});
모든 스팬 타입에 대해 update_span()이 하나씩 있어요. 공유 필드(name, input, output, metadata, retrieval_context, context, expected_output, tools_called, expected_tools)뿐 아니라 LLM 전용 필드(model, provider, 토큰 개수, 토큰당 비용(costs))을 받아들여요 — LLM 필드는 llm 스팬에서만 효력이 있어요.
update_span()은 절대 트레이스 레벨 필드를 쓰지 않고,update_trace()는 중첩된 스팬에 절대 쓰지 않아요.tags,user_id,customer_id를 설정했는데 나타나지 않는다면 거의 확실히 잘못된 것을 호출한 거예요 — 트러블슈팅의update_tracevsupdate_span을 참고하세요.
두 업데이트 헬퍼 모두 쓸 활성·기록 중인 스팬이 필요해요. 스팬 밖이거나 스팬이 끝난 후에는 조용히 아무것도 하지 않아요. 그래서 업데이트 헬퍼를 호출 반환 후에 부르기보다, 트레이스 컨텍스트로 자동 계측된 호출을 감싸야 하는 이유예요.
트레이스 속성 업데이트 (Update Trace Properties)
init()이 이미 프레임워크 호출을 계측한다면, 그 트레이스에 세부 정보를 추가하려고 스팬을 만들 필요가 없어요. 대신 호출 주위에 트레이스 컨텍스트를 열어요. 이것은 스팬을 만들지 않고, 계측된 호출이 시작하는 트레이스에 필드를 공급해요:
Python
from langchain_openai import ChatOpenAI
from confident_trace import trace_context
model = ChatOpenAI(model="gpt-4o")
def chat(message: str, user_id: str, customer_id: str, thread_id: str):
with trace_context(
user_id=user_id,
customer_id=customer_id,
thread_id=thread_id,
input=message,
):
return model.invoke(message)
컨텍스트는 with 또는 async with와 동작해요. 그 안의 모든 계측된 호출이 제공한 필드를 받아요.
TypeScript
import { ChatOpenAI } from "@langchain/openai";
import { traceContext } from "confident-trace";
const model = new ChatOpenAI({ model: "gpt-4o" });
const chat = (message: string, userId: string, customerId: string, threadId: string) =>
traceContext({ userId, customerId, threadId, input: message }, () =>
model.invoke(message),
);
콜백은 동기 또는 비동기가 될 수 있어요. 그 안의 모든 계측된 호출이 제공한 필드를 받아요.
트레이스 컨텍스트는 호출이 시작되기 전에 아는 세부 정보 — 사용자, 고객, 스레드, 입력, 환경, 태그, 메타데이터 같은 — 에 가장 적합해요. 같은 스레드 ID를 사용하는 호출들은 같은 스레드로 그룹화되는 반면, 각 호출은 여전히 자신만의 트레이스를 만들어요.
이제 이 같은 스코프 패턴은 나중에 선택된 요청의 트레이싱 끄기와 트레이스를 다른 프로젝트로 동적 라우팅에도 쓰게 될 거예요.
트레이스 컨텍스트는 트레이스도 스팬도 만들지 않아요. 그 스코프 안에서 시작된 트레이스에 필드만 공급할 뿐이죠. 대화의 각 턴에 대해 새 트레이스를 명시적으로 시작하려면
turn()을 사용하세요.
트레이스 필드를 설정하려고 이미 계측된 프레임워크 호출을 커스텀 스팬으로 감싸지 마세요. 트레이스 컨텍스트가 별도의 스팬을 트레이스에 도입하지 않으면서 그 필드를 추가해요.
스팬 안에서 업데이트 (Update from Inside a Span)
이미 커스텀 스팬을 만들었다면 그 주위에 트레이스 컨텍스트를 추가하는 것은 중복이에요 — 스팬이 이미 트레이스를 시작했으니까요. 대신 그 활성 트레이스를 직접 업데이트하세요. 이렇게 하면 작업이 끝난 후에만 아는 값(최종 출력 같은)도 설정할 수 있어요:
Python
from confident_trace import span, update_trace
@span("llm_app", type="agent")
def llm_app(query: str, user_id: str, customer_id: str):
context = retriever(query)
res = generate(query, context)
update_trace(
input=query,
output=res,
user_id=user_id,
customer_id=customer_id,
tags=["production"],
metadata={"app_version": "1.2.3"},
)
return res
TypeScript
import { span, updateTrace } from "confident-trace";
const llmApp = span({ name: "llm_app", type: "agent" }, async (
query: string,
userId: string,
customerId: string,
) => {
const context = await retriever(query);
const res = await generate(query, context);
updateTrace({
input: query,
output: res,
userId,
customerId,
tags: ["production"],
metadata: { appVersion: "1.2.3" },
});
return res;
});
트레이스 업데이트 헬퍼는 항상 현재 트레이스의 루트 스팬을 겨냥하므로, 어떤 중첩 스팬에서도 호출할 수 있어요. 다음에 사용해요:
- 전체 요청의 입력과 출력
name,tags,metadatauser_idoruser,customer_idorcustomer,thread_idandturn_id,environment- 온라인 평가(online evals)를 위한
expected_output,retrieval_context,context,tools_called,expected_tools같은 트레이스 레벨 평가 필드
원하는 만큼 여러 번 호출할 수 있어요. 나중 호출이 필드별로 이전 호출을 덮어쓰고, 생략한 필드는 그대로 남아요. 반대로 트레이스 컨텍스트는 기본값을 공급해요. 아직 설정되지 않은 필드를 채우고 기존 값을 덮어쓰지 않죠. tags, metadata, thread 같은 복합 값은 둘 사이에 절대 병합되지 않아요.
무엇을 써야 할까? (Which One Should I Use?)
| You want to… | Use |
|---|---|
| Mark where a request, tool, or retrieval step begins and ends | @span / span() / withSpan() |
| Add known trace details without creating a span | trace_context() / traceContext() |
| Set or replace trace details from inside an active span | update_trace() / updateTrace() |
| Set a step's I/O, retrieval context, or LLM token usage | update_span() / updateSpan() |
| Start a new trace per conversation turn | turn() |
이미 계측된 프레임워크 호출에는 트레이스 컨텍스트로 시작해요. 요청 주위에 의도적으로 커스텀 스팬(또는 turn())을 추가했다면, 그 스팬 안에서 활성 트레이스를 업데이트하는 대신 그렇게 해요.
다음 단계 (Next Steps)
트레이스 컨텍스트를 다듬는 법을 알았으니, 타입으로 스팬에 의미를 부여하고 Confident AI가 평가하는 I/O를 설정해요.
Configure Span Types
스팬을 LLM·리트리버·툴·에이전트로 분류하고 — 모델 이름, 토큰 비용, 검색 컨텍스트 같은 타입별 속성을 설정해요.
Set Input/Output
더 나은 시각화와 평가를 위해 트레이스와 스팬의 기본 입력·출력을 재정의해요.
더 알아보기
- LLM Tracing Quickstart — 계측의 기초와
init()구성을 배워요. - Thread Traces — 트레이스를 대화로 묶어 멀티턴을 평가해요.