LLM 应用的调试是个黑盒噩梦——输入进去,输出出来,中间发生了什么完全不透明。LangSmith 正是为解决这一痛点而生。本文从工程实践角度,系统讲解 LangSmith 在 LLM 应用开发全生命周期的应用。
一、为什么 LLM 应用需要专门的可观测性工具
1.1 传统监控的局限
传统的 APM(Application Performance Monitoring)工具,如 Datadog、Prometheus,擅长监控 HTTP 响应时间、错误率、数据库查询等结构化指标。但 LLM 应用的核心质量问题无法用这些工具衡量:
传统监控能告诉你:
✅ API 响应时间:2.3s
✅ Token 消耗:1247 tokens
✅ 错误率:0.1%
但无法告诉你:
❌ 这次回答逻辑是否连贯?
❌ 检索到的文档是否真正相关?
❌ 多步骤 Agent 的哪一步出了问题?
❌ 同一问题为何今天的回答质量比昨天差?
1.2 LLM 应用的可观测性需求
LLM 应用需要三个层次的可观测性:
L1 系统层(传统工具可覆盖)
- 延迟、吞吐量、错误率
- Token 消耗、API 费用
L2 调用链层(LangSmith 核心能力)
- 完整的 LLM 调用链路追踪
- 每一步的输入/输出
- 工具调用和检索结果可视化
L3 质量层(LangSmith 评估能力)
- 答案正确性、相关性
- 幻觉检测
- 与基准版本的 A/B 对比
二、LangSmith 核心功能概览
LangSmith 是 LangChain 团队推出的 LLM 应用可观测性和评估平台,提供:
- Tracing:自动捕获每次 LLM 调用的完整执行链路
- Playground:可视化调试和 Prompt 实验
- Datasets:管理测试数据集
- Evaluations:自动化质量评估
- Monitoring:生产环境监控和告警
三、快速上手:集成 LangSmith 追踪
3.1 基础配置
import os
from langchain_openai import ChatOpenAI
from langsmith import Client
# 设置环境变量
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "your_langsmith_api_key"
os.environ["LANGCHAIN_PROJECT"] = "my-production-app" # 项目名称
# 仅这几行配置,LangChain 会自动追踪所有调用
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
response = llm.invoke("解释一下 Transformer 的注意力机制")
# 此次调用会自动出现在 LangSmith 的 Traces 页面
3.2 自定义追踪与元数据
from langsmith import traceable
from langchain_core.tracers import LangChainTracer
@traceable(
name="RAG检索与生成",
tags=["production", "rag"],
metadata={"version": "1.2.0", "user_tier": "premium"}
)
def rag_pipeline(question: str, user_id: str) -> str:
"""带完整追踪的 RAG 流水线"""
# 第一步:检索
docs = retriever.invoke(question)
# 第二步:生成
context = "\n".join([d.page_content for d in docs])
prompt = f"基于以下上下文回答问题:\n{context}\n\n问题:{question}"
response = llm.invoke(prompt)
return response.content
# 调用时附加追踪元数据
result = rag_pipeline(
question="什么是 MoE 架构?",
user_id="user_123"
)
3.3 追踪 Agent 复杂调用链
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# LangSmith 自动追踪 Agent 的每一步工具调用
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的AI助手,可以使用工具帮助用户。"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 配合 LangSmith 追踪
)
# LangSmith 会记录:
# - Agent 的决策过程
# - 每次工具调用的输入输出
# - 最终答案的生成
result = agent_executor.invoke(
{"input": "查询今天的天气,并生成一份出行建议"},
config={"run_name": "weather_agent_run"} # 自定义追踪名称
)
四、调试工作流:用 LangSmith 定位问题
4.1 RAG 检索质量问题定位
最常见的 RAG 问题:模型生成的回答看起来合理,但其实没有使用正确的文档。
from langsmith import Client
from typing import Optional
client = Client()
def analyze_rag_traces(project_name: str, date_from: str) -> dict:
"""
分析 RAG 追踪数据,找出检索质量问题
"""
# 获取所有 RAG 追踪
traces = client.list_runs(
project_name=project_name,
start_time=date_from,
run_type="chain",
filter='eq(name, "RAG检索与生成")'
)
analysis = {
"total_runs": 0,
"low_relevance_retrievals": [],
"empty_retrievals": [],
"avg_retrieval_count": 0,
}
for trace in traces:
analysis["total_runs"] += 1
# 检查是否有检索步骤
retrieval_runs = [
r for r in trace.child_runs
if "retriever" in r.name.lower()
]
for ret_run in retrieval_runs:
docs = ret_run.outputs.get("documents", [])
if not docs:
analysis["empty_retrievals"].append({
"trace_id": trace.id,
"question": trace.inputs.get("question"),
"timestamp": trace.start_time
})
else:
# 分析检索质量(可以集成评估器)
avg_score = sum(
d.get("relevance_score", 0)
for d in docs
) / len(docs)
if avg_score < 0.5:
analysis["low_relevance_retrievals"].append({
"trace_id": trace.id,
"question": trace.inputs.get("question"),
"avg_relevance": avg_score
})
return analysis
4.2 Playground:交互式 Prompt 调试
LangSmith 的 Playground 功能允许你直接对历史追踪进行"重放"调试:
# 场景:某次 Agent 决策错误,需要在 Playground 中重现和调试
# 1. 在代码中标记需要关注的运行
from langsmith import traceable
@traceable(
name="complex_reasoning",
tags=["debug-needed"], # 打标签便于后续过滤
)
def complex_reasoning_task(input_data: dict) -> str:
# ... 复杂逻辑
pass
# 2. 在 LangSmith 界面:
# - 过滤 tags 为 "debug-needed" 的追踪
# - 点击某次失败的运行
# - 使用 "Open in Playground" 直接重放
# - 修改 Prompt 并立即看到新的输出
# - 不需要改代码、重新部署
五、评估体系:自动化质量衡量
LangSmith 的评估功能是其区别于普通追踪工具的核心能力。
5.1 创建评估数据集
from langsmith import Client
from langsmith.schemas import Example
client = Client()
# 创建包含标准答案的测试数据集
examples = [
{
"inputs": {"question": "什么是 RAG?"},
"outputs": {"answer": "RAG(检索增强生成)是一种将检索系统与生成模型结合的AI技术框架..."}
},
{
"inputs": {"question": "Transformer 的核心创新是什么?"},
"outputs": {"answer": "Transformer 的核心创新是自注意力机制(Self-Attention),它允许模型..."}
},
# ... 更多测试用例
]
dataset = client.create_dataset(
dataset_name="ai-concepts-qa-v1",
description="AI 核心概念问答评估数据集"
)
client.create_examples(
inputs=[e["inputs"] for e in examples],
outputs=[e["outputs"] for e in examples],
dataset_id=dataset.id
)
5.2 实现自定义评估器
from langsmith.evaluation import EvaluationResult, RunEvaluator
from langchain_openai import ChatOpenAI
class CorrectnessEvaluator(RunEvaluator):
"""
使用 LLM 作为裁判,评估答案的正确性
"""
def __init__(self):
self.judge_llm = ChatOpenAI(model="gpt-4o", temperature=0)
def evaluate_run(self, run, example=None) -> EvaluationResult:
if not example or not example.outputs:
return EvaluationResult(key="correctness", score=None)
predicted = run.outputs.get("answer", "")
reference = example.outputs.get("answer", "")
question = run.inputs.get("question", "")
prompt = f"""请评估以下 AI 回答的正确性。
问题:{question}
参考答案:{reference}
AI 回答:{predicted}
请从以下维度评分(0-1):
1. 事实准确性:关键事实是否正确
2. 完整性:是否覆盖了主要要点
3. 无幻觉:是否包含虚假信息
综合评分(0-1):"""
response = self.judge_llm.invoke(prompt)
# 解析评分
score = self._parse_score(response.content)
return EvaluationResult(
key="correctness",
score=score,
comment=response.content
)
def _parse_score(self, text: str) -> float:
import re
numbers = re.findall(r'\d+\.?\d*', text[-50:])
if numbers:
score = float(numbers[-1])
return min(score, 1.0) if score <= 1 else score / 10
return 0.5
class RelevanceEvaluator(RunEvaluator):
"""评估检索文档的相关性"""
def evaluate_run(self, run, example=None) -> EvaluationResult:
question = run.inputs.get("question", "")
retrieved_docs = run.outputs.get("retrieved_docs", [])
if not retrieved_docs:
return EvaluationResult(key="retrieval_relevance", score=0.0)
# 计算每个文档与问题的语义相似度
scores = []
for doc in retrieved_docs[:3]: # 只评估前3个
similarity = self._compute_similarity(question, doc["content"])
scores.append(similarity)
avg_relevance = sum(scores) / len(scores)
return EvaluationResult(
key="retrieval_relevance",
score=avg_relevance
)
5.3 运行评估实验
from langsmith.evaluation import evaluate
def my_rag_app(inputs: dict) -> dict:
"""被评估的 RAG 应用"""
question = inputs["question"]
# 检索
docs = retriever.invoke(question)
retrieved_docs = [
{"content": d.page_content, "score": d.metadata.get("score", 0)}
for d in docs
]
# 生成
context = "\n".join([d.page_content for d in docs])
response = llm.invoke(f"上下文:{context}\n问题:{question}")
return {
"answer": response.content,
"retrieved_docs": retrieved_docs
}
# 运行评估
results = evaluate(
my_rag_app,
data="ai-concepts-qa-v1", # 数据集名称
evaluators=[CorrectnessEvaluator(), RelevanceEvaluator()],
experiment_prefix="rag-v1.2-eval",
num_repetitions=3, # 每个样本运行 3 次(LLM 有随机性)
max_concurrency=5,
)
# 查看汇总结果
print(f"平均正确性: {results.aggregate_metrics['correctness']:.3f}")
print(f"平均检索相关性: {results.aggregate_metrics['retrieval_relevance']:.3f}")
六、A/B 实验与版本对比
LangSmith 的评估系统支持多版本对比,这是优化 LLM 应用的核心工作流:
# 对比两个不同版本的 Prompt
def rag_app_v1(inputs: dict) -> dict:
"""原始版本"""
question = inputs["question"]
docs = retriever.invoke(question)
context = "\n".join([d.page_content for d in docs])
prompt = f"请基于以下信息回答:{context}\n问题:{question}"
response = llm.invoke(prompt)
return {"answer": response.content}
def rag_app_v2(inputs: dict) -> dict:
"""优化版本:更好的 Prompt 结构"""
question = inputs["question"]
docs = retriever.invoke(question)
context = "\n\n".join([
f"[来源{i+1}] {d.page_content}"
for i, d in enumerate(docs)
])
prompt = f"""你是一个专业的AI知识助手。请基于以下参考信息,用准确、清晰的语言回答用户问题。
如果参考信息中没有相关内容,请明确说明"参考资料中暂无此信息",不要臆造。
参考信息:
{context}
用户问题:{question}
请给出详细、准确的回答:"""
response = llm.invoke(prompt)
return {"answer": response.content}
# 在相同数据集上对比两个版本
evaluators = [CorrectnessEvaluator(), RelevanceEvaluator()]
results_v1 = evaluate(rag_app_v1, data="ai-concepts-qa-v1",
evaluators=evaluators, experiment_prefix="rag-v1")
results_v2 = evaluate(rag_app_v2, data="ai-concepts-qa-v1",
evaluators=evaluators, experiment_prefix="rag-v2")
# LangSmith 界面可以直接对比两次实验的评分分布
七、生产监控与告警
7.1 设置生产监控规则
from langsmith import Client
import smtplib
from email.mime.text import MIMEText
client = Client()
def production_health_check(project_name: str) -> dict:
"""
每小时执行的生产健康检查
"""
import datetime
one_hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
# 统计各类指标
runs = list(client.list_runs(
project_name=project_name,
start_time=one_hour_ago,
run_type="chain"
))
if not runs:
return {"status": "no_data"}
total = len(runs)
errors = sum(1 for r in runs if r.error)
avg_latency = sum(
(r.end_time - r.start_time).total_seconds()
for r in runs if r.end_time
) / total
# Token 消耗统计
total_tokens = sum(
(r.total_tokens or 0) for r in runs
)
health = {
"total_requests": total,
"error_rate": errors / total,
"avg_latency_s": avg_latency,
"total_tokens": total_tokens,
"estimated_cost_usd": total_tokens * 0.000015, # GPT-4o 估算
}
# 告警触发
if health["error_rate"] > 0.05: # 错误率 > 5%
send_alert(
f"⚠️ LLM 应用错误率告警: {health['error_rate']:.1%}",
health
)
if health["avg_latency_s"] > 10: # 平均延迟 > 10s
send_alert(
f"⚠️ LLM 应用延迟告警: {health['avg_latency_s']:.1f}s",
health
)
return health
八、最佳实践总结
8.1 追踪设计原则
| 原则 | 实践 |
|---|---|
| 完整性 | 追踪整个请求链路,不要只追踪 LLM 调用 |
| 最小信息 | 不要记录用户 PII,只记录对调试有用的信息 |
| 合理采样 | 生产环境可以采样 10-20%,降低存储成本 |
| 标签规范 | 建立统一的 tag 命名规范,便于过滤分析 |
8.2 评估数据集维护
# 好的评估数据集维护策略
class EvalDatasetManager:
def expand_from_production(
self,
project_name: str,
days: int = 7,
sample_rate: float = 0.01 # 从 1% 的生产流量中采样
):
"""
从生产追踪中自动采样,扩充评估数据集
优先选择:
1. 用户反馈为负的请求
2. 延迟异常高的请求
3. 多样性采样(避免重复的问题类型)
"""
production_runs = self._fetch_production_runs(project_name, days)
candidates = []
for run in production_runs:
priority_score = 0
# 负面反馈优先
if run.feedback_stats.get("user_score", 1) < 0.5:
priority_score += 10
# 高延迟优先
if run.total_tokens and run.total_tokens > 2000:
priority_score += 5
candidates.append((priority_score, run))
# 按优先级采样
candidates.sort(reverse=True)
selected = candidates[:int(len(candidates) * sample_rate)]
# 添加到数据集(待人工标注答案)
self._add_to_pending_annotation(selected)
九、总结
LangSmith 将 LLM 应用开发从"黑盒调试"变成了"白盒工程"。关键价值点:
- 开发效率:Playground 让 Prompt 迭代速度提升 5-10 倍
- 质量保证:自动化评估替代人工抽查,覆盖率从 1% 提升到 100%
- 版本管理:A/B 实验让每次 Prompt 改动都有数据支撑
- 生产可信:追踪系统让生产问题可复现、可定位
从原型到生产,LangSmith 应该是 LLM 应用工程师工具箱中的标配。