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

Memory记忆机制

实际上,LLM本身没有真正的记忆,每次调用都像一次全新的对话。如果只发送当前问题而不提供历史记录,模型并不知道之前聊过什么。因此,多轮对话的实现方式并不是让模型记住内容,而是由应用程序保存历史对话,并在每次提问时将历史记录和当前问题一起发送给LLM,使模型看起来像拥有持续记忆一样。

举一个更完整的例子,可以直观说明上下文在对话中的作用。假设我们有一个周末出行规划助手。在第一轮中,用户提出一个模糊需求:

用户:帮我规划一下这个周六的出行。 AI:好的,请问你想去哪里,大概几点出发?

此时,LLM已建立基本语境,即围绕周六出行规划展开,并引导用户补充关键信息。随后,用户提供细节:

用户:去植物园,上午10点左右。

如果这句话单独输入,它只是一个零散片段,由于缺少背景信息,模型难以准确判断其意图,可能将其理解为日程记录或简单描述。

但当上下文一并输入时,语境变得完整,模型可以基于前后关联信息正确理解其含义:

用户:帮我规划一下这个周六的出行。 AI:好的,请问你想去哪里,大概几点出发? 用户:去植物园,上午10点左右。

可以看到,对话之所以能够连贯,并不是因为模型具备记忆能力,而是因为每一次请求都会将必要的上下文一起传入模型,模型只是基于当前输入重新进行理解与生成。

记忆

在LangChain框架中,负责管理对话历史的组件称为Memory,其本质是一个专门保存聊天记录的地方,用于保存每一轮的用户输入与模型输出。例如,可以通过InMemoryChatMessageHistory类实现这一机制。基于这一设计,多轮对话的执行流程如下:

  1. 接收用户输入。
  2. 从Memory中读取历史对话。
  3. 将历史对话与当前问题组合成完整上下文。
  4. 调用模型生成回复。
  5. 将回复返回给用户。
  6. 将本轮对话写入Memory。

因此,多轮对话的关键不在于模型真正记住了过去的内容,而在于应用程序持续保存并提供历史对话。模型每次回答时,实际上都是基于当前问题和历史记录共同进行推理,从而保持对话的连贯性。关于长期记忆机制,可参考:深入解读LangChain 1.0 Agent长期记忆技术要点。

示例代码如下:

from langchain_core.chat_history import InMemoryChatMessageHistory # 创建 Memory history = InMemoryChatMessageHistory() # 添加用户消息 history.add_user_message("您好,我喜欢每天跑步1小时") # 添加回复消息 history.add_ai_message("我喜欢游泳") # 添加用户消息 history.add_user_message("我每天跑步多久?") response = chat_model.invoke(history.messages) print(response.content)

记忆裁剪

在多轮对话里若不加以限制,历史记录会持续膨胀。常见做法是按轮数裁剪,只保留最近K轮对话,更早的内容直接丢弃:

from langchain_classic.memory import ConversationBufferWindowMemory from langchain_classic.chains import ConversationChain from langchain_community.llms.fake import FakeListLLM llm = FakeListLLM( responses=["OK"] * 10 ) memory = ConversationBufferWindowMemory( k=3, return_messages=True ) conversation = ConversationChain( llm=llm, memory=memory ) for i in range(5): conversation.predict( input=f"第{i+1}轮对话" ) print(f"\n=== 第{i+1}轮后 ===") history = memory.load_memory_variables({})["history"] for message in history: print(message.content)

以上代码本质上实现的是一个固定长度的滑动窗口。运行结果如下:

  • 第1~3轮:正常累加
  • 第4轮:第1轮被移除
  • 第5轮:第2轮被移除

3.3 Tools工具

LLM擅长理解意图、推理和生成文本,但很多实际任务仅依赖生成能力并不足够,例如查询天气需要实时数据、查询订单需要访问业务系统,而这些能力通常不在模型内部,因此需要通过Tool机制将外部能力封装为可调用接口。Tool本质上是对某项功能的结构化描述和执行封装,类似函数调用,常见场景包括数学计算、时间获取、天气查询、数据库检索以及业务系统接口等。

一个Tool通常包含名称、描述、参数定义和执行逻辑,其中名称用于标识工具,描述帮助模型判断是否调用,参数定义约束输入格式,执行逻辑负责完成实际操作。在执行过程中,系统首先将工具注册给模型或Agent,当模型判断需要外部能力时,会根据用户输入和工具描述选择合适的工具并生成调用参数,待工具执行完成后再将结果返回给模型,最终由模型生成回答。对于这一机制的实现原理与实践方式,可以参考:How to Build a Custom AI Agent with LangChain and Python: A Step by Step Guide。

使用@tool定义工具

在LangChain中,可通过@tool装饰器将普通Python函数封装为Tool,函数名称、文档字符串和参数类型会自动提取为结构化元信息,用于描述工具能力。

from langchain_core.tools import tool @tool def get_weather(city: str) -> str: """获取指定城市的天气信息""" weather_data = { "A": "晴天,25°C", "B": "多云,22°C", } return weather_data.get(city, f"无法获取{city}天气") # 直接调用工具,不经过 LLM result = get_weather.invoke({"city": "A"}) print(result) # 晴天,25°C # 查看工具元信息 print(f"工具名: {get_weather.name}") print(f"描述: {get_weather.description}") print(f"参数: {get_weather.args}")

通过@tool定义后,该函数既可以作为普通Python函数直接调用,也可以作为工具被模型或Agent使用,两者的区别在于前者不涉及模型参与,而后者会进入工具调用流程,由模型决定是否调用并生成参数。更多介绍可参考:LangChain 实战教程:从入门到实战。

将工具绑定到模型

工具定义完成后,可以通过bind_tools方法将多个工具绑定到模型,使模型在处理输入时能够感知这些工具。当用户输入到来时,模型先判断是否需要调用外部能力,如果需要,则选择合适工具并生成调用参数。整个过程中,模型只负责工具选择与参数生成,工具的实际执行由开发者完成并返回结果。

from langchain_core.tools import tool @tool def get_weather(city: str) -> str: """获取城市天气""" return f"{city}:晴天 25°C" @tool def search_database(query: str) -> str: """搜索数据库信息""" return f"关于'{query}'的搜索结果:示例数据" tools = [get_weather, search_database] llm_with_tools = chat_model.bind_tools(tools) response = llm_with_tools.invoke("上海天气如何?") if response.tool_calls: for tc in response.tool_calls: print(f"调用工具: {tc['name']},参数: {tc['args']}") for tool in tools: if tool.name == tc['name']: print(f"结果: {tool.invoke(tc['args'])}")

Agent中使用工具

如果希望工具选择、调用与结果整合全部自动化,可以将工具交由Agent管理。Agent会根据输入自动选择工具、执行调用,并结合结果继续推理直至生成最终答案。

from langchain.agents import create_agent from langchain_core.tools import tool @tool def multiply(a: int, b: int) -> int: """Multiply two numbers.""" return a * b agent = create_agent( model=chat_model, tools=[multiply] ) response = agent.invoke({ "messages": [ { "role": "user", "content": "12乘以9等于多少" } ] }) print(response["messages"][-1].content)

使用StructuredTool

除了使用@tool快速将函数封装为Tool外,还可以通过StructuredTool显式定义工具的名称、描述和参数结构,其中@tool更适合快速开发,StructuredTool更适合需要严格控制元信息的场景,但两者本质上都是将外部能力封装为模型可调用的Tool。

""" StructuredTool 单独演示 """ from langchain_core.tools import StructuredTool # 使用StructuredTool将函数转换为工具 multiply_tool = StructuredTool.from_function( func=multiply, name="multiply", description="Multiply two numbers" ) # 调用工具 result = multiply_tool.invoke({"a": 5, "b": 3})
http://www.jsqmd.com/news/1139724/

相关文章:

  • 可视挖耳勺新手入门推荐:可视耳勺好用吗?西圣、蜂鸟哪个好?
  • 3.4 Nginx 负载均衡——动态再平衡的反人性纪律
  • 技术分享_2026-07-06
  • 基于微信小程序的博物馆文创系统的设计与实现毕业设计任务书
  • 3大核心功能解密:iCloud照片备份命令行工具的架构设计与实战应用
  • MVC架构中的Controller
  • Scikit-learn 1.3 与 Pandas 2.0 缺失值填充:KNNImputer 与 IterativeImputer 性能实测
  • 影刀RPA新手教程:猫眼票务自动化完全指南——票房数据采集、影院排片分析与舆情监控
  • 洛雪音乐音源终极配置指南:如何打造完美的全网音乐体验
  • WebODM技术深度解析:分布式无人机图像处理架构与算法实现
  • 3DThinkVLA:面向具身智能的隐式三维空间语义对齐框架
  • 栈溢出漏洞原理与OllyDbg调试实战:从概念到利用的完整指南
  • Qwen图像编辑AI加速器:从v1到v23的完整版本选择指南
  • AI教材生成新趋势:低查重AI写教材,快速打造专业教材!
  • 别再只用微信接口发小广告了!聊聊怎么把它接进公司业务系统搞定真自动化
  • 计算机毕业设计之基于web和微信小程序学生课程管理系统
  • 7 个技术写作技巧,让你的文档更出色
  • “你这代码写得太烂了“:程序员如何识别职场PUA,拒绝被煤气灯
  • CAD图纸为什么要上传云图?多端同步,杜绝图纸丢失风险!
  • Efficient LLM Inference 与 Serving:KV Cache、Speculative Decoding、PagedAttention 和量化
  • 影刀RPA新手教程:淘宝直播数据监控完全指南——观看人数、互动数据与商品点击率采集
  • 电机控制器硬件架构演进:从三块PCBA到三合一板卡的3个关键设计挑战
  • 前端JS加密逆向实战:从算法特征识别到调试技巧全解析
  • Foliate电子书阅读器完整指南:GTK4+WebKitGTK打造的高效Linux阅读方案
  • 2026年家具AI生图工具:木创家AI 13项核心功能解析
  • 从总部项目实践看泛光照明的新走向
  • 单片机晶振电路PCB布局3大核心要点:从原理到解决EMI干扰
  • 车轮毂制动点位等区域检测数据集VOC+YOLO格式299张16类别
  • 谈表达式树的缓存(4):使用二叉搜索树(AVL树)
  • 终极GTA5安全菜单:YimMenu完整防护与功能指南