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

LangGraph实战:用MemorySaver+ChatGPT API快速搭建一个能记住上下文的天气查询机器人

LangGraph实战:用MemorySaver+ChatGPT API快速搭建能记住上下文的天气机器人

最近在开发对话式AI应用时,我发现很多初学者都面临一个共同痛点:如何让机器人记住之前的对话?上周帮朋友调试一个天气查询项目时,我们仅用2小时就基于LangGraph的MemorySaver功能实现了上下文记忆。下面分享这个实战案例的完整构建过程。

1. 环境准备与工具定义

1.1 初始化项目环境

首先创建项目目录并安装核心依赖:

mkdir weather-bot && cd weather-bot pip install langgraph langchain-openai python-dotenv

在项目根目录创建.env文件存储API密钥:

OPENAI_API_KEY=你的API密钥

1.2 构建天气查询工具

我们使用LangChain的@tool装饰器创建天气查询函数。实际项目中可以接入真实天气API,这里先用模拟数据演示:

from langchain_core.tools import tool @tool def get_weather(location: str, date: str = "今天") -> str: """获取指定地点和日期的天气信息 参数: location: 查询地点(如"上海") date: 查询日期(默认今天) 返回: 格式化天气信息字符串 """ return f"{date}{location}天气:晴,25℃,东南风3级"

提示:工具函数的docstring会直接影响LLM对工具的理解,建议明确参数类型和返回格式

2. 构建对话状态机

2.1 定义状态结构

LangGraph使用TypedDict定义对话状态。我们的天气机器人只需要维护消息历史:

from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class State(TypedDict): messages: Annotated[list, add_messages] # 自动处理消息追加

2.2 创建核心节点

定义两个关键节点:

  • 模型节点:调用ChatGPT处理用户输入
  • 工具节点:执行天气查询等具体操作
from langchain_openai import ChatOpenAI from langgraph.prebuilt import ToolNode # 初始化模型并绑定工具 llm = ChatOpenAI(model="gpt-3.5-turbo") bound_llm = llm.bind_tools([get_weather]) # 模型节点 def call_model(state: State): response = bound_llm.invoke(state["messages"]) return {"messages": response} # 工具节点 tool_node = ToolNode([get_weather])

3. 实现记忆功能

3.1 配置MemorySaver

LangGraph的MemorySaver让状态持久化变得简单:

from langgraph.graph import StateGraph from langgraph.checkpoint.memory import MemorySaver # 创建状态图 graph = StateGraph(State) graph.add_node("agent", call_model) graph.add_node("tools", tool_node) # 设置条件转移 def should_continue(state: State): last_msg = state["messages"][-1] return "tools" if hasattr(last_msg, "tool_calls") else "__end__" graph.add_edge("tools", "agent") graph.add_conditional_edges("agent", should_continue) # 启用记忆功能 memory = MemorySaver() app = graph.compile(checkpointer=memory)

3.2 线程隔离实践

通过thread_id区分不同会话,实现多用户隔离:

config = {"configurable": {"thread_id": "user123"}} # 用户唯一标识 # 第一轮对话 input_msg = HumanMessage(content="今天北京天气怎样?") for event in app.stream({"messages": [input_msg]}, config): print(event["messages"][-1].content) # 第二轮对话(机器人记得之前的内容) input_msg = HumanMessage(content="我刚才问了什么?") for event in app.stream({"messages": [input_msg]}, config): print(event["messages"][-1].content) # 输出:"您刚才询问了今天北京的天气"

4. 性能优化与扩展

4.1 内存管理技巧

当处理大量会话时,可以添加自动清理机制:

from datetime import datetime, timedelta class TimedMemorySaver(MemorySaver): def __init__(self, ttl=timedelta(hours=1)): self.ttl = ttl super().__init__() def get(self, config): thread_id = config["configurable"]["thread_id"] if thread_id in self.store: timestamp, _ = self.store[thread_id] if datetime.now() - timestamp > self.ttl: del self.store[thread_id] return super().get(config)

4.2 支持更多工具类型

扩展机器人能力只需添加新工具函数:

@tool def get_air_quality(city: str) -> str: """获取城市空气质量指数""" return f"{city}AQI:78(良)" # 更新工具列表 tools = [get_weather, get_air_quality] bound_llm = llm.bind_tools(tools)

5. 常见问题排查

在实际部署中可能会遇到:

问题1:工具调用不触发

  • 检查工具函数的参数类型提示
  • 验证模型是否成功绑定工具(print(bound_llm.tools)

问题2:记忆丢失

  • 确保每次请求使用相同的thread_id
  • 检查MemorySaver实例是否被意外重建

问题3:响应延迟高

  • 尝试减小max_tokens参数
  • 使用stream=True实现渐进式响应

这个项目最让我惊喜的是LangGraph的状态管理设计——用200行代码就实现了传统需要复杂数据库支持的对话记忆功能。最近在客户项目中,我们基于这个原型扩展出了支持10+工具的客服系统,MemorySaver在原型验证阶段节省了大量开发时间。

http://www.jsqmd.com/news/531450/

相关文章:

  • 叠加态程序员:同时被10家公司雇佣的黑暗操作
  • 深度解析ChatDev 2.0:构建下一代AI驱动的自动化开发流程与智能协作工具
  • 100%采样率引发的全线熔断:Spring Boot 链路追踪的性能绞杀与物理级调优
  • CF3.0单机版下载安装及人机8v8挑战模式完整使用教程
  • 深度解析|安科士ANBR-1414TZ光模块,工业级长距通信的性能密码
  • ShardingCore终极指南:深度剖析EF Core分库分表架构实战
  • 解锁SillyTavern多模态对话:从单调文字到沉浸式AI体验的跃迁
  • MATLAB基于背景差分法和卡尔曼滤波的运动目标检测和跟踪:1、对视频序列中的运动目标进行检测与跟踪
  • OpenClaw快速体验方案:星图GLM-4.7-Flash镜像10分钟上手
  • 从零实现PPO-Lagrangian:安全强化学习的代码架构与核心模块剖析
  • 国防科研光纤偏振控制器优质厂家推荐榜:光纤放大器公司推荐、光纤放大器厂家推荐、光纤放大器哪家好、光纤放大器哪里有选择指南 - 优质品牌商家
  • 告别虚拟机!在Win10上5分钟搞定NFS服务器,嵌入式开发挂载硬盘真香
  • OpenClaw+GLM-4.7-Flash:5个提升开发效率的自动化技巧
  • STM32H7高性能模拟库:突破Arduino ADC/DAC/I2S极限
  • 蓝牙时间同步避坑指南:为什么你的RTC万年历总是走不准?(附KT6368A解决方案)
  • DeepSeek-OCR-2部署案例:GPU算力优化下256 Token高效文档解析实操
  • 手把手教你用sglang实现Qwen2-1.5B-Instruct的PD分离部署(附mooncake传输引擎配置)
  • 保姆级教程:用C++和ROS实现差速轮与阿克曼模型的轨迹预测(附完整代码)
  • QAnything负载测试:Locust模拟高并发场景实践
  • 深入解析Windows Research Kernel:微软官方内核源码的终极学习指南
  • 解决跨平台中文字体渲染难题:PingFangSC开源字体的技术突破与应用价值
  • 【独家首发】Dify v0.8.3+ Rerank API高并发瓶颈突破方案:异步批处理+GPU卸载+缓存穿透防护三重加固
  • ArcMap注记批量修改保姆级教程:5分钟搞定字体、颜色、大小统一调整
  • HunyuanVideo-Foley 技术栈全景图:从底层驱动到上层应用的全链路解析
  • s2-pro镜像免配置快速上手:单页Web工具实现零代码语音合成
  • ZYNQ嵌入式开发实战:基于PetaLinux的Linux系统移植与优化
  • 医学影像处理指南:MRI的nii格式转2D切片的5个实用技巧与避坑指南
  • Step3-VL-10B-Base多模态模型Python爬虫实战:自动化数据采集与图像分析
  • Allegro出Gerber老手也容易踩的坑:详解‘零尺寸D码’的成因与CAM350里的两种高效查找法
  • 别光看OS了!从链接文件到Brs模块,手把手拆解TC39X芯片上Autosar代码的冷启动流程