在这里插入图片描述


一、开篇导读

欢迎来到《LangSmith从入门到精通》专栏的第1节课。作为整个专栏的开篇之作,本节课将为你构建对LangSmith的完整认知框架。

在开始之前,请你思考一个问题:当你开发一个基于大语言模型(LLM)的应用时,是否遇到过以下困扰——模型输出的结果时好时坏,完全不知道原因是什么?链路调用复杂到难以调试,出了问题不知道从哪排查?想优化Prompt却只能凭感觉盲目尝试?多个版本的提示词和模型参数无法有效管理和对比?如果这些问题你至少遇到过1-2个,那么LangSmith正是你需要的工具。

本节课将从零开始,用最通俗的语言为你讲解LangSmith是什么、它能解决什么问题、它的底层工作原理是什么,以及它在LLMOps体系中的核心价值。我们会通过一个完整的入门案例,让你亲身体验LangSmith带来的链路追踪能力。

本节课你将收获:

  • 理解LLMOps的核心概念及其与大模型应用的关系
  • 掌握LangSmith的定位、核心功能和技术架构
  • 理解Trace、Run、Project等核心概念的本质
  • 完成LangSmith账号注册和环境配置
  • 运行第一个带LangSmith追踪的完整项目
  • 学会在LangSmith面板查看和分析链路数据
  • 了解LangSmith在企业级场景下的最佳实践

二、知识前置铺垫

在正式接触LangSmith之前,我们需要先理解几个前置概念。这些概念是理解LangSmith价值的基础。

2.1 什么是大模型应用开发中的“可观测性”

可观测性(Observability) 这个词原本来自云计算和分布式系统领域。简单来说,就是一个系统的内部状态能够通过外部输出被准确推断的程度。

举个生活化的例子:你开车时,仪表盘上的速度表、油量表、发动机故障灯就是汽车的可观测性体现。如果没有这些仪表,你只能凭感觉猜测车况,那就处于“不可观测”的状态。

在大模型应用开发中,可观测性意味着:

  • 你能看到每一次模型调用传入的Prompt是什么
  • 你能知道模型返回的具体内容是什么
  • 你能追踪整个调用链路(从用户输入到最终输出经过了哪些环节)
  • 你能量化每次调用的耗时、Token消耗、成本等指标
  • 你能对比不同版本Prompt的效果差异

没有可观测性,大模型应用就像一个黑盒,你往里扔问题,它往外吐答案,中间发生了什么完全未知。这在开发阶段或许还能忍受,但一旦进入生产环境,面对成百上千的用户请求,没有可观测性将导致你无法定位问题、无法优化性能、无法控制成本。

2.2 LLMOps的提出背景

LLMOps(Large Language Model Operations) 是近年来随着大模型爆发而兴起的一个新领域。它借鉴了传统机器学习中的MLOps理念,但针对大语言模型的特性做了专门调整。

传统的MLOps专注于管理机器学习模型的训练、部署、监控全生命周期。但大模型应用有完全不同的特点:

特点一:Prompt即代码
在传统机器学习中,模型训练完成后参数就固定了。但在大模型应用中,你不需要训练模型(除非做微调),你写的是Prompt。Prompt的质量直接决定应用效果,而Prompt的编写和优化过程充满不确定性。

特点二:输出非确定性
传统程序,输入1+1,输出永远是2。但大模型每次调用结果都可能不同(即使temperature=0也不能保证100%确定)。这意味着你的测试用例今天通过明天可能就不通过。

特点三:链路复杂性
一个真实的大模型应用通常包含多个组件:

  • 用户输入预处理
  • Prompt模板渲染
  • 大模型调用
  • 输出后处理
  • 外部工具调用(如搜索引擎、数据库、API)
  • RAG检索增强
  • Agent的思考循环

这些组件串联起来形成复杂链路,任何一个环节出问题都会影响最终输出。

特点四:评估困难
传统程序判断输出是否正确很简单(断言是否相等)。但大模型的输出是自然语言,“正确答案”可以有无数种表达方式。如何自动化评估模型输出质量是个巨大挑战。

LLMOps正是为了解决这些问题而诞生的。它提供了一整套方法论和工具链,帮助开发者构建可靠、可观测、可优化的大模型应用。

2.3 LangChain生态中的定位

LangChain 是目前最流行的LLM应用开发框架。它提供了标准化的组件抽象(如ChatModel、PromptTemplate、OutputParser等)和丰富的内置工具(如文档加载器、向量数据库集成、Agent框架等)。

但LangChain本身只解决了“如何构建”的问题,没有解决“如何调试、如何评估、如何监控”的问题。这就好比给你一套乐高积木,你可以搭出各种造型,但你不知道哪块积木松了、哪个接口接触不良。

LangSmith 正是LangChain官方推出的配套可观测性平台。它的定位是:

  • 为LangChain应用提供开箱即用的链路追踪能力
  • 提供可视化的调试和分析界面
  • 内置数据集管理和模型评估功能
  • 支持生产环境监控和告警

简单来说,LangChain负责“造车”,LangSmith负责给车上装仪表盘、行车记录仪和诊断系统。

三、核心概念精讲

现在进入正题,我们逐层拆解LangSmith的核心概念。

3.1 LangSmith是什么

官方定义:LangSmith是一个用于构建、调试、评估和监控基于LLM的应用程序的平台。

我的理解:LangSmith就是大模型应用的“鹰眼系统”——它能让你看清应用内部发生的每一件事,记录下每一次调用的完整上下文,并提供工具帮你分析和优化应用表现。

LangSmith不是本地工具,而是一个SaaS云服务平台。你只需要在代码中集成LangSmith的SDK,所有追踪数据就会被自动上传到LangSmith云端,然后你通过Web界面查看和分析。

核心能力矩阵:

能力维度 具体功能 解决什么问题
链路追踪 自动记录每次调用的输入、输出、耗时、Token消耗 不知道链路中发生了什么
调试分析 可视化展示调用树,支持筛选、搜索、对比 难以定位问题根源
数据集管理 创建测试集,导入导出数据,版本管理 没有标准化的测试用例
模型评估 运行评估任务,对比不同版本效果 无法量化模型表现
生产监控 实时监控线上请求,设置告警规则 生产环境出问题无法感知

3.2 Trace(追踪)与Run(运行)

这两个概念是LangSmith最核心的抽象,必须彻底理解。

Trace(追踪) 代表一次完整的应用调用。从用户发起请求到系统返回响应的全过程,被记录为一个Trace。一个Trace可以包含多个嵌套的子步骤。

举例:用户问“今天的天气怎么样”,你的应用可能经历了:

  1. 查询天气API(1次调用)
  2. 将天气数据填入Prompt模板
  3. 调用大模型生成回答(1次LLM调用)

整个这个流程就是一个Trace。

Run(运行) 代表Trace中的一个步骤单元。上面的例子包含3个Run:

  • Run1: 查询天气API
  • Run2: Prompt模板渲染
  • Run3: LLM调用

Run之间可以形成父子关系(Run3是Run2的子Run,Run2是Run1的子Run),从而构建出完整的调用树。

Trace与Run的关系

  • 一个Trace包含至少1个Run
  • Run是Trace的组成单元
  • Trace通过唯一的trace_id标识
  • 每个Run有独立的run_id和父子关系

为什么需要区分这两个概念?
因为在实际调试中,你既需要从宏观上看到一次请求的整体表现(耗时、成功/失败),也需要深入到微观细节(某个环节的输入输出是什么)。Trace提供宏观视角,Run提供微观视角。

3.3 Project(项目)

Project 是LangSmith中组织资源的基本单位。你可以理解为“文件夹”或“工作空间”。

每个Project包含:

  • 该Project下的所有Runs(追踪记录)
  • 该Project下的所有Datasets(数据集)
  • 该Project的配置信息(如默认设置)

最佳实践

  • 按应用划分:一个应用对应一个Project
  • 按环境划分:dev、staging、production分别建Project
  • 按版本划分:v1.0、v2.0分别建Project

为什么不把所有Run都放在一个Project里?
因为Project提供了隔离性。如果所有开发、测试、生产的Run混在一起,你会面临:

  • 筛选困难:无法快速找到特定环境的Run
  • 评估混乱:评估任务会混合不同环境的数据
  • 权限管理复杂:无法针对不同团队设置不同权限

3.4 Session(会话)

Session 用于将多个相关的Trace组织在一起。最常见的场景是对话会话

在一个多轮对话中,用户的每次提问都会产生一个独立的Trace(因为每次都是独立的请求-响应)。但这些Trace属于同一个对话上下文。通过给这些Trace打上相同的session_id,你就能在LangSmith中一键查看整个对话的完整历史。

Session的应用场景

  • 多轮对话的上下文追踪
  • 用户行为分析(一个用户的多次访问)
  • A/B测试的流量分组

3.5 Tag(标签)

Tag 是键值对形式的元数据标记。你可以在创建Run时附加任意自定义标签。

典型用法

# 伪代码示例,具体语法后面会讲
with trace("处理用户请求", tags={"env": "production", "user_id": "12345", "version": "v2.0"}):
    # 你的业务逻辑

Tag的价值

  • 筛选过滤:在LangSmith面板中按标签筛选Run
  • 成本分析:按用户、按版本统计Token消耗
  • 质量分析:对比不同版本的错误率、响应时间

四、原理底层剖析

了解了基本概念后,我们需要深入理解LangSmith的底层工作原理。这能帮助你更好地使用它,并在遇到问题时快速定位原因。

4.1 数据采集原理

LangSmith的数据采集基于装饰器模式和上下文管理器。当你在代码中使用LangSmith SDK时,它会在不侵入业务逻辑的前提下,自动拦截和记录关键信息。

核心机制

1. 自动注入回调处理器
LangChain的所有核心组件(如ChatModel、LLMChain、Retriever等)都支持通过回调系统扩展行为。LangSmith SDK会在初始化时注册一个特殊的回调处理器——RunCollector

这个处理器会监听所有组件的:

  • on_llm_start:LLM即将调用前触发
  • on_llm_end:LLM调用完成后触发
  • on_chain_start:Chain开始执行前触发
  • on_chain_end:Chain执行完成后触发
  • on_tool_start/on_tool_end:工具调用的前后钩子

2. 运行时数据捕获
当你的代码执行时,回调处理器会自动记录:

  • 当前Run的输入参数(如Prompt的变量值)
  • 当前Run的输出结果
  • 执行耗时(自动计算start和end的时间差)
  • 错误堆栈(如果发生异常)
  • Token使用量(如果组件提供该信息)
  • 模型参数(temperature、max_tokens等)

3. 异步上报
为了不影响业务代码的性能,LangSmith SDK采用异步队列上报数据:

  • 每个Run的数据先存入本地内存队列
  • 后台线程定期从队列取出数据
  • 通过HTTP POST请求批量发送到LangSmith API
  • 发送失败时自动重试(带指数退避策略)

这种设计的好处

  • 业务代码无感知,不需要手动埋点
  • 即使LangSmith服务短暂不可用,数据也不会丢失(会暂时缓存在内存中)
  • 对业务性能影响极小(异步非阻塞)

4.2 数据模型设计

LangSmith的数据模型围绕前面讲的Trace和Run展开,但内部实现更精细。

Run对象的内部结构(简化版)

{
  "id": "run_abc123",
  "trace_id": "trace_xyz789",
  "parent_run_id": null,
  "name": "ChatOpenAI",
  "run_type": "llm",
  "inputs": {
    "messages": [{"role": "user", "content": "你好"}]
  },
  "outputs": {
    "generations": [[{"text": "你好!有什么可以帮助你的吗?"}]]
  },
  "start_time": "2024-01-15T10:30:00Z",
  "end_time": "2024-01-15T10:30:02Z",
  "latency": 2.0,
  "total_tokens": 50,
  "prompt_tokens": 20,
  "completion_tokens": 30,
  "error": null,
  "tags": ["env:dev"],
  "metadata": {"model": "gpt-3.5-turbo"}
}

关键字段解读

  • run_type:枚举值,包括llm(大模型调用)、chain(链路)、tool(工具调用)、retriever(检索器)、parser(解析器)等。LangSmith会根据类型在前端展示不同的UI组件。

  • parent_run_id:实现嵌套追踪的关键。如果为null,表示这是根Run(即Trace的入口)。如果不为null,则指向父Run的ID,形成树形结构。

  • latency:端到端耗时,单位秒。自动计算end_time - start_time。

  • total_tokens/prompt_tokens/completion_tokens:仅对run_type="llm"有意义。LangSmith会自动解析LLM提供商的响应,提取Token使用量。

  • error:如果Run执行过程中发生异常,这里会记录错误类型、错误消息和堆栈跟踪。

4.3 数据流与存储

在这里插入图片描述

各环节的职责

  1. 用户代码:正常编写业务逻辑,无需关心监控
  2. LangChain组件:执行实际工作(Prompt渲染、LLM调用等),同时触发回调事件
  3. 回调处理器:收集事件数据,组装Run对象
  4. 内存队列:缓冲数据,避免阻塞主线程
  5. 后台线程:消费队列,执行HTTP请求
  6. LangSmith API:接收数据,进行身份验证和数据校验
  7. 云端存储:持久化存储到数据库中(具体存储引擎未公开,推测是PostgreSQL + ClickHouse组合用于时序数据)
  8. Web UI:从存储中查询数据,提供可视化界面

数据保留策略

  • 免费版:7天数据保留期
  • 专业版:30天或更长
  • 企业版:可定制保留期

4.4 与LangChain的集成深度

LangSmith是LangChain官方产品,因此集成深度远超任何第三方监控方案。

深层次集成体现在

1. 自动识别组件类型
LangChain的每个组件都有明确的类型标识。LangSmith的SDK能自动识别你调用的是ChatModel还是LLMChain,从而在前端展示不同的详细信息面板。

2. 序列化支持
LangChain的组件支持序列化和反序列化。LangSmith可以直接存储和还原整个Chain的结构,方便你在UI中查看链路配置。

3. 评估框架集成
LangSmith内置的评估器可以与LangChain的输出解析器无缝配合,自动提取结构化数据进行评估。

4. 调试工具链
LangSmith提供了专门的调试模式,可以暂停执行、单步调试、断点分析,这些功能深度依赖于LangChain的回调系统。

这种深度集成意味着:如果你使用LangChain构建应用,启用LangSmith监控几乎是零成本的——你只需要设置环境变量,不需要改任何代码。

五、环境配置手把手实战

理论讲完了,现在进入实战环节。我们将从零开始完成LangSmith的环境配置。

5.1 注册LangSmith账号

步骤1:访问官网
打开浏览器,访问 https://smith.langchain.com

步骤2:选择注册方式

  • 使用Google账号登录(推荐,最快捷)
  • 使用GitHub账号登录
  • 使用邮箱注册

我推荐使用Google或GitHub账号,可以少记一个密码。

步骤3:完成初始设置
首次登录后,系统会引导你:

  • 阅读并同意服务条款
  • 选择使用目的(个人学习/团队协作/企业应用) —— 这一步不影响功能,随便选
  • 选择默认组织(个人账号会自动创建一个以你用户名命名的组织)

步骤4:进入控制台
完成引导后,你会进入LangSmith的主控制台。此时界面还是空的,因为我们还没有创建任何Project和Run。

5.2 获取API密钥

LangSmith使用API Key进行身份认证。你需要生成一个API Key用于代码中配置。

步骤1:找到API Key管理页面
在控制台左侧菜单栏,点击设置图标(齿轮状),进入Settings页面,然后选择"API Keys"选项卡。

步骤2:生成新Key
点击"Create API Key"按钮:

  • 输入Key的名称(建议:dev-key-2024、prod-key等,便于区分)
  • 选择权限级别(一般选择"All"即可)
  • 点击确认

步骤3:保存Key
系统会生成一个形如 lsv2_pt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 的字符串。请立即复制并保存到安全的地方。LangSmith不会再次显示完整的Key,如果你忘记了,只能删除重建。

安全提醒

  • 不要把API Key提交到Git仓库
  • 不要在代码中硬编码API Key
  • 建议使用环境变量或.env文件管理

5.3 理解免费版限制

在开始使用前,你需要了解免费版的限制,避免超出配额。

免费版提供的资源

  • 每月1000次Trace(追踪记录)
  • 每月1000次评估任务执行
  • 7天数据保留期
  • 最多5个团队成员(仅限查看,不能协作编辑)
  • 基础筛选和搜索功能

超过配额后

  • 超出1000次Trace后,新Trace会被拒绝上报(不会扣费,只是无法记录)
  • 超出7天的数据自动删除,无法恢复
  • 升级到专业版(Pro)可解除限制

对于个人学习和开发测试,免费版完全够用。但如果你是做生产环境监控(每天成千上万次请求),就需要升级到付费版本。

5.4 本地环境配置

现在配置本地开发环境。

创建项目目录

mkdir langsmith-tutorial
cd langsmith-tutorial

创建虚拟环境(推荐):

# Windows
python -m venv venv
venv\Scripts\activate

# macOS/Linux
python3 -m venv venv
source venv/bin/activate

安装依赖包

pip install langchain langchain-openai langsmith

这里解释一下三个包的作用:

  • langchain:核心框架
  • langchain-openai:OpenAI集成(用于调用GPT模型)
  • langsmith:LangSmith Python SDK

配置环境变量
创建.env文件(注意文件开头的点):

# .env 文件内容
LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
LANGCHAIN_API_KEY="你的API Key"  # 替换成刚才复制的Key
LANGCHAIN_PROJECT="quickstart"
OPENAI_API_KEY="你的OpenAI API Key"

环境变量说明

  • LANGCHAIN_TRACING_V2=true:启用v2版本的追踪功能(推荐使用v2)
  • LANGCHAIN_ENDPOINT:LangSmith服务端点,SaaS版固定为这个地址
  • LANGCHAIN_API_KEY:你的身份凭证
  • LANGCHAIN_PROJECT:指定数据发送到哪个Project(不存在会自动创建)
  • OPENAI_API_KEY:调用OpenAI服务需要的Key

加载环境变量
在你的Python代码开头加载.env文件:

from dotenv import load_dotenv
load_dotenv()  # 在import其他库之前调用

或者你也可以在运行Python脚本前手动export环境变量。

六、完整可运行代码案例

现在让我们编写第一个完整的LangSmith追踪项目。

6.1 最简单的追踪示例

这个例子展示了最基础的追踪能力——从字符串模板到LLM调用的完整链路。

# 文件名: basic_tracing.py
# 说明: 第一个LangSmith追踪示例

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# 加载环境变量(必须在其他导入之前)
load_dotenv()

# 验证环境变量是否配置正确
required_vars = ["LANGCHAIN_API_KEY", "OPENAI_API_KEY"]
for var in required_vars:
    if not os.getenv(var):
        raise ValueError(f"环境变量 {var} 未设置,请检查.env文件")

print("✅ 环境变量加载成功")
print(f"📁 LangSmith项目: {os.getenv('LANGCHAIN_PROJECT', 'default')}")

# 创建ChatOpenAI实例
# model: 使用的模型名称
# temperature: 控制随机性(0-1),0表示最确定
llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0.7
)

# 创建Prompt模板
# {topic} 是一个占位符,运行时会被替换
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个乐于助人的AI助手,请用{language}回答问题。"),
    ("human", "{input}")
])

# 创建输出解析器,将模型输出转换为纯字符串
output_parser = StrOutputParser()

# 使用LCEL语法构建Chain
# | 符号表示数据流从左到右传递
chain = prompt | llm | output_parser

# 执行Chain
print("\n🤖 开始调用...")
result = chain.invoke({
    "language": "中文",
    "input": "请介绍一下LangSmith是什么"
})

print(f"\n📝 最终回答:\n{result}")
print("\n✨ 调用完成!请登录LangSmith控制台查看追踪数据。")

6.2 复杂链路示例(含嵌套追踪)

这个例子展示了一个更真实的场景:生成一个笑话 -> 评估笑话质量 -> 如果质量不好就重新生成。

# 文件名: complex_chain.py
# 说明: 展示嵌套追踪和条件逻辑

import os
import random
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnableBranch

load_dotenv()

# 初始化模型
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.8)

# 定义各个子链

# 1. 生成笑话的链
joke_prompt = PromptTemplate.from_template(
    "请生成一个关于{topic}的短笑话。要求: 有趣、积极向上,不超过50字。"
)
joke_chain = joke_prompt | llm | StrOutputParser()

# 2. 评估笑话质量的链
eval_prompt = PromptTemplate.from_template(
    """请评估以下笑话的质量,只输出一个数字(1-5分),5分表示最好。
    
笑话内容: {joke}

质量分数(仅输出数字):"""
)
eval_chain = eval_prompt | llm | StrOutputParser()

# 3. 改进笑话的链(如果质量不够好)
improve_prompt = PromptTemplate.from_template(
    """请改进以下笑话,让它变得更有趣、更精炼。
    
原始笑话: {joke}
    
改进后的笑话:"""
)
improve_chain = improve_prompt | llm | StrOutputParser()

# 定义评分解析函数(将模型输出转换为整数)
def parse_score(score_text: str) -> int:
    """从LLM输出中提取分数"""
    # 简单实现: 提取第一个数字字符
    import re
    match = re.search(r'\d+', score_text)
    if match:
        score = int(match.group())
        return min(max(score, 1), 5)  # 限制在1-5范围内
    return 3  # 默认3分

# 定义质量检查逻辑
def check_quality(inputs: dict) -> str:
    """根据评分决定分支: good(>=4分) 或 bad(<4分)"""
    score = inputs["score"]
    if score >= 4:
        return "good"
    else:
        return "bad"

# 构建条件分支
# 如果笑话质量好,直接返回;否则进行改进
branch = RunnableBranch(
    (lambda x: check_quality(x) == "good", lambda x: x["original_joke"]),
    (lambda x: check_quality(x) == "bad", lambda x: improve_chain.invoke({"joke": x["original_joke"]})),
)

# 完整的主流程
def generate_quality_joke(topic: str) -> str:
    """
    生成高质量笑话的主函数
    """
    print(f"\n🎯 开始为话题 '{topic}' 生成笑话...")
    
    # Step 1: 生成初始笑话
    print("📝 Step 1: 生成初始笑话...")
    initial_joke = joke_chain.invoke({"topic": topic})
    print(f"   初始笑话: {initial_joke}")
    
    # Step 2: 评估质量
    print("⚖️ Step 2: 评估质量...")
    score_text = eval_chain.invoke({"joke": initial_joke})
    score = parse_score(score_text)
    print(f"   质量评分: {score}/5")
    
    # Step 3: 根据质量决定是否改进
    print("🔄 Step 3: 质量检查与改进...")
    result = branch.invoke({
        "original_joke": initial_joke,
        "score": score
    })
    
    if result == initial_joke:
        print(f"   ✅ 质量合格,直接采用")
    else:
        print(f"   🔧 质量不足,已改进: {result}")
    
    return result

# 执行主流程
if __name__ == "__main__":
    # 测试几个不同的话题
    topics = ["程序员", "人工智能", "火锅"]
    
    for topic in topics:
        print("\n" + "="*60)
        final_joke = generate_quality_joke(topic)
        print(f"\n🎉 最终笑话:\n{final_joke}")
        print("="*60)
    
    print("\n✨ 所有任务完成!请登录LangSmith查看完整的嵌套追踪链路。")
    print("   提示: 在Trace详情页可以看到3个步骤的完整调用树。")

6.3 错误追踪示例

这个例子展示LangSmith如何自动捕获和记录异常。

# 文件名: error_tracing.py
# 说明: 展示错误情况下的追踪

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate

load_dotenv()

def run_with_potential_error(enable_error: bool):
    """
    运行可能出错的链
    """
    llm = ChatOpenAI(model="gpt-3.5-turbo")
    
    # 故意使用一个不存在的变量(如果enable_error为True)
    if enable_error:
        prompt = PromptTemplate.from_template("你好,{name}! 今天的{undefined_variable}很好。")
    else:
        prompt = PromptTemplate.from_template("你好,{name}!")
    
    chain = prompt | llm
    
    try:
        result = chain.invoke({"name": "张三"})
        print(f"✅ 成功: {result}")
    except Exception as e:
        print(f"❌ 失败: {type(e).__name__}: {e}")
        # 异常会被LangSmith自动捕获并记录到Trace中
        raise  # 重新抛出,让调用者看到错误

if __name__ == "__main__":
    print("第一个调用(正常):")
    run_with_potential_error(enable_error=False)
    
    print("\n第二个调用(故意制造错误):")
    try:
        run_with_potential_error(enable_error=True)
    except:
        pass  # 错误已经被记录,这里静默处理避免中断
    
    print("\n📊 请在LangSmith控制台查看两个Trace的对比:")
    print("   - 第一个Trace: 状态为success")
    print("   - 第二个Trace: 状态为error,包含错误详情")

七、代码逐行详解

我们来深入分析第一个示例basic_tracing.py,理解每一行代码的作用。

7.1 导入和初始化部分

import os
from dotenv import load_dotenv
  • os模块用于读取环境变量
  • load_dotenv函数从.env文件加载环境变量到os.environ
from langchain_openai import ChatOpenAI
  • ChatOpenAI是LangChain提供的OpenAI聊天模型适配器
  • 它封装了OpenAI API的调用细节,提供了统一的接口
from langchain_core.prompts import ChatPromptTemplate
  • ChatPromptTemplate用于构建聊天格式的Prompt
  • 支持system、human、ai等角色消息
from langchain_core.output_parsers import StrOutputParser
  • StrOutputParser将LLM的输出(通常是AIMessage对象)转换为纯字符串
  • 简化了后续处理
load_dotenv()
  • 读取当前目录下的.env文件
  • 将文件中的键值对设置到环境变量中
  • 必须在其他导入之前调用,因为LangSmith的初始化会读取环境变量

7.2 配置验证部分

required_vars = ["LANGCHAIN_API_KEY", "OPENAI_API_KEY"]
for var in required_vars:
    if not os.getenv(var):
        raise ValueError(f"环境变量 {var} 未设置,请检查.env文件")
  • 主动检查必需的配置是否存在
  • 避免运行时才发现配置缺失,浪费时间排查
print(f"📁 LangSmith项目: {os.getenv('LANGCHAIN_PROJECT', 'default')}")
  • 打印当前使用的Project名称
  • os.getenv的第二个参数是默认值(如果不设置环境变量)

7.3 组件构建部分

llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0.7
)
  • model参数指定使用的模型,gpt-3.5-turbo是目前性价比最高的选择
  • temperature控制输出的随机性:0表示最确定,1表示最随机。0.7是较好的平衡点
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个乐于助人的AI助手,请用{language}回答问题。"),
    ("human", "{input}")
])
  • from_messages方法从消息列表构建模板
  • 每条消息是(role, content)元组
  • {language}{input}是占位符,运行时会被替换
output_parser = StrOutputParser()
  • 创建输出解析器实例,不做任何复杂处理,只做类型转换

7.4 LCEL链路组合

chain = prompt | llm | output_parser
  • |操作符是LCEL(LangChain Expression Language)的核心语法
  • 表示数据从左到右依次经过每个组件
  • prompt接收输入字典 → 生成消息列表 → llm调用模型 → 返回AIMessage → output_parser转换为字符串

7.5 执行调用

result = chain.invoke({
    "language": "中文",
    "input": "请介绍一下LangSmith是什么"
})
  • invoke是LangChain的统一调用接口
  • 传入一个字典,key对应Prompt模板中的占位符
  • LangSmith会自动监控这次调用的整个过程

7.6 LangSmith的自动追踪如何工作

当你运行这段代码时,幕后发生的事情:

  1. 导入时的初始化

    • from langchain_openai import ChatOpenAI执行时,LangSmith的回调系统被激活
    • 检查环境变量LANGCHAIN_TRACING_V2,如果为true则启用追踪
  2. 组件创建时

    • 每个LangChain组件在创建时都会被分配一个ID
    • 但此时还没有实际追踪,因为还没有调用
  3. 调用chain.invoke

    • 触发on_chain_start回调,创建根Run
    • 执行prompt渲染,触发on_chain_end(对于PromptTemplate)
    • 调用llm,触发on_llm_start → 发送HTTP请求到OpenAI → 收到响应 → 触发on_llm_end
    • 执行output_parser,触发相应回调
    • 最后触发on_chain_end,完成根Run
  4. 数据上报

    • 所有Run数据被放入队列
    • 后台线程异步发送到LangSmith API

八、常见坑点与避坑指南

基于大量实战经验,总结以下常见问题和解决方案。

8.1 环境变量未生效

症状

  • 代码运行正常,但LangSmith控制台看不到任何数据
  • 或者看到错误日志:LangSmith API key is not set

原因

  • .env文件位置不对(不在当前工作目录)
  • load_dotenv()调用位置太晚(在某些库初始化之后)
  • 环境变量名拼写错误(如LANGCHAIN_API_KEY写成LANGCHAIN-API-KEY)

解决方案

# 正确的做法
from dotenv import load_dotenv
load_dotenv()  # 在所有其他导入之前

# 验证配置
import os
print(os.getenv("LANGCHAIN_API_KEY"))  # 应该输出你的Key
print(os.getenv("LANGCHAIN_TRACING_V2"))  # 应该输出'true'

8.2 免费版配额超限

症状

  • 代码运行成功,但LangSmith中没有新数据
  • 或者收到错误响应:Quota exceeded

原因

  • 当月Trace次数已超过1000次(免费版限制)
  • 可能是在循环或批处理中大量调用,无意中消耗了配额

解决方案

  • 在开发和测试阶段,使用采样策略:只记录部分请求
  • 或者升级到付费版本
  • 或者在代码中判断环境,只在必要的时候启用追踪:
import os
if os.getenv("ENVIRONMENT") == "production":
    os.environ["LANGCHAIN_TRACING_V2"] = "true"
else:
    os.environ["LANGCHAIN_TRACING_V2"] = "false"

8.3 网络连接问题

症状

  • 代码执行缓慢,有多次重试日志
  • 部分Trace丢失

原因

  • 公司网络限制,无法访问api.smith.langchain.com
  • 需要配置HTTP代理

解决方案

# 配置代理(如果公司网络需要)
os.environ["HTTP_PROXY"] = "http://proxy.example.com:8080"
os.environ["HTTPS_PROXY"] = "http://proxy.example.com:8080"

或者使用离线模式(数据只保存在本地,不同步到云端):

os.environ["LANGCHAIN_TRACING_OFFLINE"] = "true"

8.4 敏感数据泄露

症状

  • 不小心把用户隐私数据(如手机号、身份证)记录到了LangSmith
  • 无法删除(免费版不支持数据删除)

原因

  • LangSmith默认会记录所有输入输出,包括敏感信息

解决方案

  • 使用@traceable装饰器的参数控制哪些数据被记录:
from langsmith import traceable

@traceable(
    capture_input=False,  # 不记录输入
    capture_output=False  # 不记录输出
)
def process_user_data(user_info):
    # 处理敏感数据
    pass
  • 或者在环境变量中全局禁用数据捕获:
LANGCHAIN_HIDE_INPUTS=true
LANGCHAIN_HIDE_OUTPUTS=true

8.5 异步代码追踪失效

症状

  • 在async函数中调用LLM,LangSmith没有记录

原因

  • LangSmith的回调系统对异步上下文有特殊要求

解决方案
使用ainvoke而不是invoke

# 错误的做法
async def my_func():
    result = chain.invoke(...)  # 同步调用,但在异步函数中

# 正确的做法
async def my_func():
    result = await chain.ainvoke(...)  # 异步调用

8.6 大量小Trace影响性能

症状

  • 代码运行明显变慢
  • CPU占用率升高

原因

  • 每个小的组件调用都创建Run,产生大量网络请求开销

解决方案

  • 使用with tracing上下文管理器合并多个操作为一个Trace:
from langsmith import trace_context

with trace_context("batch_processing"):
    for item in items:
        process_item(item)  # 这些调用都会被放在同一个Trace下
  • 或者提高采样率,只记录一部分请求:
import random
if random.random() < 0.1:  # 10%采样
    os.environ["LANGCHAIN_TRACING_V2"] = "true"

九、企业级落地最佳实践

基于多个生产环境的实践经验,总结以下最佳实践。

9.1 项目组织策略

多Project并行管理

- project: myapp-dev (开发环境)
- project: myapp-staging (预发布环境)  
- project: myapp-production (生产环境)
- project: myapp-experiment (A/B测试)

命名规范

格式: {团队}_{应用}_{环境}_{用途}
示例: 
- ai_chatbot_prod_main
- ai_chatbot_staging_test
- search_engine_dev_embeddings

为什么要这样组织

  • 环境隔离:开发和生产的Trace混在一起,会导致监控数据污染
  • 权限分离:生产环境应该只有运维人员可访问
  • 成本控制:对开发环境可以降低采样率,节省配额

9.2 标签规范化

定义标准标签集

# 统一的标签字典
STANDARD_TAGS = {
    "version": "v2.1.0",           # 应用版本
    "user_group": "premium",        # 用户分组
    "region": "us-east",            # 地理区域
    "model": "gpt-4",               # 使用的模型
    "agent_type": "rag_agent",      # Agent类型
    "session_id": "sess_12345",     # 会话ID
    "ab_test": "variant_b"          # A/B测试组
}

# 使用时合并
chain.invoke(input, config={"tags": list(STANDARD_TAGS.items())})

标签使用原则

  • 基数(cardinality)低的属性适合做标签(如versionenv)
  • 基数高的属性(如user_id)容易导致筛选面板卡顿,建议用metadata
  • 标签数量控制在10个以内,过多会影响性能

9.3 生产环境采样策略

生产环境不可能追踪每一个请求(即使是付费版本,也会受限于API速率)。采用智能采样策略。

方案一:按用户分层采样

def should_trace(user_id: str) -> bool:
    """决定是否追踪该请求"""
    # 白名单用户:100%追踪
    if user_id in PREMIUM_USERS:
        return True
    
    # 普通用户:5%随机采样
    import hashlib
    hash_val = int(hashlib.md5(user_id.encode()).hexdigest(), 16)
    return (hash_val % 100) < 5

# 使用
if should_trace(current_user_id):
    os.environ["LANGCHAIN_TRACING_V2"] = "true"
else:
    os.environ["LANGCHAIN_TRACING_V2"] = "false"

方案二:按业务价值采样

# 只追踪高价值请求
def should_trace_by_intent(user_query: str) -> bool:
    # 包含特定关键词的请求才追踪
    high_value_keywords = ["支付", "退款", "订阅", "企业版"]
    return any(kw in user_query for kw in high_value_keywords)

方案三:自适应采样

# 动态调整采样率,错误越多的时段采样率越高
error_rate = get_recent_error_rate(minutes=5)
if error_rate > 0.1:  # 错误率超过10%
    sample_rate = 0.5  # 提高采样率到50%
else:
    sample_rate = 0.05  # 正常5%采样

should_trace = random.random() < sample_rate

9.4 成本控制

LangSmith本身免费版有配额,付费版按量计费。控制成本的策略:

1. 精简Run数据

# 配置不记录大字段
import langsmith
langsmith.config.hide_inputs = True  # 不记录Prompt(可能很长)
langsmith.config.hide_outputs = True  # 不记录输出(但这样就失去了意义)

2. 合并细粒度Run

# 不好的写法:每个操作都有独立Run
chain = step1 | step2 | step3 | step4  # 4个Run

# 好的写法:合并为一个大Run
@traceable(name="combined_chain")
def combined_process(input_data):
    intermediate1 = step1(input_data)
    intermediate2 = step2(intermediate1)
    return step3(intermediate2)

3. 定期清理旧数据
虽然免费版自动清理7天前数据,但付费版需要手动清理:

from langsmith import Client

client = Client()
# 删除30天前的Runs
client.delete_runs(older_than_days=30)

9.5 团队协作规范

Code Review检查点

  • 是否配置了正确的LANGCHAIN_PROJECT(不要在PR中硬编码个人测试项目)
  • 是否添加了有意义的tagsmetadata
  • 是否处理了敏感数据(不应该记录密码、Token等)

开发流程集成

# 在本地开发时自动使用个人Project
if os.getenv("CI") != "true":  # 不是CI环境
    os.environ["LANGCHAIN_PROJECT"] = f"{os.getenv('USER')}-dev"

共享链接规范
LangSmith支持生成分享链接。在提交Issue或PR时,附上相关Trace的分享链接:

# 获取当前Trace的分享链接
from langsmith import get_current_run_tree
run_tree = get_current_run_tree()
share_url = run_tree.get_url()
print(f"📎 调试链接: {share_url}")

十、本节知识点总结

本节课我们系统学习了以下内容:

理论层面

  1. LLMOps核心概念:理解了大模型应用为什么需要可观测性,LLMOps与传统MLOps的区别
  2. LangSmith定位:LangSmith是LangChain官方的可观测性平台,负责调试、评估、监控
  3. 核心概念
    • Trace:一次完整请求的全流程记录
    • Run:Trace中的单个步骤
    • Project:资源隔离单元
    • Session:多轮对话的组织方式
    • Tag:自定义标记,用于筛选分析
  4. 工作原理
    • 基于回调系统自动捕获数据
    • 异步队列上报,不影响业务性能
    • 深度集成LangChain组件

实操层面

  1. 环境配置
    • 注册LangSmith账号并获取API Key
    • 配置环境变量(LANGCHAIN_TRACING_V2、API_KEY、PROJECT)
    • 安装必要的Python包
  2. 代码实战
    • 第一个链式调用示例(basic_tracing.py)
    • 复杂链路示例(嵌套调用、条件分支)
    • 错误追踪示例
  3. 调试分析
    • 学会在LangSmith面板查看Trace详情
    • 理解每个Run的输入输出和耗时

避坑指南

  • 环境变量必须在导入前加载
  • 免费版每月1000次Trace限额
  • 注意敏感数据的泄露风险
  • 异步代码需要使用ainvoke

企业实践

  • 多Project组织策略(按环境、按团队)
  • 标签规范化(统一命名、控制基数)
  • 生产环境采样策略(按用户、按业务价值)
  • 成本控制方法(精简数据、定期清理)
  • 团队协作规范(Code Review、共享链接)

十一、课后思考练习题

为了巩固本节课的内容,请完成以下练习。

练习题1:理论理解(简答题)

1.1 请用自己的话解释Trace和Run的区别,并举例说明。(提示:类比浏览器开发者工具中的网络请求和子请求)

1.2 为什么大模型应用比传统应用更需要可观测性?列出至少3个原因。

1.3 如果公司只有3个人使用LangSmith(你、同事A、同事B),你们分别负责开发、测试、运维,应该如何规划Project?请说明理由。

练习题2:动手实践

2.1 改造basic_tracing.py,添加以下功能:

  • 增加一个temperature参数,让用户可以在调用时指定(而不是硬编码)
  • 使用with trace_context()将整个处理过程包装在一个自定义Trace下
  • 添加3个自定义标签:env="test"user_type="beginner"model="gpt3.5"

2.2 编写一个函数,接收一个用户问题列表questions = ["什么是LangSmith?", "如何安装LangSmith?", "LangSmith收费吗?"],循环处理每个问题。要求:

  • 每个问题单独一个Trace
  • 所有问题共享同一个Session ID
  • 在LangSmith面板中,能够一键筛选出这个Session的所有对话

提示:使用config={"callbacks": [SessionCallback(session_id="...")]}或查阅文档了解如何设置Session。

练习题3:场景设计

3.1 你正在开发一个客服机器人,面临以下问题:

  • 用户投诉回答质量不好,但原因不明
  • 每次修改Prompt后,不确定效果是否提升
  • 有些用户问相同的问题,机器人却给出不同的答案

请设计一个基于LangSmith的解决方案,回答:

  • 你会如何配置Project和标签?
  • 如何使用LangSmith定位“回答质量不好”的问题?
  • 如果要对新Prompt进行A/B测试,LangSmith能提供什么帮助?

3.2 生产环境流量较大(每天约10万次请求),免费版的1000次/月显然不够。请设计一个成本可控的监控方案:

  • 采样率如何设置?
  • 哪些请求必须100%监控?
  • 如何平衡成本和可观测性?

练习题4:源码分析(选做)

阅读LangSmith Python SDK的部分源码(https://github.com/langchain-ai/langsmith-sdk),回答:

  • traceable装饰器的实现原理是什么?
  • 在什么情况下会触发on_chain_start回调?
  • 异步队列的默认最大容量是多少?

提交方式

  • 将代码和答案整理成文档
  • 在LangSmith社区分享你的学习心得
  • 参考答案将在下节课公布

下节课预告

第2课我们将深入学习LangSmith的账号体系、权限模型和计费规则。届时会详细讲解:

  • 不同付费版本的差异对比
  • 团队成员的邀请和权限管理
  • 如何估算你的使用量并选择合适的套餐
  • 企业版的自托管方案

请确保完成本节课的环境配置和基础代码调试,下节课我们将在同样的环境基础上继续深入。

学习建议

  1. 本节课的所有代码示例建议你亲自运行一遍,不要只看不练
  2. 运行代码后,务必登录LangSmith控制台查看Trace详情,直观感受数据呈现
  3. 尝试故意制造一些错误(如删掉API Key、使用错误的Project名),观察LangSmith的行为
  4. 对照课后练习,检验自己的掌握程度

如果有任何疑问,欢迎在专栏评论区留言,我会定期回复。下一节课见!


🔗《20节课 LangSmith 从入门到精通》系列课程导航

去订阅

🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐