告别黑盒盲调:LangSmith 全链路追踪、LLM 自动化评估与企业级综合实战
·
在开发基于 LLM 的应用(如 RAG、Agent)时,开发者最常遇到的困境就是“黑盒执行”:
-
知识库回答错误,到底是向量检索没召回,还是Prompt 上下文太长被模型忽略,亦或是模型直接产生幻觉?
-
智能体回复异常,到底有没有触发 Function Call?传给接口的参数对不对?工具返回的原始数据是否被模型正确理解?
如果仅靠终端打印 print(),排查多轮嵌套链路将极其痛苦。本篇我们将深入 LangChain 官方的可观测性与评估平台——LangSmith,并串联前面学过的全部核心技术,落地一个完整的企业级客服与业务综合项目。
一、为什么 LLM 应用必须具备全链路追踪?
传统的 Web 服务排查依赖 APM 链路追踪(如 SkyWalking、Jaeger),而 LLM 应用的排查维度更加复杂:
┌────────────────────────────────────────┐
│ LangSmith 监控平台 │
└───────────────────┬────────────────────┘
│ (异步无侵入上报)
┌─────────────────────────────────────────────────┼──────────────────────────────────────────────┐
│ Trace(一次完整调用链路) │ │
│ ▼ │
│ ┌───────────────────────────────┐ ┌────────────────────────────────┐ ┌─────────────┐ │
│ │ 1. Run: Vector Retriever ├────►│ 2. Run: Prompt Template ├────►│ 3. Run: LLM │ │
│ │ • 查看召回的 Top-K 文档块 │ │ • 查看最终组装的完整上下文 │ │ • Token │ │
│ │ • 记录检索耗时 │ │ • 检查是否存在 Prompt 注入 │ │ • 耗时 │ │
│ └───────────────────────────────┘ └────────────────────────────────┘ └─────────────┘ │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
LangSmith 核心追踪要素
| 要素 | 核心作用 | 排查重点 |
| Trace & Run | 追踪请求生命周期与具体子步骤 | 定位在哪一个子步骤发生阻塞或异常报错 |
| Prompt & IO | 记录模型输入、系统设定与原始输出 | 观察动态注入的上下文格式是否完整规范 |
| Tool Calls | 追踪 Agent 工具调用的参数与返回值 | 验证参数类型是否匹配、返回值是否有效 |
| Token & Latency | 统计单步与全链路耗时、输入/输出 Token 消耗 | 性能瓶颈分析与 API 调用成本核算 |
| Tags & Metadata | 注入自定义业务标签与追踪上下文 | 区分测试/生产环境、租户 ID、会话 ID |
二、配置与开启 LangSmith 追踪
无需重构业务代码,通过环境变量注入即可实现 零代码侵入 的链路自动捕获。
1. 配置环境变量 (.env)
Ini, TOML
# 开启 LangSmith 追踪
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=lsv2_pt_your_api_key_here
LANGSMITH_PROJECT=enterprise-customer-service
# 模型服务配置 (以 DeepSeek 为例)
DEEPSEEK_API_KEY=sk-your-deepseek-key
DEEPSEEK_BASE_URL=https://api.deepseek.com
2. 注入 Metadata 与 Run Tags
在调用 LCEL 链或 Agent 时,可以通过 config 参数给 Trace 附加元数据与标签,方便在后台进行组合筛选:
Python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("你是一位资深技术专家,请解答:{question}")
model = ChatOpenAI(model="deepseek-chat")
chain = prompt | model | StrOutputParser()
# 携带追踪元数据与标签进行调用
response = chain.invoke(
{"question": "解释什么是向量检索中的余弦相似度?"},
config={
"run_name": "TechDocQA_Chain",
"tags": ["knowledge-base", "production"],
"metadata": {
"user_id": "usr_9527",
"environment": "prod",
"tenant": "enterprise_a"
}
}
)
三、构建自动化评估体系(Evaluation Harness)
“盲目修改 Prompt 容易顾此失彼”。在上线前,必须构建基准测试集对链路做回归测试。
┌────────────────────────────┐
│ 标准测试集 (Ground Truth) │
│ • 测试输入 (Query) │
│ • 预期工具/关键词 │
└─────────────┬──────────────┘
│
▼
┌───────────────────┐
│ 批量执行目标应用 │
│ (Chain / Agent) │
└─────────┬─────────┘
│
┌──────────────────┴──────────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ RAG 问答效果评估 │ │ Agent 工具调用评估 │
│ • 关键词/语义召回匹配 │ │ • 是否命中指定工具 │
│ • 拒绝回答边界测试 │ │ • 参数提取是否精确 │
└───────────────────────┘ └───────────────────────┘
1. RAG 问答排查决策树
当测试集中的 RAG 用例评估未通过时,按照以下链路逐层下钻,切忌直接修改 Prompt:
RAG 回答不符合预期?
│
[检查 Step 1: 向量召回内容]
召回文档是否包含正确答案?
/ \
[否] [是]
/ \
检查切分 Chunk 大小 [检查 Step 2: 组装后的 Prompt]
与 Embedding 语义相似度 上下文是否被注入?是否被截断?
/ \
[否] [是]
/ \
检查检索管道拼接 [检查 Step 3: 模型推理]
优化 System Prompt 约束
降低 Temperature 随机度
2. Agent 自动化测试脚本 (eval_agent.py)
Python
def evaluate_agent(agent_executor, test_cases):
passed = 0
for idx, case in enumerate(test_cases, 1):
query = case["query"]
expected_tool = case["expected_tool"]
result = agent_executor.invoke({"messages": [("user", query)]})
messages = result["messages"]
# 提取模型产生的工具调用名称
called_tools = []
for msg in messages:
if hasattr(msg, "tool_calls") and msg.tool_calls:
for tc in msg.tool_calls:
called_tools.append(tc["name"])
# 校验工具调用正确性
is_tool_correct = expected_tool in called_tools if expected_tool else len(called_tools) == 0
if is_tool_correct:
passed += 1
print(f"[PASS] Case {idx}: {query} -> 工具调用正确: {called_tools}")
else:
print(f"[FAIL] Case {idx}: {query} -> 预期工具: {expected_tool}, 实际调用: {called_tools}")
print(f"\n测试通过率: {passed}/{len(test_cases)} ({passed/len(test_cases)*100:.1f}%)")
四、企业级智能客服助手:综合实战
我们将 知识库问答(RAG) 与 业务工具调用(Agent) 进行融合,构建一个具备分流调度能力的综合客服系统。
┌───────────────────────┐
│ 用户终端输入 │
└───────────┬───────────┘
│
▼
┌─────────────────────────┐
│ 意图分流与路由 │
│ (Router / Dispatcher) │
└────┬───────────────┬────┘
│ │
[知识库咨询类] │ │ [业务操作/查询类]
(如制度、条款、FAQ) │ │ (如查单、查库存、算价格)
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ RAG 服务 │ │ Agent 执行器 │
│ (VectorStore检索)│ │ (Tool 动态调用) │
└────────┬─────────┘ └────────┬─────────┘
│ │
└────────────┬──────────┘
│
▼
┌───────────────────────┐
│ 格式化输出最终回答 │
└───────────────────────┘
1. 业务工具集实现 (business_tools.py)
Python
from langchain_core.tools import tool
@tool
def get_order_status(order_id: str) -> str:
"""根据订单号查询订单物流与状态信息。"""
orders = {
"A1001": "已出库,承运商:顺丰速运,单号:SF888888",
"A1002": "等待仓库拣货中",
}
return orders.get(order_id, f"未查询到订单号 {order_id},请核对。")
@tool
def get_product_stock(product_id: str) -> str:
"""根据商品编号查询当前库存数量。"""
stocks = {"P2001": 150, "P2002": 0}
count = stocks.get(product_id)
if count is None:
return f"商品 {product_id} 不存在"
return f"商品 {product_id} 当前库存剩余:{count} 件"
@tool
def calculate_discount(original_price: float, discount_rate: float) -> str:
"""计算商品打折后的实际应付金额。discount_rate 取值区间为 (0, 1]。"""
if not (0 < discount_rate <= 1):
return "折扣率参数非法,必须在 0 到 1 之间。"
return f"原价 {original_price} 元,折后结算价:{round(original_price * discount_rate, 2)} 元"
2. 调度与应用主入口 (app.py)
Python
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from business_tools import get_order_status, get_product_stock, calculate_discount
load_dotenv()
# 初始化 LLM 模型
llm = ChatOpenAI(
model="deepseek-chat",
openai_api_key=os.getenv("DEEPSEEK_API_KEY"),
openai_api_base=os.getenv("DEEPSEEK_BASE_URL"),
temperature=0.0
)
# 构建业务 Agent
agent_tools = [get_order_status, get_product_stock, calculate_discount]
agent_executor = create_agent(
model=llm,
tools=agent_tools,
system_prompt="你是企业业务办理助手。请使用对应工具精准查询,不要主观臆测数据。"
)
def handle_query(query: str):
"""
分流调度中心:根据提问特征分流至 RAG 或 Agent
(生产中可升级为基于 LLM 的 IntentClassifier 语义路由)
"""
rag_keywords = ["补卡", "流程", "规则", "制度", "退款多久", "发票"]
# 命中知识库规则时走 RAG 检索
if any(kw in query for kw in rag_keywords):
print("[Router] 识别为制度咨询,分流至 -> RAG 知识库问答")
# 此处调用已封装的 RAGService.query(query)
return "【知识库返回】根据员工考勤管理办法:每月最多可补卡 3 次。"
# 默认分流至业务 Agent 处理
print("[Router] 识别为动态业务请求,分流至 -> Agent 动态工具链")
result = agent_executor.invoke(
{"messages": [("user", query)]},
config={"run_name": "Customer_Agent_Dispatch"}
)
return result["messages"][-1].content
if __name__ == "__main__":
print(handle_query("请问每个月最多可以补卡几次?"))
print("-" * 50)
print(handle_query("帮我查一下订单 A1001 发货了没有?"))
五、LLM 应用生产上线前自检清单(Pre-flight Checklist)
在将应用部署到生产环境之前,务必对照此清单逐项核验:
[安全与鉴权]
[ ] API Key 与敏感凭证全部放入 .env,且 .env 已加入 .gitignore
[ ] 生产环境对用户输入与输出进行了 PII(个人敏感信息)脱敏检测
[ ] 限制了 Tool 的执行权限,高危写操作(如退款、删除)已加入人工确认机制
[链路与稳定性]
[ ] LangSmith 开启了生产独立 Project,配置了合理的采样率控制
[ ] 针对单次请求配置了超时机制(Timeout)与重试退避策略
[ ] 对超长对话配置了 Summarization 中间件,避免 Token 溢出与成本失控
[Prompt & RAG 质量]
[ ] System Prompt 明确限制了“资料不足时直接说明不知道,严禁编造”
[ ] 向量知识库索引已建立自动增量更新机制,并验证了 Chunk 分割边界
[ ] 固化了包含边界 Case 的基准测试集,回归测试通过率达标
六、全系列学习路径总结
回顾整个技术演进脉络,构建企业级智能应用的核心技能链路如下:
1. 基础调用与结构化输出 (ChatModel -> PromptTemplate -> PydanticOutputParser)
│
2. 管道化链式编排 (LCEL: 组件解耦与流水线组装)
│
3. 外部知识接入 (DocumentLoader -> TextSplitter -> Embedding -> VectorStore -> RAG)
│
4. 自主规划与动态执行 (Tool 封装 -> Harness 调度 -> ReAct Agent)
│
5. 生产治理与可观测性 (中间件拦截 -> LangSmith 链路追踪 -> 自动化 Benchmark 评估)
更多推荐



所有评论(0)