[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()
相关推荐
默_笙3 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
rockmelodies3 天前
# Windows Server2008 R2 Standard(SP1镜像U盘)重置本地管理员密码【完整详细步骤】
windows·密码破解
qq_426003963 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技3 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时3 天前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
染指11103 天前
122.Agent-LangChain核心组件-中间件-动态提示词(dynamic_promapt)
人工智能·langchain·agent·agents
奇思妙想聪明勤奋的小羊3 天前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型