[LangGraph] 案例 2 : 支持搜索的智能代理系统

此案例调用大模型来完成应用系统,

基于聊天模型, 能够理解用户的问题并解决是否需要调用搜索工具

快速上手 :

1. 准备工作 定义 LLM 并 绑定工具

复制代码
# 准备工作
# 定义大语言模型
model = init_chat_model("gpt-4o-mini",temperature = 0) # 温度为 0

# 绑定工具
search = TavilySearch(max_results = 4)
tools = [search]
model_with_tools = model.bind_tools(tools)

2. 设置状态 State

message : 类型listAnyMessage , 是追加更新, 作用: 存放任意消息对象的列表

llm_calls : 类型int, 是覆盖更新, 作用: 跟踪 LLM 的调用次数

复制代码
# 1. 状态定义
class MessageState(TypedDict):
    # 消息列表 (记忆功能, 维护上下文)
    message:Annotated[list[AnyMessage],operator.add]  # 追加更新

    # 调用llm次数
    llm_calls:int

3. 设置 Nodes :

节点 1 llm_call

专门负责搜索, 获取搜索结果

来到 llm_call 有两种情况 :

① 用户输入问题->llm_call (messagesHumanMessage): 此时 llm 先判断是否需要进行搜索工具搜索, 需要, 则调用工具节点; 不需要, 则直接输出 AIMessage

② 调用完工具节点->llm_call (messagesHumanMessage,AIMessage,ToolMessage) : 此时 llm 根据 完整 messages 整合后生成新的 AIMessage 并输出

节点 2 tool_node

专门负责调用 LLM, 获取最终结果

根据 llm 调用工具的输出结构, 在 tool_calls中 包含执行工具所需要的属性, 并构建 ToolMessage

复制代码
# 2. 节点定义
def llm_call(state:MessageState):
    """LLM 决定是否调用工具"""
    # 由于当前节点可能是START 过来的, 也有可能是工具节点过来的
    # 因此state["message"]获取的是[H] 或[H,A,T]
    messages = state["message"]         # 拿到HumanMessage
    # result 可能 1 : 带有tool_calls的 AIMessage
    # result 可能 2 : 不带tool_calls的 AIMessage(最终结果)
    result = model_with_tools.invoke(
        [
            SystemMessage(content = "你是一个乐于助人的助手, 支持调用工具进行搜索")
        ]+
        messages
    )
    return {
        "message": [result],                #追加更新
        "llm_calls": state.get("llm_calls",0) + 1
    }
# {键表达式: 值表达式 for 变量 in 可迭代对象}
tools_by_name = {tool.name: tool for tool in tools}

def tool_node(state:MessageState):
    """执行工具调用"""
    result = []

    # 拿到当前最新的消息 AIMessage
    for tool_call in state["message"][-1].tool_calls :
        # 就可以获取到tool_call 的 name,args,id...
        tool = tools_by_name[tool_call["name"]]
        obs = tool.invoke(tool_call["args"])
        result.append(ToolMessage(content = obs,tool_call_id = tool_call["id"]))

    return {
        "message": result,                #追加更新
    }

4. 定义图, 设置节点和边

此处只讲解 条件边的构建, 其余方法参考图结构和案例 1

路由条件 : should_continue

判断最新消息是AIMessage 是否带有tool_calls : 携带则执行tool_node 节点, 不携带则执行 END

复制代码
# 3. 定义图, 添加节点和边
agent_builder = StateGraph(MessageState)

agent_builder.add_node(llm_call)
agent_builder.add_node(tool_node)

agent_builder.add_edge(START,"llm_call")

# 路由条件
def should_continue(state:MessageState):
    # 判断最新消息是AIMessage 是否带有tool_calls
    # 带tool_calls 走tool_node
    # 不带 END
    last_message = state["message"][-1]
    if last_message.tool_calls:
        return "tool_node"
    return END

agent_builder.add_conditional_edges(
    "llm_call",         
    should_continue,
    ["tool_node",END]
)
agent_builder.add_edge("tool_node","llm_call")

# 4. 编译图
agent_search = agent_builder.compile()

5.生成图样式

借助工具库 在终端中执行pip install matplotlib****, 或 使用在线绘图工具Mermaid 在线绘图工具 | 菜鸟工具

复制代码
# 5. 生成图样式
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

try:
    # 生成 Mermaid 图表并保存为图片
    mermaid_code = agent_search.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()       # 弹出窗口显示图片

except Exception as e:
    print(f"An error occurred: {e}")

6.执行图

复制代码
# 6.执行图
result = agent_search.invoke({
    "message":[HumanMessage(content="今天西安的天气如何?")]
})
# result 是最终的结果状态
print(f"一共调用了{result['llm_calls']}次大模型")
for msg in result["message"]:
    msg.pretty_print()

完整代码 :

复制代码
import operator
from typing import TypedDict, Annotated

from langchain.chat_models import init_chat_model
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langchain_tavily import TavilySearch
from langchain_core.messages import ToolMessage
from langgraph.graph import StateGraph, START,END


# 准本工作
# 定义大语言模型
model = init_chat_model("gpt-4o-mini",temperature = 0) # 温度为 0

# 绑定工具
search = TavilySearch(max_results = 4)
tools = [search]
model_with_tools = model.bind_tools(tools)

# 1. 状态定义
class MessageState(TypedDict):
    # 消息列表 (记忆功能, 维护上下文)
    message:Annotated[list[AnyMessage],operator.add]  # 追加更新

    # 调用llm次数
    llm_calls:int

# 2. 节点定义
def llm_call(state:MessageState):
    """LLM 决定是否调用工具"""
    # 由于当前节点可能是START 过来的, 也有可能是工具节点过来的
    # 因此state["message"]获取的是[H] 或[H,A,T]
    messages = state["message"]         # 拿到HumanMessage
    # result 可能 1 : 带有tool_calls的 AIMessage
    # result 可能 2 : 不带tool_calls的 AIMessage(最终结果)
    result = model_with_tools.invoke(
        [
            SystemMessage(content = "你是一个乐于助人的助手, 支持调用工具进行搜索")
        ]+
        messages
    )
    return {
        "message": [result],                #追加更新
        "llm_calls": state.get("llm_calls",0) + 1
    }
# (这是字典 Dict,建立了 名称 -> 工具对象 的映射)
tools_by_name = {tool.name: tool for tool in tools}

def tool_node(state:MessageState):
    """执行工具调用"""
    result = []

    # 拿到当前最新的消息 AIMessage
    for tool_call in state["message"][-1].tool_calls :
        # 就可以获取到tool_call 的 name,args,id...
        tool = tools_by_name[tool_call["name"]]
        obs = tool.invoke(tool_call["args"])
        result.append(ToolMessage(content = obs,tool_call_id = tool_call["id"]))

    return {
        "message": result,                #追加更新
    }

# 3. 定义图, 添加节点和边
agent_builder = StateGraph(MessageState)
agent_builder.add_node(llm_call)
agent_builder.add_node(tool_node)

agent_builder.add_edge(START,"llm_call")

# 路由条件
def should_continue(state:MessageState):
    # 最新消息是AIMessage 判断是否带有tool_calls
    # 带tool_calls 走tool_node
    # 不带 END
    last_message = state["message"][-1]
    if last_message.tool_calls:
        return "tool_node"
    return END

agent_builder.add_conditional_edges(
    "llm_call",
    should_continue,
    ["tool_node",END]
)
agent_builder.add_edge("tool_node","llm_call")


# 4. 编译图
agent_search = agent_builder.compile()

# # 5. 生成图样式
# import matplotlib.pyplot as plt
# import matplotlib.image as mpimg
#
# try:
#     # 生成 Mermaid 图表并保存为图片
#     mermaid_code = agent_search.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()       # 弹出窗口显示图片
#
# except Exception as e:
#     print(f"An error occurred: {e}")

# 6.执行图
result = agent_search.invoke({
    "message":[HumanMessage(content="今天西安的天气如何?")]
})
# result 是最终的结果状态
print(f"一共调用了{result['llm_calls']}次大模型")
for msg in result["message"]:
    msg.pretty_print()
相关推荐
鬼手点金9 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
VidDown9 小时前
24fps 转 25fps 不只是改个数字:帧率转换、PAL/NTSC 与音频补偿
python·网络协议·音视频·视频编解码·视频
贵州诚鑫致达科技有限公司10 小时前
Linux的yum源怎么重启?源不是服务
linux·运维·服务器
遇印记11 小时前
传输层,网络层
运维·服务器·网络·windows·学习
死磕音视频11 小时前
让小模型做选择题:限定候选项并读取 logits 概率的入门实践(附 Python 代码)
开发语言·python·大模型·文本分类·transformers
Elastic 中国社区官方博客11 小时前
一条 ES|QL 查询,涵盖365天的审计日志:在热数据和可搜索快照上执行相同的搜索
大数据·运维·服务器·elasticsearch·搜索引擎·全文检索
凤山老林12 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
北京中科新远科技12 小时前
高速光模块上线验收实战:DOM、FEC、BER与端口计数器怎么一起看
服务器·网络·php
又見山12 小时前
Obsidian 跨平台同步实测:Nutstore Sync 在 Windows、Mac、Linux、iOS、Android 全平台综合测评
windows·macos·obsidian·obsidian同步·obsidian教程·obsidian入门·obsidian笔记库搭建
澜山奕秋13 小时前
RHCSA
linux·运维·服务器