<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>第 5 章 Agent 记忆机制与管理 on 《从零学AI指南手册》</title>
		<link>https://mlwithme.github.io/agent/chapter05/</link>
		<description>Recent content in 第 5 章 Agent 记忆机制与管理 on 《从零学AI指南手册》</description>
		<generator>Hugo</generator>
		<language>zh_CN</language>
		
		
		
		
			<lastBuildDate>Sat, 27 Jun 2026 11:05:09 +0800</lastBuildDate>
		
			<atom:link href="https://mlwithme.github.io/agent/chapter05/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>5.1 Agent 记忆机制概览</title>
				<link>https://mlwithme.github.io/agent/chapter05/a652af9313584855/</link>
				<pubDate>Sat, 27 Jun 2026 10:33:11 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/a652af9313584855/</guid>
				<description>&lt;h1 id=&#34;5-agent-记忆机制与管理&#34;&gt;5 Agent 记忆机制与管理&lt;a class=&#34;anchor&#34; href=&#34;#5-agent-%e8%ae%b0%e5%bf%86%e6%9c%ba%e5%88%b6%e4%b8%8e%e7%ae%a1%e7%90%86&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在第4章内容中我们详细介绍了 Agent 的工具调用与检索能力，但细心的读者可能已经注意到一个问题：在 RAG Agent 这个场景里至始至终都只涉及到了一个 Agent 来理解问题、检索内容和回答问题。但是在实际应用中，我们接触到更多的其实是多个 Agent 协作交互的场景。&lt;/p&gt;&#xA;&lt;p&gt;例如一个 Agent 专门帮你创建日程，一个 Agent 帮你发送邮件，可能还有一个 Agent 帮你检查排期等等，如先前大火的 OpenClaw 就是一个类似这样的个人助理。&lt;/p&gt;&#xA;&lt;p&gt;因此，这个时候同时还延伸出了一个非常重要的问题——记忆管理。在本章内容中，将会就这两块内容进行详细介绍。&lt;/p&gt;&#xA;&lt;h1 id=&#34;51-agent-记忆机制概览&#34;&gt;5.1 Agent 记忆机制概览&lt;a class=&#34;anchor&#34; href=&#34;#51-agent-%e8%ae%b0%e5%bf%86%e6%9c%ba%e5%88%b6%e6%a6%82%e8%a7%88&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;511-记忆机制&#34;&gt;5.1.1 记忆机制&lt;a class=&#34;anchor&#34; href=&#34;#511-%e8%ae%b0%e5%bf%86%e6%9c%ba%e5%88%b6&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;记忆机制是一个让 Agent 能够记住历史交互信息的系统。具体来说，我们希望它能够做到三件事情：① 记住之前的对话内容；② 从用户的反馈中学习；③ 根据用户的偏好调整自身的行为。虽然这三件事听起来好像很简单也很自然，但对于一个实用的 AI Agent 来说却至关重要。&lt;/p&gt;&#xA;&lt;p&gt;试想一下，如果每次对话结束以后，Agent 对之前发生的事情都一无所知，无论用户告诉过它什么偏好、纠正过它什么错误，下一次对话都只会从头开始滔滔不绝，那这样的体验用户显然是不可接受的。当然，如果是涉及到处理复杂、多轮交互任务时，更是会变得让人抓狂，而这就是 Agent 记忆机制所要解决的问题。&lt;/p&gt;&#xA;&lt;p&gt;根据记忆的作用范围不同还可以分为两种类型：短期记忆和长期记忆。&lt;/p&gt;&#xA;&lt;h2 id=&#34;512-短期记忆&#34;&gt;5.1.2 短期记忆&lt;a class=&#34;anchor&#34; href=&#34;#512-%e7%9f%ad%e6%9c%9f%e8%ae%b0%e5%bf%86&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;短期记忆（Short-term Memory），也称为线程作用域记忆（Thread-scoped Memory），它的作用范围仅限定在一次会话（Session）之内，例如我们在使用豆包或者 ChatGPT 时所打开的一个对话窗口。简单来说，它负责记录当前这一轮对话中发生的所有消息（上下文），让 Agent 在同一次对话里知道&amp;quot;之前聊了什么&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;在 LangGraph 中，短期记忆是作为 Agent 状态（State） 的一部分来进行管理，并且会通过一个叫做检查点管理器（Checkpointer ）的机制来将当前状态进行持久化。在 Agent 每执行一步时 LangGraph 都会同时从持久化实体中读取最新的状态作为输入，并在执行结束以后更新状态信息。可以看出，这样做的好处是即使对话中途中断，也可以在任意时刻从断点处恢复，使得整个会话的上下文不会丢失。&lt;/p&gt;&#xA;&lt;h2 id=&#34;513-长期记忆&#34;&gt;5.1.3 长期记忆&lt;a class=&#34;anchor&#34; href=&#34;#513-%e9%95%bf%e6%9c%9f%e8%ae%b0%e5%bf%86&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;长期记忆（Long-term Memory）的出现是为了解决短期记忆不能处理跨会话信息保留的问题。与短期记忆不同，长期记忆存储的是用户专属信息或应用级别的数据，它不归属于某一次具体的对话，而是可以在任何时间、任何会话中被读取和写入。如图5-1所示便是短期记忆与长期记忆之间的对比结果。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;450&#34; src=&#34;https://mlwithme.github.io/images/agent/202604211513.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-1 短期记忆与长期记忆对比图&lt;/center&gt;&#xA;&lt;p&gt;从图5-1可以看出，短期记忆实质上就是在当前会话中将之前的所有的上下文信息都输入到LLM中来生成本轮的回答；而长期记忆则是将不会时间、不同会话中的上下文输入到LLM中来生成本轮的回答。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.2 短期记忆持久化与使用</title>
				<link>https://mlwithme.github.io/agent/chapter05/2caa39af072b43d5/</link>
				<pubDate>Sat, 27 Jun 2026 10:30:02 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/2caa39af072b43d5/</guid>
				<description>&lt;h1 id=&#34;52-短期记忆持久化与使用&#34;&gt;5.2 短期记忆持久化与使用&lt;a class=&#34;anchor&#34; href=&#34;#52-%e7%9f%ad%e6%9c%9f%e8%ae%b0%e5%bf%86%e6%8c%81%e4%b9%85%e5%8c%96%e4%b8%8e%e4%bd%bf%e7%94%a8&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;LangGraph 没有为短期记忆单独设计一套存储系统，而是直接复用了我们在第4.9节中介绍过的状态机制（State）。Agent 在一次对话中需要记住的所有内容——包括对话历史、用户上传的文件、检索到的文档片段、中间生成的结果——都可以作为字段放进 State 里。换句话说，State 本身就是短期记忆的载体，不需要额外引入任何新概念。&lt;/p&gt;&#xA;&lt;h2 id=&#34;521-记忆的组织结构&#34;&gt;5.2.1 记忆的组织结构&lt;a class=&#34;anchor&#34; href=&#34;#521-%e8%ae%b0%e5%bf%86%e7%9a%84%e7%bb%84%e7%bb%87%e7%bb%93%e6%9e%84&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;如果仅将 Agent 的全局状态存在内存里是不够的，因为一旦程序重启或连接中断数据就丢失了。LangGraph 通过 Checkpointer 机制来解决这个问题，每执行完一个节点当前的 State 快照就会被自动保存到数据库中。这意味着一次对话哪怕中途中断，下次重新连接时也可以从断点处完整恢复，之前的所有上下文都不会丢失。&lt;/p&gt;&#xA;&lt;p&gt;在 LangGraph 中，所有的状态信息都是通过 &lt;code&gt;StateSnapshot&lt;/code&gt; 这个类来进行管理的，全局状态每更新一次就会生成一个新的 &lt;code&gt;StateSnapshot&lt;/code&gt; 类对象，所有类对象将会按时间顺序放到一个列表中。&lt;/p&gt;&#xA;&lt;p&gt;具体地，&lt;code&gt;StateSnapshot&lt;/code&gt; 类的定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#0e84b5;font-weight:bold&#34;&gt;StateSnapshot&lt;/span&gt;(NamedTuple):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;     values: &lt;span style=&#34;color:#007020&#34;&gt;dict&lt;/span&gt;[&lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;, Any] &lt;span style=&#34;color:#666&#34;&gt;|&lt;/span&gt; Any&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt;     &lt;span style=&#34;color:#007020&#34;&gt;next&lt;/span&gt;: &lt;span style=&#34;color:#007020&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#666&#34;&gt;...&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt;     config: RunnableConfig&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;     metadata: CheckpointMetadata &lt;span style=&#34;color:#666&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;6&lt;/span&gt;     created_at: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;7&lt;/span&gt;     parent_config: RunnableConfig &lt;span style=&#34;color:#666&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;8&lt;/span&gt;     tasks: &lt;span style=&#34;color:#007020&#34;&gt;tuple&lt;/span&gt;[PregelTask, &lt;span style=&#34;color:#666&#34;&gt;...&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;9&lt;/span&gt;     interrupts: &lt;span style=&#34;color:#007020&#34;&gt;tuple&lt;/span&gt;[Interrupt, &lt;span style=&#34;color:#666&#34;&gt;...&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，第1行 &lt;code&gt;StateSnapshot&lt;/code&gt; 是 LangGraph 中定义了一个用于表示状态快照的类，通过其中的成员变量来记录图在某一个超步执行结束后的完整运行状态。第2行 &lt;code&gt;values&lt;/code&gt; 表示截止到当前时候所有超步运行后产生的结果值。第3行 &lt;code&gt;next&lt;/code&gt; 表示下一步需要执行的节点名称，并且因为一个超步内可以包含多个节点所以类型是 &lt;code&gt;tuple&lt;/code&gt;。第4行&lt;code&gt;config&lt;/code&gt; 表示程序想要读取这个快照的时候需要使用到的信息，形式为 &lt;code&gt;{&#39;configurable&#39;: {&#39;thread_id&#39;: &#39;1&#39;, , &#39;checkpoint_id&#39;: &#39;1f14ab...&#39;}}&lt;/code&gt;。第5行 &lt;code&gt;metadata&lt;/code&gt; 表示一些元数据信息，例如 &lt;code&gt;{&#39;source&#39;: &#39;input&#39;, &#39;step&#39;: -1, &#39;parents&#39;: {}, &#39;k&#39;: 20, &#39;top_n&#39;: 3}&lt;/code&gt; 。第6行表示快照的创建时间戳。第7行 &lt;code&gt;parent_config&lt;/code&gt; 表示父快照对应的 &lt;code&gt;config&lt;/code&gt; 信息，可以理解为当前快照是从哪个快照演化来的。第8行 &lt;code&gt;tasks&lt;/code&gt; 表示当前超步中真正要执行的任务，它与 &lt;code&gt;next&lt;/code&gt; 的却别在于 &lt;code&gt;next&lt;/code&gt; 记录的是节点名称而 &lt;code&gt;tasks&lt;/code&gt; 记录的是节点中具体的任务，它是某一轮超步中要执行的最小任务单元。第9行 &lt;code&gt;interrupts&lt;/code&gt; 表示当前等待解决的中断是 Human-in-the-loop 的核心，例如人工审批、用户交互等。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.3 短期记忆管理</title>
				<link>https://mlwithme.github.io/agent/chapter05/4cb3a72787f44a51/</link>
				<pubDate>Sat, 27 Jun 2026 10:30:02 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/4cb3a72787f44a51/</guid>
				<description>&lt;h1 id=&#34;53-短期记忆管理&#34;&gt;5.3 短期记忆管理&lt;a class=&#34;anchor&#34; href=&#34;#53-%e7%9f%ad%e6%9c%9f%e8%ae%b0%e5%bf%86%e7%ae%a1%e7%90%86&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;理解了短期记忆的底层机制之后，还有一个工程问题不得不面对，那就是对话历史会无限增长但模型的上下文窗口是有限的，这也是我们在第5.2节末尾所提及的问题，也是在实际业务场景中最容易出现的问题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;531-记忆管理的根源&#34;&gt;5.3.1 记忆管理的根源&lt;a class=&#34;anchor&#34; href=&#34;#531-%e8%ae%b0%e5%bf%86%e7%ae%a1%e7%90%86%e7%9a%84%e6%a0%b9%e6%ba%90&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;在一个对话型应用中，每一轮交互都会产生新的消息——用户说一句，模型回一句，来回往复。最终，这些消息会被依次追加到全局状态的消息列表中，因此随着对话轮次的增加，列表会越来越长。当然，这在短对话中完全不是问题，不过一旦对话持续时间较长，消息列表的词元总量就会持续膨胀，进而带来两类后果。&lt;/p&gt;&#xA;&lt;p&gt;（1）超出上下文窗口直接报错&lt;/p&gt;&#xA;&lt;p&gt;每个大模型都有固定的上下文窗口限制（Context Window），例如某些模型是 8K，某些是 128K。当消息列表的总词元数超过这个上限时，模型会直接报错——而且这个错误通常是不可恢复的，只能截断历史或重新开始对话。&lt;/p&gt;&#xA;&lt;p&gt;（2）即使没超限，效果也会变差&lt;/p&gt;&#xA;&lt;p&gt;同时，即便模型支持足够长的上下文，随着历史消息越堆越多，模型的实际表现往往会明显下降。原因在于模型容易被早期那些已经过时或与当前问题无关的消息&amp;quot;分散注意力&amp;quot;，导致回答质量下降、响应速度变慢，这一点也更隐蔽、更容易被忽视。&lt;/p&gt;&#xA;&lt;p&gt;总结起来就是，更长的上下文不等于更好的效果，超过一定长度之后冗余的历史消息反而是一种干扰，因此我们需要通过不同的策略来进行处理。在 LangGraph 中提供了3中策略来对短期记忆进行管理，分别是记忆修剪、记忆总结和记忆删除，下面依次进行介绍。&lt;/p&gt;&#xA;&lt;h2 id=&#34;532-记忆删除&#34;&gt;5.3.2 记忆删除&lt;a class=&#34;anchor&#34; href=&#34;#532-%e8%ae%b0%e5%bf%86%e5%88%a0%e9%99%a4&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;首先，最简单的一种记忆管理方式就是将历史消息部分或全部删除，只保留一定数量需要的消息内容，如图5-3所示。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;500&#34; src=&#34;https://mlwithme.github.io/images/agent/202605171755.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-3 记忆删除示意图&lt;/center&gt;&#xA;&lt;p&gt;在 LangGraph 中可以通过 &lt;code&gt;RemoveMessage&lt;/code&gt; 来根据消息ID将部分或全部记忆进行删除。具体地，先定义一个函数来将待删除的记忆内容置空，示例代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06287e&#34;&gt;delete_messages&lt;/span&gt;(agent, config: RunnableConfig) &lt;span style=&#34;color:#666&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;     snapshot &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; agent&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;get_state(config)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt;     messages &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; snapshot&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;values&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;, []) &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;if&lt;/span&gt; snapshot&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;values &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;else&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt;     &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#007020&#34;&gt;len&lt;/span&gt;(messages) &lt;span style=&#34;color:#666&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;         &lt;span style=&#34;color:#007020&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;当前记忆不足 2 条，跳过删除。&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;6&lt;/span&gt;         &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;7&lt;/span&gt;     updates &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;: [RemoveMessage(&lt;span style=&#34;color:#007020&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;message&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;id) &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;for&lt;/span&gt; message &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;in&lt;/span&gt; messages[:&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;]]}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;8&lt;/span&gt;     agent&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;update_state(config, updates, as_node&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;generate_answer&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;9&lt;/span&gt;     &lt;span style=&#34;color:#007020&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;已删除最前面的两条记忆。&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，第3行是获取到快照状态中所有的历史消息。第7行便是构造前两条（最先发生）消息的删除指令。第8行是将待删除的消息更新到状态快照中，即写回 checkpoint 状态里，其中 &lt;code&gt;as_node&lt;/code&gt; 的作用是“假装这次更新是由 generate_answer 这个节点产出的”，因为 &lt;code&gt;update_state()&lt;/code&gt; 不是单纯改数据库里的一行 JSON，它是按 LangGraph 的图执行模型去改状态的，所以它需要知道要改什么，以及这次修改算哪个节点产生的。&lt;/p&gt;&#xA;&lt;p&gt;对于 &lt;code&gt;as_node&lt;/code&gt; 这个节点的选择没有强制要求，如果不指定则默认会使用图的最后一个节点，但前提是不要有歧义否则会报错。不过建议显示指定图中真实存在的节点名称，例如图4-13中的 decide、my_retrieve、rewrite_question 和 generate_answer。同时，还有一个需要注意的地方是，&lt;code&gt;update_state()&lt;/code&gt; 是对 &lt;code&gt;messages&lt;/code&gt; 字段进行更新，所以需要选一个本来就返回 &lt;code&gt;{&amp;quot;messages&amp;quot;: ...} &lt;/code&gt;的节点，并且从语义上看 &lt;code&gt;generate_answer&lt;/code&gt; 更接近“对消息状态做一次人工修正”，所以这里选择了 &lt;code&gt;generate_answer&lt;/code&gt; 节点。当然，选择 &lt;code&gt;rewrite_question&lt;/code&gt; 也是能正常运行的，但是从执行逻辑上来看最好是 &lt;code&gt;generate_answer&lt;/code&gt; 节点，不然状态快照中 &lt;code&gt;next()&lt;/code&gt;记录的信息会不符合逻辑。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.4 拥有短期记忆的 RAG Agent</title>
				<link>https://mlwithme.github.io/agent/chapter05/0d7e1e8dd4294524/</link>
				<pubDate>Sat, 27 Jun 2026 10:28:22 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/0d7e1e8dd4294524/</guid>
				<description>&lt;h1 id=&#34;54-拥有短期记忆的-rag-agent&#34;&gt;5.4 拥有短期记忆的 RAG Agent&lt;a class=&#34;anchor&#34; href=&#34;#54-%e6%8b%a5%e6%9c%89%e7%9f%ad%e6%9c%9f%e8%ae%b0%e5%bf%86%e7%9a%84-rag-agent&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在上一节内容中详细介绍了短期记忆管理的3种方式，删除、修剪和总结。在本节内容中，我们将基于第4.10节的内容实现一个类似 ChatGPT 的 RAG 问答工具，做到每次回答完用户问题后都判断是否需要对历史消息进行一次总结，并同步将会话消息实时保存到数据库，使得每次启动程序时都能恢复历史会话。&lt;/p&gt;&#xA;&lt;h2 id=&#34;541-流程构建思路&#34;&gt;5.4.1 流程构建思路&lt;a class=&#34;anchor&#34; href=&#34;#541-%e6%b5%81%e7%a8%8b%e6%9e%84%e5%bb%ba%e6%80%9d%e8%b7%af&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;为了实现上述目的，需要在图4-13的图结构之上再添加一个条件判断边和一个节点，来判断是否需要对历史记忆进行总结以及如何总结，整个流程如图5-6所示。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;350&#34; src=&#34;https://mlwithme.github.io/images/agent/202605201552.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-6 带记忆能力的 RAG Agent 图结构可视化 &lt;/center&gt;&#xA;&lt;p&gt;从图5-6可以看出，在每一轮回答结束以后两个分支都会统一汇集到 &lt;code&gt;check_messages&lt;/code&gt; 节点，然后再通过条件分支判断是否需要进行历史消息总结。下面逐一对这部分内容进行介绍。&lt;/p&gt;&#xA;&lt;h2 id=&#34;542-分支汇集与条件判断&#34;&gt;5.4.2 分支汇集与条件判断&lt;a class=&#34;anchor&#34; href=&#34;#542-%e5%88%86%e6%94%af%e6%b1%87%e9%9b%86%e4%b8%8e%e6%9d%a1%e4%bb%b6%e5%88%a4%e6%96%ad&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;由于 &lt;code&gt;decide&lt;/code&gt; 这条边已经用 &lt;code&gt;tools_condition&lt;/code&gt; 在做“是否调用工具”的分流，即 &lt;code&gt; add_conditional_edges(&amp;quot;decide&amp;quot;,tools_condition, {&amp;quot;tools&amp;quot;: &amp;quot;my_retrieve&amp;quot;, END: END})&lt;/code&gt;，而 &lt;code&gt;tools_condition&lt;/code&gt; 的映射是静态的，没法在左侧的 &lt;code&gt;END&lt;/code&gt; 分支里再动态判断一次是否应该进行记忆总结，所以需要添加一个空节点来做汇集，即图5-6中的 &lt;code&gt;check_messages&lt;/code&gt;，其对应的实现为：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06287e&#34;&gt;passthrough&lt;/span&gt;(state: MyState) &lt;span style=&#34;color:#666&#34;&gt;-&amp;gt;&lt;/span&gt; MyState:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;     &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt; {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，第2行 &lt;code&gt;return {}&lt;/code&gt; 表示更新内容为空，即不对传入的 &lt;code&gt;state&lt;/code&gt; 做任何修改。&lt;/p&gt;&#xA;&lt;p&gt;进一步，实现是否需要进行总结的条件判断，示例代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06287e&#34;&gt;count_messages&lt;/span&gt;(state: MyState) &lt;span style=&#34;color:#666&#34;&gt;-&amp;gt;&lt;/span&gt; Literal[END, &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;to_summarize&amp;#34;&lt;/span&gt;]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;     messages &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; state[&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt;     threshold &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt;     &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#007020&#34;&gt;len&lt;/span&gt;(messages) &lt;span style=&#34;color:#666&#34;&gt;&amp;gt;&lt;/span&gt; threshold:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;         &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;to_summarize&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;6&lt;/span&gt;     &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;7&lt;/span&gt;         &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt; END&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们简单地使用消息条数来进行判断，当然也可以是其它策略。第4~7行是根据条件判断流向 &lt;code&gt;to_summarize&lt;/code&gt; 节点或直接结束。&lt;/p&gt;&#xA;&lt;h2 id=&#34;543-历史记忆总结&#34;&gt;5.4.3 历史记忆总结&lt;a class=&#34;anchor&#34; href=&#34;#543-%e5%8e%86%e5%8f%b2%e8%ae%b0%e5%bf%86%e6%80%bb%e7%bb%93&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;接着，在第5.2.4节实现的 &lt;code&gt;summarize_conversation&lt;/code&gt; 基础之上，再来对内部进行一些改造，使得能够更加灵活的总结和删除对应的历史记忆，示例代码如下：&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.5 长期记忆管理与持久化</title>
				<link>https://mlwithme.github.io/agent/chapter05/7490dfb32ad44afc/</link>
				<pubDate>Sat, 27 Jun 2026 10:33:11 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/7490dfb32ad44afc/</guid>
				<description>&lt;h1 id=&#34;55-长期记忆管理与持久化&#34;&gt;5.5 长期记忆管理与持久化&lt;a class=&#34;anchor&#34; href=&#34;#55-%e9%95%bf%e6%9c%9f%e8%ae%b0%e5%bf%86%e7%ae%a1%e7%90%86%e4%b8%8e%e6%8c%81%e4%b9%85%e5%8c%96&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在前面几节内容中我们介绍了短期记忆的底层机制以及消息列表的管理策略。从本节开始，我们将把目光转向长期记忆——它解决的是一个短期记忆根本无法触及的问题——如何让 Agent 在不同的对话会话之间也能记住用户。为了能更加深刻地理解长期记忆，下面我们先来看人类是如何描述长期记忆的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;551-人类的长期记忆模式&#34;&gt;5.5.1 人类的长期记忆模式&lt;a class=&#34;anchor&#34; href=&#34;#551-%e4%ba%ba%e7%b1%bb%e7%9a%84%e9%95%bf%e6%9c%9f%e8%ae%b0%e5%bf%86%e6%a8%a1%e5%bc%8f&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;人类的记忆像是一个包罗万象的信息库，自然而然描述记忆的方式就多种多样。尽管研究者们对不同记忆类型的描述并非总是相同，但一些关键概念已经逐渐形成共识。在日常生活中各种形式的记忆有时会相互重叠，因此也被归纳为几大类。持续时间很短的记忆可以称为短期记忆，这个我们在前面的内容中已经介绍过，而任何被保存下来以便日后回忆的信息都可以称为长期记忆。对于人类的长期记忆来说，主要可以分为如下3个类别：&lt;/p&gt;&#xA;&lt;p&gt;（1）情景记忆&lt;/p&gt;&#xA;&lt;p&gt;当一个人回忆起过去经历的某个特定事件（或“片段”）时，这就是情景记忆（Episodic Memory）。这种长期记忆能够唤起人们对各种细节的关注，从小时候的特殊经历到二十分钟前与人交谈时的感受等等，它可以是近期发生的也可以是几十年前的。&lt;/p&gt;&#xA;&lt;p&gt;（2）语义记忆&lt;/p&gt;&#xA;&lt;p&gt;语义记忆（Semantic Memory）是人们长期储存知识的一种方，它由各种信息组成，例如在学校学到的事实、概念的含义及其相互关系或者某个特定词语的定义等，例如现在是哪一年、中国的首都是哪里、“机器学习”的含义是什么。&lt;/p&gt;&#xA;&lt;p&gt;（3）程序性记忆&lt;/p&gt;&#xA;&lt;p&gt;程序性记忆（Procedural Memory）指的是对如何做事（包括生理和心理方面）的长期记忆，它参与了技能学习的过程，包括从习以为常的基本技能到需要大量练习才能掌握的技能。例如多年未骑自行车后，再次坐上车却能立刻想起该怎么做，这就是程序性记忆的典型例子。&lt;/p&gt;&#xA;&lt;h2 id=&#34;552-智能体认知架构&#34;&gt;5.5.2 智能体认知架构&lt;a class=&#34;anchor&#34; href=&#34;#552-%e6%99%ba%e8%83%bd%e4%bd%93%e8%ae%a4%e7%9f%a5%e6%9e%b6%e6%9e%84&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;与短期记忆按 &lt;code&gt;thread_id&lt;/code&gt; 隔离不同，长期记忆存储在自定义命名空间下，它可以跨任意会话被读取和写入。不过，长期记忆的设计没有放之四海而皆准的标准答案，所以，在动手实现之前通常需要先回答两个问题，即“要存什么类型的记忆？” 和“什么时候写入记忆？ ”。这两个问题的答案决定了整个长期记忆系统的架构设计。&lt;/p&gt;&#xA;&lt;p&gt;LangGraph 在设计 Agent 的记忆系统时，借鉴了心理学对人类记忆的分类方式。根据存储内容的不同，长期记忆可以分为3种类型，如表5-1所示。&lt;/p&gt;&#xA;&lt;center&gt; 表 5-1 长期记忆类型划分表&lt;/center&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;记忆类型&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;存储内容&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;人类类比&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Agent 应用场景&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;语义记忆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;事实与知识&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;课堂上学到的知识&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;用户偏好、个人信息、事实陈述等&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;情景记忆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;经历与事件&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;做过的事情&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;过去执行过的操作和案例等&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;程序记忆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;规则与指令&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;骑自行车的肌肉记忆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Agent 的系统提示词、代码逻辑、模型权重等&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;从表5-1可以看出，语义记忆、情景记忆和程序记忆这样的类型划分方式，恰好也能够满足对于智能体长期记忆的归纳和管理，而采用这样的架构设计也被称之为语言智能体认知架构（Cognitive architectures for language agents, CoALA）[2]，如图5-7所示。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;500&#34; src=&#34;https://mlwithme.github.io/images/agent/202605291402.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-7 语言智能体认知架构[2]&lt;/center&gt;&#xA;&lt;p&gt;如图5-7所示，它展示了一个典型的智能体记忆系统架构，于2024年由普林斯顿大学所提出。CoALA 借鉴了人类认知科学中的记忆分类方式，将智能体的记忆划分为程序记忆、语义记忆和情景记忆以及工作记忆（短期记忆）4个部分。&lt;/p&gt;&#xA;&lt;p&gt;在智能体运行过程中，外部环境会持续向智能体提供观察，短期记忆则负责对当前任务的状态进行临时存储与推理，智能体通过检索不同类型的记忆，结合决策过程生成对应动作，并与数字世界、物理世界以及对话场景进行交互。同时，系统还能将新的经验反向写入长期记忆中，从而实现动态学习与持续进化。整个架构体现了现代 AI 智能体从“单次生成”向“具备长期记忆与自主学习能力”的重要演进方向。&lt;/p&gt;&#xA;&lt;h2 id=&#34;553-语义记忆&#34;&gt;5.5.3 语义记忆&lt;a class=&#34;anchor&#34; href=&#34;#553-%e8%af%ad%e4%b9%89%e8%ae%b0%e5%bf%86&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;语义记忆用于存储关于用户、组织或其他实体的具体事实、规则和结构化信息，最典型的应用场景就是个性化信息，让 Agent 记住用户的姓名、年龄、偏好、工作等个人信息，从而使智能体具备知识检索与持续学习的能力。&lt;/p&gt;&#xA;&lt;p&gt;在具体实现上，语义记忆有两种存储方式，用户画像和记忆集合  [4]。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.6 从零实现 Mini ChatGPT 助手</title>
				<link>https://mlwithme.github.io/agent/chapter05/4d323650e581404a/</link>
				<pubDate>Sat, 27 Jun 2026 10:32:33 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/4d323650e581404a/</guid>
				<description>&lt;h1 id=&#34;56-从零实现-mini-chatgpt-助手&#34;&gt;5.6 从零实现 Mini ChatGPT 助手&lt;a class=&#34;anchor&#34; href=&#34;#56-%e4%bb%8e%e9%9b%b6%e5%ae%9e%e7%8e%b0-mini-chatgpt-%e5%8a%a9%e6%89%8b&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在上一节内容中，我们详细介绍了长期记忆的模式、管理和持久化，为了能够更加清晰地理解记忆的运行机制，接下来将会详细介绍如何基于本地持久化的长期记忆来从零构建一个类似 ChatGPT 一样具有用户隔离以及记忆功能的个人助手。&lt;/p&gt;&#xA;&lt;h2 id=&#34;561-mini-chatgpt-结构设计&#34;&gt;5.6.1 Mini ChatGPT 结构设计&lt;a class=&#34;anchor&#34; href=&#34;#561-mini-chatgpt-%e7%bb%93%e6%9e%84%e8%ae%be%e8%ae%a1&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;为了实现和 ChatGPT 类似的效果，我们需要设计长期记忆以实现同一用户的跨会话使用；其次需要加入短期记忆来保证同一会话中的用户上下文不会丢失；最后需要构造一个工具来插入或更新记忆内容。对于整个状态扭转过程中的详细情况，我们可以通过图5-11表示。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;700&#34; src=&#34;https://mlwithme.github.io/images/agent/202606081345.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-11 Mini ChatGPT 状态扭转示意图&lt;/center&gt;&#xA;&lt;p&gt;如图5-11所示，左上角和右下角分别对应的是长期记忆和短期记忆，分别作用于不同用户间和同一会话内。当用户发起一个请求后，系统将会判断长期记忆中是否存在该用户的相关记忆，如果存在则根据相似度搜索并返回前 Top-K 的相关记忆，如果不存在则直接进入下一步。进一步，系统会将检索到的用户长期记忆、短期记忆（如果有）以及用户请求内容拼接形成上下问内容，并连同工具信息一起输入到大模型中。最后，系统会根据大模型的返回结果判断是否需要调用工具。如果不需要则直接根据大模型返回内容生成回答并将用户问题和回答追加到短期记忆中；如果需要调用工具则根据大模型提取的参数运行工具，同步更新旧记忆或插入新记忆，再将大模型返回内容和工具运行结果追加到旧的上下文内容中形成新的上下文内容，然后再次发起大模型调用。&lt;/p&gt;&#xA;&lt;h2 id=&#34;562-工程目录介绍&#34;&gt;5.6.2 工程目录介绍&lt;a class=&#34;anchor&#34; href=&#34;#562-%e5%b7%a5%e7%a8%8b%e7%9b%ae%e5%bd%95%e4%bb%8b%e7%bb%8d&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;下面，根据图5-11所示的过程，来逐一实现。为了使下面的介绍更为清晰，先来简单介绍一个整个工程目录结构，以下完整示例代码可参见 &lt;code&gt;Code/Chapter05/mini-chatgpt&lt;/code&gt; 工程。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;150&#34; src=&#34;https://mlwithme.github.io/images/agent/202606140923.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-12 mini-chatgpt 工程目录结构&lt;/center&gt;&#xA;&lt;p&gt;在图5-12中，&lt;code&gt;main.py&lt;/code&gt; 是整个项目的主函数入口，通过 &lt;code&gt;python main.py&lt;/code&gt; 启动；&lt;code&gt;cli.py&lt;/code&gt; 中定义了整个命令行与用户之间的交互逻辑；&lt;code&gt;agent.py&lt;/code&gt; 中定义了用户请求与大模型之间的交互逻辑，即图5-11中间部分；&lt;code&gt;store.py&lt;/code&gt; 中定义的记忆的存储结构；&lt;code&gt;tools.py&lt;/code&gt; 中定义了工具的逻辑；&lt;code&gt;context.py&lt;/code&gt; 和 &lt;code&gt;qwen.py&lt;/code&gt; 中分别定义了上下文的字段结构和大模型的加载模块。&lt;/p&gt;&#xA;&lt;h2 id=&#34;563-记忆存储结构实现&#34;&gt;5.6.3 记忆存储结构实现&lt;a class=&#34;anchor&#34; href=&#34;#563-%e8%ae%b0%e5%bf%86%e5%ad%98%e5%82%a8%e7%bb%93%e6%9e%84%e5%ae%9e%e7%8e%b0&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;首先需要定义长期记忆的存储结构以及不同处理情况下的组织结构，这里通过2个类来实现，即 &lt;code&gt;MemoryRecord&lt;/code&gt;和&lt;code&gt;MemorySearchResult&lt;/code&gt; 分别用来表示一条记忆以及记忆的搜索结果，对应模块为 &lt;code&gt;store.py&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;（1）记忆存储结构&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; MemoryType &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; Literal[&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;episodic&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;semantic&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;procedural&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#555;font-weight:bold&#34;&gt;@dataclass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#0e84b5;font-weight:bold&#34;&gt;MemoryRecord&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt;     memory_id: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;     content: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;6&lt;/span&gt;     context: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;7&lt;/span&gt;     created_at: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;8&lt;/span&gt;     updated_at: &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;9&lt;/span&gt;     memory_type: MemoryType &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;semantic&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;10&lt;/span&gt;     embedding: &lt;span style=&#34;color:#007020&#34;&gt;list&lt;/span&gt;[&lt;span style=&#34;color:#007020&#34;&gt;float&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;11&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;12&lt;/span&gt;     &lt;span style=&#34;color:#555;font-weight:bold&#34;&gt;@property&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;13&lt;/span&gt;     &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06287e&#34;&gt;text&lt;/span&gt;(&lt;span style=&#34;color:#007020&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#666&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;14&lt;/span&gt;         &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#4070a0&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#70a0d0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;memory_type&lt;span style=&#34;color:#70a0d0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#4070a0;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#70a0d0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;content&lt;span style=&#34;color:#70a0d0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#4070a0;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#70a0d0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;context&lt;span style=&#34;color:#70a0d0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;strip()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，第4行 &lt;code&gt;memory_id&lt;/code&gt; 表示每一条记忆对应的唯一 ID。第6～7行分别用来记录这条记忆的核心内容以及对应的补充上下文说明。第7～8行分别用来记录这条记忆的创建时间以及后续的更新时间。第9行用来标识长期记忆的类型。第10行用来记录这条记录的词嵌入表示，便于后续对记忆进行检索。第12～14行定义了一个 &lt;code&gt;text&lt;/code&gt; 属性，便于后续直接取记忆对应文本内容。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.7 pgvector 配置安装</title>
				<link>https://mlwithme.github.io/agent/chapter05/0e8a82c9b973429e/</link>
				<pubDate>Sat, 27 Jun 2026 10:29:12 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/0e8a82c9b973429e/</guid>
				<description>&lt;h1 id=&#34;57-pgvector-配置安装&#34;&gt;5.7 pgvector 配置安装&lt;a class=&#34;anchor&#34; href=&#34;#57-pgvector-%e9%85%8d%e7%bd%ae%e5%ae%89%e8%a3%85&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在前面章节关于 RAG 的实现中，我们已经采用了 Milvus 作为向量数据库，用于承载知识库文档的向量化存储与语义检索。而在本节中转向 pgvector，并不意味着前面的技术路线发生变化，而是因为当前讨论的重点已经从外部知识检索转向了 Agent 长期记忆管理，为了实现记忆的语义检索就必须进行向量化处理。&lt;/p&gt;&#xA;&lt;p&gt;下面将围绕 pgvector 的基本作用、常用距离类型、安装过程以及数据库内启用方法进行系统介绍，帮助读者完成从安装到验证的完整配置过程。&lt;/p&gt;&#xA;&lt;h2 id=&#34;571-pgvector-与-milvus-区别&#34;&gt;5.7.1 pgvector 与 Milvus 区别&lt;a class=&#34;anchor&#34; href=&#34;#571-pgvector-%e4%b8%8e-milvus-%e5%8c%ba%e5%88%ab&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Milvus 与 pgvector 的定位并不完全相同，Milvus 是专门面向大规模向量检索场景设计的向量数据库，更适合知识库召回、海量向量检索以及高吞吐的相似度搜索任务；而 pgvector 则仅仅是 PostgreSQL 的一个扩展模块，它在关系型数据库内部补充了向量字段、向量距离计算和向量索引能力，因此更适合那些既包含结构化字段、又需要附带向量检索能力的数据管理场景。&lt;/p&gt;&#xA;&lt;p&gt;对于 LangGraph 的长期记忆而言，记忆内容本身并不只是单纯的向量数据。它通常既包含用户 ID、命名空间、时间戳、记忆类型等结构化字段，也包含需要进行语义检索的文本内容及其向量表示。因此，这类数据天然更接近附带向量检索能力的关系型记录，而不是纯粹的向量集合。更重要的是，在 LangGraph 当前的长期记忆持久化方案中，官方提供的数据库型实现主要是 &lt;code&gt;PostgresStore&lt;/code&gt;。这意味着如果希望沿用框架已有的长期记忆存储抽象，那么基于 PostgreSQL 及其扩展 pgvector 来实现，通常会比继续引入 Milvus 更直接，也更方便与结构化记忆数据统一管理。&lt;/p&gt;&#xA;&lt;p&gt;因此，pgvector 的目标并不是替代 RAG 场景下使用的 Milvus，而是为 LangGraph 的长期记忆检索能力提供一个更贴合框架实现方式、也更适合混合型记忆数据管理的底层存储方案。&lt;/p&gt;&#xA;&lt;h2 id=&#34;572-pgvector-源码安装&#34;&gt;5.7.2 pgvector 源码安装&lt;a class=&#34;anchor&#34; href=&#34;#572-pgvector-%e6%ba%90%e7%a0%81%e5%ae%89%e8%a3%85&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;pgvector 本质上是 PostgreSQL 的本地扩展，因此在许多环境下需要先完成源码编译与安装，再进入数据库内部执行扩展创建命令。其基本流程如下。&lt;/p&gt;&#xA;&lt;p&gt;（1）下载源码&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/pgvector/pgvector.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#007020&#34;&gt;cd&lt;/span&gt; pgvector&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（2）执行编译&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译过程中会调用当前 PostgreSQL 实例对应的开发头文件与构建工具链。若编译成功，终端中通常会出现一系列 &lt;code&gt;gcc&lt;/code&gt; 编译输出信息。&lt;/p&gt;&#xA;&lt;p&gt;（3）执行安装&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装完成后，&lt;code&gt;vector.so&lt;/code&gt;、&lt;code&gt;vector.control&lt;/code&gt; 以及对应的 SQL 脚本会被复制到 PostgreSQL 的扩展目录中。例如，输出中通常会出现如下信息：&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.8 长期记忆检索与遗忘</title>
				<link>https://mlwithme.github.io/agent/chapter05/ca097b913d174c22/</link>
				<pubDate>Sat, 27 Jun 2026 10:33:11 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/ca097b913d174c22/</guid>
				<description>&lt;h1 id=&#34;58-长期记忆检索与遗忘&#34;&gt;5.8 长期记忆检索与遗忘&lt;a class=&#34;anchor&#34; href=&#34;#58-%e9%95%bf%e6%9c%9f%e8%ae%b0%e5%bf%86%e6%a3%80%e7%b4%a2%e4%b8%8e%e9%81%97%e5%bf%98&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在前面的内容中，我们已经完成了长期记忆的持久化存储，但如果一个系统只是不断累积记忆，却无法在需要时准确找回，也无法在信息过时后及时清理，那么这些记忆最终就会从系统资产变成系统负担。本节内容将系统介绍长期记忆的检索方式、过期机制以及自定义遗忘策略的实现思路。&lt;/p&gt;&#xA;&lt;h2 id=&#34;581-检索与遗忘&#34;&gt;5.8.1 检索与遗忘&lt;a class=&#34;anchor&#34; href=&#34;#581-%e6%a3%80%e7%b4%a2%e4%b8%8e%e9%81%97%e5%bf%98&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;对于短期记忆而言，当前对话上下文通常规模有限，模型可以直接在消息列表中读取信息；但长期记忆不同，它往往跨越多个会话持续积累，数据量会不断增长。如果没有检索机制，即便是拥有记忆也无法在真正需要时把它拿出来使用；而如果没有遗忘机制，记忆库又会不断膨胀，使早已失效的内容长期占据存储空间，甚至影响召回结果的准确性。&lt;/p&gt;&#xA;&lt;p&gt;从工程实现角度看，长期记忆系统通常会面临如下几类问题。&lt;/p&gt;&#xA;&lt;p&gt;（1）记忆越多，越难直接遍历&lt;/p&gt;&#xA;&lt;p&gt;当历史记忆数量较少时，开发者或许还可以通过简单的枚举方式读取所有内容；但随着用户数量和交互轮次增加，遍历式读取很快就会失去效率，必须引入向量化检索。&lt;/p&gt;&#xA;&lt;p&gt;（2）用户提问与记忆原文的差异&lt;/p&gt;&#xA;&lt;p&gt;例如，记忆中存储的是“用户最喜欢的编程语言是 Python”，用户提问却可能是“我一般使用哪个编程语言？”。如果只做关键词匹配，系统未必能够稳定命中；而语义检索恰恰就是为了解决“表达不同但含义相近”的问题。&lt;/p&gt;&#xA;&lt;p&gt;（3）并非所有记忆都值得永久保留&lt;/p&gt;&#xA;&lt;p&gt;有些记忆是稳定事实，如用户姓名、长期偏好；有些记忆却是阶段性上下文，如最近一次报错、某段临时安排、短期任务状态。后者如果长期留存，不仅价值有限，反而可能干扰后续决策。&lt;/p&gt;&#xA;&lt;p&gt;因此，一个成熟的长期记忆系统不应只有写入能力，还需要形成写入、检索、衰减、清理的完整闭环。&lt;/p&gt;&#xA;&lt;h2 id=&#34;582-记忆检索&#34;&gt;5.8.2 记忆检索&lt;a class=&#34;anchor&#34; href=&#34;#582-%e8%ae%b0%e5%bf%86%e6%a3%80%e7%b4%a2&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;下面先来看长期记忆的语义检索实现，对应完整代码参见 &lt;code&gt;Code/Chapter05/C09_long_memory_store_pg.py&lt;/code&gt; 文件。&lt;/p&gt;&#xA;&lt;p&gt;（1）向量索引&lt;/p&gt;&#xA;&lt;p&gt;与第5.5节中仅把记忆写入 PostgreSQL 不同，在上一节内容中我们安装完成了 pgvector 插件的安装，因此在创建 &lt;code&gt;PostgresStore&lt;/code&gt; 时可以额外传入了 &lt;code&gt;index&lt;/code&gt; 参数来对记忆进行向量化并构建索引。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; embeddings &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; DashScopeEmbeddings(model&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;text-embedding-v3&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt; memory_index &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;embed&amp;#34;&lt;/span&gt;: embeddings,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt; &#x9;&#x9;&#x9;    &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;dims&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#40a070&#34;&gt;1024&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt; &#x9;&#x9;&#x9;    &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;fields&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt; &#x9;&#x9;&#x9;    &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;distance_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;cosine&amp;#34;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，第2~5行便是构建向量索引，这段配置表明后续长期记忆在写入时不仅会把原始字段保存到数据库中，还会对指定字段进行向量化。第4行是指定写入记忆时的内容中需要进行向量化的字段名称，换句话说一条记忆中可以包含多个描述字段均可进行向量化，详见后续示例。第5行是指定相似度的计算方式，常见的有 &lt;code&gt;&amp;quot;cosine&amp;quot; &lt;/code&gt;、&lt;code&gt;&amp;quot;l2&amp;quot;&lt;/code&gt; 和 &lt;code&gt;&amp;quot;inner_product&amp;quot;&lt;/code&gt;，默认为&lt;code&gt;&amp;quot;cosine&amp;quot;&lt;/code&gt;，详见第3.3.4节内容。&lt;/p&gt;&#xA;&lt;p&gt;（2）写入内容&lt;/p&gt;&#xA;&lt;p&gt;进一步，构造两条长期记忆示例用于写入数据库：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#007020;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06287e&#34;&gt;save_normal_memory&lt;/span&gt;(store: PostgresStore, namespace: &lt;span style=&#34;color:#007020&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#666&#34;&gt;...&lt;/span&gt;]):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;2&lt;/span&gt;     store&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;put(namespace, key&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;(uuid&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;uuid4()),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;3&lt;/span&gt;               value&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;memory_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;语义记忆&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;4&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;: datetime&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;now()&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;isoformat(timespec&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;seconds&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;5&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;用户最喜欢的编程语言是 Python。&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;6&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;记录用户的编程语言喜好，后续在编程方面可以用到&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;7&lt;/span&gt;     store&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;put(namespace, key&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#007020&#34;&gt;str&lt;/span&gt;(uuid&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;uuid4()),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;8&lt;/span&gt;               value&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;memory_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;情景记忆&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#40a070&#34;&gt;9&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;: datetime&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;now()&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;isoformat(timespec&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;seconds&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;10&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;2026-01-31，用户完成了一次 RAG 检索结果为空的排查。&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#40a070&#34;&gt;11&lt;/span&gt;                      &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#4070a0&#34;&gt;&amp;#34;记录用户曾经做过的事情，在谈论到 RAG 话题到时候能用到&amp;#34;&lt;/span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（3）执行检索&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.9 基于 LangGraph 的Mini ChatGPT 助手</title>
				<link>https://mlwithme.github.io/agent/chapter05/938a0c83bc1e4da2/</link>
				<pubDate>Sat, 27 Jun 2026 10:32:33 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/938a0c83bc1e4da2/</guid>
				<description>&lt;h1 id=&#34;59-基于-langgraph-的mini-chatgpt-助手&#34;&gt;5.9 基于 LangGraph 的Mini ChatGPT 助手&lt;a class=&#34;anchor&#34; href=&#34;#59-%e5%9f%ba%e4%ba%8e-langgraph-%e7%9a%84mini-chatgpt-%e5%8a%a9%e6%89%8b&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在第5.6节内容中，我们详细介绍了如何基于本地持久化的长期记忆来从零实现一个类似 ChatGPT 的个人助手。不过，从工程实现角度看，第5.6节中的版本更多是手工维护流程：短期记忆需要由程序员自己维护，长期记忆则保存在本地 JSON 文件中，整体更适合理解原理。&lt;/p&gt;&#xA;&lt;p&gt;进一步，如果希望把这个助手继续扩展为一个更接近实际生产形态的系统，那么仅靠手动拼接消息和手动维护状态的方式就会逐渐暴露出局限。例如，会话状态如何持久化、不同线程之间如何隔离、长期记忆如何统一交给数据库管理，以及工具调用与消息状态如何形成更加稳定的闭环，都会成为新的问题。&lt;/p&gt;&#xA;&lt;p&gt;为了解决上述问题，接下来我们将借助 LangGraph 来重新实现这一套 Mini ChatGPT 助手。与第5.6节版本相比，这里的核心变化有两个：其一，短期记忆不再由我们手动维护，而是交由 LangGraph 的 Checkpointer 自动管理；其二，长期记忆不再落到本地 JSON 文件，而是迁移到 PostgreSQL，并借助 &lt;code&gt;PostgresStore&lt;/code&gt; 完成统一存储与检索。这样一来，我们不仅保留了原有长期记忆、短期记忆、工具写入的整体思路，还进一步获得了更清晰的状态编排能力。&lt;/p&gt;&#xA;&lt;h2 id=&#34;591-整体设计思路&#34;&gt;5.9.1 整体设计思路&lt;a class=&#34;anchor&#34; href=&#34;#591-%e6%95%b4%e4%bd%93%e8%ae%be%e8%ae%a1%e6%80%9d%e8%b7%af&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;首先，长期记忆的定位并没有变化，仍然用于保存用户跨会话共享的稳定信息，例如用户的技术栈、长期目标、持续中的项目以及偏好的回答风格等。这些内容不应绑定在某一次会话中，而应该绑定在用户层面。换言之，只要 &lt;code&gt;user_id&lt;/code&gt; 不变，即使用户切换了新的会话线程，系统也仍然应当能够检索到这些历史记忆。&lt;/p&gt;&#xA;&lt;p&gt;其次，短期记忆的职责也没有变化，仍然用于保存同一轮会话中的上下文消息，以便模型能够理解当前这句回答是在接着前面哪段对话继续。不过在第5.6节中，这部分短期记忆是由 &lt;code&gt;history&lt;/code&gt; 列表手动传递和维护的；而在本节中，短期记忆将交由 LangGraph 的 Checkpointer 自动保存，从而让会话上下文状态正式变成图运行过程中的一部分。&lt;/p&gt;&#xA;&lt;p&gt;最后，整个系统的驱动方式也从手工调用大模型并循环拼接上下文转变为让 LangGraph 统一组织状态、节点与持久化。虽然当前工程中的图结构只有一个核心节点 &lt;code&gt;chat&lt;/code&gt;，如图5-12所示，但这个节点已经被放入了标准的 &lt;code&gt;StateGraph&lt;/code&gt; 中运行，因此后续如果需要继续扩展出摘要节点、记忆筛选节点、回复评估节点等，也都可以沿着同一套图结构继续增加。&lt;/p&gt;&#xA;&lt;div align=center&gt;&lt;img width=&#34;100&#34; src=&#34;https://mlwithme.github.io/images/agent/202606140836.jpg&#34;/&gt; &lt;/div&gt;&lt;center&gt;图 5-13 Mini-ChatGPT 图结构&lt;/center&gt;&#xA;&lt;p&gt;如图5-13所示是从 Agent 节点编排的角度来看的整个图结构。从这个角度看，本节的 Mini ChatGPT 已经不只是有记忆的聊天助手，而是一个真正基于图状态编排的聊天智能体。同时， &lt;code&gt;chat&lt;/code&gt; 节点的内部处理流程与图5-11一致，整个工程的目录结构同图5-12一致，这里均不再赘述。&lt;/p&gt;&#xA;&lt;p&gt;下面，我们将围绕 &lt;code&gt;Code/Chapter05/mini-chatgpt-langgraph&lt;/code&gt; 工程中的代码，依旧按照第5.6节中的顺序，逐步介绍这个版本的实现过程。&lt;/p&gt;&#xA;&lt;h2 id=&#34;592-记忆存储结构改造&#34;&gt;5.9.2 记忆存储结构改造&lt;a class=&#34;anchor&#34; href=&#34;#592-%e8%ae%b0%e5%bf%86%e5%ad%98%e5%82%a8%e7%bb%93%e6%9e%84%e6%94%b9%e9%80%a0&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;在第5.6节中，长期记忆是通过 &lt;code&gt;JsonMemoryStore&lt;/code&gt; 写入本地文件实现的；而在当前 LangGraph 版本中，这部分能力迁移到了 &lt;code&gt;store.py&lt;/code&gt; 中定义的 &lt;code&gt;PostgresMemoryStore&lt;/code&gt; 类，并统一由 PostgreSQL 承载。&lt;/p&gt;</description>
			</item>
			<item>
				<title>5.10 使用 create_agent 搭建 Mini ChatGPT</title>
				<link>https://mlwithme.github.io/agent/chapter05/6072b2c567ba4d15/</link>
				<pubDate>Sat, 27 Jun 2026 10:32:33 +0800</pubDate>
				<guid>https://mlwithme.github.io/agent/chapter05/6072b2c567ba4d15/</guid>
				<description>&lt;h1 id=&#34;510-使用-create_agent-搭建-mini-chatgpt&#34;&gt;5.10 使用 create_agent 搭建 Mini ChatGPT&lt;a class=&#34;anchor&#34; href=&#34;#510-%e4%bd%bf%e7%94%a8-create_agent-%e6%90%ad%e5%bb%ba-mini-chatgpt&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在第4.10节内容中我们详细介绍了 ReAct 框架的原理并且知道了 LangChain 中 &lt;code&gt;create_agent&lt;/code&gt; 函数背后的实现机制就是基于的 ReAct  范式。同时，在第5.6节和第5.9节我们也分别介绍了如何从零实现 ReAct 处理流程，为了后续能够更加方便及熟练地使用 LangChain 中的 &lt;code&gt;creat_agent&lt;/code&gt; 来进行多 Agent 开发，在本节内容中将会介绍如何使用 &lt;code&gt;create_agent&lt;/code&gt; 来完成 Mini ChatGPT 的构建。&lt;/p&gt;&#xA;&lt;h2 id=&#34;5101-整体设计思路&#34;&gt;5.10.1 整体设计思路&lt;a class=&#34;anchor&#34; href=&#34;#5101-%e6%95%b4%e4%bd%93%e8%ae%be%e8%ae%a1%e6%80%9d%e8%b7%af&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;create_agent&lt;/code&gt; 是 LangChain 基于 LangGraph 提供的一个Agent 快速构造函数（Factory Function），用于构建一个具备消息管理、工具调用、循环推理和状态控制能力的 Agent 运行时，使开发者无需手动搭建图结构就能够创建一个符合 ReAct 思想的智能体。直白一点，我们可以将 &lt;code&gt;create_agent&lt;/code&gt; 理解成一个快速构建单一智能体的方法，它将大模型调用、工具管理以及 Agent 的执行流程进行了统一封装，开发者只需要提供模型、工具和必要的配置，即可获得一个可以独立完成任务的智能体。&lt;/p&gt;&#xA;&lt;p&gt;正因为 &lt;code&gt;create_agent&lt;/code&gt; 已经封装好了单一智能体的大部分运行逻辑，所以在绝大多数问答和 RAG 等场景中，我们只需要使用一个 &lt;code&gt;create_agent&lt;/code&gt; 就能够完成需求。而当业务流程变得更加复杂，需要多个智能体协同工作时，则可以进一步利用 LangGraph 提供的图编排能力，将多个 Agent 组织成一个更复杂的 Multi-Agent 系统。&lt;/p&gt;&#xA;&lt;p&gt;在本节内容中将要介绍的便是在所有功能一致的情况下如何使用单一的 &lt;code&gt;create_agent&lt;/code&gt;  来完成 Mini ChatGPT 的构建，并且有了前面两种实现过程的对比，我们将对 &lt;code&gt;create_agent&lt;/code&gt;  有着更加清晰地认识。需要说明的是，基于 &lt;code&gt;create_agent&lt;/code&gt; 的版本将依然依赖于 LangGraph 中 &lt;code&gt;PostgresSaver&lt;/code&gt; 与 &lt;code&gt;PostgresStore&lt;/code&gt; 来分别完成短期记忆和长期记忆的管理，最终，构建完成后的 Agent 其内部编排逻辑如图5-16所示。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
