在这里插入图片描述

为什么你的LangChain应用总是"薛定谔的能跑"?因为你在用瞎子摸象的方式调试LLM! 本文将手把手教你用LangSmith给LCEL装上"透视眼",从黑盒调试迈向全链路追踪,让每个Prompt、每次Token消耗、每个中间变量都无所遁形。读完这篇文章,你将掌握企业级LLM应用的可观测性实战方案,从此告别"本地能跑线上崩"的尴尬局面。

LangSmith集成实战:
追踪调试你的应用

1. 为什么LCEL
需要LangSmith

2. 环境配置:
避开那些坑

3. 基础追踪:
可视化链式调用

4. Prompt调试:
告别黑盒开发

5. 性能监控:
LLM体检报告

6. 回归测试:
数据集守护Prompt

7. 生产环境
最佳实践

文章目录:

  1. 为什么LCEL需要LangSmith:从盲人摸象到透视一切
  2. 环境配置:避开API Key和项目初始化的那些坑
  3. 基础追踪:让你的链式调用"可视化"
  4. Prompt调试:不再做"黑盒程序员"
  5. 性能监控:LLM应用的"体检报告"
  6. 回归测试:用数据集守护你的Prompt版本
  7. 生产环境最佳实践:从玩具到企业级

嗨,大家好呀,我是你的老朋友精通代码大仙。接下来我们一起学习 《LangChain核心技术与LLM项目实践》,震撼你的学习轨迹!


“本地能跑,线上就崩”,这应该是每个程序员深夜改bug时最想砸键盘的瞬间。学LangChain的时候,你是不是也这样:本地跑个问答机器人挺顺畅,一部署到生产环境,用户反馈"答非所问"、“响应慢得像蜗牛”,而你对着满屏的print日志一脸懵逼,根本不知道哪一步出了问题?

更扎心的是,LLM应用跟你以前写的CRUD完全不一样。传统的API调用,输入输出明确,错了就是404或者500。但大模型呢?同样的Prompt,GPT-4和GPT-3.5输出可能天差地别; temperature调高一点,回答就从"严谨教授"变成"胡言乱语患者"。你用LCEL(LangChain Expression Language)写了一长串-processing chain,中间某个Runnable出了问题,你根本不知道是哪一环在"作妖"。

这种无力感,就像你蒙着眼走迷宫,还指望能一口气走到终点。但好在,LangSmith就是给我们准备的"夜视仪"和"GPS"。这节课,咱们不玩虚的,直接上手把LangSmith焊进你的LCEL项目里,让你从此做LLM应用的"透视者"。

1. 为什么LCEL需要LangSmith:从盲人摸象到透视一切

点题:

LangSmith既不是简单的日志库,也不是普通的调试工具。它是LangChain官方推出的全生命周期LLM应用开发平台,核心能力就是可观测性(Observability)。当你的应用用LCEL写成管道式的处理流(Retriever → Prompt → LLM → Parser),LangSmith能把这个管道完全透明化,让你看到每一步的输入输出、Token消耗、延迟耗时,甚至Prompt的精确版本。

用个比喻,LCEL是你的高速公路,车(数据)在上面飞驰,而LangSmith就是沿途的监控摄像头+ETC计费系统+路况显示屏,三合一。

痛点分析:

新手最容易犯的错,就是以为print()大法能搞定LLM调试。比如下面这段典型的"作死"代码:

from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

chain = (
    RunnableParallel({
        "context": retriever, 
        "question": RunnablePassthrough()
    })
    | prompt
    | ChatOpenAI()
    | parser
)

result = chain.invoke("什么是RAG?")
print(f"结果:{result}")  # 就打印个最终结果?

痛点一:中间态黑盒。如果最终输出错了,是Retriever搜错了文档,还是Prompt模板填充有问题,或者是LLM理解错了?你完全不知道。

痛点二:性能盲区。用户说"好慢啊",你猜是网络问题还是模型问题?没有数据支撑,优化就是瞎折腾。

痛点三:Prompt版本混乱。你改了Prompt上线,发现效果变差了,想回滚,却发现根本不知道上一版Prompt长啥样。

解决方案/正确做法:

接入LangSmith只需要三步,但这一步能让你后期的调试效率提升十倍:

import os
from langchain_core.callbacks.manager import tracing_v2_enabled

# 1. 配置环境变量(后续详细讲)
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-api-key"
os.environ["LANGCHAIN_PROJECT"] = "rag-debug-project"

# 2. 运行追踪(上下文管理器方式)
with tracing_v2_enabled(project_name="rag-debug-project"):
    result = chain.invoke("什么是RAG?")
    
# 3. 或者直接在链上配置(推荐)
chain_with_tracing = chain.with_config({"run_name": "my_rag_chain"})

这样做的好处是,每一次调用都会在LangSmith的后台生成一个完整的Trace(追踪记录)。你可以像看瀑布流一样,看到:

  • Retriever返回了哪些文档(带相似度分数)
  • 最终送到LLM的Prompt全文(填充后的真实内容)
  • LLM的原始输出(不是解析后的)
  • 每一步的执行时间(毫秒级)

小结:
别再当"print调试工程师"了,LLM应用的复杂度远超传统软件。LangSmith不是可选项,而是生产环境的必需品。早接入,早解脱。

2. 环境配置:避开API Key和项目初始化的那些坑

点题:

万事开头难,LangSmith的初始化配置虽然只要几行代码,但新手在这里踩的坑能绕地球三圈。从API Key的获取到项目的隔离,从环境变量的管理到多环境中的切换,这一步做不对,后面全白搭。

痛点分析:

坑一:Key到处飞,安全没保障。我见过太多同学直接把LANGCHAIN_API_KEY硬编码在代码里,然后不小心commit到GitHub,第二天收到邮件:“您的API Key已泄露,已被禁用”。

坑二:项目混用,数据污染。测试环境的调用跑到生产项目的Dashboard里,调试记录和真实用户请求混在一起,分析起来像在大海捞针。

坑三:Tracing开关忘记关。本地开发时开了追踪,跑单元测试时也在往LangSmith发数据,结果免费额度瞬间用完(LangSmith有免费Tier,但有次数限制)。

坑四:代理和网络问题。有些公司网络有代理,直接调用LangSmith API timeout,报错信息还不明显,新手以为是代码问题,折腾半天。

解决方案/正确做法:

正确的环境配置应该像洋葱一样,分层管理:

# config.py - 配置分离,永不硬编码
import os
from dotenv import load_dotenv

load_dotenv()

class Settings:
    # LangSmith配置
    LANGCHAIN_TRACING_V2 = os.getenv("LANGCHAIN_TRACING_V2", "false").lower() == "true"
    LANGCHAIN_API_KEY = os.getenv("LANGCHAIN_API_KEY")
    LANGCHAIN_ENDPOINT = os.getenv("LANGCHAIN_ENDPOINT", "https://api.smith.langchain.com")
    LANGCHAIN_PROJECT = os.getenv("LANGCHAIN_PROJECT", "default-project")
    
    # 环境区分
    ENV = os.getenv("APP_ENV", "development")

settings = Settings()

# 使用示例
if settings.LANGCHAIN_TRACING_V2:
    print(f"✅ 已开启LangSmith追踪,项目:{settings.LANGCHAIN_PROJECT}")
else:
    print("⚠️ 追踪已关闭,生产环境请开启")

.env文件示例(记得加进.gitignore!):

# 开发环境
APP_ENV=development
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls-xxx...
LANGCHAIN_PROJECT=rag-dev-v2

# 生产环境另外配置,或用不同的.env.production

进阶技巧:条件追踪

不要用"全或无"的方式控制追踪,要学会按条件开启:

from langchain_core.runnables import RunnableConfig

def get_chain_with_tracing(chain, user_query: str, enable_trace: bool = True):
    """只在需要时开启追踪,比如只追踪10%的生产流量"""
    config = RunnableConfig(
        run_name="qa_chain",
        tags=["production", "v1.2.0"],
        metadata={
            "user_id": "user_123",
            "feature_flag": "new_prompt"
        }
    )
    
    if enable_trace and settings.LANGCHAIN_TRACING_V2:
        return chain.with_config(config)
    return chain

# 采样追踪:每10个请求追踪1个
import random
should_trace = random.random() < 0.1
result = get_chain_with_tracing(chain, query, should_trace).invoke(query)

这样做的好处:

  1. 安全:Key在环境变量,代码干净
  2. 隔离:开发/测试/生产完全分开
  3. 成本控制:采样追踪,既保留观测性,又不烧钱

小结:
配置是地基,地基歪了楼就塌。记住口诀:Key进环境变量,项目要隔离,追踪可采样,代理要配置。

3. 基础追踪:让你的链式调用"可视化"

点题:

现在进入实战核心。我们要让LCEL的每一个Runnable都"发光",让你能在浏览器里像看监控大屏一样观察数据流。LangSmith的Trace由Run组成,每个Run对应一个可执行单元,形成树状结构。

痛点分析:

痛点:链条太长,找不到北。比如一个复杂的RAG应用:

用户Query

Query重写

向量检索

重排序

Prompt构建

LLM生成

后处理

如果最终答案错了,是B重写坏了Query,还是C检索出了无关文档?没有可视化追踪,你得在每个节点加print,代码瞬间变成"打印地狱":

# 错误示范:打印地狱
def rewrite_query(q):
    new_q = llm.invoke(f"改写:{q}")
    print(f"改写后:{new_q}")  # 恶心!
    return new_q

def retrieve_docs(q):
    docs = vectorstore.similarity_search(q)
    print(f"检索到:{len(docs)}篇")  # 更恶心!
    return docs

代码里充斥着print,既难看又影响性能,最重要的是,生产环境你根本看不到print输出(除非看日志,但日志是文本,不是结构化数据)。

解决方案/正确做法:

使用@traceable装饰器或LCEL原生集成(LangChain 0.1.x后自动集成):

from langsmith import traceable
from langchain_core.runnables import RunnableLambda

# 方式一:装饰器(适合自定义函数)
@traceable(run_type="chain", name="query_rewriter")
def rewrite_query(query: str) -> str:
    """查询重写模块"""
    prompt = f"优化以下搜索查询:{query}"
    response = llm.invoke(prompt)
    return response.content

# 方式二:LCEL原生(推荐)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

template = """基于以下上下文回答问题:
{context}

问题:{question}
"""

prompt = ChatPromptTemplate.from_template(template)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 自动追踪,无需额外代码
rag_chain = (
    {"context": retriever | (lambda docs: "\n\n".join(doc.page_content for doc in docs)), 
     "question": RunnablePassthrough()}
    | prompt
    | ChatOpenAI(model="gpt-4")
    | StrOutputParser()
)

# 运行后,去LangSmith后台看Trace树
response = rag_chain.invoke("什么是量子计算?")

在LangSmith的Web界面,你会看到:

Run: RunnableSequence (总耗时2.3s)
├── Run: VectorStoreRetriever (800ms)
│   └── 输出:3个Document对象(内容可见)
├── Run: ChatPromptTemplate (5ms)
│   └── 输出:填充后的完整Prompt(可查看)
├── Run: ChatOpenAI (1.4s)
│   ├── 输入:Messages(完整可见)
│   ├── 输出:AIMessage
│   └── Token使用:Input 450 / Output 120
└── Run: StrOutputParser (1ms)

实战技巧:自定义Metadata

给追踪添加标签,方便后续筛选:

config = {
    "tags": ["rag", "v2.0", "customer_support"],
    "metadata": {
        "user_id": "customer_456",
        "session_id": "sess_789",
        "feature_flag": "hybrid_search"
    }
}

result = rag_chain.invoke(query, config=config)

这样你可以在LangSmith里按标签过滤,只看"customer_support"相关的调用,或者对比"v1.0"和"v2.0"的性能差异。

小结:
可视化不是奢侈品,是LLM开发的必需品。关掉print,打开Trace,让数据流动看得见、摸得着。

4. Prompt调试:不再做"黑盒程序员"

点题:

Prompt是LLM应用的"源代码",但很多人管理Prompt就像管理一团乱麻。LangSmith的Prompt Hub和Playground功能,让你能像Git管理代码一样管理Prompt,还能在浏览器里实时调试。

痛点分析:

痛点一:Prompt版本灾难。你在代码里改了Prompt,上线后发现效果变差,想回滚?不好意思,Git里只记录了代码变更,Prompt的具体文本早就随着部署被覆盖了。

痛点二:变量注入错误。LCEL的PromptTemplate经常需要注入变量,比如:

template = "基于{context}回答问题:{question}"

如果context里包含了特殊字符(比如大括号、引号),导致Prompt格式错乱,LLM输出混乱,你很难在生产环境重现当时的具体Prompt长啥样。

痛点三:无法A/B测试。你想试试不同的Prompt写法,但不知道哪个效果更好,只能凭感觉,没有数据支撑。

解决方案/正确做法:

第一步:Prompt版本化管理

把Prompt从代码里抽离,使用LangSmith Prompt Hub:

from langsmith import Client

client = Client()

# 将当前Prompt推送到Hub(版本化管理)
prompt = ChatPromptTemplate.from_template("""你是一个专业的客服助手。

用户问题:{question}
相关文档:{context}

请用友好的语气回答,如果不知道就说不知道。""")

# 保存到Hub,获得一个可以引用的commit hash
prompt_url = client.push_prompt("my-project/customer-service-v1", 
                                  object=prompt)
print(f"Prompt已保存:{prompt_url}")

在代码里引用Hub中的Prompt:

from langchain import hub

# 总是拉取最新版本,或用特定版本
prompt = hub.pull("my-project/customer-service-v1:latest")
# 或固定版本,确保可复现
prompt = hub.pull("my-project/customer-service-v1:abc123")

第二步:查看实际渲染后的Prompt

在LangSmith的Trace详情页,点击Prompt节点,你能看到最终送到LLM的完整文本,包括所有变量填充后的样子。如果发现输出了"我不知道"(明明文档里有答案),一看Prompt,发现context变量是空的!瞬间定位问题。

第三步:Playground调试

在LangSmith网页端,你可以拿历史Trace中的输入,直接在Playground里修改Prompt,换不同的模型(比如从GPT-3.5换成Claude),实时查看输出差异,无需修改代码。

# 在Trace中标记某个Run为"需要调试"
from langsmith.run_trees import RunTree

run = RunTree(
    name="debug_run",
    run_type="chain",
    inputs={"question": "如何退款?"}
)

进阶:Prompt对比实验

# 同时测试两个Prompt版本
prompt_v1 = hub.pull("my-project/prompt:v1")
prompt_v2 = hub.pull("my-project/prompt:v2")

chain_v1 = prompt_v1 | llm | parser
chain_v2 = prompt_v2 | llm | parser

# 用同样的测试集跑两边,在LangSmith比较结果
test_queries = ["如何退款?", "怎么开发票?", "客服电话是多少?"]

for q in test_queries:
    r1 = chain_v1.invoke(q)
    r2 = chain_v2.invoke(q)
    # LangSmith会自动记录两次调用的差异

这样做的好处:

  • 可复现:每个Prompt版本都有hash,永远能回到过去的状态
  • 可调试:看到实际渲染的Prompt,不再猜变量注入结果
  • 可优化:Playground里快速迭代,找到最佳Prompt

小结:
Prompt不是字符串,是资产。用管理代码的方式管理Prompt,用调试前端的方式调试Prompt,这才是专业LLM工程师的素养。

5. 性能监控:LLM应用的"体检报告"

点题:

LLM调用是昂贵的,无论是时间成本还是金钱成本。LangSmith提供详细的性能指标:Token使用量、首Token延迟(TTFT)、总耗时、成本估算。这些数据是优化应用的金矿。

痛点分析:

痛点一:成本失控。你接入了GPT-4,用户疯狂使用,月底收到账单吓傻了,但根本不知道哪一步最烧钱。

痛点二:延迟黑洞。用户抱怨"卡顿",你知道是LLM慢,但不知道慢在哪个环节。是检索慢?还是生成慢?还是Prompt太长导致输入处理慢?

痛点三:无法量化优化效果。你把检索从k=10改成k=3,感觉快了点,但不确定;你换了更小的模型,感觉 cost 降了,但用户体验是否变差了?没有数据,优化就是盲人摸象。

解决方案/正确做法:

成本监控实战

在LangSmith的Dashboard里,你可以看到:

  • Total Tokens:总体消耗
  • Prompt Tokens vs Completion Tokens:输入输出比例(如果输入远大于输出,说明你在浪费钱传输无关上下文)
  • Cost per Run:单次调用成本

代码里获取精确指标:

from langchain.callbacks.manager import tracing_v2_enabled

with tracing_v2_enabled() as cb:
    result = chain.invoke("复杂的问题...")
    # 运行完成后,LangSmith后台会记录详细指标
    
# 如果你需要在代码里实时获取(用于动态限流)
from langchain.callbacks import get_callback_manager

callback = get_callback_manager().get_callback("langsmith")
# 注意:实际API可能变化,以最新文档为准

性能优化实战案例

假设你发现某个Trace总耗时5秒,LangSmith显示:

  • Retriever:3.5秒(瓶颈!)
  • LLM:1.2秒
  • 其他:0.3秒

优化方案:

  1. 检索优化:把向量数据库从CPU版换成GPU加速版,或加缓存
  2. Prompt压缩:如果Prompt Tokens太多(比如塞了10篇长文档),用Map-Reduce策略,或者先用小模型做摘要
# 优化前:直接塞全文
def old_retriever(q):
    return vectorstore.similarity_search(q, k=10)  # 10篇长文,Token爆炸

# 优化后:先摘要再填充
@traceable(name="smart_retriever")
def smart_retriever(q):
    docs = vectorstore.similarity_search(q, k=5)
    # 用小模型快速摘要,减少Token
    summaries = [summarize_chain.invoke(doc.page_content) for doc in docs]
    return summaries

设置监控告警(结合LangSmith API):

# 定期检查高成本调用
from langsmith import Client

client = Client()
runs = client.list_runs(
    project_name="production",
    filter='gt(total_tokens, 4000)',  # 找出Token使用超过4k的调用
    start_time=datetime.now() - timedelta(days=1)
)

# 分析这些"胖调用",优化Prompt设计
for run in runs:
    print(f"高成本调用:{run.name}, Tokens: {run.total_tokens}")
    # 发送告警到钉钉/Slack

A/B测试性能

# 测试两个模型的成本/质量平衡
gpt4_chain = prompt | ChatOpenAI(model="gpt-4") | parser
gpt35_chain = prompt | ChatOpenAI(model="gpt-3.5-turbo") | parser

# 同样的问题,对比两者的Token消耗和延迟
# LangSmith会自动生成对比报告

这样做的好处:

  • 省钱:一眼看出哪里在浪费Token,针对性压缩Prompt
  • 提速:精确找到性能瓶颈,不是盲目优化
  • 可控:设置Token上限,防止用户输入超长文本导致账单爆炸

小结:
不监控成本的LLM应用,就像不开油表开跑车,迟早要抛锚。用数据驱动优化,让每一分钱都花在刀刃上。

6. 回归测试:用数据集守护你的Prompt版本

点题:

LLM应用最大的噩梦就是"负优化"——你改了个Prompt想提升效果,结果把原本好的case搞砸了。LangSmith的Dataset和Evaluation功能,让你能像单元测试一样测试你的Chain,确保每次改动都是正向优化。

痛点分析:

痛点一:** regressions(回归错误)**。你加了个新功能(比如支持多轮对话),结果导致单轮问答质量下降。上线后才发现,用户投诉不断。

痛点二:主观评估。你看了10个例子,觉得新Prompt更好,但样本量太小,上线后发现有20%的case变差了,只是你碰巧没测试到。

痛点三:无法自动化。每次改Prompt都手动测十几个问题,累死人,还容易遗漏。

解决方案/正确做法:

构建测试数据集

在LangSmith中创建Dataset,包含输入和期望输出(或评估标准):

from langsmith import Client
from langsmith.schemas import Example

client = Client()

# 创建数据集
dataset = client.create_dataset(
    dataset_name="qa_test_set",
    description="客服场景关键问题测试集"
)

# 添加测试用例(包括边界情况)
examples = [
    Example(
        inputs={"question": "如何退款?"},
        outputs={"answer": "您可以在订单页面点击申请退款..."},  # 期望输出
        metadata={"category": "售后", "difficulty": "easy"}
    ),
    Example(
        inputs={"question": "你们CEO是谁?"},
        outputs={"answer": "我不知道"},  # 期望拒绝回答
        metadata={"category": "隐私", "difficulty": "hard"}
    ),
    Example(
        inputs={"question": "用Python写个冒泡排序"},  # 边界:无关问题
        outputs={"answer": "我只处理客服问题..."},
        metadata={"category": "边界", "difficulty": "medium"}
    )
]

client.create_examples(
    dataset_id=dataset.id,
    examples=examples
)

自动化评估

定义评估函数,自动对比输出与期望:

from langsmith.evaluation import evaluate, LangChainStringEvaluator

# 方式一:用LLM作为评委(LLM-as-a-judge)
qa_evaluator = LangChainStringEvaluator(
    "qa",
    config={"llm": ChatOpenAI(model="gpt-4", temperature=0)}
)

# 方式二:自定义评估逻辑
def correctness_evaluator(run, example):
    """检查是否包含关键信息点"""
    prediction = run.outputs["output"]
    expected = example.outputs["answer"]
    
    # 简单包含检查,实际可用更复杂的语义相似度
    score = 1.0 if expected in prediction else 0.0
    return {
        "key": "correctness",
        "score": score,
        "comment": f"Expected: {expected}, Got: {prediction[:100]}..."
    }

# 运行评估实验
results = evaluate(
    rag_chain.invoke,  # 你的链
    data="qa_test_set",  # 数据集名称
    evaluators=[qa_evaluator, correctness_evaluator],
    experiment_prefix="prompt-v2-test"  # 实验名称
)

对比实验(A/B Test)

运行两个版本的Prompt,LangSmith会自动对比指标:

实验A (Baseline): 
- 平均Correctness: 0.85
- 平均Latency: 2.1s
- 失败Case: 退款流程说明不完整

实验B (New Prompt):
- 平均Correctness: 0.92 (↑)
- 平均Latency: 2.3s (↑ 可接受)
- 失败Case: 隐私问题回答过于详细

基于数据决策:虽然B慢一点,但准确率高,而且隐私问题那个case可以单独修复。

CI/CD集成

在GitHub Actions里跑评估:

# .github/workflows/eval.yml
name: LLM Eval
on: [pull_request]

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run LangSmith Eval
        run: |
          python -m pytest tests/test_prompts.py
          python scripts/evaluate_chain.py  # 调用上面的evaluate代码
        env:
          LANGCHAIN_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}

回归测试实战技巧

  1. 黄金数据集:保留20个最难的case,每次改Prompt必须全过
  2. 对抗测试:专门准备"攻击性"输入(Prompt Injection、超长输入、乱码),确保系统不崩溃
  3. 影子模式:新Prompt上线前,先在Shadow Mode跑(处理真实请求但不返回给用户),在LangSmith对比新旧版本结果

这样做的好处:

  • 防回退:确保新代码不会破坏旧功能
  • 可量化:用分数说话,不用"我觉得"
  • 自动化:解放双手,专注开发

小结:
没有测试的Prompt修改是赌博。用数据集构建安全网,让每次上线都心里有底。

7. 生产环境最佳实践:从玩具到企业级

点题:

最后,咱们聊聊怎么把LangSmith用到真实的企业环境。包括多租户隔离、隐私合规、成本控制、与现有监控体系的集成。这一步决定了你的LLM应用是"Demo"还是"产品"。

痛点分析:

痛点一:数据隐私。用户输入可能包含手机号、地址等PII(个人身份信息),直接发送到LangSmith云端可能违反GDPR或公司政策。

痛点二:规模问题。每天百万级调用,LangSmith的存储和查询会不会成为瓶颈?费用会不会爆炸?

痛点三:信息孤岛。LangSmith的数据如何与现有的APM(如Datadog、New Relic)打通?团队已经习惯看Grafana大盘了。

解决方案/正确做法:

隐私保护:数据脱敏

import re
from langchain_core.runnables import RunnableLambda

def mask_pii(text: str) -> str:
    """脱敏处理"""
    # 手机号
    text = re.sub(r'\d{3}-\d{4}-\d{4}', '[PHONE]', text)
    # 邮箱
    text = re.sub(r'\S+@\S+\.\S+', '[EMAIL]', text)
    return text

# 在送进LangSmith前脱敏
sanitized_chain = (
    RunnablePassthrough() | 
    RunnableLambda(lambda x: {**x, "question": mask_pii(x["question"])})
    | rag_chain
)

# 或者使用LangSmith的隐藏输入功能(更推荐)
config = {
    "metadata": {"sensitive": True},
    # 某些版本支持在配置中标记不记录输入
}

分级追踪策略

不是所有调用都需要详细追踪,分级处理:

def should_trace(query: str, user_tier: str) -> bool:
    """根据策略决定是否追踪"""
    # 1. 付费用户全量追踪(服务好金主)
    if user_tier == "premium":
        return True
    # 2. 包含错误关键词的追踪(用于发现bug)
    if any(word in query for word in ["error", "bug", "坏了"]):
        return True
    # 3. 采样:普通用户只追踪5%
    return random.random() < 0.05

# 动态开关
if should_trace(query, user.tier):
    result = chain.with_config({"run_name": "production"}).invoke(query)
else:
    # 不追踪,节省额度
    result = chain.invoke(query)

自建追踪后端(可选)

如果对数据隐私极度敏感,LangSmith支持私有化部署(Enterprise版),或者你可以用开源替代方案(如Langfuse、Phoenix)作为后端,但保持LangChain的追踪接口不变。

集成现有监控体系

把LangSmith数据导出到你的监控平台:

# 定期同步关键指标到Prometheus/Grafana
from prometheus_client import Counter, Histogram

llm_calls = Counter('llm_calls_total', 'Total LLM calls')
llm_latency = Histogram('llm_latency_seconds', 'LLM latency')

# 在Callback中上报
class MyCallbackHandler(BaseCallbackHandler):
    def on_llm_end(self, response, **kwargs):
        llm_calls.inc()
        # 同时LangSmith也记录一份,双保险

错误追踪与告警

from langsmith.run_trees import RunTree

try:
    result = chain.invoke(query)
except Exception as e:
    # 记录失败Run
    run = RunTree(
        name="failed_run",
        run_type="chain",
        error=str(e),
        inputs={"query": query}
    )
    run.end()
    # 发送告警
    send_alert(f"Chain failed: {e}")
    raise

团队协作规范

  1. 命名规范project_name{team}-{env}-{app}格式,如nlp-prod-customer-bot
  2. Tag规范:必须加versionenvmodel标签,方便筛选
  3. Review机制:像Review代码一样Review Trace,特别是失败案例

这样做的好处:

  • 合规:满足企业数据安全要求
  • 可持续:成本控制,避免追踪费用超过LLM调用费用
  • 融合:不打破现有DevOps体系,平滑接入

小结:
生产环境无小事。隐私、成本、监控、协作,四手抓四手都要硬。LangSmith不是银弹,但用对了就是瑞士军刀。


写在最后

编程之路不易,但每一步成长都算数。回想我们刚开始学LangChain的时候,是不是觉得LCEL的管道语法很酷,但一旦出错就抓瞎?今天咱们给这套系统装上了LangSmith这个"透视镜",从配置到追踪,从调试到测试,从开发到生产,构建了一整套LLM应用的可观测性体系。

记住,写LLM应用和写传统软件最大的区别就是不确定性。你无法像控制数据库查询一样精确控制大模型的输出,但你可以控制的是观察它、理解它、测试它的能力。LangSmith给你的就是这种掌控感。

保持好奇,持续学习,你也能成为LLM应用开发的高手。无论你现在是在为Debug而头秃,还是在为优化Prompt而失眠,都是成长的必经之路。利用好工具,相信数据,保持对技术的敬畏和热爱,你写的每一行代码,都会在未来给你回报。

加油,咱们下节课见!


获取更多学习资料请加威信:caogenzhishi 推荐朋友B占UP:个人快速成长/精进学习资料
+微辛备注:“资料代找获取”,全网计算机学习资料代找:例如:
《课程:2026 年多模态大模型实战训练营》
《课程:AI 大模型工程师系统课程 (22 章完整版 持续更新)》
《课程:AI 大模型系统实战课第四期 (2026 年开课 持续更新)》
《课程:2026 年 AGI 大模型系统课 23 期》
《课程:2026 年 AGI 大模型系统课 21 期》
《课程:AI 大模型实战课 8 期 (2026 年 2 月最新完结版)》
《课程:AI 大模型系统实战课三期》
《课程:AI 大模型系统课程 (2026 年 2 月开课 持续更新)》
《课程:AI 大模型全阶课程 (2025 年 12 月开课 2026 年 6 月结课)》
《课程:AI 大模型工程师全阶课程 (2025 年 10 月开课 2026 年 4 月结课)》
《课程:2026 年最新大模型 Agent 开发系统课 (持续更新)》
《课程:LLM 多模态视觉大模型系统课》
《课程:大模型 AI 应用开发企业级项目实战课 (2026 年 1 月开课)》
《课程:大模型智能体线上速成班 V2.0》
《课程:Java+AI 大模型智能应用开发全阶课》
《课程:Python+AI 大模型实战视频教程》
《书籍:软件工程 3.0: 大模型驱动的研发新范式.pdf》
《课程:人工智能大模型系统课 (2026 年 1 月底完结版)》
《课程:AI 大模型零基础到商业实战全栈课第五期》
《课程:Vue3.5+Electron + 大模型跨平台 AI 桌面聊天应用实战 (2025)》
《课程:AI 大模型实战训练营 从入门到实战轻松上手》
《课程:2026 年 AI 大模型 RAG 与 Agent 智能体项目实战开发课》
《课程:大模型训练营配套补充资料》

更多推荐