当前位置: 首页 > news >正文

LangGraph-AI应用开发框架(三)

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

1.案例介绍

2.编码思路

a.设置Nodes

b.设置State

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

messages = [ HumanMessage(content="你好"), AIMessage(content="你好!我是AI助手"), HumanMessage(content="什么是机器学习?"), AIMessage(content="机器学习是...") ]

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

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

c.设置Edges

3.代码实现

a.步骤1:准备工作,定义聊天模型和搜索工具

from typing import TypedDict, Annotated import operator from langchain.chat_models import init_chat_model from langchain_core.messages import AnyMessage from langchain_tavily import TavilySearch #准备工作 serach = TavilySearch(max_results=4) tools = [serach] model = init_chat_model("glm-4",temperature=0) model_with_tools = model.bind_tools(tools)

b.步骤2:定义状态

#1.状态定义 class MessageState(TypedDict): #消息列表 message: Annotated[list[AnyMessage],operator.add] #调用LLM次数 llm_calls: int

c.步骤3:定义模型节点

#2.定义节点 def llm_calls(state: MessageState): """LLM决定是否调用工具""" #由于当前节点可能是start过来的,也可能是工具过来的 #因此state["message"]可能是[H],[H,A,T] messages = state["messages"] #带tool_calls或不带tool_calls的AIMessage result = model_with_tools.invoke( [ SystemMessage(content="你是一个乐于助人的助手,支持调用工具进行搜索"), ] + messages ) return { "messages": [result], "llm_calls": state["llm_calls"] + 1 }

d.步骤4:定义工具节点

回顾

要点1:回顾AIMessage消息结构

要点2:构造ToolMessage

要点3:在State中访问messages

tools_by_name = {tool.name:tool for tool in tools} def tool_node(state: MessageState): """执行工具调用节点""" #result 就是 toolmessage result = [] for tool_call in state["messages"][-1].tool_calls: #获取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 { "messages": result }

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

#3.定义图,添加节点和边 agent_builder = StateGraph(MessageState) agent_builder.add_node(llm_calls) agent_builder.add_node(tool_node) agent_builder.add_edge(START,"llm_calls") def should_continue(state: MessageState): #最新消息是AIMessage,判断是否带有tool_calls last_messages = state["messages"][-1] if last_messages.tool_calls: return "tool_node" else: return END agent_builder.add_conditional_edges( "llm_calls", should_continue, ["tool_node",END] ) agent_builder.add_edge("tool_node","llm_calls") agent_search = agent_builder.compile()

f.步骤6:可视化图

绘图工具链接:https://www.jyshare.com/front-end/9729/

import matplotlib.pyplot as plt import matplotlib.image as mpimg try: # 生成 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() # 弹出窗口显示图片 except Exception as e: print(f"An error occurred: {e}")

我们能直观的看到,我们的代码写的对不对

这个得到的就是我们对应的图生成的代码

g.步骤7:执行(非流式与流式)

result = agent_search.invoke({ "messages": [HumanMessage(content="今天北京的天气如何?")], "llm_calls": 0 }) print(f"一共调用了 {result['llm_calls']} 次LLM") for msg in result["messages"]: msg.pretty_print()

4.总代码

from typing import TypedDict, Annotated import operator from langchain_core.messages import AnyMessage, SystemMessage, ToolMessage, HumanMessage from langchain_tavily import TavilySearch from langgraph.constants import START, END from langgraph.graph import StateGraph from langchain_openai import ChatOpenAI # ===================== 终极兼容补丁(必须放在最顶部!)===================== import langchain langchain.verbose = False langchain.debug = False langchain.llm_cache = None # ======================================================================== import os api_key = os.getenv("ZHIPUAI_API_KEY") model = ChatOpenAI( model="glm-5", api_key=api_key, base_url="https://open.bigmodel.cn/api/paas/v4/", # 智谱官方接口 temperature=0 ) # 工具 search = TavilySearch(max_results=4) tools = [search] model_with_tools = model.bind_tools(tools) # 状态 class MessageState(TypedDict): messages: Annotated[list[AnyMessage], operator.add] llm_calls: int # LLM 节点 def llm_calls(state: MessageState): messages = state["messages"] result = model_with_tools.invoke( [SystemMessage(content="你是一个乐于助人的助手,支持调用工具搜索")] + messages ) return { "messages": [result], "llm_calls": state["llm_calls"] + 1 } # 工具节点 tools_by_name = {tool.name: tool for tool in tools} def tool_node(state: MessageState): result = [] for tool_call in state["messages"][-1].tool_calls: 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 {"messages": result} # 构建图 agent_builder = StateGraph(MessageState) agent_builder.add_node("llm_calls", llm_calls) agent_builder.add_node("tool_node", tool_node) agent_builder.add_edge(START, "llm_calls") def should_continue(state: MessageState): last_message = state["messages"][-1] return "tool_node" if last_message.tool_calls else END agent_builder.add_conditional_edges("llm_calls", should_continue, ["tool_node", END]) agent_builder.add_edge("tool_node", "llm_calls") # 运行 agent_search = agent_builder.compile() 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}") result = agent_search.invoke({ "messages": [HumanMessage(content="今天北京的天气如何?")], "llm_calls": 0 }) print(f"一共调用了 {result['llm_calls']} 次LLM") for msg in result["messages"]: msg.pretty_print()
http://www.jsqmd.com/news/662544/

相关文章:

  • 技术顶尖却始终赚不到大钱:程序员最容易忽略的那门“手艺”
  • LFM2.5-1.2B-Thinking-GGUF系统管理辅助:基于命令行的智能运维问答
  • 【AI面试临阵磨枪】什么是上下文窗口(Context Window)限制?主流解决方法有哪些?
  • 技术革新与资源聚合,2026广州汽车测试测量展勾勒行业新图景
  • Ventoy引导自定义菜单配置详解:手把手教你用ventoy_grub.cfg启动硬盘里的Manjaro
  • 文件处理:如何正确反转文件内容
  • 蓝桥杯单片机省赛复盘:用STC15F2K60S2搞定ADC、EEPROM与矩阵键盘的实战避坑指南
  • 数字IC面试核心:从MUX基础到Glitch-Free时钟切换电路深度剖析
  • Superpowers - 17 把「写技能」当成工程实践:面向 Claude 的自定义技能编写完整指南
  • 如何为NVIDIA显卡显示器实现专业级色彩校准:novideo_srgb深度指南
  • Obsidian PDF导出终极指南:从笔记到专业文档的完美转换
  • 销售竞争的真正战场,从来不是营销和案例,而是销售流程
  • 一文搞懂前端请求超时与取消:从 Promise.race 到 AbortController
  • 别再为竖屏视频发愁!用Premiere一键旋转并适配横屏的完整工作流
  • 从Pwin3.2到Win11:otvdmw如何成为16位程序的‘时光机’?聊聊它的原理与局限
  • GDSDecomp深度解析:Godot游戏逆向工程的架构设计与性能优化
  • 如何用LeRobot在3天内打造你的第一个智能机器人?
  • BetterNCM安装器完全指南:3步解锁网易云音乐插件生态
  • 【进阶篇 / DNS】(7.0) ❀ 02. 多线接入下的DNS策略优化 ❀ FortiGate 防火墙
  • 安装materials studio 2023版本
  • 从XSA到启动卡:Petalinux定制嵌入式Linux系统的全流程实战
  • 本地AI部署硬件之争,为什么Mac Mini和塔式机“都对”却永远吵不完
  • 基于STM32标准库的MS5837驱动移植与IIC时序调试实战
  • 高通SDM660手机开机到Linux内核,ABL的LinuxLoader都干了啥?(代码流程详解)
  • 【注意力机制演进】从SE到CBAM:通道注意力核心思想与代码实战解析
  • 从Bash切换到Zsh后,如何让Kali的渗透测试工具(如Msfvenom)命令补全更丝滑?
  • 别再瞎改retarget.c了!深入理解Keil AC5/AC6/GCC的printf重定向底层差异
  • 3步彻底解决Windows系统卡顿问题:Winhance中文版完全指南
  • 家用路由器当AP用?小心这个坑!详解双路由器组网下的设备互访与防火墙设置
  • ABAP AES加密实战:从标准类库到外部集成的安全方案