LangGraph入门:支持搜索的智能代理系统

目录

​编辑

一、案例介绍

[二、 编码思路](#二、 编码思路)

[2.1 设置 Nodes](#2.1 设置 Nodes)

[2.2 设置 State](#2.2 设置 State)

[2.3 设置 Edges](#2.3 设置 Edges)

三、代码实现

[3.1 定义聊天模型和搜索工具](#3.1 定义聊天模型和搜索工具)

[3.2 定义状态](#3.2 定义状态)

[3.3 定义模型节点](#3.3 定义模型节点)

[3.4 定义工具节点](#3.4 定义工具节点)

[3.4.1 回顾 AIMessage 消息结构](#3.4.1 回顾 AIMessage 消息结构)

[3.4.2 ToolMessage的构造与条件](#3.4.2 ToolMessage的构造与条件)

[3.5 构建图,设置节点与边](#3.5 构建图,设置节点与边)

[3.6 执行(非流式与流式)](#3.6 执行(非流式与流式))

[3.6.1 使用 invoke() 方法进行非流式执行:](#3.6.1 使用 invoke() 方法进行非流式执行:)

[3.6.2 使用 stream() 进行流式输出:](#3.6.2 使用 stream() 进行流式输出:)

四、完整代码


一、案例介绍

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

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

流程设置如下:

二、 编码思路

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

2.1 设置 Nodes

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

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

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

2.2 设置 State

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

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

python 复制代码
#定义消息状态
class MessageState(TypedDict):
    messages:Annotated[list[AnyMessage],add]
    llm_calls:int

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

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

messages 的具体作用:

  • 1.存储完整的对话流程
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 ToolCalls(state:MessageState):
    res=[]
    for tool_call in state["messages"][-1].tool_calls:
        ret=tools_by_name[tool_call["name"]].invoke(tool_call["args"])
        res.append(ToolMessage(content=ret,tool_call_id=tool_call["id"]))
    return{
        "messages":res
    }

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

2.3 设置 Edges

对于流程设置如下所示:

且已经定义了两个节点:

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

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

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

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

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

最终 Graph 效果:

三、代码实现

3.1 定义聊天模型和搜索工具

python 复制代码
from langchain_tavily import TavilySearch
from langchain_deepseek import ChatDeepSeek

model=ChatDeepSeek(model="deepseek-chat")
search=TavilySearch(max_results=4)
tools=[search]
model_with_tools=model.bind_tools(tools)

3.2 定义状态

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

#定义消息状态
class MessageState(TypedDict):
    messages:Annotated[list[AnyMessage],add]
    llm_calls:int

3.3 定义模型节点

模型节点主要完成的工作就是获取状态中的消息列表messages并调用大模型返回AIMessage,由于节点不需要返回整个状态模式,只需一个更新。且 add 表示新消息会追加到列表中,而不是替换。因此模型节点代码如下:

python 复制代码
#调用大模型
def LLMCall(state:MessageState):
    res=[]
    res.append(
        model_with_tools.invoke([SystemMessage("你是一个有求必应的人工智能助手")]+state["messages"]
    ))
    return{
        "messages":res,
        "llm_calls":state.get("llm_calls",0)+1
    }

这里需要注意的是,add要求两个参数必须是同一类型的两list合并,所以 model_with_tools.invoke返回的AIMessage不能直接在return中返回,而是必须放在一个list中。除了上述写法也可以这样写:

python 复制代码
#调用大模型
def LLMCall(state:MessageState):
    {
        model_with_tools.invoke([SystemMessage("你是一个有求必应的人工智能助手")]+state["messages"]
    )
    return{
        "messages":[res],
        "llm_calls":state.get("llm_calls",0)+1
    }

还需要注意的是,因为在大模型调用之后我们需要根据返回的AIMessage中的tool_calls字段是不是为空来决定是否调用工具。所以执行invoke的模型必须是model_with_tools也就是绑定工具后的模型而不能是model,因为如果是model(没有绑定工具的模型)中的tool_calls字段无论是否需要调用工具其始终为空。

3.4 定义工具节点

3.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_XAa0MF8j9YQp6KFk28tqIvpB',
            'type': 'tool_call'
        }
    ],
    usage_metadata={
        ...
    }
)

其中包含一个 tool_calls 字段,其类型为字典列表。当大模型根据消息列表判断后续无需调用相关工具时,该字段为一个空列表;反之,当大模型判断后续需要调用相关工具时,则会对该字段进行填充,其中包含执行工具所需的一切信息,包括工具名称和输入参数。有了这些内容,便可以进行工具调用。

所以我们就可以在大模型调用之后判断返回的 AIMessage 中的tool_calls 字段是不是为空来判断后续是否来调用相关工具。

3.4.2 ToolMessage的构造与条件

工具调用后返回的结果主要有以下两种结果:

  • 返回:ToolMessage 对象

这种情况需要我们调用工具的时候模拟大模型调用姿势,也就是需要传入完整 tool_call 字典(包含 name、args、id),LangChain 会自动封装结果,返回ToolMessage,这时LangChain会自动将传入的 tool_call 中的 id 字段初始化 ToolMessage 中的 tool_call_id,用来回传给大模型。

这时节点代码可以设计为:

python 复制代码
#需要调用工具进行搜索
def ToolCalls(state:MessageState):
    for tool_call in state["messages"][-1].tool_calls:
        #这里直接传入tool_call会返回ToolMessage
        ret=tools_by_name[tool_call["name"]].invoke(tool_call)
    return{
        "messages":[ret]
    }
  • 原始业务结果(str/int/dict,不会包装成 ToolMessage)

如果只传工具参数,不是完整 tool_call,工具直接返回函数执行结果,不会自动包 ToolMessage。 此时如果你要传给大模型,必须手动自己构造 ToolMessage才能给我们返回真正需要的答案:

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

python 复制代码
ToolMessage(content=${工具的返回}, tool_call_id=tool_calls[n]["id"])

这里需要注意的是,手动构造Toolmessage的时候必须指定tool_call_id。且 tool_call_id 必须和模型返回的 tool_call 里的 id 完全一致。

此时节点代码可以设计为:

python 复制代码
#需要调用工具进行搜索
def ToolCalls(state:MessageState):
    for tool_call in state["messages"][-1].tool_calls:
        #这里传入tool_call["args"]工具调用参数返回的是业务类型
        ret=tools_by_name[tool_call["name"]].invoke(tool_call["args"])
        #需要手动构造ToolMessage并指定tool_call_id
        toolmess=ToolMessage(content=ret,tool_call_id=tool_call["id"])
    return{
        "messages":[toolmess]
    }

这个 tool_call_id 的核心作用就是配对。当大模型一次性发起多个并行工具调用时,会一次性返回多条独立的tool_call,每一条都自带唯一 id。我们每执行完一个工具,生成的ToolMessage就要带上对应这条调用的 id,追加进消息列表。

大模型拿到消息列表后,依靠tool_call_id来区分:哪一段工具返回结果,对应哪一次它发起的工具请求。如果 ID 写错、漏写,模型就无法关联工具结果,会直接造成工具调用逻辑失效或者报错。

简单总结两种调用的区别:

  • 传入完整tool_call对象调用工具:tool.invoke(tool_call),自动返回ToolMessage,自动填充 tool_call_id,不用手动构造;
  • 仅传入 args 参数调用工具:tool.invoke(args),返回原始业务数据,想要回传给模型,必须手动封装 ToolMessage 并绑定 tool_call_id。

3.5 构建图,设置节点与边

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

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

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

判断 LLM 要不要调用工具的关键就在于其返回的AIMessage中的too_calls字段是不是为空。

python 复制代码
#定义图
Intelligent_Agent_System=StateGraph(MessageState)

#添加节点
Intelligent_Agent_System.add_node("LLMCall",LLMCall)
Intelligent_Agent_System.add_node("ToolCalls",ToolCalls)

#定义边
#条件判断接口
def IsCallTool(state:MessageState):
    if state["messages"][-1].tool_calls:
        return "ToolCalls"
    else:
        return END

Intelligent_Agent_System.add_edge(START,"LLMCall")
Intelligent_Agent_System.add_conditional_edges(
    "LLMCall",
    IsCallTool,
    ["ToolCalls",END]
)
Intelligent_Agent_System.add_edge("ToolCalls","LLMCall")

3.6 执行(非流式与流式)

3.6.1 使用 invoke() 方法进行非流式执行:

python 复制代码
finstate=Agent.invoke(
        {
            "messages":[HumanMessage(content="今天兰州市榆中县的天气如何100字总结一下")],
            "llm_calls":0
        }
)

print(f"大模型一共调用了{finstate["llm_calls"]}次\n")

for mess in finstate["messages"]:
    mess.pretty_print()

运行结果:

python 复制代码
大模型一共调用了2次

================================ Human Message =================================

今天兰州市榆中县的天气如何100字总结一下
================================== Ai Message ==================================
Tool Calls:
  tavily_search (call_00_hKcUQOQHm3Tc3d2usP9C6408)
 Call ID: call_00_hKcUQOQHm3Tc3d2usP9C6408
  Args:
    query: 兰州市榆中县今天天气
    topic: general
    search_depth: advanced
================================= Tool Message =================================

{'query': '兰州市榆中县今天天气', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'url': 'https://www.tianqi.com/tianqi/yuzhong/20260802.html', 'title': '榆中天气预报兰州榆中2026年08月02日天气', 'content': '榆中天气的关键词是"小雨"。今天白天榆中小雨,最高气温19℃, 最低气温13℃。风的方面,今天白天到夜间,本地有东风2级, 相对湿度97%,紫外线最弱', 'score': 0.8732259, 'raw_content': None, 'id': '26a08d-00'}, {'url': 'http://eia-data.com/%E5%85%B0%E5%B7%9E%E5%B8%82-%E6%A6%86%E4%B8%AD%E5%8E%BF%E5%9F%BA%E6%9C%AC%E6%B0%94%E8%B1%A1%E8%B5%84%E6%96%99', 'title': '兰州市/榆中县基本气象资料', 'content': '兰州市/榆中县气象资料及空气质量信息 ; 天气多云 ; 气温17.5 ℃ ; 风向西北风 ; 风速8 km/h (2级) ; 湿度71 %.', 'score': 0.86981434, 'raw_content': None, 'id': '9ff95d-01'}, {'url': 'https://m.ip138.com/73/lanzhou/yuzhong/tianqi/15tian.htm', 'title': '榆中天气预报,榆中县15天天气预报,甘肃省榆中天气预报 - iP138查询网', 'content': 'iP查询\n 手机号码归属地查询\n 汇率查询\n 邮编区号查询\n 天气预报查询\n 身份证号码查询\n 列车时刻表查询\n 火车票代售点查询\n\niP查询\n\n首页 > 天气预报查询 > 甘肃 > 兰州 > 榆中\n\n# 榆中15天天气预报\n\n|  |  |  |  |\n ---  --- |\n| 09/17  今天 | 多云 | 7℃~23℃ | 微风  <3级 |\n| 09/18  星期五 | 多云 | 9℃~23℃ | 微风  <3级 |\n| 09/19  星期六 | 阴 | 8℃~15℃ | 微风  <3级 |\n| 09/20  星期日 | 阴 | 9℃~15℃ | 微风  <3级 |\n| 09/21  星期一 | 阴转晴 | 6℃~20℃ | 微风  <3级 |\n| 09/22  星期二 | 多云 | 8℃~21℃ | 微风  <3级 |\n| 09/23  星期三 | 阴 | 7℃~20℃ | 微风  <3级 |\n| 09/24  星期四 | 多云 | 10℃~19℃ | 南风转东南风  <3级 |\n| 09/25  星期五 | 雨转阴 | 9℃~16℃ | 东南风  <3级 |\n| 星期六 | 阴 | 10℃~18℃ | 东南风  <3级 |\n\n 5天天气预报\n 7天天气预报\n 一周天气预报\n 10天天气预报\n 两周天气预报\n 15天天气预报\n\n  \n\n 安宁\n 城关\n 皋兰\n 红古\n 七里河\n 西固\n 永登\n 榆中\n\n厦门市漫游科技有限公司   版权所有 © 2026\n\n闽网文2517-043号  \n 闽ICP备15026659号-7  \n 闽公网安备35020302000956号', 'score': 0.82117355, 'raw_content': None, 'id': '6fd339-02'}, {'url': 'http://www.nmc.cn/publish/forecast/AGS/yuzhong.html', 'title': '榆中-天气预报', 'content': '+ 土壤水分监测\n  + 农业干旱综合监测\n  + 关键农时农事\n  + 农业气象周报\n  + 农业气象月报\n  + 农业气象专报\n  + 作物发育期监测\n  + 农业气象灾害风险预警\n  + 国外农业气象月报\n 数值预报\n\n  + CMA全球天气模式\n  + CMA全球集合模式\n  + CMA区域模式\n  + CMA区域集合模式\n  + CMA台风模式\n  + 海浪模式\n\n1. 当前位置:首页\n2. 甘肃省\n3. 榆中天气预报\n\n省份:城市:\n\n日出\n\n榆中\n\n日落\n\n降水量\n\n相对湿度\n\n体感温度\n\n雷达图\n\n\n\n24小时预报7天预报10天预报11-30天预报\n\n发布时间:09-09 20:00\n\n09/09   \n周三\n\n10℃\n\n阴\n\n无持续风向\n\n微风\n\n09/10   \n周四\n\n多云\n\n无持续风向\n\n微风\n\n18℃\n\n7℃\n\n多云\n\n无持续风向\n\n微风\n\n09/11   \n周五\n\n晴\n\n无持续风向\n\n微风\n\n20℃\n\n9℃\n\n多云\n\n无持续风向\n\n微风\n\n09/12   \n周六\n\n多云\n\n无持续风向\n\n微风\n\n18℃\n\n8℃\n\n小雨\n\n无持续风向\n\n微风\n\n09/13   \n周日\n\n小雨\n\n无持续风向\n\n微风\n\n20℃\n\n8℃\n\n小雨\n\n无持续风向\n\n微风\n\n09/14   \n周一\n\n阴\n\n无持续风向\n\n微风\n\n20℃\n\n5℃\n\n阴\n\n无持续风向\n\n微风\n\n09/15   \n周二\n\n小雨\n\n无持续风向\n\n微风\n\n21℃\n\n5℃\n\n小雨\n\n无持续风向\n\n微风\n\n07/18 周四\n\n降水\n\n气温\n\n风速\n\n风向\n\n气压\n\n湿度\n\n云量\n\n08:00\n\n-\n\n11.1℃\n\n2.9m/s\n\n东南风\n\n822.8hPa\n\n98%\n\n80%\n\n11:00\n\n-\n\n15.3℃\n\n3.3m/s\n\n东风\n\n822.7hPa\n\n79.5%\n\n72.8%\n\n14:00\n\n- [...] 08:00\n\n-\n\n9.1℃\n\n3.1m/s\n\n南风\n\n819.1hPa\n\n87.4%\n\n10.1%\n\n11:00\n\n-\n\n16.1℃\n\n2.6m/s\n\n东风\n\n818.8hPa\n\n72.9%\n\n8%\n\n14:00\n\n-\n\n18.6℃\n\n3.3m/s\n\n东风\n\n816.4hPa\n\n68.6%\n\n3.4%\n\n17:00\n\n-\n\n19.8℃\n\n2.9m/s\n\n东南风\n\n814.7hPa\n\n65.7%\n\n7.3%\n\n20:00\n\n-\n\n15.3℃\n\n3.1m/s\n\n东南风\n\n815.8hPa\n\n57.6%\n\n6.1%\n\n23:00\n\n-\n\n13.3℃\n\n3.3m/s\n\n东南风\n\n817.3hPa\n\n87.7%\n\n10.1%\n\n12日02:00\n\n-\n\n9.9℃\n\n3.2m/s\n\n东南风\n\n817.1hPa\n\n90.6%\n\n10.1%\n\n05:00\n\n-\n\n9.2℃\n\n2.7m/s\n\n东南风\n\n817hPa\n\n98.2%\n\n10.1%\n\n08:00\n\n-\n\n11.9℃\n\n2.5m/s\n\n东南风\n\n818.8hPa\n\n96.4%\n\n10.1%\n\n11:00\n\n-\n\n16.1℃\n\n3.3m/s\n\n东风\n\n819.7hPa\n\n77.4%\n\n10.1%\n\n14:00\n\n-\n\n17.8℃\n\n3.1m/s\n\n东风\n\n818.5hPa\n\n68%\n\n79.9%\n\n17:00\n\n-\n\n17℃\n\n3m/s\n\n东风\n\n817.4hPa\n\n67%\n\n79.9%\n\n20:00\n\n-\n\n15℃\n\n2.7m/s\n\n东北风\n\n819hPa\n\n57.5%\n\n79.9%\n\n23:00\n\n0.7mm\n\n13.2℃\n\n3m/s\n\n东风\n\n819.6hPa\n\n85.3%\n\n75.1%\n\n13日02:00\n\n0.3mm\n\n9.2℃\n\n3.1m/s\n\n东南风\n\n820.9hPa\n\n85.4%\n\n64.9%\n\n05:00 [...] -\n\n15.3℃\n\n3.3m/s\n\n东风\n\n822.7hPa\n\n79.5%\n\n72.8%\n\n14:00\n\n-\n\n17.8℃\n\n3.3m/s\n\n东南风\n\n820.3hPa\n\n70.2%\n\n44.9%\n\n17:00\n\n-\n\n16.3℃\n\n3.3m/s\n\n东南风\n\n819.2hPa\n\n64.3%\n\n49.4%\n\n20:00\n\n-\n\n14.9℃\n\n3.2m/s\n\n东南风\n\n819.4hPa\n\n56.3%\n\n78.5%\n\n23:00\n\n-\n\n10.1℃\n\n3.1m/s\n\n南风\n\n819.9hPa\n\n84.3%\n\n79.9%\n\n11日02:00\n\n-\n\n7.3℃\n\n3.3m/s\n\n南风\n\n819.2hPa\n\n88.7%\n\n32.7%\n\n05:00\n\n-\n\n7.2℃\n\n3m/s\n\n南风\n\n818.4hPa\n\n92.6%\n\n13.2%\n\n11:00\n\n-\n\n15.3℃\n\n3.3m/s\n\n东风\n\n822.7hPa\n\n79.5%\n\n72.8%\n\n14:00\n\n-\n\n17.8℃\n\n3.3m/s\n\n东南风\n\n820.3hPa\n\n70.2%\n\n44.9%\n\n17:00\n\n-\n\n16.3℃\n\n3.3m/s\n\n东南风\n\n819.2hPa\n\n64.3%\n\n49.4%\n\n20:00\n\n-\n\n14.9℃\n\n3.2m/s\n\n东南风\n\n819.4hPa\n\n56.3%\n\n78.5%\n\n23:00\n\n-\n\n10.1℃\n\n3.1m/s\n\n南风\n\n819.9hPa\n\n84.3%\n\n79.9%\n\n11日02:00\n\n-\n\n7.3℃\n\n3.3m/s\n\n南风\n\n819.2hPa\n\n88.7%\n\n32.7%\n\n05:00\n\n-\n\n7.2℃\n\n3m/s\n\n南风\n\n818.4hPa\n\n92.6%\n\n13.2%\n\n08:00\n\n-\n\n9.1℃\n\n3.1m/s\n\n南风\n\n819.1hPa\n\n87.4%\n\n10.1%\n\n11:00', 'score': 0.8127637, 'raw_content': None, 'id': 'd0c03f-03'}], 'response_time': 1.9, 'request_id': 'b0a09926-fe04-467a-b0d9-67c450bd4587'}
================================== Ai Message ==================================

兰州市榆中县今天天气以小雨为主,白天最高气温约19℃,夜间最低气温约13℃,昼夜温差较大。风况方面为东风2级,风力较小;相对湿度约97%,空气湿度偏高,体感较为湿凉;紫外线强度最弱,无需特别防晒。整体天气偏阴冷潮湿,建议外出携带雨具,适当增添衣物,注意保暖防潮。

进程已结束,退出代码为 0

3.6.2 使用 stream() 进行流式输出:

python 复制代码
for chunk in Agent.stream(
        {
            "messages":[HumanMessage(content="今天兰州市榆中县的天气如何100字总结一下")],
            "llm_calls":0
        }
    ):
    print(chunk)
python 复制代码
{'LLMCall': {'messages': [AIMessage(content='我来帮你查一下今天兰州市榆中县的天气情况。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 74, 'prompt_tokens': 1822, 'total_tokens': 1896, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 1664, 'image_tokens': None, 'text_tokens': None}, 'prompt_cache_hit_tokens': 1664, 'prompt_cache_miss_tokens': 158}, 'model_provider': 'deepseek', 'model_name': 'deepseek-flash', 'system_fingerprint': 'aeb56401ca74e127821c4f9126dcb669', 'id': '60f74023-bcd2-4956-9e76-8e1cb1149825', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--01a0ae5a-1929-73f1-a384-92a5d8a17f1d-0', tool_calls=[{'name': 'tavily_search', 'args': {'query': '兰州市榆中县今天天气', 'time_range': 'day'}, 'id': 'call_00_PyLPQWRheb2yNOkXGsOm7081', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 1822, 'output_tokens': 74, 'total_tokens': 1896, 'input_token_details': {'cache_read': 1664}, 'output_token_details': {}})], 'llm_calls': 1}}
{'ToolCalls': {'messages': [ToolMessage(content='{\'query\': \'兰州市榆中县今天天气\', \'follow_up_questions\': None, \'answer\': None, \'images\': [], \'results\': [{\'url\': \'https://www.weather.com.cn/textFC/gansu.shtml\', \'title\': \'甘肃天气预报\', \'content\': \'|  |  |  |  |  |  |  |  |  |\\n ---  ---  ---  --- \\n| 兰州 | 兰州 | 晴 | 东风 <3级 | 25 | 多云 | 东风 <3级 | 13 | 详情 |\\n| 皋兰 | 晴 | 无持续风向 <3级 | 23 | 多云 | 无持续风向 <3级 | 10 | 详情 |\\n| 永登 | 晴 | 无持续风向 <3级 | 19 | 多云 | 无持续风向 <3级 | 9 | 详情 |\\n| 榆中 | 晴 | 无持续风向 <3级 | 20 | 多云 | 无持续风向 <3级 | 9 | 详情 |\\n| 城关 | 晴 | 东风 <3级 | 25 | 多云 | 东风 <3级 | 13 | 详情 |\\n| 七里河 | 晴 | 东风 <3级 | 25 | 多云 | 东风 <3级 | 13 | 详情 |\\n| 西固 | 晴 | 东风 <3级 | 25 | 多云 | 东风 <3级 | 13 | 详情 |\\n| 安宁 | 晴 | 东风 <3级 | 25 | 多云 | 东风 <3级 | 13 | 详情 | [...] |  |  |  |  |  |  |  |  |  |\\n ---  ---  ---  --- \\n| 兰州 | 兰州 | 多云 | 无持续风向 <3级 | 21 | 多云 | 无持续风向 <3级 | 11 | 详情 |\\n| 皋兰 | 阴 | 无持续风向 <3级 | 21 | 阴 | 无持续风向 <3级 | 8 | 详情 |\\n| 永登 | 多云 | 无持续风向 <3级 | 17 | 多云 | 无持续风向 <3级 | 6 | 详情 |\\n| 榆中 | 阴 | 无持续风向 <3级 | 19 | 阴 | 无持续风向 <3级 | 5 | 详情 |\\n| 城关 | 多云 | 无持续风向 <3级 | 21 | 多云 | 无持续风向 <3级 | 11 | 详情 |\\n| 七里河 | 多云 | 无持续风向 <3级 | 21 | 多云 | 无持续风向 <3级 | 11 | 详情 |\\n| 西固 | 多云 | 无持续风向 <3级 | 21 | 多云 | 无持续风向 <3级 | 11 | 详情 |\\n| 安宁 | 多云 | 无持续风向 <3级 | 21 | 多云 | 无持续风向 <3级 | 11 | 详情 | [...] |  |  |  |  |  |  |  |  |  |\\n ---  ---  ---  --- \\n| 兰州 | 兰州 | 多云 | 东风 <3级 | 21 | 晴 | 东风 <3级 | 11 | 详情 |\\n| 皋兰 | 多云 | 无持续风向 <3级 | 20 | 多云 | 无持续风向 <3级 | 8 | 详情 |\\n| 永登 | 多云 | 无持续风向 <3级 | 17 | 多云 | 无持续风向 <3级 | 7 | 详情 |\\n| 榆中 | 多云 | 无持续风向 <3级 | 18 | 多云 | 无持续风向 <3级 | 7 | 详情 |\\n| 城关 | 多云 | 东风 <3级 | 21 | 晴 | 东风 <3级 | 11 | 详情 |\\n| 七里河 | 多云 | 东风 <3级 | 21 | 晴 | 东风 <3级 | 11 | 详情 |\\n| 西固 | 多云 | 东风 <3级 | 21 | 晴 | 东风 <3级 | 11 | 详情 |\\n| 安宁 | 多云 | 东风 <3级 | 21 | 晴 | 东风 <3级 | 11 | 详情 |\', \'score\': 0.7511561, \'raw_content\': None, \'id\': \'56725b-00\'}, {\'url\': \'https://www.weather.com.cn/weathern/101160101.shtml\', \'title\': \'兰州天气预报,兰州7天天气预报,兰州15天天气预报,兰州天气查询 - 中国天气网\', \'content\': \'全国 >\\n\\n北京 天津 河北 山西 内蒙古 辽宁 吉林 黑龙江 上海 江苏 浙江 安徽 福建 江西 山东 河南 湖北 湖南 广东 广西 海南 重庆 四川 贵州 云南 西藏 陕西 甘肃 青海 宁夏 新疆 香港 澳门 台湾\\n\\n甘肃 >\\n\\n兰州定西平凉庆阳武威金昌张掖酒泉天水武都临夏合作白银嘉峪关\\n\\n兰州 >\\n\\n城区皋兰永登榆中城关七里河西固安宁红古\\n\\n城区)\\n\\n今天 7天 8-15天 40天 雷达图\\n\\n 16日\\n\\n  昨天\\n 17日\\n\\n  今天\\n 18日\\n\\n  明天\\n 19日\\n\\n  后天\\n 20日\\n\\n  周日\\n 21日\\n\\n  周一\\n 22日\\n\\n  周二\\n 23日\\n\\n  周三\\n\\n 小雨\\n\\n  3-4级转<3级\\n 多云\\n\\n  <3级\\n 多云\\n\\n  <3级\\n 阴\\n\\n  <3级\\n 小雨转阴\\n\\n  <3级\\n 阴转晴\\n\\n  <3级\\n 晴转多云\\n\\n  <3级\\n 阴\\n\\n  <3级\\n\\n逐小时预报   今日日落 19:37   明日日出 19:37   蓝天预报\\n\\n 08时\\n 08时\\n 08时\\n 08时\\n 08时\\n 08时\\n 08时\\n 08时\\n\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n 西北风 [...] 3级\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n 西北风\\n\\n  3级\\n\\n# 生活助手定制我的指数08:00更新  09-17  09-18  09-19  09-20  09-21  09-22  09-23\\n\\n ## 紫外线\\n ## 运动\\n ## 过敏\\n ## 穿衣\\n\\n  \\n ## 洗车\\n ## 空气污染扩散\\n\\n很强\\n:   涂擦SPF20以上,PA++护肤品,避强光。\\n\\n适宜\\n:   天气较好,尽情感受运动的快乐吧。\\n\\n易发\\n:   应减少外出,外出需采取防护措施。\\n\\n舒适\\n:   建议穿长袖衬衫单裤等服装。\\n\\n较适宜\\n:   无雨且风力较小,易保持清洁度。\\n\\n中\\n:   易感人群应适当减少室外活动。\\n\\n中等\\n:   涂擦SPF大于15、PA+防晒护肤品。\\n\\n适宜\\n:   天气较好,尽情感受运动的快乐吧。\\n\\n易发\\n:   应减少外出,外出需采取防护措施。\\n\\n舒适\\n:   建议穿长袖衬衫单裤等服装。\\n\\n适宜\\n:   天气较好,适合擦洗汽车。\\n\\n中\\n:   易感人群应适当减少室外活动。 [...] 最弱\\n:   辐射弱,涂擦SPF8-12防晒护肤品。\\n\\n较适宜\\n:   天气凉,在户外运动请注意增减衣物。\\n\\n易发\\n:   应减少外出,外出需采取防护措施。\\n\\n较冷\\n:   建议着厚外套加毛衣等服装。\\n\\n较适宜\\n:   无雨且风力较小,易保持清洁度。\\n\\n较差\\n:   气象条件较不利于空气污染物扩散。。\\n\\n最弱\\n:   辐射弱,涂擦SPF8-12防晒护肤品。\\n\\n较不宜\\n:   有降水,推荐您在室内进行休闲运动。\\n\\n易发\\n:   应减少外出,外出需采取防护措施。\\n\\n较冷\\n:   建议着厚外套加毛衣等服装。\\n\\n不宜\\n:   有雨,雨水和泥水会弄脏爱车。\\n\\n良\\n:   气象条件有利于空气污染物扩散。\\n\\n最弱\\n:   辐射弱,涂擦SPF8-12防晒护肤品。\\n\\n适宜\\n:   天气较好,尽情感受运动的快乐吧。\\n\\n易发\\n:   应减少外出,外出需采取防护措施。\\n\\n舒适\\n:   建议穿长袖衬衫单裤等服装。\\n\\n较适宜\\n:   无雨且风力较小,易保持清洁度。\\n\\n较差\\n:   气象条件较不利于空气污染物扩散。。\\n\\n很强\\n:   涂擦SPF20以上,PA++护肤品,避强光。\', \'score\': 0.6821721, \'raw_content\': None, \'id\': \'1a712a-01\'}, {\'url\': \'https://www.163.com/dy/article/L6VCISI50530JPVV.html\', \'title\': \'兰州市气象台发布雷雨大风黄色预警|雷阵雨|昼夜温差_网易订阅\', \'content\': \'### 新闻\\n\\n### 体育\\n\\n### 娱乐\\n\\n### 财经\\n\\n### 汽车\\n\\n### 科技\\n\\n### 时尚\\n\\n### 手机 / 数码\\n\\n### 房产 / 家居\\n\\n### 教育\\n\\n# 兰州市气象台发布雷雨大风黄色预警\\n\\n用微信扫码二维码\\n\\n分享至好友和朋友圈\\n\\n兰州市气象台2026年9月16日15时05分发布雷雨大风黄色预警信号:\\n\\n预计今天下午至傍晚,城关南部、七里河、兰州新区北部、永登北部及榆中南部将受雷雨大风影响,平均风力4 ~5级,阵风7级以上,并伴有雷阵雨,其余地区有分散性阵雨。请注意防范!\\n\\n来源丨@甘肃气象\\n\\n特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台"网易号"用户上传并发布,本平台仅提供信息存储服务。 [...] ## 娱乐要闻\\n\\n### 比起敬一丹的退而不休 章伟秋更显明智\\n\\n### 香港知名女星病重入住ICU,闺蜜轮番去医院探病\\n\\n### 胡歌坦言电影市场"特别现实"遭热议\\n\\n### 蒋丽莎和陈浩民直播时吵架:给前女友买房子\\n\\n### 与范志毅离婚后,如今的她,与女儿范斯晶常联系\\n\\n## 财经要闻\\n\\n### 美联储加息!沃什重磅发声\\n\\n### 超20城集体放宽公积金大病提取\\n\\n### 邢自强:中美AI决胜点不在算力\\n\\n### 中际旭创六家供应商股东疑现关联人\\n\\n### 退出造车的恒大汽车:1.82亿元资产,327亿元负债\\n\\n## 科技要闻\\n\\n### 赛力斯全面接盘,华为真的"撤"了吗?\\n\\n### 全新iPhone上手测评!苹果最听劝的一集来了?\\n\\n### 赛力斯接管问界,撑得住华为给的身价吗?\\n\\n### 估值1.2万亿美元,OpenAI又在谈融资\\n\\n### 华为高层发声:ICT业务目标是成为"英伟达"\\n\\n## 汽车要闻\\n\\n### 激光雷达+EVA机器人+爆胎稳行 星瑞L PLUS预售限时价11.57万起\\n\\n### 奔驰周娟:全新长轴距GLE SUV第一次拥有了"中国脑,中国心" [...] 深度长文:我们每秒呼吸的氧气,竟是 24 亿年前的一场灾难!\\n\\n### 深度长文:我们每秒呼吸的氧气,竟是 24 亿年前的一场灾难!\\n\\nCCTV5直播中国女篮战泰国队,张子宇场外遇麻烦,宫鲁鸣重组双塔\\n\\n### CCTV5直播中国女篮战泰国队,张子宇场外遇麻烦,宫鲁鸣重组双塔\\n\\n65岁刘德华自曝年龄焦虑:20岁勇气已耗尽,极致自律也挡不住断崖式衰老\\n\\n### 65岁刘德华自曝年龄焦虑:20岁勇气已耗尽,极致自律也挡不住断崖式衰老\\n\\n阿莫林:我们在控球时很紧张且多次丢失球权,球迷也很紧张\\n\\n### 阿莫林:我们在控球时很紧张且多次丢失球权,球迷也很紧张\\n\\n国足2-1逆转朝鲜,场上最差三人浮出水面,网友惊呼到底谁选的?\\n\\n### 国足2-1逆转朝鲜,场上最差三人浮出水面,网友惊呼到底谁选的?\\n\\n新华社消息|徐湖平等相关责任人被依规依纪依法处理\\n\\n### 新华社消息|徐湖平等相关责任人被依规依纪依法处理\\n\\n生涯第49冠!梅西传射+百球里程碑,迈阿密国际2-0夺北冠杯\\n\\n### 生涯第49冠!梅西传射+百球里程碑,迈阿密国际2-0夺北冠杯\', \'score\': 0.59089786, \'raw_content\': None, \'id\': \'229aa0-02\'}, {\'url\': \'https://www.weather.com.cn/weather/101160101.shtml\', \'title\': \'兰州雷达图\', \'content\': \'首页 预报 预警 雷达 云图 天气地图 专业产品 资讯 视频 节气\\n\\n更多\\n\\n台风路径 空间天气 图片 专题 环境 旅游 碳中和 气象科普 一带一路 产创平台\\n\\n国内) 本地) 国际)\\n\\n:   北京 上海 成都 杭州 南京 天津 深圳 重庆 西安 广州 青岛 武汉\\n\\n:   故宫 阳朔漓江 龙门石窟 野三坡 颐和园 九寨沟 东方明珠 凤凰古城 秦始皇陵 桃花源\\n\\n高球\\n:   佘山 春城湖畔 华彬庄园 观澜湖 依必朗 旭宝 博鳌 玉龙雪山 番禺南沙 东方明珠\\n\\n<<返回 全国\\n\\n河北下辖区域\\n\\n热门城市\\n\\n:   曼谷 东京 首尔 吉隆坡 新加坡 巴黎 罗马 伦敦 雅典 柏林 纽约 温哥华 墨西哥城 哈瓦那 圣何塞 巴西利亚 布宜诺斯艾利斯 圣地亚哥 利马 基多 悉尼 墨尔本 惠灵顿 奥克兰 苏瓦 开罗 内罗毕 开普敦 维多利亚 拉巴特\\n\\n:   亚洲 欧洲 北美洲 南美洲 非洲 大洋洲\\n\\n全国> 甘肃 > 兰州> 城区\\n\\n 今天\\n 7天\\n 8-15天\\n 40天\\n 雷达图\\n\\n # 16日(今天)\\n\\n  多云\\n\\n  11℃\\n\\n  <3级\\n # 17日(明天)\\n\\n  多云 [...] 多云\\n\\n  26℃/13℃\\n\\n  <3级\\n # 18日(后天)\\n\\n  多云转阴\\n\\n  26℃/13℃\\n\\n  <3级\\n # 19日(周六)\\n\\n  阴\\n\\n  20℃/10℃\\n\\n  <3级\\n # 20日(周日)\\n\\n  小雨转阴\\n\\n  18℃/11℃\\n\\n  <3级\\n # 21日(周一)\\n\\n  阴转晴\\n\\n  23℃/10℃\\n\\n  <3级\\n # 22日(周二)\\n\\n  晴转阴\\n\\n  25℃/13℃\\n\\n  <3级\\n\\n分时段预报 生活指数\\n\\n蓝天预报 \\n\\n### 蓝天预报综合天气现象、能见度、空气质量等因子,预测未来一周的天空状况。\\n\\n 天空蔚蓝 可见透彻蓝天,或有蓝天白云美景。\\n 天空淡蓝 天空不够清澈,以浅蓝色为主。\\n 天空阴沉 阴天或有雨雪,天空灰暗。\\n 天空灰霾 出现霾或沙尘,天空灰蒙浑浊。\\n\\n 易发 感冒指数\\n\\n  大幅度降温,适当增加衣服。\\n 较适宜 运动指数\\n\\n  天气凉,在户外运动请注意增减衣物。\\n 易发 过敏指数\\n\\n  应减少外出,外出需采取防护措施。\\n \\n\\n  较冷 穿衣指数\\n\\n  建议着厚外套加毛衣等服装。)\\n 适宜 洗车指数 [...] 天气较好,尽情感受运动的快乐吧。\\n 易发 过敏指数\\n\\n  应减少外出,外出需采取防护措施。\\n \\n\\n  舒适 穿衣指数\\n\\n  建议穿长袖衬衫单裤等服装。)\\n 适宜 洗车指数\\n\\n  天气较好,适合擦洗汽车。\\n 很强 紫外线指数\\n\\n  涂擦SPF20以上,PA++护肤品,避强光。\\n\\n# 天气资讯\', \'score\': 0.55796635, \'raw_content\': None, \'id\': \'f6460e-03\'}], \'response_time\': 0.98, \'request_id\': \'69f04ec4-b1b8-40ef-a328-25072798cea8\'}', tool_call_id='call_00_PyLPQWRheb2yNOkXGsOm7081')]}}
{'LLMCall': {'messages': [AIMessage(content='根据查询结果,我为你总结一下今天兰州市榆中县的天气情况:\n\n**兰州市榆中县今日天气总结**\n\n今天榆中县白天**晴**,最高气温约**20℃**;夜间转**多云**,最低气温约**9℃**。全天风力较小,为**无持续风向,风力小于3级**。昼夜温差较大,接近11℃,早晚偏凉。此外,兰州市气象台曾发布雷雨大风黄色预警,榆中南部地区下午至傍晚可能受雷雨大风影响,伴有雷阵雨,需注意防范。建议穿着长袖衬衫加薄外套,并随身携带雨具以备不时之需。\n\n(注:天气数据来自中国天气网,实际天气请以最新预报为准。)', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 159, 'prompt_tokens': 6007, 'total_tokens': 6166, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 1792, 'image_tokens': None, 'text_tokens': None}, 'prompt_cache_hit_tokens': 1792, 'prompt_cache_miss_tokens': 4215}, 'model_provider': 'deepseek', 'model_name': 'deepseek-flash', 'system_fingerprint': 'aeb56401ca74e127821c4f9126dcb669', 'id': 'acaf429b-d971-4296-8ebe-e0cc026e6ac4', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--01a0ae5a-273a-7910-9909-80b8a58d7b72-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 6007, 'output_tokens': 159, 'total_tokens': 6166, 'input_token_details': {'cache_read': 1792}, 'output_token_details': {}})], 'llm_calls': 2}}

进程已结束,退出代码为 0

四、完整代码

python 复制代码
from langchain.messages import AnyMessage
from langchain_core.messages import SystemMessage, ToolMessage, HumanMessage
from langchain_deepseek import ChatDeepSeek
from typing import TypedDict, Annotated
from operator import add
from langchain_tavily import TavilySearch
from langgraph.graph import StateGraph
from langgraph.constants import START,END

model=ChatDeepSeek(model="deepseek-chat")
search=TavilySearch(max_results=4)
tools=[search]
model_with_tools=model.bind_tools(tools)

#定义消息状态
class MessageState(TypedDict):
    messages:Annotated[list[AnyMessage],add]
    llm_calls:int

#定义节点
#调用大模型
def LLMCall(state:MessageState):
    res=[]
    res.append(
        model_with_tools.invoke([SystemMessage("你是一个有求必应的人工智能助手")]+state["messages"]
    ))
    return{
        "messages":res,
        "llm_calls":state.get("llm_calls",0)+1
    }

tools_by_name={tool.name:tool for tool in tools}

#需要调用工具进行搜索
def ToolCalls(state:MessageState):
    for tool_call in state["messages"][-1].tool_calls:
        #这里传入tool_call["args"]工具调用参数返回的是业务类型
        ret=tools_by_name[tool_call["name"]].invoke(tool_call["args"])
        #需要手动构造ToolMessage并指定tool_call_id
        toolmess=ToolMessage(content=ret,tool_call_id=tool_call["id"])
    return{
        "messages":[toolmess]
    }


#定义图
Intelligent_Agent_System=StateGraph(MessageState)

#添加节点
Intelligent_Agent_System.add_node("LLMCall",LLMCall)
Intelligent_Agent_System.add_node("ToolCalls",ToolCalls)

#定义边
#条件判断接口
def IsCallTool(state:MessageState):
    if state["messages"][-1].tool_calls:
        return "ToolCalls"
    else:
        return END

Intelligent_Agent_System.add_edge(START,"LLMCall")
Intelligent_Agent_System.add_conditional_edges(
    "LLMCall",
    IsCallTool,
    ["ToolCalls",END]
)
Intelligent_Agent_System.add_edge("ToolCalls","LLMCall")

#编译图
Agent=Intelligent_Agent_System.compile()

#测试图
for chunk in Agent.stream(
        {
            "messages":[HumanMessage(content="今天兰州市榆中县的天气如何100字总结一下")],
            "llm_calls":0
        }
    ):
    print(chunk)

# finstate=Agent.invoke(
#         {
#             "messages":[HumanMessage(content="今天兰州市榆中县的天气如何100字总结一下")],
#             "llm_calls":0
#         }
# )
#
# print(f"大模型一共调用了{finstate["llm_calls"]}次\n")
#
# for mess in finstate["messages"]:
#     mess.pretty_print()
相关推荐
吴佳浩1 小时前
Agent Memory 架构设计:短时记忆、长期记忆与 Memory 生命周期
人工智能·agent·ai编程
GlobalInfo1 小时前
上新 | 全球及中国Agent测试用例生成软件行业研究报告(2026版):市场现状、行业前景与占有率分析
人工智能·ai·agent
shujudang1 小时前
零售客户分析平台的四种路径:身份关联、分析模型与系统对接
大数据·人工智能·数据分析
袁俪1 小时前
大模型幻觉
人工智能
找方案1 小时前
AI+汽车座舱:智能座舱如何重新定义驾驶体验
人工智能·汽车
武子康1 小时前
Expert Parallel 深入解析:专家分得均匀,负载为什么仍不均
人工智能·llm·agent
她的男孩2 小时前
做了"异步导出",用户点了还是卡 3 分钟:扒完 4223 行源码,@Async 压根没生效
人工智能·后端·程序员
szxinmai主板定制专家2 小时前
【嵌入式实战】RK3588+GMSL多路相机采集方案|SerDes长距离低延时视频流落地教程
人工智能·单片机·机器人·边缘计算·zynq
Ada's2 小时前
【智能体系统AgentOS】核心25:Agentic AI
人工智能