LangGraph 入门教程:构建 AI 工作流 [ 案例二 ]

【案例二】支持搜索的智能代理系统

接下来我们上手第二个实战案例 ------支持搜索的智能代理系统 ,我们要把这套系统搭建完成,并且将它规范定义为 LangGraph 图示化工作流架构。

案例介绍

案例一只是为了演示什么是 Graph 。对于 LangGraph ,实际上是要调用 LLM 来完成智能应用系统。因此该案例会将使用 Graph API ,来完成一个支持搜索、支持调用 LLM 的智能代理系统。核心功能如下:

  • 智能对话与工具调用:基于聊天模型,能够理解用户问题并决定是否需要调用搜索工具
  • 自动搜索整合 :通过 Tavily 搜索工具获取实时信息,并将搜索结果整合到回答中
  • 循环决策机制:能够多次调用工具和模型,直到获得满意答案

我们本次依旧使用 Tavily 搜索工具,这个工具在 LangChain 中已经详细讲解过基础用法,而 LangChain 里的工具、模型等基础组件,都可以直接复用在 LangGraph 中。和之前 LangChain 链式调用不同,本次核心是把知识搜索系统改造成图结构工作流的方式来编写代码,理解其图示化的构建逻辑。

流程设置如下:

根据实际自行拓展,例如我们可以用它来:

实时信息查询:查询最新新闻、股价、天气等

今天特斯拉的最新股价是多少?还有哪些重要的科技新闻?

事实核查:验证信息的准确性

有人说阿波罗登月是伪造的,请提供证据来验证登月的真实性

深度研究:多轮搜索获取全面信息

我需要关于 ' 人工智能在医疗诊断中的应用 ' 的最新研究论文、临床试验结果和专家观点,请提供全面的综述

知识扩展:补充模型知识库之外的信息

GPT-4o-mini 的训练数据截止到什么时候?之后有哪些重要的 AI 发展事件?


编码思路

构建 Graph 图,首先需要定义状态,然后定义并添加节点和边,最后编译它。编译提供了对图形结构的一些基本检查(没有孤立节点等)。

正式编码前,我们先梳理整套业务流程:第一步是用户输入提问 ,我们可以准备两类问题做对比,一类是需要实时数据的股价、天气类问题,一类是1+1等于几这类常识简单问题。

输入问题后,交由大模型自主判断 是否需要调用搜索工具:像股价、天气这类有时效性的问题,大模型训练数据存在时间截止,无法给出最新结果,必须触发网络搜索;而基础常识类问题,大模型可直接作答,无需调用工具。

如果判定需要搜索,就调用 Tavily 搜索工具执行检索,工具调用完成后会生成 ToolMessage;再由大模型结合用户原始问题与工具返回的检索结果,做内容整合,生成最终的 AIMessage 给到用户。结果整合完成后,会再次进入判断逻辑:若已有完整答案,就直接结束流程输出结果;若仍需补充信息,则继续循环检索、整合,直到给出最终答案。

在正式定义 LangGraph 节点和流程前,我们先回顾 LangChain 的消息规范体系:用户输入对应 HumanMessage,交给绑定工具的大模型后,会返回两种 AIMessage

一种不带工具调用参数,直接给出最终答案;

另一种携带 tool_calls 工具调用参数 ,代表需要执行搜索工具。

一旦返回带工具调用的 AIMessage,就必须执行工具调用,工具执行后产出 ToolMessage;注意 ToolMessage 不能直接返回给用户,需要把 HumanMessage、带工具调用的 AIMessage、ToolMessage 整条消息列表再次交给大模型,由大模型整合生成最终的 AIMessage,这才是呈现给用户的最终回复。

之前用 LangChain 实现工具调用时,最大的痛点是需要手动维护历史消息列表 :要手动把 AIMessageToolMessage 依次追加到消息记录中,多轮调用模型、手动管理消息,写法繁琐且不优雅。而 LangGraph 完美解决了这个问题:我们可以在状态 State 中定义一个消息列表 messages,配置追加合并策略,框架会自动帮我们维护对话历史,无需手动逐条添加消息,写法简洁优雅,还能完整留存对话上下文。

除此之外,我们还可以在状态中额外定义 llm_calls 整型字段,用来记录大模型调用次数 ,方便监控计费、统计模型调用频次。在业务系统中,状态里定义 messages 消息列表几乎是标配,核心优势有三点:

  1. 完整对话记忆:全程留存所有聊天消息,实现多轮对话上下文记忆;
  2. 全局上下文维护:任意节点都能读取完整历史消息,随时调用上下文信息;
  3. 状态持久化:图流程流转过程中,对话状态不会丢失,自动追加更新消息记录。

梳理完消息规范和状态设计后,我们再来精简设计 LangGraph 节点 :整套系统只需要两个核心节点就可以完成全部流程,无需拆分过多精细节点。

设置 Nodes

根据节点的单一职责特性,即每个节点只做一件事,我们可以设置:

第一个是大模型调用节点 :负责接收历史消息、调用大模型,返回带工具调用或不带工具调用的两种 AIMessage;

第二个是工具执行节点 :专门解析工具调用参数、执行 Tavily 搜索,生成并返回 ToolMessage

即:

  • 节点 1(tool_node):专门负责搜索,获取搜索结果。
  • 节点 2(llm_call):专门负责调用 LLM,获取最终结果。

根据节点的独立性特性,即每个节点间不直接通信,而是通过 State 交互。它们接收 State,返回 State 的更新。

设置 State

根据状态的共享性特性,即所有节点都能读取和修改状态。LangGraph 在许多情况下,将以前的对话历史记录存储为 State 中的消息列表会很有帮助,这样就能通过 State 中的消息列表来跟踪整个对话的完整历史,这是构建对话系统的关键。

为此,我们可以在图状态中添加一个 messages 键,如下所示:

python 复制代码
from langchain.messages import AnyMessage
from typing_extensions import TypedDict, Annotated
import operator

class MessagesState(TypedDict):
    # 类型: list[AnyMessage] - 任意消息对象的列表
    # 合并策略: operator.add - 使用加法操作符进行状态合并
    # 效果: 当状态更新时,新的消息会追加到现有列表中,而不是替换
    messages: Annotated[list[AnyMessage], operator.add]
    # 类型: int - 整数值
    # 用途: 跟踪LLM(大语言模型)的调用次数
    llm_calls: int

注意 Annotated[list[AnyMessage], operator.add] 这个注解特别重要:

  • operator.add 表示新消息会追加到列表中,而不是替换;
  • 这确保了对话历史的连续性。

messages 的具体作用:

对话记忆

python 复制代码
# 存储完整的对话流程
messages = [
    HumanMessage(content="你好"),
    AIMessage(content="你好! 我是AI助手"),
    HumanMessage(content="什么是机器学习? "),
    AIMessage(content="机器学习是...")
]

上下文维护

当专门负责调用 LLM 的节点(llm_call 节点)需要调用 LLM 时,可以通过 State 将整个 messages 历史作为上下文:

python 复制代码
# LLM 基于完整的对话历史生成回复
response = llm.invoke(state["messages"])

状态持久化

State 中的 messages 字段确保在图的各个节点之间传递时,对话状态不会丢失:

python 复制代码
def node(state: MessagesState):
    # 可以访问完整的对话历史
    all_messages = state["messages"]
    latest_message = state["messages"][-1]
    # 处理并添加新消息
    return {"messages": [new_ai_message]}

因此,没有这个字段,系统就无法记住之前的对话内容,每次都会像第一次对话一样。

设置 Edges

节点确定后,再设计工作流的边逻辑:包含固定边和条件边。

  1. 固定边:流程起始节点固定走向大模型调用节点;工具执行节点执行完搜索后,固定回流到大模型调用节点,重新整合结果生成最终答案。
  2. 条件边 :以大模型调用节点为起点做分支判断 ------ 如果返回的 AIMessage 带有 tool_calls 参数,就路由到工具节点执行搜索;如果没有工具调用参数,说明已生成最终答案,直接走向结束节点。

对于流程设置如下所示:

且已经定义了两个节点:

  • 节点 1(tool_node):专门负责搜索,获取搜索结果。
  • 节点 2(llm_call):专门负责调用 LLM,获取最终结果。

因此,可以定义以下逻辑:

对于开始逻辑 :是当用户进行输入后,直接让 LLM 进行处理。是否需要搜索工具调用,也是通过 LLM 进行判断。因此图的入口点也是 llm_call 节点。

对于结束逻辑 :可以根据 LLM 返回的结构判断是否调用工具,来决定我们是应该继续循环还是停止循环:

  • 如果 LLM 要调用工具,则进入tool_node节点进行处理;
  • 如果 LLM 不要调用工具,则结束。

最终 Graph 效果如下图所示:

整套 LangGraph 工作流逻辑非常清晰:用户输入问题进入流程 → 进入大模型节点判断是否需要搜索 → 需要搜索则走工具节点执行检索,再回流大模型整合结果 → 无需搜索则直接结束流程输出答案。

最后总结代码实现的标准五步流程:

  • 第一步自定义图状态 State;
  • 第二步定义两大核心业务节点;
  • 第三步创建 StateGraph 图结构并添加节点;
  • 第四步配置固定边与条件边;
  • 第五步编译图、调用执行并测试效果。

只要吃透 LangChain 消息规范、理解三种消息类型的生成逻辑,就能轻松完成节点与流程设计,多实操几次就能熟练掌握 LangGraph 图示化系统的搭建思路。

代码实现

步骤 1:准备工作,定义聊天模型和搜索工具
python 复制代码
# 步骤 1: 定义工具和模型
from langchain.chat_models import init_chat_model
from langchain_tavily import TavilySearch

search = TavilySearch(max_results=4)
tools = [search]

# 绑定工具
model = init_chat_model("gpt-4o-mini", temperature=0)
model_with_tools = model.bind_tools(tools)

步骤 2:定义状态
python 复制代码
# 步骤 2: 定义状态
from langchain.messages import AnyMessage
from typing_extensions import TypedDict, Annotated
import operator

class MessagesState(TypedDict):
    # 类型: list[AnyMessage] - 任意消息对象的列表
    # 合并策略: operator.add - 使用加法操作符进行状态合并
    # 效果: 当状态更新时,新的消息会追加到现有列表中,而不是替换
    messages: Annotated[list[AnyMessage], operator.add]
    # 类型: int - 整数值
    # 用途: 跟踪LLM(大语言模型)的调用次数
    llm_calls: int

步骤 3:定义模型节点

由于节点不需要返回整个状态模式,只需一个更新。且 operator.add 表示新消息会追加到列表中,而不是替换。因此模型节点代码如下:

python 复制代码
# 步骤 3: 定义模型节点
from langchain.messages import SystemMessage

def llm_call(state: dict):
    """
    LLM决定是否调用工具的函数节点
    
    这个函数是LangGraph工作流中的一个节点,负责:
    1. 接收当前状态(包含对话历史)
    2. 调用绑定了工具的LLM模型
    3. 让模型决定是否需要使用工具来回答用户问题
    4. 返回更新后的状态
    
    Args:
        state (dict): 当前的工作流状态,包含以下键:
            - "messages": 对话历史消息列表
            - "llm_calls": 记录LLM被调用的次数(可选)
    
    Returns:
        dict: 更新后的状态,包含:
            - "messages": 添加了LLM响应后的消息列表
            - "llm_calls": 调用次数加1后的计数
    """
    
    # 调用绑定了工具的LLM模型
    # model_with_tools 是在之前步骤中创建的、绑定了工具列表的模型实例
    # invoke() 方法会执行模型推理,让模型分析当前对话并决定:
    #   - 如果需要额外信息,模型会生成一个工具调用请求
    #   - 如果可以直接回答,模型会生成文本回复
    response = model_with_tools.invoke(
        # 构建输入消息列表,按照 LangChain 的标准格式
        [
            # 系统消息:设置模型的角色和行为准则
            # 这个SystemMessage会告诉模型它的身份和可以使用的工具
            SystemMessage(
                content="你是一个乐于助人的助手,支持调用工具进行搜索。"
                # 可以在这里添加更多系统指令,比如:
                # - "搜索时请优先使用中文关键词"
                # - "如果没有找到相关信息,请诚实告知用户"
            )
        ]
        # 加上当前的对话历史
        # state["messages"] 包含了用户和助手之间的所有历史对话
        # 这些消息的类型可能是:HumanMessage(用户)、AIMessage(助手)、ToolMessage(工具结果)
        + state["messages"]
    )
    
    # 返回更新后的状态
    return {
        # 更新消息列表:将LLM生成的响应追加到现有消息列表后面
        # 这个响应可能是文本回复,也可能是工具调用请求
        "messages": [response],  # LangGraph会自动将这条消息与state["messages"]合并
        # 更新调用计数器:记录LLM被调用的次数
        # state.get('llm_calls', 0) 获取当前的调用次数,如果没有则默认为0
        # + 1 表示本次调用增加一次计数
        "llm_calls": state.get('llm_calls', 0) + 1
    }

步骤 4:定义工具节点
要点 1:回顾 AIMessage 消息结构

在学习 LangChain 篇章时,我们便知道当 LLM 需要调用工具时,LLM 输出结果 AIMessage 的结构如下所示:

python 复制代码
AIMessage(
    content='',
    additional_kwargs={'refusal': None},
    response_metadata={
        ...
    },
    id='lc_run--30b6a3d7-1bd0-4093-8a36-9b43bea458fc-0',
    tool_calls=[
        {
            'name': 'tavily_search',
            'args': {
                'query': '西安天气',
                'search_depth': 'basic'
            },
            'id': 'call_XAa0MF8j9YQp6FK28tqIvpB',
            'type': 'tool_call'
        }
    ],
    usage_metadata={
        ...
    }
)

其中包含一个 tool_calls 属性。此属性包括执行工具所需的一切,包括工具名称和输入参数。有了这些内容,便可以进行工具调用。

要点 2:构造 ToolMessage

仅仅成功调用工具还不行,我们需要将工具的返回构造成 ToolMessage,再传输给聊天模型。才能给我们返回真正需要的答案:

  1. 将工具输出传递给聊天模型,包括 HumanMessageAIMessage(工具调用)、ToolMessage
  2. 聊天模型根据以上消息列表的输入,将最终结果 AIMessage 返回。

可以使用下面这种方式定义 ToolMessage

python 复制代码
# ToolMessage 的创建和使用示例

# 导入必要的类
from langchain_core.messages import ToolMessage

# 创建 ToolMessage 对象
tool_message = ToolMessage(
    content=工具的返回,           # 工具执行后返回的结果内容
    tool_call_id=tool_calls[n]["id"]  # 对应工具调用的唯一标识符
)
要点 3:在 State 中访问 messages

由于 LangChain 允许传输下面这种格式的消息:

python 复制代码
{
    "messages": [
        {
            "type": "human",
            "content": "message"
        }
    ]
}

但 State 中对于 message 的更新,总是会反序列化为 LangChain Message 格式!如将上述格式反序列化为下面这种格式:

python 复制代码
{
    "messages": [
        HumanMessage(content="message")
    ]
}

因此,应该使用点表示法来访问消息属性,例如要访问 AIMessage 中的 tool_calls 属性时,应使用 state["messages"][-1].tool_calls 来获取。

因此,定义工具节点完整代码如下:

python 复制代码
# 步骤 4: 定义工具节点
from langchain.messages import ToolMessage

# 创建一个字典,将工具名称映射到工具对象本身
# 这样可以快速根据工具名称找到对应的工具实例
# 格式:{"工具名称1": 工具对象1, "工具名称2": 工具对象2, ...}
# 例如:{"tavily_search": TavilySearch对象, "calculator": Calculator对象}
tools_by_name = {tool.name: tool for tool in tools}

def tool_node(state: dict):
    """
    执行工具调用的节点函数
    
    这个函数负责:
    1. 从状态中提取LLM请求的工具调用信息
    2. 根据工具名称找到对应的工具实例
    3. 执行工具并获取执行结果
    4. 将结果包装成ToolMessage格式返回
    
    工作流程:
    LLM节点 → 生成tool_calls → 工具节点 → 执行工具 → 返回ToolMessage → 回到LLM节点
    
    Args:
        state (dict): 当前的工作流状态,必须包含:
            - "messages": 消息列表,最后一条消息应该是包含tool_calls的AIMessage
            - AIMessage.tool_calls: 包含多个工具调用请求的列表
    
    Returns:
        dict: 更新后的状态,包含:
            - "messages": ToolMessage列表,每个ToolMessage对应一个工具执行结果
    
    Example:
        输入state示例:
        {
            "messages": [
                AIMessage(
                    content="",
                    tool_calls=[
                        {
                            "id": "call_123",
                            "name": "tavily_search",
                            "args": {"query": "人工智能新闻"}
                        },
                        {
                            "id": "call_456",
                            "name": "calculator",
                            "args": {"expression": "100 * 0.15"}
                        }
                    ]
                )
            ]
        }
        
        输出state示例:
        {
            "messages": [
                ToolMessage(content="搜索到的新闻内容...", tool_call_id="call_123"),
                ToolMessage(content="15.0", tool_call_id="call_456")
            ]
        }
    """
    
    # 初始化结果列表,用于存储所有工具执行的结果
    # 每个结果都会被包装成一个ToolMessage对象
    result = []
    
    # 获取最后一条消息(应该是包含tool_calls的AIMessage)
    # state["messages"][-1] 访问消息列表的最后一项
    last_message = state["messages"][-1]
    
    # 遍历LLM请求中的所有工具调用
    # last_message.tool_calls 是一个列表,可能包含0个、1个或多个工具调用请求
    # 每个tool_call的格式:
    # {
    #     "id": "唯一标识符",      # 用于匹配工具调用和结果
    #     "name": "工具名称",      # 对应tools_by_name中的键
    #     "args": {"参数名": "参数值"}  # 传递给工具的参数
    # }
    for tool_call in last_message.tool_calls:
        
        # 步骤1: 根据工具名称获取对应的工具实例
        # tools_by_name[tool_call["name"]] 快速找到工具对象
        # 例如:tool_call["name"] = "tavily_search"
        # 那么 tool = TavilySearch实例
        tool = tools_by_name[tool_call["name"]]
        
        # 步骤2: 执行工具,传入参数
        # tool.invoke(tool_call["args"]) 调用工具并传入参数
        # 例如:search_tool.invoke({"query": "人工智能新闻"})
        # observation 是工具返回的原始结果(字符串、字典或其他格式)
        observation = tool.invoke(tool_call["args"])
        
        # 步骤3: 将工具执行结果包装成ToolMessage
        # ToolMessage的作用:
        #   - 标准化工具返回结果的格式
        #   - 通过tool_call_id将结果与请求关联
        #   - 让LLM能够理解这是工具的执行结果
        tool_message = ToolMessage(
            content=observation,        # 工具返回的具体内容
            tool_call_id=tool_call["id"]  # 重要!必须与请求中的id匹配
        )
        
        # 步骤4: 将ToolMessage添加到结果列表
        result.append(tool_message)
    
    # 返回更新后的状态
    # {"messages": result} 会如何处理?
    # 在LangGraph中,返回的消息会自动与state中的现有消息合并
    # 最终state["messages"]会变成:原来的消息 + result中的ToolMessages
    return {"messages": result}

步骤 5:构建图,设置节点与边

对于开始逻辑:是当用户进行输入后,直接让 LLM 进行处理。是否需要搜索工具调用,也是通过 LLM 进行判断。因此图的入口点则是 llm_call 节点。

对于结束逻辑:可以根据 LLM 返回的结构判断是否调用工具,来决定我们是应该继续循环还是停止循环:

  • 如果 LLM 要调用工具,则进入tool_node节点进行处理;
  • 如果 LLM 不要调用工具,则结束。
python 复制代码
# 步骤 5: 构建图
from langgraph.graph import StateGraph, START, END
from typing import Literal, List

def should_continue(state: MessagesState) -> Literal["tool_node", END]:
    """
    条件路由函数:根据LLM的响应决定下一步执行路径
    
    这个函数是LangGraph中的条件边(conditional edge)的核心组件,
    它根据当前状态判断工作流应该走向哪个节点。
    
    工作流程:
    1. LLM节点执行后,会产生响应(可能包含tool_calls)
    2. 这个函数检查LLM的响应
    3. 如果LLM请求调用工具 → 路由到工具执行节点
    4. 如果LLM直接回答了问题 → 结束流程
    
    Args:
        state (MessagesState): 当前工作流状态,包含消息列表
            state["messages"] 中的最后一条消息通常是LLM的响应
    
    Returns:
        Literal["tool_node", END]: 返回下一个节点的名称
            - "tool_node": 表示需要执行工具
            - END: 表示流程结束(LangGraph内置常量,表示终止)
    
    Example:
        场景1 - 需要工具:
            state["messages"][-1] = AIMessage(
                tool_calls=[{"name": "search", "args": {...}}]
            )
            → 返回 "tool_node"
        
        场景2 - 不需要工具:
            state["messages"][-1] = AIMessage(content="这是最终答案")
            → 返回 END
    """
    
    # 从状态中获取消息列表
    messages = state["messages"]
    
    # 获取最后一条消息(应该是LLM节点的响应)
    # 在LangGraph中,节点返回的消息会自动追加到消息列表末尾
    last_message = messages[-1]
    
    # 检查LLM是否请求调用工具
    # tool_calls 属性在AIMessage中定义,当LLM绑定了工具并决定使用时,
    # 这个属性会包含一个列表,每个元素是一个工具调用请求
    # 格式示例:
    # last_message.tool_calls = [
    #     {
    #         "id": "call_abc123",
    #         "name": "tavily_search", 
    #         "args": {"query": "人工智能新闻"}
    #     }
    # ]
    if last_message.tool_calls:
        # 如果LLM请求使用工具,则路由到工具节点
        # 工具节点会执行这些工具调用并返回结果
        return "tool_node"
    
    # 如果LLM没有请求工具(即直接生成了答案),则结束流程
    # END 是LangGraph内置常量,表示工作流终止
    return END


# 创建状态图构建器
# StateGraph 是LangGraph的核心类,用于定义状态机的工作流
# 泛型参数 MessagesState 指定了状态的数据结构
agent_builder = StateGraph(MessagesState)

# ===== 添加节点 =====
# 节点是工作流中的处理单元,每个节点都是一个可调用对象(函数)
# 节点接收当前状态,返回更新后的状态

# 添加LLM调用节点
# 这个节点负责调用大语言模型,决定是否需要工具
agent_builder.add_node("llm_call", llm_call)

# 添加工具执行节点
# 这个节点负责执行LLM请求的工具调用
agent_builder.add_node("tool_node", tool_node)


# ===== 定义流程边 =====

# 1. 添加起始边
# START 是LangGraph内置常量,表示工作流的入口点
# 从START连接到"llm_call"节点,意味着工作流开始时自动执行llm_call
agent_builder.add_edge(START, "llm_call")

# 2. 添加条件边(最重要的部分)
# 条件边允许根据状态动态决定下一个节点,实现循环和控制流
agent_builder.add_conditional_edges(
    # 参数1: source (源节点)
    # 指定从哪个节点出发,这里是从"llm_call"节点出发
    "llm_call",
    
    # 参数2: path (路由函数)
    # 指定用于判断下一个节点的函数
    # should_continue 函数会检查LLM响应,返回下一个节点的名称
    should_continue,
    
    # 参数3: path_map (路径映射,可选)
    # 明确指定路由函数可能返回的所有节点名称
    # 这有助于类型检查和错误验证,不是必须的,但推荐提供
    [
        "tool_node",  # 如果需要执行工具,路由到这个节点
        END           # 如果已完成,路由到结束
    ]
)

# 3. 添加普通边
# 从工具节点连接回LLM节点,形成循环
# 工作流程:LLM → 工具 → LLM → (可能再次工具) → 最终结束
agent_builder.add_edge("tool_node", "llm_call")
# 这条边的作用:
#   - 工具节点执行完毕后,将结果(ToolMessages)返回给LLM
#   - LLM会理解工具结果,然后决定:
#     a) 如果信息足够,生成最终答案(不再调用工具)
#     b) 如果还需要更多信息,继续调用其他工具


# ===== 编译图 =====
# compile() 方法将图定义转换为可执行的工作流
# 编译过程会进行验证:
#   - 检查所有节点是否都已添加
#   - 检查边是否形成有效的路径
#   - 确保没有孤立节点
#   - 验证条件边的返回值有效
agent = agent_builder.compile()

# 编译后得到的 agent 是一个可调用对象,可以:
#   - agent.invoke(state) - 同步执行整个工作流
#   - agent.stream(state) - 流式执行,逐步返回结果
#   - agent.ainvoke(state) - 异步执行

步骤 6:可视化图

我们可以通过 Mermaid 图表,来展示出 Graph 效果。Mermaid 是一种使用文本生成流程图、饼状图、甘特图等图表的描述语言,它可以帮助用户以简单、直观的方式创建各种类型的图表,包括流程图、时序图、甘特图等。Mermaid 在线绘图工具:https://www.yyshare.com/front-end/9729/

使用姿势:

  1. LangGraph 中,编译后的图提供了获取 Graph 可绘制表示的方法:get_graph(),其参数 xray=Truexray=1,表示显示详细视图。
  2. 再通过 draw_mermaid_png(),将图形转换为 Mermaid 格式并生成 PNG 图片的二进制数据。
  3. 最后需要导入 matplotlib 库用于图像显示。

代码如下:

python 复制代码
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# 生成 Mermaid 图表并保存为图片
mermaid_code = agent.get_graph(xray=True).draw_mermaid_png()
# 保存文件
with open("./jpg/graph1.jpg", "wb") as f:
    f.write(mermaid_code)

# 使用 matplotlib 显示图像
img = mpimg.imread("./jpg/graph1.jpg")
plt.imshow(img)  # 显示图片
plt.axis('off')  # 关闭坐标轴
plt.show()      # 弹出窗口显示图片
步骤 7:执行(非流式与流式)
  • 使用 invoke() 方法进行非流式执行:
python 复制代码
from langchain.messages import HumanMessage

messages = agent.invoke({
    "messages": [
        HumanMessage(content="今天西安的天气如何? ")
    ]
})

print(f"调用 LLM 总次数: {messages['llm_calls']}次")
for m in messages["messages"]:
    m.pretty_print()

运行结果:

python 复制代码
调用 LLM 总次数: 2次
======================= Human Message =======================
今天西安的天气如何?
======================= Ai Message =======================
Tool Calls:
  tavily_search (call_6LQafbVZn9qCzWix40b3auPI)
  Call ID: call_6LQafbVZn9qCzWix40b3auPI
  Args:
    query: 西安天气
    time_range: day
======================= Tool Message =======================
{'query': '西安天气', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'url': 'https://feeds-drcn.cloud.huawei.com.cn/landingpage/latest?docid=10512921993454170538700817&to_app=hwbrowser&dy_scenario=relate&tn=8f7efc36ddc74442b888d848e6ae3f3b32b722a839fa5de71583e48ad5dc75fb&channel=HW_JINGXUAN_ZH&ctype=news&cpid=666&r=CN&emuiVer=27', 'title': '陕西省西安市今日天气(11月26日)', 'content': '今晨6时,西安晴,气温0℃,东北风3-4级,相对湿度88%。 预计,今天白天多云,最高气温13.7℃,微风,今天夜间晴,最低气温-1.1℃,微风。 ', 'score': 0.826278, 'raw_content': None}, {'url': 'https://weather.cma.cn/web/weather/V8870.html', 'title': '西安 - 中国气象局-天气预报-城市预报', 'content': '13℃ 2℃ 12℃ 12℃ 12℃ 2℃ 1℃ | 气温 | 13℃ | 5.3℃ | 4℃ | 2.6℃ | 3.5℃ | 10.5℃ | 10.5℃ | 12.2℃ | | 气温 | 10.5℃ | 10.5℃ | 12.2℃ | 10.4℃ | 7.6℃ | 5.5℃ | 4.4℃ | 3.3℃ | | 气温 | 2℃ | 10.2℃ | 11℃ | 11.8℃ | 5.4℃ | 5.2℃ | 4.9℃ | 4.3℃ | | 气温 | 8.3℃ | 16.6℃ | 16.8℃ | 12.8℃ | 8.6℃ | 7.4℃ | 4℃ | 4.3℃ | | 气温 | 4.5℃ | 11℃ | 12℃ | 11.2℃ | 5.9℃ | 5℃ | 3.4℃ | 3.1℃ | | 气温 | 2.2℃ | 7.6℃ | 10.6℃ | 9.6℃ | 8.5℃ | 5.3℃ | 2.1℃ | 1.6℃ |', 'score': 0.82492816, 'raw_content': None}, {'url': 'https://shaanxi.weather.com.cn/xian/index.shtml', 'title': '西安天气预报 - 陕西', 'content': '城市预报列表(11-26 07:30发布)。西安: 17℃/2℃。长安: 14℃/0℃。临潼: 15℃/', 'score': 0.7265231, 'raw_content': None}, {'url': 'https://weather.yahoo.co.jp/weather/world/CN/57036/', 'title': '西安(シーアン)(中国)の天気 - Yahoo!天気・災害', 'content': '西安(シーアン)(中国)の今日・明日・週間天気予報が確認できます。天気、最低気温、最高気温はもちろん、現地時刻、日の出、日の入り時刻までわかります。', 'score': 0.7060491, 'raw_content': None}], 'response_time': 0.86, 'request_id': '4d571ab8-52b8-4628-94d9-e37c5ae9727f'}
======================= Ai Message =======================
今天西安的天气情况如下:

- **今晨6时**: 晴,气温0℃,东北风3-4级,相对湿度88%。
- **白天气温**: 预计最高气温13.7℃,天气多云,微风。
- **夜间气温**: 预计最低气温-1.1℃,天气晴,微风。

如果需要更详细的信息,可以查看以下链接:
- [陕西省西安市今日天气](https://feeds-drcn.cloud.huawei.com.cn/landingpage/latest?docid=10512921993454170538700817&to_app=hwbrowser&dy_scenario=relate&tn=8f7efc36ddc74442b888d848e6ae3f3b32b722a839fa5de71583e48ad5dc75fb&channel=HW_JINGXUAN_ZH&ctype=news&cpid=666&r=CN&emuiVer=27)
- [中国气象局 - 西安天气预报](https://weather.cma.cn/web/weather/V8870.html)
- [西安天气预报 - 陕西](https://shaanxi.weather.com.cn/xian/index.shtml)
  • 使用 stream() 进行流式输出:
python 复制代码
from langchain.messages import HumanMessage

for chunk in agent.stream({
    "messages": [HumanMessage(content="今天西安的天气如何? ")]
}):
    print(chunk)

注意,这里的流式,指的是 Graph 执行步骤的流式输出 ,即途径节点的过程。每个节点对 Graph State 的更新值(注:并非是更新后的值),都可以通过这种方式追踪。由此,chunk 类型为 dict:key 代表节点名称;value 代表将更新的 State 值。

相关推荐
yaoxin5211231 小时前
403. Java 文件操作基础 - 写入二进制文件
java·开发语言·python
dfdfadffa1 小时前
c++怎么利用std--filesystem--path处理包含多个扩展名的文件名【详解】
jvm·数据库·python
爱喝水的鱼丶1 小时前
SAP-ABAP:ABAP Development Tools(ADT)安装配置学习分享教程(四篇连载) 第二篇:ADT客户端完整安装与初始配置教程
运维·开发语言·学习·sap·abap
青岛前景互联信息技术有限公司1 小时前
数字孪生助力园区安全:隐患排查到应急演练全链路实践
人工智能
dinl_vin1 小时前
LangChain 系列·(九):Agent——让 AI 自己做决策
前端·人工智能·langchain
孟祥_成都1 小时前
前端唯一的护城河?结合 AI 将字节组件库 Headless 化后的感想~
前端·人工智能·react.js
数智工坊1 小时前
【经典RL算法】Q-Learning:强化学习的里程碑——从理论到收敛证明的完整解析
论文阅读·人工智能·深度学习·算法·transformer
AKA__Zas1 小时前
初识多线程(2.0)
java·开发语言·学习方法
叼烟扛炮1 小时前
C++ 知识点19 匿名对象
开发语言·c++·算法·匿名对象