LangSmith는 OpenTelemetry 기반 추적을 지원하여, OpenTelemetry 호환 애플리케이션에서 추적 데이터를 전송할 수 있습니다. 이 가이드는 LangChain 애플리케이션의 자동 계측과 다른 프레임워크의 수동 계측을 모두 다룹니다.
OpenTelemetry를 사용하여 LangSmith로 LLM 애플리케이션을 추적하는 방법을 알아보세요.
셀프 호스팅 설치 환경이나 EU 리전의 조직인 경우, 아래 요청에서 LangSmith URL을 적절히 업데이트하세요. EU 리전의 경우 eu.api.smith.langchain.com을 사용하세요.
LangChain 애플리케이션 추적하기
LangChain 또는 LangGraph를 사용하는 경우, 내장된 통합을 사용하여 애플리케이션을 추적하세요:
-
OpenTelemetry 지원이 포함된 LangSmith 패키지를 설치합니다:
pip install "langsmith[otel]"
pip install langchain
Python SDK 버전 langsmith>=0.3.18이 필요합니다. 중요한 OpenTelemetry 수정 사항의 혜택을 받으려면 langsmith>=0.4.25를 권장합니다.
-
LangChain/LangGraph 앱에서
LANGSMITH_OTEL_ENABLED 환경 변수를 설정하여 OpenTelemetry 통합을 활성화합니다:
LANGSMITH_OTEL_ENABLED=true
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=<your_langsmith_api_key>
# 여러 워크스페이스에 연결된 LangSmith API 키의 경우, LANGSMITH_WORKSPACE_ID 환경 변수를 설정하여 사용할 워크스페이스를 지정하세요.
-
추적 기능이 포함된 LangChain 애플리케이션을 생성합니다. 예시:
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# 체인 생성
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI()
chain = prompt | model
# 체인 실행
result = chain.invoke({"topic": "programming"})
print(result.content)
-
애플리케이션이 실행되면 LangSmith 대시보드에서 추적 정보를 확인할 수 있습니다 (예시).
LangChain이 아닌 애플리케이션 추적하기
LangChain이 아닌 애플리케이션이나 커스텀 계측의 경우, 표준 OpenTelemetry 클라이언트를 사용하여 LangSmith에서 애플리케이션을 추적할 수 있습니다. (langsmith ≥ 0.4.25를 권장합니다.)
-
OpenTelemetry SDK, OpenTelemetry exporter 패키지 및 OpenAI 패키지를 설치합니다:
pip install openai
pip install opentelemetry-sdk
pip install opentelemetry-exporter-otlp
-
엔드포인트에 대한 환경 변수를 설정하고, 특정 값으로 대체합니다:
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.smith.langchain.com/otel
OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your langsmith api key>"
otel exporter의 설정 방식에 따라, 추적 데이터만 전송하는 경우 엔드포인트에 /v1/traces를 추가해야 할 수 있습니다.
LangSmith를 셀프 호스팅하는 경우, 기본 엔드포인트를 LangSmith API 엔드포인트로 교체하고 /api/v1을 추가하세요. 예시: OTEL_EXPORTER_OTLP_ENDPOINT=https://ai-company.com/api/v1/otel
선택사항: “default”가 아닌 커스텀 프로젝트 이름을 지정합니다:
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.smith.langchain.com/otel
OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your langsmith api key>,Langsmith-Project=<project name>"
-
추적을 기록합니다.
이 코드는 LangSmith로 추적 데이터를 전송하는 OTEL tracer와 exporter를 설정합니다. 그런 다음 OpenAI를 호출하고 필요한 OpenTelemetry 속성을 전송합니다.
from openai import OpenAI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
otlp_exporter = OTLPSpanExporter(
timeout=10,
)
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
tracer = trace.get_tracer(__name__)
def call_openai():
model = "gpt-4o-mini"
with tracer.start_as_current_span("call_open_ai") as span:
span.set_attribute("langsmith.span.kind", "LLM")
span.set_attribute("langsmith.metadata.user_id", "user_123")
span.set_attribute("gen_ai.system", "OpenAI")
span.set_attribute("gen_ai.request.model", model)
span.set_attribute("llm.request.type", "chat")
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{
"role": "user",
"content": "Write a haiku about recursion in programming."
}
]
for i, message in enumerate(messages):
span.set_attribute(f"gen_ai.prompt.{i}.content", str(message["content"]))
span.set_attribute(f"gen_ai.prompt.{i}.role", str(message["role"]))
completion = client.chat.completions.create(
model=model,
messages=messages
)
span.set_attribute("gen_ai.response.model", completion.model)
span.set_attribute("gen_ai.completion.0.content", str(completion.choices[0].message.content))
span.set_attribute("gen_ai.completion.0.role", "assistant")
span.set_attribute("gen_ai.usage.prompt_tokens", completion.usage.prompt_tokens)
span.set_attribute("gen_ai.usage.completion_tokens", completion.usage.completion_tokens)
span.set_attribute("gen_ai.usage.total_tokens", completion.usage.total_tokens)
return completion.choices[0].message
if __name__ == "__main__":
call_openai()
-
LangSmith 대시보드에서 추적 정보를 확인합니다 (예시).
대체 제공자로 추적 데이터 전송하기
LangSmith가 OpenTelemetry 추적의 기본 대상이지만, OpenTelemetry를 구성하여 다른 관찰성 플랫폼으로 추적 데이터를 전송할 수도 있습니다.
LangSmith Python SDK ≥ 0.4.1에서 사용 가능합니다. OTEL 내보내기 및 하이브리드 팬아웃 안정성을 개선하는 수정 사항을 위해 ≥ 0.4.25를 권장합니다.
전역 구성을 위한 환경 변수 사용
기본적으로 LangSmith OpenTelemetry exporter는 LangSmith API OTEL 엔드포인트로 데이터를 전송하지만, 표준 OTEL 환경 변수를 설정하여 이를 사용자 정의할 수 있습니다:
OTEL_EXPORTER_OTLP_ENDPOINT: 엔드포인트 URL 재정의
OTEL_EXPORTER_OTLP_HEADERS: 커스텀 헤더 추가 (LangSmith API 키와 프로젝트는 자동으로 추가됨)
OTEL_SERVICE_NAME: 커스텀 서비스 이름 설정 (기본값: "langsmith")
LangSmith는 기본적으로 HTTP trace exporter를 사용합니다. 자체 추적 제공자를 사용하려면 다음 중 하나를 선택할 수 있습니다:
- 위에 표시된 대로 OTEL 환경 변수를 설정하거나,
- LangChain 컴포넌트를 초기화하기 전에 전역 trace provider를 설정합니다. LangSmith는 이를 감지하고 자체 제공자를 생성하는 대신 이를 사용합니다.
대체 OTLP 엔드포인트 구성
다른 제공자로 추적 데이터를 전송하려면, 제공자의 엔드포인트로 OTLP exporter를 구성하세요:
import os
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# LangChain을 위한 환경 변수 설정
os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"
# 커스텀 엔드포인트를 위한 OTLP exporter 구성
provider = TracerProvider()
otlp_exporter = OTLPSpanExporter(
# 제공자의 엔드포인트로 변경
endpoint="https://otel.your-provider.com/v1/traces",
# 인증에 필요한 헤더 추가
headers={"api-key": "your-api-key"}
)
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# LangChain 애플리케이션 생성 및 실행
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI()
chain = prompt | model
result = chain.invoke({"topic": "programming"})
print(result.content)
하이브리드 추적은 버전 ≥ 0.4.1에서 사용 가능합니다. OTEL 엔드포인트만으로 추적 데이터를 전송하려면 다음을 설정하세요:LANGSMITH_OTEL_ONLY="true"
(권장: langsmith ≥ 0.4.25 사용)
지원되는 OpenTelemetry 속성 및 이벤트 매핑
OpenTelemetry를 통해 LangSmith로 추적 데이터를 전송할 때, 다음 속성들이 LangSmith 필드에 매핑됩니다:
핵심 LangSmith 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
langsmith.trace.name | Run name | 실행의 span 이름을 재정의합니다 |
langsmith.span.kind | Run type | 값: llm, chain, tool, retriever, embedding, prompt, parser |
langsmith.trace.session_id | Session ID | 관련 추적을 위한 세션 식별자 |
langsmith.trace.session_name | Session name | 세션의 이름 |
langsmith.span.tags | Tags | span에 연결된 커스텀 태그 (쉼표로 구분) |
langsmith.metadata.{key} | metadata.{key} | langsmith 접두사가 있는 커스텀 메타데이터 |
GenAI 표준 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
gen_ai.system | metadata.ls_provider | GenAI 시스템 (예: “openai”, “anthropic”) |
gen_ai.operation.name | Run type | ”chat”/“completion”을 “llm”에, “embedding”을 “embedding”에 매핑 |
gen_ai.prompt | inputs | 모델로 전송된 입력 프롬프트 |
gen_ai.completion | outputs | 모델이 생성한 출력 |
gen_ai.prompt.{n}.role | inputs.messages[n].role | n번째 입력 메시지의 역할 |
gen_ai.prompt.{n}.content | inputs.messages[n].content | n번째 입력 메시지의 콘텐츠 |
gen_ai.prompt.{n}.message.role | inputs.messages[n].role | 역할의 대체 형식 |
gen_ai.prompt.{n}.message.content | inputs.messages[n].content | 콘텐츠의 대체 형식 |
gen_ai.completion.{n}.role | outputs.messages[n].role | n번째 출력 메시지의 역할 |
gen_ai.completion.{n}.content | outputs.messages[n].content | n번째 출력 메시지의 콘텐츠 |
gen_ai.completion.{n}.message.role | outputs.messages[n].role | 역할의 대체 형식 |
gen_ai.completion.{n}.message.content | outputs.messages[n].content | 콘텐츠의 대체 형식 |
gen_ai.input.messages | inputs.messages | 입력 메시지 배열 |
gen_ai.output.messages | outputs.messages | 출력 메시지 배열 |
gen_ai.tool.name | invocation_params.tool_name | 도구 이름, 실행 타입을 “tool”로 설정 |
GenAI 요청 매개변수
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
gen_ai.request.model | invocation_params.model | 요청에 사용된 모델 이름 |
gen_ai.response.model | invocation_params.model | 응답에서 반환된 모델 이름 |
gen_ai.request.temperature | invocation_params.temperature | Temperature 설정 |
gen_ai.request.top_p | invocation_params.top_p | Top-p 샘플링 설정 |
gen_ai.request.max_tokens | invocation_params.max_tokens | 최대 토큰 설정 |
gen_ai.request.frequency_penalty | invocation_params.frequency_penalty | Frequency penalty 설정 |
gen_ai.request.presence_penalty | invocation_params.presence_penalty | Presence penalty 설정 |
gen_ai.request.seed | invocation_params.seed | 생성에 사용된 랜덤 시드 |
gen_ai.request.stop_sequences | invocation_params.stop | 생성을 중지하는 시퀀스 |
gen_ai.request.top_k | invocation_params.top_k | Top-k 샘플링 매개변수 |
gen_ai.request.encoding_formats | invocation_params.encoding_formats | 출력 인코딩 형식 |
GenAI 사용량 메트릭
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
gen_ai.usage.input_tokens | usage_metadata.input_tokens | 사용된 입력 토큰 수 |
gen_ai.usage.output_tokens | usage_metadata.output_tokens | 사용된 출력 토큰 수 |
gen_ai.usage.total_tokens | usage_metadata.total_tokens | 사용된 전체 토큰 수 |
gen_ai.usage.prompt_tokens | usage_metadata.input_tokens | 사용된 입력 토큰 수 (더 이상 사용되지 않음) |
gen_ai.usage.completion_tokens | usage_metadata.output_tokens | 사용된 출력 토큰 수 (더 이상 사용되지 않음) |
gen_ai.usage.details.reasoning_tokens | usage_metadata.reasoning_tokens | 사용된 추론 토큰 수 |
TraceLoop 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
traceloop.entity.input | inputs | TraceLoop의 전체 입력 값 |
traceloop.entity.output | outputs | TraceLoop의 전체 출력 값 |
traceloop.entity.name | Run name | TraceLoop의 엔티티 이름 |
traceloop.span.kind | Run type | LangSmith 실행 타입으로 매핑 |
traceloop.llm.request.type | Run type | ”embedding”은 “embedding”으로, 나머지는 “llm”으로 매핑 |
traceloop.association.properties.{key} | metadata.{key} | traceloop 접두사가 있는 커스텀 메타데이터 |
OpenInference 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
input.value | inputs | 전체 입력 값, 문자열 또는 JSON일 수 있음 |
output.value | outputs | 전체 출력 값, 문자열 또는 JSON일 수 있음 |
openinference.span.kind | Run type | 다양한 종류를 LangSmith 실행 타입으로 매핑 |
llm.system | metadata.ls_provider | LLM 시스템 제공자 |
llm.model_name | metadata.ls_model_name | OpenInference의 모델 이름 |
tool.name | Run name | span kind가 “TOOL”일 때의 도구 이름 |
metadata | metadata.* | 병합될 메타데이터의 JSON 문자열 |
LLM 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
llm.input_messages | inputs.messages | 입력 메시지 |
llm.output_messages | outputs.messages | 출력 메시지 |
llm.token_count.prompt | usage_metadata.input_tokens | 프롬프트 토큰 수 |
llm.token_count.completion | usage_metadata.output_tokens | 완료 토큰 수 |
llm.token_count.total | usage_metadata.total_tokens | 전체 토큰 수 |
llm.usage.total_tokens | usage_metadata.total_tokens | 대체 전체 토큰 수 |
llm.invocation_parameters | invocation_params.* | 호출 매개변수의 JSON 문자열 |
llm.presence_penalty | invocation_params.presence_penalty | Presence penalty |
llm.frequency_penalty | invocation_params.frequency_penalty | Frequency penalty |
llm.request.functions | invocation_params.functions | 함수 정의 |
프롬프트 템플릿 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
llm.prompt_template.variables | Run type | 실행 타입을 “prompt”로 설정, input.value와 함께 사용됨 |
Retriever 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
retrieval.documents.{n}.document.content | outputs.documents[n].page_content | n번째로 검색된 문서의 콘텐츠 |
retrieval.documents.{n}.document.metadata | outputs.documents[n].metadata | n번째로 검색된 문서의 메타데이터 (JSON) |
도구 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
tools | invocation_params.tools | 도구 정의 배열 |
tool_arguments | invocation_params.tool_arguments | JSON 또는 키-값 쌍으로 된 도구 인자 |
Logfire 속성
| OpenTelemetry 속성 | LangSmith 필드 | 비고 |
|---|
prompt | inputs | Logfire 프롬프트 입력 |
all_messages_events | outputs | Logfire 메시지 이벤트 출력 |
events | inputs/outputs | Logfire 이벤트 배열, 입력/선택 이벤트를 분리함 |
OpenTelemetry 이벤트 매핑
| 이벤트 이름 | LangSmith 필드 | 비고 |
|---|
gen_ai.content.prompt | inputs | 이벤트 속성에서 프롬프트 콘텐츠를 추출 |
gen_ai.content.completion | outputs | 이벤트 속성에서 완료 콘텐츠를 추출 |
gen_ai.system.message | inputs.messages[] | 대화의 시스템 메시지 |
gen_ai.user.message | inputs.messages[] | 대화의 사용자 메시지 |
gen_ai.assistant.message | outputs.messages[] | 대화의 어시스턴트 메시지 |
gen_ai.tool.message | outputs.messages[] | 도구 응답 메시지 |
gen_ai.choice | outputs | 완료 이유가 포함된 모델 선택/응답 |
exception | status, error | 상태를 “error”로 설정하고 예외 메시지/스택트레이스를 추출 |
이벤트 속성 추출
메시지 이벤트의 경우, 다음 속성들이 추출됩니다:
content → 메시지 콘텐츠
role → 메시지 역할
id → tool_call_id (도구 메시지의 경우)
gen_ai.event.content → 전체 메시지 JSON
선택 이벤트의 경우:
finish_reason → 선택 완료 이유
message.content → 선택 메시지 콘텐츠
message.role → 선택 메시지 역할
tool_calls.{n}.id → 도구 호출 ID
tool_calls.{n}.function.name → 도구 함수 이름
tool_calls.{n}.function.arguments → 도구 함수 인자
tool_calls.{n}.type → 도구 호출 타입
예외 이벤트의 경우:
exception.message → 오류 메시지
exception.stacktrace → 오류 스택트레이스 (메시지에 추가됨)
구현 예시
LangSmith SDK를 사용한 추적
LangSmith SDK의 OpenTelemetry 헬퍼를 사용하여 내보내기를 구성합니다:
import asyncio
from langsmith.integrations.otel import configure
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types
# LangSmith OpenTelemetry 내보내기 구성 (OTEL 환경 변수나 헤더 불필요)
configure(project_name="adk-otel-demo")
async def main():
agent = LlmAgent(
name="travel_assistant",
model="gemini-2.5-flash-lite",
instruction="You are a helpful travel assistant.",
)
session_service = InMemorySessionService()
runner = Runner(app_name="travel_app", agent=agent, session_service=session_service)
user_id = "user_123"
session_id = "session_abc"
await session_service.create_session(app_name="travel_app", user_id=user_id, session_id=session_id)
new_message = types.Content(parts=[types.Part(text="Hi! Recommend a weekend trip to Paris.")], role="user")
for event in runner.run(user_id=user_id, session_id=session_id, new_message=new_message):
print(event)
if __name__ == "__main__":
asyncio.run(main())
OTEL 환경 변수나 exporter를 설정할 필요가 없습니다. configure()가 LangSmith를 위해 자동으로 연결합니다; instrumentor (예: GoogleADKInstrumentor)가 span을 생성합니다.
- LangSmith 대시보드에서 추적 정보를 확인합니다 (예시).
고급 구성
팬아웃을 위한 OpenTelemetry Collector 사용
더 고급 시나리오의 경우, OpenTelemetry Collector를 사용하여 텔레메트리 데이터를 여러 대상으로 팬아웃할 수 있습니다. 이는 애플리케이션 코드에서 여러 exporter를 구성하는 것보다 더 확장 가능한 접근 방식입니다.
-
환경에 맞는 OpenTelemetry Collector를 설치합니다.
-
여러 대상으로 내보내는 구성 파일(예:
otel-collector-config.yaml)을 생성합니다:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
exporters:
otlphttp/langsmith:
endpoint: https://api.smith.langchain.com/otel/v1/traces
headers:
x-api-key: ${env:LANGSMITH_API_KEY}
Langsmith-Project: my_project
otlphttp/other_provider:
endpoint: https://otel.your-provider.com/v1/traces
headers:
api-key: ${env:OTHER_PROVIDER_API_KEY}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/langsmith, otlphttp/other_provider]
-
Collector로 전송하도록 애플리케이션을 구성합니다:
import os
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# 로컬 OpenTelemetry Collector를 가리킴
otlp_exporter = OTLPSpanExporter(
endpoint="http://localhost:4318/v1/traces"
)
provider = TracerProvider()
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# LangChain을 위한 환경 변수 설정
os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"
# LangChain 애플리케이션 생성 및 실행
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI()
chain = prompt | model
result = chain.invoke({"topic": "programming"})
print(result.content)
이 접근 방식은 여러 가지 장점을 제공합니다:
- 모든 텔레메트리 대상에 대한 중앙 집중식 구성
- 애플리케이션 코드의 오버헤드 감소
- 더 나은 확장성과 복원력
- 애플리케이션 코드를 변경하지 않고 대상을 추가하거나 제거할 수 있는 기능
LangChain과 OpenTelemetry를 사용한 분산 추적
분산 추적은 LLM 애플리케이션이 여러 서비스나 프로세스에 걸쳐 있을 때 필수적입니다. OpenTelemetry의 컨텍스트 전파 기능은 서비스 경계를 넘어 추적이 연결된 상태로 유지되도록 보장합니다.
분산 추적에서의 컨텍스트 전파
분산 시스템에서 컨텍스트 전파는 관련된 span들이 동일한 추적에 연결되도록 서비스 간에 추적 메타데이터를 전달합니다:
- Trace ID: 전체 추적에 대한 고유 식별자
- Span ID: 현재 span에 대한 고유 식별자
- 샘플링 결정: 이 추적을 샘플링해야 하는지 여부를 나타냄
LangChain으로 분산 추적 설정
여러 서비스에 걸쳐 분산 추적을 활성화하려면:
import os
from opentelemetry import trace
from opentelemetry.propagate import inject, extract
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import requests
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# OpenTelemetry trace provider 설정
provider = TracerProvider()
otlp_exporter = OTLPSpanExporter(
endpoint="https://api.smith.langchain.com/otel/v1/traces",
headers={"x-api-key": os.getenv("LANGSMITH_API_KEY"), "Langsmith-Project": "my_project"}
)
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# 서비스 A: span을 생성하고 컨텍스트를 서비스 B로 전파
def service_a():
with tracer.start_as_current_span("service_a_operation") as span:
# 체인 생성
prompt = ChatPromptTemplate.from_template("Summarize: {text}")
model = ChatOpenAI()
chain = prompt | model
# 체인 실행
result = chain.invoke({"text": "OpenTelemetry is an observability framework"})
# 서비스 B로 컨텍스트 전파
headers = {}
inject(headers) # 헤더에 추적 컨텍스트를 주입
# 추적 컨텍스트와 함께 서비스 B 호출
response = requests.post(
"http://service-b.example.com/process",
headers=headers,
json={"summary": result.content}
)
return response.json()
# 서비스 B: 컨텍스트를 추출하고 추적을 계속
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/process", methods=["POST"])
def service_b_endpoint():
# 요청 헤더에서 추적 컨텍스트를 추출
context = extract(request.headers)
with tracer.start_as_current_span("service_b_operation", context=context) as span:
data = request.json
summary = data.get("summary", "")
# 다른 LLM 체인으로 요약 처리
prompt = ChatPromptTemplate.from_template("Analyze the sentiment of: {text}")
model = ChatOpenAI()
chain = prompt | model
result = chain.invoke({"text": summary})
return jsonify({"analysis": result.content})
if __name__ == "__main__":
app.run(port=5000)