【9】Langchain实战案例:构建一个聊天机器人

文章目录

Langchain实战案例:构建一个聊天机器人

使用 LangChain 构建聊天机器人,核心在于将我们之前学过的大模型(LLM)提示词模板(Prompt) 、**记忆组件(Memory)以及检索增强(RAG)**等组件进行编排。

根据 LangChain 官方教程,构建一个具备上下文理解能力的聊天机器人,通常遵循以下核心架构和步骤:

1. 核心架构设计

一个标准的 LangChain 聊天机器人主要由以下几个模块协同工作:

  • 消息与提示词(Messages & Prompts):聊天机器人接收的是带有角色属性(如 System, Human, AI)的消息,而非纯文本。提示词模板负责将这些消息格式化。
  • 记忆系统(Memory):用于管理短期记忆(如当前对话的上下文窗口)和长期记忆(如基于向量数据库的历史检索),使机器人具备"记住"前文的能力。
  • 检索增强(RAG / Retrieval):如果机器人需要回答特定领域的问题,需要为其添加检索功能,使其能先从外部知识库中查找资料,再生成回答。
  • 工具与代理(Tools & Agents):赋予聊天机器人执行外部操作(如联网搜索、调用 API)的能力,使其从"被动问答"变为"主动解决问题"。

2. 基础实现步骤

结合 LangChain 的官方指南,构建一个聊天机器人的标准流程如下:

  • 初始化聊天模型:选择支持对话交互的 ChatModel(如 OpenAI、通义千问等),并设定好系统提示词(System Prompt)来定义机器人的性格和行为边界。
  • 挂载记忆组件 :在构建对话链(Chain)时,接入 ConversationBufferMemory 或滑动窗口记忆等组件。LangChain 会自动将历史消息注入到当前的 Prompt 中。
  • 处理长对话历史:当对话轮数过多导致超出模型上下文限制时,可以通过"上下文压缩(Contextual Compression)"或摘要机制,提炼关键信息,防止 Token 溢出。
  • 接入外部知识(可选):如果是一个企业客服或文档问答机器人,可以将向量数据库作为检索器(Retriever)挂载到聊天机器人上,实现基于私有数据的精准问答。

3. 进阶与生产级优化

如果你希望构建一个生产级别的聊天机器人,还可以进一步探索:

  • 使用 LangGraph 进行高级编排:LangGraph 提供了更强大的状态机机制,适合构建包含复杂条件分支、循环和人工介入(Human-in-the-loop)的高级聊天机器人。
  • 流式输出(Streaming):通过 LangChain 的流式传输功能,实现打字机效果,大幅提升用户的交互体验。
  • 监控与调试:接入 LangSmith 平台,对聊天机器人的每一轮对话、Token 消耗、检索命中率进行追踪、评估和调试。

如果你已经准备好了,我们可以直接开始写代码。你是想先实现一个带记忆的基础聊天机器人 ,还是想直接挑战带有 RAG 知识库检索的高级聊天机器人

基础版:带记忆的基础聊天机器人

在 LangChain 当前主推的 LCEL(LangChain Expression Language)体系中,构建带记忆的聊天机器人比早期更加透明和灵活。在新的 Runnable 体系中,Memory 不再是 Chain 的"隐藏参数",而是显式的状态管理。

下面我将为你提供一份完整、可运行的代码,演示如何使用 LCEL 构建一个基础版聊天机器人:

核心代码实现

python 复制代码
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# 1. 初始化大模型与输出解析器
# 确保你已经配置了环境变量 OPENAI_API_KEY
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
parser = StrOutputParser()

# 2. 定义 Prompt 模板(关键:必须显式包含 {history} 变量)
prompt = PromptTemplate(
    input_variables=["history", "question"],
    template="""你是一个友好且聪明的AI助手。
以下是之前的对话历史:
{history}

当前用户问题:{question}
请基于上下文给出连贯、准确的回答。"""
)

# 3. 使用 LCEL 组合基础链 (Prompt | LLM | Parser)
chain = prompt | llm | parser

# 4. 定义对话历史存储机制
store = {}
def get_session_history(session_id: str):
    """根据 session_id 获取或创建聊天历史"""
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

# 5. 构建带记忆的 Runnable
chat_chain = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="question",      # 对应 prompt 中的用户输入变量
    history_messages_key="history",     # 对应 prompt 中的历史变量
)

# 6. 测试多轮对话
config = {"configurable": {"session_id": "test-session-001"}}

print("--- 第一轮对话 ---")
response1 = chat_chain.invoke({"question": "你好,我叫小明。"}, config=config)
print("AI:", response1)

print("\n--- 第二轮对话 ---")
response2 = chat_chain.invoke({"question": "你还记得我叫什么名字吗?"}, config=config)
print("AI:", response2)

核心逻辑解析

  1. LCEL 表达式写法 :通过 prompt | llm | parser 这种管道符写法,将提示词、模型和输出解析器串联起来。这比早期的 LLMChain 更透明、也更可组合。
  2. 显式状态管理 :历史是否生效,取决于 Prompt 模板中是否显式使用了 {history} 占位符。这是很多人第一次使用 RunnableWithMessageHistory 时最容易忽略的一点。
  3. Session 隔离 :通过传入 session_id,系统可以支持多个用户的并发对话,且各自的记忆互不干扰。所有数据默认只存在你的本地机器上(内存中)。

💡 进阶演进建议

当前我们使用的是 InMemoryChatMessageHistory,这意味着一旦程序重启,记忆就会丢失。在实际的工程落地中,Memory 的存储是需要可演进的。

当你的应用需要长期保存用户偏好时,你可以将 InMemoryChatMessageHistory 替换为基于 Redis 或本地 SQLite/PostgreSQL 的持久化存储方案(如 Checkpointer),从而实现真正的长期记忆。


基础版跑通后,你的 AI 助手已经具备了"记忆力"。接下来,你想:

  1. 接入 RAG 知识库:让这个聊天机器人能够查阅你本地的 PDF 或 TXT 文档来回答问题?
  2. 搭建可视化界面:用 Streamlit 快速给它套上一个类似微信的聊天网页界面?

扩展版:带有 RAG 知识库检索的高级聊天机器人

给聊天机器人接入 RAG(检索增强生成)知识库,是让 AI 从"通用百科"进化为"私人领域专家"的关键一步。

在 LangChain 最新的 LCEL(LangChain Expression Language)体系中,实现这个功能的核心思路是:让大模型在回答前,先去向量数据库中"查资料",并将查到的资料(Context)和用户的历史对话(History)一起打包发给大模型。

下面我为你提供一套完整、可运行的代码,演示如何将多轮对话记忆本地文档检索完美结合。

第一步:准备环境依赖

除了基础的 LangChain 和 OpenAI(或你使用的其他模型),你还需要安装文档解析和向量数据库相关的库:

bash 复制代码
pip install langchain langchain-openai langchain-community pypdf chromadb

第二步:核心代码实现

这段代码将文档加载、向量化、历史记忆和检索问答全部串联在了一起:

python 复制代码
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma

# ================= 1. 构建本地 RAG 知识库 =================
def build_rag_retriever(file_path):
    # 根据文件后缀选择加载器
    if file_path.endswith(".pdf"):
        loader = PyPDFLoader(file_path)
    elif file_path.endswith(".txt"):
        loader = TextLoader(file_path, encoding="utf-8")
    else:
        raise ValueError("仅支持 PDF 或 TXT 文件")
        
    documents = loader.load()
    # 将长文档切分为小块,保证上下文连贯
    splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
    texts = splitter.split_documents(documents)
    
    # 存入本地 Chroma 向量数据库
    embeddings = OpenAIEmbeddings()
    vectorstore = Chroma.from_documents(texts, embeddings)
    return vectorstore.as_retriever(search_kwargs={"k": 3})

# 替换为你的本地文档路径
retriever = build_rag_retriever("your_document.pdf") 

# ================= 2. 构建带记忆与检索的 LCEL 链 =================
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# 核心 Prompt:同时包含历史对话和检索到的文档上下文
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的AI助手。请严格根据以下参考文档回答用户问题。如果文档中没有相关信息,请如实告知,不要编造。\n\n参考文档:\n{context}"),
    MessagesPlaceholder(variable_name="chat_history"),  # 注入历史对话
    ("human", "{input}")
])

# 使用 LangChain 内置工具,自动将检索结果拼接为字符串
from langchain_core.runnables import RunnablePassthrough
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# 组装核心处理链
rag_chain = (
    {"context": retriever | format_docs, "input": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# ================= 3. 挂载对话记忆 =================
store = {}
def get_history(session_id):
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

# 最终带记忆的 RAG 链
final_chain = RunnableWithMessageHistory(
    rag_chain,
    get_history,
    input_messages_key="input",
    history_messages_key="chat_history",
)

# ================= 4. 开始多轮对话测试 =================
config = {"configurable": {"session_id": "user_001"}}

print("💡 提示:输入 'exit' 退出对话")
while True:
    query = input("\n你:")
    if query.lower() in ["exit", "q"]:
        break
    response = final_chain.invoke(query, config=config)
    print(f"AI:{response}")

核心逻辑解析

  1. 检索与生成的解耦{"context": retriever | format_docs, "input": RunnablePassthrough()} 这一行是 LCEL 的精髓。它在后台并行处理:把用户问题发给检索器拿回文档,同时把用户原话透传下去。
  2. 防幻觉机制:在 System Prompt 中明确约束了"严格根据参考文档回答,不要编造",这是 RAG 系统保证回答准确性的底线。
  3. 记忆与检索的融合MessagesPlaceholder 确保了 AI 不仅"懂文档",还"记得你"。比如你可以先问"文档里提到的核心概念是什么?",接着问"你能用通俗的话给我解释一下吗?",AI 能够结合上一轮的上下文和文档内容完美作答。

现在,你的 AI 助手已经具备了"查阅资料"和"记住对话"的双重能力!

接下来,你想如何完善它?

  1. 搭建可视化界面:用 Streamlit 快速给它套上一个类似微信的网页聊天界面,方便分享和测试?
  2. 进阶优化:了解如何优化检索质量(比如引入混合检索、重排序 Rerank 机制),让 AI 找资料更准?

进阶版:LangGraph构建生产级别的聊天机器人

从基础的 LCEL 链过渡到 LangGraph,意味着你的 AI 应用从"流水线"进化成了"状态机"。LangGraph 的核心优势在于它能精确控制执行路径、管理持久化状态,并支持条件分支与人工介入(Human-in-the-loop),这正是构建生产级复杂系统的关键。

下面我为你梳理一套构建生产级聊天机器人的核心架构与实战指南:

1. 核心架构设计:状态与节点

在 LangGraph 中,我们不再只是传递字符串,而是维护一个共享的"状态(State)"。

  • 定义状态结构 :使用 TypedDict 严格声明字段类型。特别是 messages 字段,必须使用 Annotated[list[AnyMessage], operator.add](或内置的 add_messages),确保新消息追加到列表而不是覆盖旧消息,防止对话历史丢失。
  • 模块化节点(Nodes):将意图识别、RAG 检索、Agent 决策等拆分为独立的函数。每个节点只读取和更新自己关心的状态字段,互不干扰。

2. 工作流编排:边与条件路由

通过图结构(Graph)将节点连接起来,实现复杂的业务逻辑:

  • 条件边(Conditional Edges):例如,在"意图分类"节点后,根据用户的意图(如"技术支持"、"账单查询")动态路由到不同的专家 Agent 节点。
  • 循环与回退 :当 Agent 的分析结果置信度低于阈值时,可以通过显式的 add_edge 构造循环路径,让其回到路由器重新决策,并配合 retry_count 状态变量防止无限死循环。

3. 生产级必备:状态持久化与记忆

生产环境不能仅依赖内存,必须引入 Checkpointer(检查点机制):

  • 会话隔离 :在调用 app.invoke() 时传入 config={"configurable": {"thread_id": "user-001"}}。相同的 thread_id 会共享同一段对话历史,实现跨会话的连续性。
  • 断点恢复:生产环境中若服务宕机,系统可从数据库(如 PostgresSaver)中读取最近的 checkpoint 恢复执行,无需从头开始。

4. 人工介入(Human-in-the-Loop)

对于敏感操作(如退款审批、发送邮件),AI 不能擅自做主:

  • 中断与恢复 :在关键决策节点使用 interrupt() 函数。图运行到此处会暂停,并将当前完整状态保存到 Checkpointer 中。
  • 等待人类决策 :暂停时长不受限制(几分钟到几天均可)。当人类通过外部 UI 做出"批准"或"拒绝"的决定后,使用 Command(resume="approved") 唤醒图,从暂停的位置继续往下走。

5. 生产环境的工程化保障

  • 错误处理与降级 :为节点添加装饰器(如 @robust_node_wrapper),捕获异常并在状态中注入错误信息,甚至自动触发转人工审核(requires_human_review)的降级流程。
  • 时间旅行(调试利器) :利用 app.get_state_history(thread_id) 查看一次运行的完整历史快照。如果对某一步的结果不满意,可以直接回到该步的 checkpoint 修改输入并重新执行,极大降低了复杂 Agent 的调试成本。
  • 流式输出 :使用 app.stream() 进行单步调试或向前端推送打字机效果,实时观察当前节点名和状态快照,这是排查死循环最有效的手段。

LangGraph 的架构虽然强大,但代码量相对较长。为了让你更直观地理解,我们可以直接上手写代码。你想先看哪个具体场景的实现?

  1. 带条件路由的多智能体客服系统:演示意图识别 + 动态分发到不同专家 Agent。
  2. 带人工审批的自动化工作流 :演示 interrupt 暂停、等待人类输入并恢复执行的完整闭环。

进阶版:案例一:LangGraph构建带条件路由的多智能体客服系统

构建带条件路由的多智能体客服系统,是 LangGraph 最经典的应用场景之一。它的核心思想是:将复杂的客服任务拆解为多个专职节点(如意图识别、订单专家、物流专家),并通过一个"路由器"根据用户的实时意图,动态决定将任务交给谁处理。

下面为你提供一套完整的实战代码,演示如何实现"意图识别 → 动态路由 → 专家处理"的完整闭环:

核心代码实现

python 复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_core.messages import HumanMessage, AIMessage

# ================= 1. 定义共享状态 (State) =================
class CustomerServiceState(TypedDict):
    # 使用 add_messages 确保对话历史是追加而不是覆盖
    messages: Annotated[list, add_messages]
    # 记录识别出的用户意图
    intent: str

# ================= 2. 定义各个专职节点 (Nodes) =================
# 意图识别节点
def intent_recognition(state: CustomerServiceState):
    # 实际生产中这里会调用 LLM 进行分类
    last_msg = state["messages"][-1].content.lower()
    if "订单" in last_msg:
        intent = "ORDER"
    elif "物流" in last_msg or "快递" in last_msg:
        intent = "LOGISTICS"
    else:
        intent = "GENERAL"
    return {"intent": intent}

# 订单处理专家节点
def order_agent(state: CustomerServiceState):
    response = AIMessage(content="【订单专家】:您好,已为您查询到订单状态,目前正在仓库打包中,预计今天发货。")
    return {"messages": [response]}

# 物流处理专家节点
def logistics_agent(state: CustomerServiceState):
    response = AIMessage(content="【物流专家】:您好,您的包裹已到达郑州转运中心,预计明天上午为您派送。")
    return {"messages": [response]}

# 通用兜底节点
def general_agent(state: CustomerServiceState):
    response = AIMessage(content="【通用助手】:您好,请问有什么我可以帮您的吗?")
    return {"messages": [response]}

# ================= 3. 定义条件路由逻辑 (Router) =================
def route_to_agent(state: CustomerServiceState):
    # 根据状态中的 intent 字段,动态决定下一步走向
    if state['intent'] == 'ORDER':
        return "order_agent"
    elif state['intent'] == 'LOGISTICS':
        return "logistics_agent"
    else:
        return "general_agent"

# ================= 4. 构建并编译工作流图 (Graph) =================
workflow = StateGraph(CustomerServiceState)

# 添加节点
workflow.add_node("intent_recognition", intent_recognition)
workflow.add_node("order_agent", order_agent)
workflow.add_node("logistics_agent", logistics_agent)
workflow.add_node("general_agent", general_agent)

# 设置入口:所有请求先经过意图识别
workflow.add_edge(START, "intent_recognition")

# 设置条件边:根据路由函数的返回值,动态分发到对应的专家节点
workflow.add_conditional_edges(
    "intent_recognition",
    route_to_agent,
    {
        "order_agent": "order_agent",
        "logistics_agent": "logistics_agent",
        "general_agent": "general_agent"
    }
)

# 所有专家处理完毕后,结束当前流程
workflow.add_edge("order_agent", END)
workflow.add_edge("logistics_agent", END)
workflow.add_edge("general_agent", END)

# 编译图
app = workflow.compile()

# ================= 5. 运行与测试 =================
test_inputs = [
    "帮我查一下我的订单到哪了",
    "我的快递怎么还没更新物流信息?",
    "你们营业时间是几点到几点?"
]

for user_input in test_inputs:
    print(f"👤 用户: {user_input}")
    config = {"configurable": {"thread_id": "test-session"}}
    result = app.invoke(
        {"messages": [HumanMessage(content=user_input)]}, 
        config=config
    )
    print(f"🤖 AI: {result['messages'][-1].content}\n")

架构亮点解析

  1. 状态隔离与共享CustomerServiceState 是整个图的"中枢神经"。每个节点只修改自己负责的字段(如意图节点只改 intent,专家节点只追加 messages),互不干扰,极大降低了系统耦合度。
  2. 动态路由 (add_conditional_edges) :这是 LangGraph 区别于传统 Chain 的核心。系统不再是一条直线,而是像十字路口,根据实时的 intent 状态,精准将任务派发给最合适的专家。
  3. 高度可扩展性 :如果未来需要增加"售后退款专家",你只需新增一个节点函数,并在 route_to_agent 中增加一个 elif 判断,完全不需要修改现有的业务代码。

这套架构已经具备了企业级智能客服的雏形。接下来,你想继续挑战哪个方向?

  1. 带人工审批的自动化工作流 :当 AI 无法处理时,演示如何使用 interrupt 暂停流程,等待人类客服介入并恢复执行。
  2. 接入真实大模型 :把上面硬编码的 if/else 替换为真实的 LLM 意图分类,让路由更聪明。

进阶版:案例二:LangGraph构建带人工审批的自动化工作流

在 LangGraph 中实现"人工介入(Human-in-the-loop)"的核心在于中断(Interrupt)与恢复(Resume)机制。这通常用于处理高风险操作(如退款审批、敏感信息发送)或 AI 遇到无法解决的复杂问题时。

要实现这个闭环,系统需要三个核心要素:

  1. Checkpointer(检查点):用于在暂停时保存当前状态,以便稍后能"断点续跑"。
  2. interrupt() 函数:在特定节点主动挂起工作流,并将需要人工审核的数据抛出。
  3. Command(resume=...):接收人类的决策(如批准或拒绝),并唤醒工作流继续执行。

下面我为你编写一套完整的实战代码,演示当 AI 遇到复杂问题时,如何自动暂停并等待人类客服接管:

核心代码实现

python 复制代码
import operator
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import HumanMessage, AIMessage

# ================= 1. 定义共享状态 =================
class SupportState(TypedDict):
    messages: Annotated[list, add_messages]
    resolution: str  # 记录最终处理结果

# ================= 2. 定义工作流节点 =================
# AI 自动处理节点
def ai_agent(state: SupportState):
    last_msg = state["messages"][-1].content
    # 模拟 AI 判断:如果包含"投诉"或"退款",AI 无法处理,需要人工介入
    if "投诉" in last_msg or "退款" in last_msg:
        return {"messages": [AIMessage(content="检测到复杂售后问题,正在为您转接人工客服...")]}
    return {"messages": [AIMessage(content="您好,我是AI助手,已为您解决常规问题。")]}

# 人工审核/介入节点
def human_review(state: SupportState):
    # 【核心】调用 interrupt() 暂停流程,并将当前对话抛给前端/人类
    # 流程会在这里挂起,直到收到 Command(resume=...)
    decision = interrupt({
        "prompt": "AI无法处理该问题,请人工客服介入",
        "messages": state["messages"]
    })
    
    # 恢复执行后,interrupt() 会返回人类的决策
    if decision == "approve":
        return {"messages": [AIMessage(content="【人工客服】:您好,您的退款申请已批准,款项将在3个工作日内原路返回。")], "resolution": "approved"}
    else:
        return {"messages": [AIMessage(content="【人工客服】:抱歉,您的退款申请未通过审核。")], "resolution": "rejected"}

# 结束通知节点
def end_node(state: SupportState):
    return {"messages": [AIMessage(content="工单已处理完毕,感谢您的反馈。")]}

# ================= 3. 构建并编译工作流 =================
workflow = StateGraph(SupportState)

workflow.add_node("ai_agent", ai_agent)
workflow.add_node("human_review", human_review)
workflow.add_node("end_node", end_node)

workflow.add_edge(START, "ai_agent")

# 条件路由:根据 AI 的回复内容决定是否转人工
def should_transfer_to_human(state: SupportState):
    last_msg = state["messages"][-1].content
    return "转接人工" in last_msg

workflow.add_conditional_edges(
    "ai_agent",
    should_transfer_to_human,
    {
        True: "human_review",   # 需要人工介入
        False: "end_node"       # AI 已解决,直接结束
    }
)

workflow.add_edge("human_review", "end_node")
workflow.add_edge("end_node", END)

# 【关键】编译时必须传入 checkpointer,否则无法保存暂停时的状态
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

# ================= 4. 运行与测试 =================
config = {"configurable": {"thread_id": "user-session-001"}}

# 第一阶段:触发 AI 无法处理的场景
print("=== 用户发起复杂请求 ===")
result = app.invoke(
    {"messages": [HumanMessage(content="我要投诉!我买的商品坏了,要求立刻退款!")]}, 
    config=config
)
print(f"当前进度: {result['messages'][-1].content}")

# 第二阶段:模拟人类客服查看并做出决策
# 在实际生产中,这里是通过 Web 界面提交表单,然后调用 app.invoke(Command(...))
print("\n=== 人工客服介入并批准退款 ===")
final_result = app.invoke(
    Command(resume="approve"),  # 传入 "approve" 唤醒 human_review 节点
    config=config
)

print(f"最终回复: {final_result['messages'][-1].content}")

架构亮点与生产建议

  1. 优雅的挂起机制interrupt() 并不是让程序死循环等待(这会耗尽服务器资源),而是将当前状态序列化存入 Checkpointer 后直接结束本次请求。当人类做出决策后,系统通过 Command(resume=...) 重新加载状态并继续执行。
  2. 状态一致性 :由于使用了 thread_id,无论人类是 1 分钟后审批,还是 3 天后审批,系统都能精准找回当时的对话上下文。
  3. 生产环境建议 :在真实的业务中,建议将 MemorySaver 替换为 PostgresSaverRedisSaver 等持久化存储,防止服务器重启导致审批中的工单丢失。同时,审批权限应与业务系统的 RBAC(基于角色的权限控制)打通,确保只有对应权限的客服才能恢复该流程。

至此,你已经掌握了从基础对话、RAG 知识库,到多智能体路由,再到人工介入的完整 LangGraph 生产级架构!

接下来,你想探索哪个方向?

  1. 流式输出(Streaming):如何配合前端实现"打字机"效果,并实时展示当前正在执行哪个节点?
  2. 时间旅行(Time Travel):如何利用 LangGraph 的 Checkpoint 历史,回溯到对话的任意一步进行修改和重新执行(超级强大的调试功能)?

其他技术:流式输出(Streaming)

在 LangGraph 中实现流式输出(Streaming),不仅能打造类似 ChatGPT 的"打字机"效果,还能让前端实时感知后台 Agent 的执行进度。这需要通过不同的 stream_mode 来实现。

以下是实现该功能的核心方案与代码示例:

1. 核心流式模式选择

LangGraph 提供了多种流式模式,通常可以组合使用以满足不同的需求:

  • messages 模式:实现 Token 级别的流式输出。它会在 LLM 生成内容的瞬间,将文本片段(chunk)实时推送到前端,非常适合打字机效果。
  • updates 模式:在节点(Node)执行结束后触发。它会返回刚刚执行完的节点名称及其产出的状态更新,非常适合用来在前端展示"当前正在执行哪个节点"的进度提示。

2. 实战代码:打字机效果 + 节点进度提示

假设你已经编译好了一个 LangGraph 图 app,可以通过以下代码同时获取文本流和节点状态更新:

python 复制代码
import asyncio

async def stream_agent():
    # 初始化输入和配置
    inputs = {"messages": [("user", "帮我查一下订单状态")]}
    config = {"configurable": {"thread_id": "session-001"}}

    # 同时开启 updates 和 messages 两种流式模式
    async for chunk in app.astream(inputs, config, stream_mode=["updates", "messages"]):
        # 1. 处理节点状态更新(展示进度)
        if isinstance(chunk, dict) and "updates" in str(type(chunk)):
            # 提取当前执行的节点名称
            for node_name, update_data in chunk.items():
                print(f"[系统进度] 正在执行节点: {node_name}...")
        
        # 2. 处理 Token 流(打字机效果)
        elif isinstance(chunk, tuple):
            message_chunk, metadata = chunk
            # 提取模型生成的文本片段并实时输出
            if hasattr(message_chunk, "content") and message_chunk.content:
                print(message_chunk.content, end="", flush=True)

# 运行异步流式输出
asyncio.run(stream_agent())

3. 前端对接与生产级优化建议

在实际生产环境中,LangGraph 的流式数据需要通过 SSE(Server-Sent Events)或 WebSocket 桥接到前端。为了保证用户体验,建议在前端做好以下处理:

  • 增量渲染(Partial Message) :前端不应简单地"收到什么就显示什么",而应维护一个状态机。收到 on_text_delta 事件时将 chunk 追加到当前内容;收到 on_chain_end 事件时,标记该消息为已完成,防止文字抖动或内容回退。
  • 背压处理(Backpressure):大模型每秒可能吐出几十个 Token,如果前端处理不过来会导致消息堆积。建议在服务端实现批量打包机制(例如每收集 10 个 delta 或等待 50ms 后一次性推送),以减少网络往返和渲染开销。
  • 工具调用提示 :除了文本流,你还可以监听 on_tool_starton_tool_end 事件。当 Agent 调用外部 API 时,前端可以显示"正在查询数据库..."等加载动画,极大提升交互体验。

掌握了流式输出后,你的 Agent 在交互体验上已经非常接近商业级产品了。接下来,你想探索哪个方向?

  1. 时间旅行(Time Travel):如何利用 Checkpoint 历史,回溯到对话的任意一步,修改状态并重新执行(超级强大的调试与纠错功能)?
  2. LangSmith 监控:如何将这套系统接入 LangSmith,在云端可视化追踪每一次调用的 Token 消耗、检索命中率和执行路径?

其他技术:执行回退(Time Travel)

LangGraph 的"时间旅行(Time Travel)"是其最强大的特性之一。它基于 Checkpoint(检查点)机制,让你能够像使用 Git 一样管理 Agent 的执行历史。你可以随时回溯到任意历史节点,查看当时的状态,甚至修改状态后从该节点重新执行,探索不同的业务走向。

要实现时间旅行,核心依赖于以下三个 API:

  1. app.get_state_history(config) :获取该会话(thread_id)的所有历史 Checkpoint 列表。
  2. app.get_state(config):获取某个特定 Checkpoint 的完整状态快照(包括当前消息、下一步要执行的节点等)。
  3. app.update_state(config, values, as_node="..."):在指定的历史节点上手动修改状态,并创建一条新的"分叉时间线"。

核心代码演示:查看历史与修改重跑

假设你已经运行了一个带 Checkpointer 的 LangGraph 图,以下是实现时间旅行的代码:

python 复制代码
from langgraph.checkpoint.memory import MemorySaver

# 假设 app 已经使用 checkpointer 编译完成
# memory = MemorySaver()
# app = graph.compile(checkpointer=memory)

config = {"configurable": {"thread_id": "user-session-001"}}

# ================= 1. 查看完整执行历史 =================
print("=== 获取对话历史快照 ===")
history = list(app.get_state_history(config))
for i, state in enumerate(history):
    print(f"Step {i}: 下一步将执行 {state.next}, 消息数量: {len(state.values.get('messages', []))}")

# ================= 2. 回溯到特定历史节点 =================
# 假设我们想回到倒数第二步(索引为 1 的 Checkpoint)
if len(history) > 1:
    old_checkpoint_config = history.config
    
    # 查看该历史节点的详细状态
    snapshot = app.get_state(old_checkpoint_config)
    print(f"\n=== 回溯到 Step 1 的状态 ===")
    print(f"当前消息: {snapshot.values['messages'][-1].content}")
    print(f"下一步节点: {snapshot.next}")[[source_group_web_1]]

    # ================= 3. 修改状态并创建新分支(时间旅行) =================
    # 如果我们觉得 AI 在 Step 1 之后的回答不好,可以修改当时的输入或状态,让它重新生成
    new_messages = [{"role": "human", "content": "请换一种更幽默的方式重新回答"}]
    
    # 使用 update_state 从该节点重跑
    # as_node 指定从哪个节点开始继续执行
    new_config = app.update_state(
        old_checkpoint_config, 
        {"messages": new_messages}, 
        as_node="ai_agent"  # 假设你要从 ai_agent 节点重新执行
    )
    
    print("\n=== 已创建新的时间分支,继续执行 ===")
    # 此时图会从修改后的状态继续往下走,且不会影响原来的历史
    for event in app.stream(None, new_config, stream_mode="updates"):
        print(event)

核心逻辑解析

  1. 透明的状态管理:LangGraph 的 Checkpointer 会在每个节点执行后自动保存完整的状态快照。你不需要手动拼接历史消息,框架会替你管理。
  2. 创建分叉时间线update_state 并不会覆盖原来的历史记录,而是基于旧的 Checkpoint 创建了一个新的父级引用。这就像在 Git 中基于某个旧 Commit 拉出了一个新分支,非常适合调试失败步骤或探索"如果当时做了不同选择会怎样"的替代路径。
  3. 人工介入的终极形态 :在生产环境中,如果 AI 写了一段文案或生成了一个退款方案,审核人员可以直接调用 update_state 修改初稿字段,然后让图从修改后的状态继续执行,而不是退回让 AI 重新生成,极大节省了 Token 和时间。

至此,LangGraph 的核心高级特性(状态机、人工介入、流式输出、时间旅行)你已经全部掌握!

接下来,为了让你的 Agent 真正具备"生产级"的可观测性,你想探索哪个方向?

  1. LangSmith 监控:如何将这套系统接入 LangSmith,在云端可视化追踪每一次调用的 Token 消耗、检索命中率和完整执行路径?
  2. 生产级部署架构:如何将 Checkpointer 替换为 PostgreSQL/Redis,并结合 FastAPI 部署一套支持高并发、多租户的真实后端服务?

其他技术:LangSmith 监控

将 LangGraph 接入 LangSmith 是迈向生产级应用的关键一步。LangSmith 作为 LangChain 生态下的核心可观测性平台,能够对你的 LLM 应用进行全链路的追踪(Tracing)、调试(Debugging)和评估(Evaluation)。

得益于 LangChain 生态的无缝协作,接入 LangSmith 的门槛极低。你完全不需要修改任何现有的业务代码,只需通过配置环境变量即可开启全链路监控。

以下是完整的接入指南:

第一步:获取 LangSmith API Key

  1. 访问 LangSmith 官方平台注册并登录(支持 GitHub 等快捷登录)。
  2. 登录后,进入平台「Settings」(设置)页面,找到「API Keys」选项,创建并复制你的个人 API Key。

第二步:配置环境变量

在你的运行环境(如 .env 文件、终端或 Python 代码中)设置以下核心环境变量:

bash 复制代码
# 1. 填入你刚刚获取的 API Key
export LANGSMITH_API_KEY="lsv2_pt_你的密钥"

# 2. 开启 LangSmith 追踪功能(必须)
export LANGCHAIN_TRACING_V2="true"

# 3. 设置项目名称(可选,用于在 LangSmith 中分类和管理不同的应用)
export LANGCHAIN_PROJECT="my-langgraph-agent"

配置完成后,再次运行你的 LangGraph 代码,所有的执行数据就会自动上报到 LangSmith 平台。

第三步:在云端可视化追踪你的 Agent

登录 LangSmith 控制台,你可以直观地看到以下核心监控数据:

  1. 执行路径与耗时分布(Waterfall 追踪)
    在 Trace 详情页,你可以看到 Agent 执行的全流程耗时分布。例如,意图识别、RAG 检索、LLM 生成等每个节点的具体耗时,帮助你快速定位性能瓶颈。
  2. Token 消耗与成本监控
    平台会记录每一次 LLM 调用的输入、输出、Token 消耗量以及错误信息。这对于控制生产环境的 API 成本至关重要。
  3. 交互式调试(LangSmith Studio)
    如果你发现某次对话 AI 回答出错,可以直接在 LangSmith 中查看该次运行的详细 Trace。你可以在 Studio 中模拟输入,甚至在任意节点暂停、检查中间状态、修改输入后继续执行,这比单纯看日志排查问题高效得多。

💡 国内开发者注意事项

由于 LangSmith 的服务器部署在国外,国内普通网络环境可能无法直接访问。如果你的团队面临网络限制或私有化部署需求,推荐考虑国内替代方案 Langfuse

  • 优势:Langfuse 是开源的,支持完全免费的私有部署,且功能与 LangSmith 高度对齐。
  • 兼容性:LangSmith 的追踪代码几乎可以无缝平移到 Langfuse,学习成本极低。

至此,你已经打通了从代码编写、流式输出、状态调试到云端监控的完整闭环!接下来,你可以选择:

  1. 生产级部署架构:将内存 Checkpointer 替换为 PostgreSQL/Redis,结合 FastAPI 部署支持高并发的真实后端服务。
  2. 自动化评估(Evals):利用 LangSmith 构建测试数据集,在每次修改 Prompt 后自动跑分,确保系统不会发生"回归退化"。
相关推荐
白白白飘1 小时前
【8】补充与拓展:Langchain的联网搜索Agent,使用Tavily工具
服务器·网络·langchain
爱奥尼欧2 小时前
【LangChain】2.提示词工程
人工智能·microsoft·langchain
txg6663 小时前
机器人领域热点简报(2026-06-30 — 2026-07-07)
人工智能·搜索引擎·机器人
yonlin11 小时前
Telegram Rss Bot - RssStream 面向创作者和开发者的 Telegram 自动化机器人简介
机器人·telegram·电报
神经智研社14 小时前
ROS2--8章 机器人坐标系 tf2库
机器人·ros2·机器人环境搭建·ros2 jazzy·机器人坐标系系统
YMWM_15 小时前
企业级机器人数据平台开发
机器人·数据平台
某林21217 小时前
从硬件解耦、3D公差设计到ROS 2柔性导航
人工智能·3d·机器人·ros2·技术复盘
陳陈陳19 小时前
🤖 Agent开发完全指南:从LLM到智能体的蜕变之旅
langchain·llm·agent
倾颜20 小时前
AI Mind 的多会话短期记忆容器:如何隔离多个聊天上下文
langchain·agent·next.js