LangSmith 支持基于 OpenTelemetry 的追踪,允许您从任何兼容 OpenTelemetry 的应用程序发送追踪信息。本指南涵盖了针对 LangChain 应用的自动埋点和其他框架的手动埋点。
了解如何使用 OpenTelemetry 结合 LangSmith 来追踪您的 LLM 应用程序。
在以下请求中,请根据自托管安装或位于欧盟地区的组织,适当更新 LangSmith URL。对于欧盟地区,请使用 eu.api.smith.langchain.com。
追踪 LangChain 应用程序
如果您使用 LangChain 或 LangGraph,请使用内置集成来追踪您的应用程序:
-
安装支持 OpenTelemetry 的 LangSmith 包:
pip install "langsmith[otel]"
pip install langchain
需要 Python SDK 版本 langsmith>=0.3.18。我们建议使用 langsmith>=0.4.25 以受益于重要的 OpenTelemetry 修复。
-
在您的 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 导出器包以及 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 导出器配置方式,如果您仅发送追踪信息,可能需要在端点后追加 /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>"
-
记录一个追踪。
此代码设置了一个 OTEL 追踪器和导出器,用于将追踪信息发送到 LangSmith。然后它调用 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-4.1-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 版本中可用。我们建议使用 >= 0.4.25 版本以获得改进 OTEL 导出和混合扇出稳定性的修复。
使用环境变量进行全局配置
默认情况下,LangSmith OpenTelemetry 导出器会将数据发送到 LangSmith API 的 OTEL 端点,但可以通过设置标准的 OTEL 环境变量来自定义:
OTEL_EXPORTER_OTLP_ENDPOINT: 覆盖端点 URL
OTEL_EXPORTER_OTLP_HEADERS: 添加自定义头部(LangSmith API 密钥和项目将自动添加)
OTEL_SERVICE_NAME: 设置自定义服务名称(默认为 "langsmith")
LangSmith 默认使用 HTTP 追踪导出器。如果您想使用自己的追踪提供者,可以:
- 如上所示设置 OTEL 环境变量,或者
- 在初始化 LangChain 组件之前设置一个全局追踪提供者,LangSmith 将检测并使用它,而不是创建自己的提供者。
配置备用的 OTLP 端点
要将追踪发送到不同的提供商,请使用您提供商的端点配置 OTLP 导出器:
import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 为 LangChain 设置环境变量
os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"
# 为您的自定义端点配置 OTLP 导出器
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 | 覆盖运行项的跨度名称 |
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 | 附加到跨度的自定义标签(逗号分隔) |
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 | 温度设置 |
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 | 频率惩罚设置 |
gen_ai.request.presence_penalty | invocation_params.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 | 当跨度类型为 “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 | 存在惩罚 |
llm.frequency_penalty | invocation_params.frequency_penalty | 频率惩罚 |
llm.request.functions | invocation_params.functions | 函数定义 |
提示模板属性
| OpenTelemetry 属性 | LangSmith 字段 | 说明 |
|---|
llm.prompt_template.variables | Run type | 将运行类型设置为 “prompt”,与 input.value 一起使用 |
检索器属性
| 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 辅助函数来配置导出。以下示例追踪了一个 Google ADK 代理:
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 环境变量或导出器。configure() 会自动为 LangSmith 配置它们;埋点工具(如 GoogleADKInstrumentor)会创建跨度。
这是 LangSmith 中生成的追踪的示例。
向追踪添加附件
LangSmith 支持向追踪附加文件。这在构建具有多模态输入或输出的代理时非常有用。使用 OpenTelemetry 进行追踪时也支持附件。
下面的示例追踪了一个 Google ADK 代理并向追踪添加了一个附件。它结合使用了 LangSmith 的 OtelSpanProcessor 和一个自定义的 AttachmentSpanProcessor,后者使用 on_end() 向父跨度添加一个图像附件。
import asyncio
import base64
import json
from pathlib import Path
from dotenv import load_dotenv
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
from langsmith.integrations.otel import OtelSpanProcessor
class AttachmentSpanProcessor(SpanProcessor):
"""自定义 SpanProcessor,用于向调用跨度添加附件。"""
def __init__(self):
self.attachment_data = None
def set_attachment(self, attachment_data):
self.attachment_data = attachment_data
def on_end(self, span):
if span.name == "invocation" and self.attachment_data:
attachments_json = json.dumps([self.attachment_data])
span._attributes["langsmith.attachments"] = attachments_json
load_dotenv()
# 手动设置 TracerProvider
provider = TracerProvider()
trace.set_tracer_provider(provider)
# 首先添加附件处理器(在 LangSmith 处理器之前运行)
attachment_processor = AttachmentSpanProcessor()
provider.add_span_processor(attachment_processor)
# 其次添加 LangSmith 处理器(接收已修改的跨度)
langsmith_processor = OtelSpanProcessor(project="travel-assistant")
provider.add_span_processor(langsmith_processor)
def get_flight_info(destination: str, departure_date: str) -> dict:
"""获取目的地的航班信息。"""
return {
"destination": destination,
"departure_date": departure_date,
"price": "$450",
"duration": "5h 30m",
"airline": "Example Airways"
}
def get_hotel_recommendations(city: str, check_in: str) -> dict:
"""获取城市的酒店推荐。"""
return {
"city": city,
"check_in": check_in,
"hotels": [
{"name": "Grand Plaza Hotel", "rating": 4.5, "price": "$120/night"},
{"name": "City Center Inn", "rating": 4.2, "price": "$95/night"}
]
}
async def main():
# 准备附件
receipt_path = Path("receipt-template-example.png")
with open(receipt_path, "rb") as img_file:
image_bytes = img_file.read()
image_base64 = base64.b64encode(image_bytes).decode("ascii")
attachment_data = {
"name": "receipt-template-example",
"content": image_base64,
"mime_type": "image/jpeg",
}
attachment_processor.set_attachment(attachment_data)
# 创建 ADK 代理
agent = LlmAgent(
name="travel_assistant",
tools=[get_flight_info, get_hotel_recommendations],
model="gemini-2.0-flash-exp",
instruction="You are a helpful travel assistant that can help with flights and hotels.",
)
# 设置会话和运行器
session_service = InMemorySessionService()
runner = Runner(
app_name="travel_app",
agent=agent,
session_service=session_service
)
await session_service.create_session(
app_name="travel_app",
user_id="traveler_456",
session_id="session_789"
)
# 向代理发送消息
new_message = types.Content(
parts=[types.Part(text="I need to book a flight to Paris for March 15th and find a good hotel.")],
role="user",
)
# 运行代理并处理事件
events = runner.run(
user_id="traveler_456",
session_id="session_789",
new_message=new_message,
)
for event in events:
print(event)
if __name__ == "__main__":
asyncio.run(main())
这是 LangSmith 中生成的追踪的示例。
高级配置
使用 OpenTelemetry Collector 进行扇出
当您需要 OTEL 扇出时,使用 LANGSMITH_OTEL_ENABLED=true。配置您的应用程序仅发送一次 OTEL 跨度,然后使用 OpenTelemetry Collector 将它们路由到 LangSmith 和任何其他可观测性后端。
当您正在追踪应用程序并希望进行多目标路由时,请使用此方法。如果您正在操作 LangSmith 平台基础设施遥测(来自 Kubernetes 上自托管 LangSmith 服务的日志、指标、追踪),请改用为 LangSmith 遥测配置您的收集器指南。
对于更高级的场景,您可以使用 OpenTelemetry Collector 将您的遥测数据扇出到多个目标。这比在应用程序代码中配置多个导出器更具可扩展性。
-
为您的环境安装 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]
-
配置您的应用程序以将数据发送到收集器:
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 的上下文传播功能确保追踪在服务边界之间保持连接。
分布式追踪中的上下文传播
在分布式系统中,上下文传播在服务之间传递追踪元数据,以便相关的跨度链接到同一个追踪:
- 追踪 ID:整个追踪的唯一标识符
- 跨度 ID:当前跨度的唯一标识符
- 采样决策:指示是否应对此追踪进行采样
使用 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 追踪提供者
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:创建一个跨度并将上下文传播到服务 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)