第 22 期 | 观测体系:LangSmith 探针无缝挂载 Graph

更新于 2026/4/16

🎯 本期学习目标

嘿,各位未来的 AI 架构师们!欢迎来到《LangGraph 多智能体专家课》的第 22 期。走到这一步,你们的“AI 万能内容创作机构”想必已经初具规模,Agent 们各司其职,在 LangGraph 的编排下运转得有模有样。但是,我敢打赌,你们肯定遇到过这样的窘境:

某个 Agent 突然开始“胡言乱语”,或者工作流卡在某个节点,或者输出的结果驴唇不对马嘴,你却完全不知道是哪个环节出了问题,哪个大模型在哪个 Edge 下开了小差?是不是感觉像在蒙眼拆炸弹?别慌,这期课就是来给你送“透视眼镜”的!

本期,我们将深入探讨 LangGraph 复杂工作流的“观测”与“调试”艺术,尤其聚焦于 LangChain 生态中那个无价之宝——LangSmith

学完本期,你将:

  1. 透彻理解:为什么在多智能体和 LLM 驱动的复杂系统中,观测与调试是开发效率的生命线。
  2. 掌握核心:LangSmith 的基本概念、工作原理及其在 LangGraph 中的无缝集成机制。
  3. 实战演练:为我们的“AI 万能内容创作机构”项目挂载 LangSmith 探针,实时追踪 Planner、Researcher、Writer、Editor 等 Agent 的每一个决策和行动。
  4. 高效排障:学会利用 LangSmith 的可视化 Trace 功能,秒级定位 Agent 之间的协作问题、LLM 的 Prompt 缺陷或工具调用的错误,告别“盲盒调试”时代。

📖 原理解析

大模型时代,你还在“print大法”调试?醒醒吧!

在传统的软件开发中,我们有各种 IDE、Debugger、日志系统来帮助我们理解代码的执行流程。但到了大模型时代,尤其是构建多智能体系统时,传统的调试手段突然变得苍白无力。为什么?

  1. 黑箱效应:LLM 内部的推理过程是高度不透明的。你给它一个 Prompt,它吐出一个 Response,中间发生了什么,你很难直接窥探。
  2. 非确定性:LLM 的输出往往不是 100% 确定性的,即使是相同的 Prompt,也可能在不同时间、不同温度参数下产生细微差异。这让复现问题变得困难。
  3. 复杂链路:LangGraph 编排的多智能体系统,往往涉及多个 LLM 调用、多个工具使用、多个 Agent 之间的状态传递和决策。一个微小的错误或误解,都可能在链路上被放大,最终导致整个系统崩溃或输出偏差。
  4. 状态流转:LangGraph 的核心是状态机,状态在节点之间流转和变异。如果不能清晰地看到每个节点接收了什么状态,又输出了什么状态,调试简直是噩梦。

想象一下,你的 Planner Agent 规划出一个不靠谱的标题,导致 Researcher 找不到相关资料,Writer 只能瞎编,Editor 又基于瞎编的内容进行修改。最终,你得到了一篇离谱的文章。如果你没有一个好的观测工具,你根本不知道问题最初出在 Planner 环节,你可能还在 Writer 的 Prompt 上反复修改,这不就是“头痛医脚”吗?

LangSmith:你的多智能体系统“X光机”

LangSmith 就是为解决这些痛点而生的。它是 LangChain 官方推出的一款开发者平台,旨在帮助你调试、测试、评估和监控基于 LLM 的应用程序。对于 LangGraph 这种复杂的多智能体编排,LangSmith 简直是“天作之合”。

它的核心思想是:捕获并可视化你的 LLM 应用的每一次“运行” (Run)。无论是单个 LLM 调用、一个 Chain、一个 Tool,还是我们复杂的 LangGraph 流程,LangSmith 都能将其分解成一系列可追溯的事件,并以树状结构(或称为 Trace)清晰地展示出来。

LangSmith 如何与 LangChain/LangGraph 协同?

LangChain/LangGraph 库内部集成了Tracing机制。当你设置了相应的环境变量后,任何通过 LangChain 库发起的 LLM 调用、Tool 调用、Chain 执行,都会被自动“拦截”并发送到 LangSmith 后端服务。这意味着,你几乎不需要修改任何业务逻辑代码,就能获得强大的观测能力!

它能给你展示什么?

  • 完整的调用链路 (Trace):从用户输入到最终输出,每一个 LLM 调用、Agent 决策、工具使用都被记录下来。
  • 输入/输出:每个步骤的详细输入 Prompt 和 LLM 的输出。
  • 中间步骤 (Intermediate Steps):Agent 的思考过程、工具调用的参数和结果。
  • 延迟与成本:每个步骤的执行时间,以及预估的 token 使用量和成本。
  • 错误信息:如果某个环节出错,LangSmith 会高亮显示并提供错误堆栈。

这就像给你的“AI 万能内容创作机构”安装了无数个微型摄像头和麦克风,每一个 Agent 的“所思所想”、“所作所为”都尽收眼底。

Mermaid 图解:LangSmith 如何“监视”你的 Agency

让我们用一个 Mermaid 图来直观地看看 LangSmith 是如何穿透你的多智能体工作流的:

graph TD
    subgraph AI 万能内容创作机构工作流
        start[用户需求] --> Planner(规划师 Agent);
        Planner -- 规划结果 --> Research(研究员 Agent);
        Research -- 研究报告 --> Write(撰稿人 Agent);
        Write -- 初稿 --> Edit(编辑 Agent);
        Edit -- 终稿 --> end[最终内容输出];
    end

    subgraph LangSmith 观测体系
        direction LR
        A[LangSmith UI] --> B{实时观测 & 分析}
        B --> C(性能瓶颈定位)
        B --> D(Prompt 优化建议)
        B --> E(Agent 行为理解)
        B --> F(成本与延迟追踪)
        B --> G(错误快速排查)
    end

    style A fill:#f9f,stroke:#333,stroke-width:2px,color:#333
    style B fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333
    style C fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333
    style D fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333
    style E fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333
    style F fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333
    style G fill:#e0e0e0,stroke:#333,stroke-width:1px,color:#333

    start -- 触发 --> LS_Tracer_Start(LangSmith Tracer);
    Planner -- 调用/执行 --> LS_Tracer_Planner(LangSmith Tracer);
    Research -- 调用/执行 --> LS_Tracer_Research(LangSmith Tracer);
    Write -- 调用/执行 --> LS_Tracer_Write(LangSmith Tracer);
    Edit -- 调用/执行 --> LS_Tracer_Edit(LangSmith Tracer);
    end -- 结束 --> LS_Tracer_End(LangSmith Tracer);

    LS_Tracer_Start -- 记录 Trace --> LangSmith_Backend(LangSmith 后端服务);
    LS_Tracer_Planner -- 记录 Trace --> LangSmith_Backend;
    LS_Tracer_Research -- 记录 Trace --> LangSmith_Backend;
    LS_Tracer_Write -- 记录 Trace --> LangSmith_Backend;
    LS_Tracer_Edit -- 记录 Trace --> LangSmith_Backend;
    LS_Tracer_End -- 记录 Trace --> LangSmith_Backend;

    LangSmith_Backend -- 数据展示 --> A;

从图中可以看到,每一个 Agent 的执行,每一个 LangChain 组件的调用(包括 LangGraph 的整个执行),都会被 LangSmith Tracer 捕获,并将这些事件发送到 LangSmith 的后端服务。最终,我们可以在 LangSmith 的 Web UI 上,以直观的图形化界面看到整个工作流的详细执行轨迹。这就是你的“AI 万能内容创作机构”的透明化改造!

💻 实战代码演练

现在,让我们把 LangSmith 真正地集成到我们的“AI 万能内容创作机构”项目中。为了演示的简洁性,我们将构建一个简化版的 LangGraph 工作流:用户提供一个主题,Planner Agent 规划内容大纲,Writer Agent 基于大纲撰写初稿。

1. LangSmith 账户与 API Key 配置

首先,你需要前往 LangSmith 官网 注册一个账户。注册后,在你的个人设置页面可以找到你的 API Key。

为了让 LangChain 知道将 Trace 发送到哪里,你需要设置几个环境变量。最简单的方式是在你的 .env 文件中(如果你使用 python-dotenv)或者直接在运行脚本的环境中设置:

# .env 文件内容示例
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY="sk-YOUR_LANGSMITH_API_KEY"
LANGCHAIN_PROJECT="AI Content Agency - Dev" # 可选,用于在 LangSmith UI 中组织你的项目

重要提示LANGCHAIN_TRACING_V2=true 是启用 LangChain V2 Tracing 机制的关键。LANGCHAIN_API_KEY 是你的 LangSmith API 密钥。LANGCHAIN_PROJECT 是一个可选的名称,用于在 LangSmith UI 中将你的所有运行归类到这个项目下,方便管理。强烈建议设置这个,不然你的 LangSmith 界面会一片混乱。

2. 安装必要的库

确保你的环境中安装了 LangChain、LangGraph、OpenAI 等库,以及 langsmith 本身:

pip install -qU langchain langgraph langchain_openai langsmith python-dotenv

3. 构建简化版 Agency Graph

我们将模拟一个简单的内容创作流程:Planner -> Writer

import os
from dotenv import load_dotenv
from typing import TypedDict, Annotated, List, Union
import operator

from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

from langgraph.graph import StateGraph, END

# 加载环境变量
load_dotenv()

# ====================================================================================
# 1. 定义我们AI内容创作机构的状态 (AgencyState)
# ====================================================================================
class AgencyState(TypedDict):
    """
    代表AI内容创作机构当前状态的结构。
    它追踪了从用户请求到最终内容产出的所有关键信息。
    """
    topic: str  # 用户请求创作内容的原始主题
    plan: str   # Planner Agent 生成的内容大纲/规划
    draft: str  # Writer Agent 撰写的内容初稿
    messages: Annotated[List[BaseMessage], operator.add] # 用于Agent间通信的消息历史

# ====================================================================================
# 2. 初始化大模型 (LLM)
# ====================================================================================
# 建议使用 GPT-4o 或 GPT-4-turbo,它们在理解复杂指令方面表现更佳
# 如果你没有这些模型的访问权限,可以尝试 gpt-3.5-turbo,但效果可能不如预期
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)

# ====================================================================================
# 3. 定义 Agent 节点函数
#    每个函数都接收 AgencyState,并返回修改后的状态或消息。
# ====================================================================================

def planner_agent(state: AgencyState) -> AgencyState:
    """
    规划师 Agent:根据用户提供的主题,生成详细的内容大纲。
    """
    print("---Planner Agent: 正在规划内容大纲---")
    topic = state["topic"]
    messages = state["messages"]

    # 规划师的 Prompt
    planner_prompt = PromptTemplate.from_template(
        """
        你是一位经验丰富的内容规划师。你的任务是根据给定的主题,
        为一篇长文章生成一个详细且逻辑清晰的大纲。

        主题: {topic}

        请确保大纲包含以下要素:
        1. 吸引人的标题
        2. 简介 (Introduction)
        3. 至少 3-5 个主要章节标题
        4. 每个主要章节下包含 2-3 个子章节标题
        5. 结论 (Conclusion)
        6. 行动号召 (Call to Action, 可选)

        请直接输出大纲内容,不需要任何额外说明。
        """
    )

    # 构建规划链
    planner_chain = planner_prompt | llm | StrOutputParser()

    # 调用规划链生成大纲
    plan_output = planner_chain.invoke({"topic": topic})

    # 更新状态:保存规划结果,并添加规划师的输出到消息历史
    return {
        "plan": plan_output,
        "messages": [AIMessage(content=f"规划师已完成规划:\n{plan_output}")]
    }

def writer_agent(state: AgencyState) -> AgencyState:
    """
    撰稿人 Agent:根据规划师提供的大纲,撰写文章初稿。
    """
    print("---Writer Agent: 正在撰写文章初稿---")
    topic = state["topic"]
    plan = state["plan"]
    messages = state["messages"]

    # 撰稿人的 Prompt
    writer_prompt = PromptTemplate.from_template(
        """
        你是一位专业的撰稿人。你的任务是根据以下主题和详细大纲,
        撰写一篇高质量、内容丰富且引人入胜的文章初稿。

        主题: {topic}

        大纲:
        {plan}

        请遵循大纲结构,扩充每个章节和子章节,使文章内容连贯、富有洞察力。
        字数要求:至少 800 字。
        请直接输出文章初稿,不需要任何额外说明。
        """
    )

    # 构建撰稿链
    writer_chain = writer_prompt | llm | StrOutputParser()

    # 调用撰稿链生成初稿
    draft_output = writer_chain.invoke({"topic": topic, "plan": plan})

    # 更新状态:保存初稿,并添加撰稿人的输出到消息历史
    return {
        "draft": draft_output,
        "messages": [AIMessage(content=f"撰稿人已完成初稿:\n{draft_output}")]
    }

# ====================================================================================
# 4. 构建 LangGraph 工作流
# ====================================================================================

def create_agency_workflow():
    """
    创建并编译AI内容创作机构的LangGraph工作流。
    """
    workflow = StateGraph(AgencyState)

    # 添加节点
    workflow.add_node("planner", planner_agent)
    workflow.add_node("writer", writer_agent)

    # 设置入口点
    workflow.set_entry_point("planner")

    # 定义边:规划师完成后交给撰稿人
    workflow.add_edge("planner", "writer")

    # 撰稿人完成后结束流程
    workflow.add_edge("writer", END)

    # 编译图
    app = workflow.compile()
    return app

# ====================================================================================
# 5. 运行工作流并观察 LangSmith
# ====================================================================================

if __name__ == "__main__":
    print("---AI内容创作机构启动,LangSmith 观测已开启---")

    # 创建工作流
    app = create_agency_workflow()

    # 定义初始状态:用户请求一个主题
    initial_state = {
        "topic": "人工智能在医疗健康领域的应用与挑战",
        "plan": "",
        "draft": "",
        "messages": [HumanMessage(content="请帮我写一篇关于人工智能在医疗健康领域应用的文章。")]
    }

    # 运行工作流
    # LangGraph 会自动捕获并发送 Trace 到 LangSmith
    final_state = app.invoke(initial_state)

    print("\n---工作流执行完毕---")
    print("\n最终文章初稿:")
    print(final_state["draft"])

    print("\n请前往 LangSmith UI (https://www.langsmith.com/app/) 查看本次运行的详细 Trace。")
    print(f"你可以在 'Projects' 下找到名为 '{os.getenv('LANGCHAIN_PROJECT', 'default')}' 的项目。")

4. 运行代码并观察 LangSmith UI

  1. 确保你的 .env 文件配置正确并与你的 LangSmith API Key 匹配。
  2. 运行上述 Python 脚本。
    python your_script_name.py
    
  3. 脚本会开始执行,你会在控制台看到 ---Planner Agent: 正在规划内容大纲------Writer Agent: 正在撰写文章初稿--- 等输出。
  4. 打开你的浏览器,访问 LangSmith UI: https://www.langsmith.com/app/
  5. 在左侧导航栏,点击 "Projects"。你应该能看到你设置的 LANGCHAIN_PROJECT 名称(例如 AI Content Agency - Dev)。点击进入该项目。
  6. 你将看到一个列表,其中包含你刚刚运行的 Trace。每个 Trace 代表一次 app.invoke() 调用。

在 LangSmith UI 中你会看到什么?

点击最新的 Trace,你将进入一个详细的视图:

  • Timeline View:一个时间轴视图,清晰展示了每个组件(包括整个 LangGraph 运行、planner 节点、writer 节点,以及每个节点内部的 ChatOpenAI LLM 调用)的执行顺序和耗时。
  • Graph View:对于 LangGraph 来说,这个视图尤其酷炫,它会以图形化的方式展示你的 Agent 流程,并高亮显示当前 Trace 经过的路径。
  • Detailed Steps
    • LangGraph Run: 顶层 Run 会显示整个 Graph 的输入 (topic, messages) 和最终输出 (draft, messages)。
    • Node Runs: 点击 planner 节点或 writer 节点,你会看到该节点作为函数被调用的输入 (state 字典) 和输出 (state 字典)。
    • LLM Calls: 在每个节点内部,你会看到 ChatOpenAI 的调用。点击它,你就能看到发送给 LLM 的完整 Prompt (messages 数组),以及 LLM 返回的原始 Response (content)。你还可以看到 token 使用量和预估成本。

LangSmith 的价值在哪里?

假设你的 Writer Agent 写出来的文章不够好。在 LangSmith 中,你可以:

  • 检查 Planner Agent 的输出:是不是 Planner 给的大纲本身就有问题?是不是它对主题的理解偏差了?
  • 检查 Writer Agent 的输入 Prompt:是不是 Writer 的 Prompt 没能正确利用 Planner 的大纲?是不是 Prompt 指令不够清晰?
  • 检查 Writer Agent 调用 LLM 的实际输入/输出:是不是 LLM 在某个特定 Prompt 下表现不佳?

通过这种可视化的、层层深入的检查,你就能迅速定位问题,是 Planner 的 Prompt 需要优化,还是 Writer 的逻辑有缺陷,亦或是 LLM 自身的问题。告别盲猜,拥抱科学调试!

坑与避坑指南

作为一名资深导师,我见过太多同学在 LangSmith 这块“宝地”上踩坑。来,我给你们指几条“光明大道”,让你少走弯路:

  1. “环境不活,万事皆休”:环境变量是生命线!

    • :最常见的问题是没有正确设置 LANGCHAIN_TRACING_V2=trueLANGCHAIN_API_KEY。很多人只设置了 API Key,却忘了启用 V2 Tracing,导致 LangSmith 界面一片空白。
    • 避坑务必确认这两个环境变量都已正确设置且在你的脚本运行环境中生效。使用 python-dotenv 是个好习惯,但也要确保 .env 文件被正确加载。在脚本开头加一句 print(os.getenv("LANGCHAIN_TRACING_V2")) 检查一下是个好方法。
  2. “项目名乱飞,界面一片灰”:善用 LANGCHAIN_PROJECT

    • :不设置 LANGCHAIN_PROJECT,或者每次运行都用不同的项目名。LangSmith UI 会默认把所有 Trace 放在 default 项目下,或者创建一堆零散的项目。当你项目多了,找一个特定的 Trace 简直是大海捞针。
    • 避坑:为你的每个独立项目(例如我们的“AI 万能内容创作机构”)设置一个固定且有意义LANGCHAIN_PROJECT 名称。在开发不同功能分支时,可以考虑使用 LANGCHAIN_PROJECT="AI Content Agency - FeatureX" 来区分。
  3. “数据安全,重于泰山”:敏感信息切勿上传。

    • :在开发过程中,不小心将包含用户隐私、公司机密等敏感信息的 Prompt 或 LLM 输出发送到了 LangSmith。
    • 避坑:LangSmith 是一个强大的调试工具,但它是一个云服务。在生产环境或处理敏感数据时,请务必审慎。对于高度敏感的信息,考虑在发送到 LangSmith 之前进行脱敏处理。LangSmith 也提供了自部署选项 (LangSmith On-premise),但对于大多数开发者来说,云服务更便捷。
  4. “Trace 过长,眼花缭乱”:学会筛选和搜索。

    • :当你的工作流非常复杂,或者运行次数过多时,一个 Trace 可能包含上百个步骤,在 LangSmith UI 上查看会非常痛苦。
    • 避坑:LangSmith UI 提供了强大的筛选、搜索和分组功能。你可以按 Agent 名称、LLM 类型、状态、时间范围等进行过滤。学习使用这些功能能极大地提高你的调试效率。此外,可以给你的 Run 添加 tags,方便后续查找。
  5. “自定义组件,何去何从?”:手动集成 Tracer。

    • :如果你在 LangGraph 节点中使用了完全自定义的 Python 函数,而不是 LangChain 提供的 Runnable 组件,这些自定义逻辑内部的子步骤(例如一个自定义的外部 API 调用)可能不会被 LangSmith 自动捕获。
    • 避坑:对于你完全自定义的、不继承 Runnable 的组件,如果想让其内部操作也被追踪,你需要手动引入 LangChainTracer。例如,可以使用 with get_tracer().start_as_current_span("my_custom_tool_call") as span: 来包裹你的自定义逻辑,并在 span 中添加 inputsoutputs。这需要更深入地理解 LangChain 的 CallbackManager 机制。
  6. “性能影响,不可不察”:Trace 也有开销。

    • :虽然 LangSmith Trace 的开销通常很小,但在极高并发或对延迟要求极高的场景下,网络传输和数据记录可能会带来微小的额外延迟。
    • 避坑:在生产环境部署时,评估 LangSmith Tracing 带来的性能影响。通常,在开发和测试阶段开启,在生产环境可以考虑只对一部分请求进行采样追踪,或者在出现问题时动态开启。
  7. “成本计算,仅供参考”:与实际账单有偏差。

    • :LangSmith 显示的成本通常是基于 token 数量的估算,它可能与你实际的 LLM 提供商账单存在细微差异(例如,API 调用次数、并发折扣、不同模型的精确计费方式等)。
    • 避坑:将 LangSmith 的成本数据作为快速参考和趋势分析的工具,但最终的财务核算仍需以 LLM 提供商