前几篇文章,我们已经逐步搭建了 Agent 的基本认知:

  • 第一篇:理解什么是 Agent,以及它与普通聊天机器人的区别。
  • 第二篇:理解工具调用(Tool Calling)和执行循环(Agent Loop)。
  • 第三篇:理解任务规划(Planner)、任务执行(Executor)与任务调度(Scheduler)。
  • 第四篇:通过 Python 编写一个最小的 Agent Runtime,把规划、执行、调度和状态管理连接起来。

到这里,我们已经能够理解一个 Agent 如何接收目标、生成任务、调用工具,并根据执行结果推进任务。

但一个新的问题出现了。

假设用户昨天告诉 Agent:“我平时不喜欢太早出门,安排旅行时尽量不要把活动放在早上七点以前。”

今天,用户又说:“帮我规划一次东京三日游。”

如果 Agent 每次都把今天的请求当成全新的任务,它可能完全忘记昨天的偏好。

再假设,用户上传了一份体检报告。几天后,用户问:“我上次报告里的这个指标有没有变化?”

如果 Agent 无法找到之前的报告,即使它很擅长理解语言,也无法准确比较两次报告。这说明,Agent 除了执行任务,还需要管理历史信息、检索相关知识,并决定当前应该向大模型提供哪些内容。

这就涉及三个重要概念:

  • 记忆(Memory):保存和使用过去对话、用户偏好或任务经历等信息。
  • 检索增强生成(Retrieval-Augmented Generation,简称 RAG):先从外部知识中检索相关内容,再让大模型基于这些内容生成答案。
  • 上下文工程(Context Engineering):有目的地组织、筛选和安排提供给大模型的信息。

这三个概念经常一起出现,但它们解决的问题并不相同。

一、为什么 Agent 需要记忆?

1. 大模型为什么会“忘记”?

大模型(Large Language Model,简称 LLM)在生成回答时,会根据当前收到的输入内容进行计算。

这些输入通常包括:

  • 当前用户的问题。
  • 本轮对话中提供的历史消息。
  • 系统指令和开发者指令。
  • 检索得到的知识。
  • 工具调用结果。
  • 其他与当前任务有关的数据。

这些内容共同构成了模型当前能够使用的上下文(Context)。

上下文窗口(Context Window)是模型单次处理输入和生成输出时能够容纳的内容范围。不同模型支持的范围可能不同。

即使某个模型的上下文窗口很大,也不意味着它会自动记住所有历史对话,更不意味着它能够在未来的请求中可靠地找回某条信息。

如果应用程序没有把过去的信息再次提供给模型,模型就可能无法利用这些信息。

因此,Agent 不能仅仅依赖大模型自身的上下文能力来完成长期记忆管理。

2. 记忆究竟解决什么问题?

我们用一个简单的旅行助手举例。

用户第一次说:“我喜欢樱花,不喜欢早起,旅行预算大约一千元。”

Agent 可以提取出以下信息:

user_memory = {    "preferences": {        "favorite_scenery": ["樱花"],        "avoid": ["太早出门"]    },    "constraints": {        "travel_budget": 1000    }}

几天后,用户提出新的旅行规划请求。应用程序可以先读取与当前任务相关的记忆,再把这些信息提供给大模型,这样,Agent 就不必要求用户每次重新输入相同的偏好。不过,并不是所有信息都应该永久保存。

例如:

  • “我喜欢樱花”可能是相对长期的偏好。
  • “这次旅行预算是一千元”可能只适用于当前旅行。
  • “帮我查询一下今天的天气”通常不需要保存为长期记忆。

因此,记忆系统不仅需要保存信息,还需要考虑信息的用途、有效期、更新方式和适用范围。

二、Agent 的记忆可以怎样分类?

工程中并不存在唯一适用于所有 Agent 的记忆分类。为了便于入门,我们可以先按用途理解以下几类。

1. 对话记忆(Conversation Memory)

对话记忆用于保存和利用对话历史。

例如:

用户:帮我查北京明天的天气。Agent:明天北京可能下雨。用户:那上海呢?

如果 Agent 只收到最后一句“那上海呢?”,就可能不知道用户问的是上海明天的天气。

这时,系统需要提供必要的历史对话,或者把历史对话整理成明确的当前任务。

对话记忆的核心价值是:让 Agent 能够理解当前问题与此前对话的关系。

但不一定要把所有历史消息原封不动地发送给模型。对话很长时,可以保留最近的消息,并将较早的内容压缩成摘要。

2. 用户记忆(User Memory)

用户记忆用于保存相对稳定、未来可能重复使用的用户信息。

例如:

user_memory = {    "user_id": "user_001",    "preferences": {        "language": "中文",        "travel_style": "慢节奏",        "avoid_early_morning": True    }}

用户记忆通常适合保存:

  • 用户明确表达且可能长期有效的偏好。
  • 用户允许应用程序保存的个性化设置。
  • 未来任务可能需要的非敏感信息。

3. 任务记忆(Task Memory)

任务记忆用于保存某个任务的执行过程和结果。

例如,前一篇文章中的 Agent Runtime 可能记录:

task_memory = {    "goal": "比较北京和上海的天气",    "completed_tasks": [        {            "task_id": "T1",            "result": {                "city": "北京",                "weather": "晴"            }        },        {            "task_id": "T2",            "result": {                "city": "上海",                "weather": "下雨"            }        }    ]}

这些信息可以帮助系统继续尚未完成的任务,也可以为后续的执行记录和问题排查提供依据。

不过,任务记忆与运行时状态并不完全相同。

运行时状态(Runtime State)通常描述当前一次执行正在进行到哪里;任务记忆则可以用于保存跨越当前执行过程、未来可能需要再次使用的信息。

例如:

  • 当前任务是否正在执行,属于运行时状态。
  • 上次任务最终得出了什么结论,可能属于任务记忆。
  • 为了故障恢复而保存的完整执行快照,则属于持久化状态管理的一部分。

它们可以使用同一套存储系统,但在设计上最好明确各自的用途。

4. 长期记忆(Long-term Memory)

长期记忆强调信息可以跨越多次请求或多个会话继续使用。

例如,用户连续多次告诉 Agent 自己喜欢慢节奏旅行,系统可以将这一偏好保存在数据库中,并在未来规划旅行时读取。

需要注意,长期记忆不是把所有历史消息永久堆积起来。

更实用的方式是:

  1. 从历史交互中识别值得保留的信息。
  2. 检查信息是否有明确来源。
  3. 判断信息是否已经过期或被更新。
  4. 按用户或业务实体保存。
  5. 在相关任务发生时检索。
  6. 在信息不再需要时允许更新或删除。

长期记忆的价值不在于保存得多,而在于能够在合适的时间,可靠地取出合适的信息。

三、记忆应该存在哪里?

记忆不仅是一种模型能力,更是应用程序中的数据管理问题。

1. 先从 Python 字典开始

最简单的实现方式,是将记忆保存在内存中的字典里。

memory_store = {}def save_memory(user_id: str, memory: dict) -> None:    memory_store[user_id] = memorydef get_memory(user_id: str) -> dict | None:    return memory_store.get(user_id)

这里的 MemoryStore 可以理解为记忆存储(Memory Store),也就是负责保存和读取记忆的模块。

这段代码可以帮助我们理解接口设计,但它有明显限制:

  • 程序重启后,内存中的数据通常会丢失。
  • 多个应用进程之间不一定共享同一份字典。
  • 缺少持久化、权限管理和数据更新策略。
  • 不能直接作为可靠的生产级记忆系统。

因此,内存字典适合教学和原型验证,而不是长期保存重要用户信息。

2. 使用数据库保存结构化记忆

结构化数据(Structured Data)指的是字段和数据类型相对明确的数据。

例如,用户偏好可以设计为数据库记录:

UserMemory├── id├── user_id├── memory_type├── content├── source├── created_at└── updated_at

字段含义如下:

字段含义
id记忆记录的唯一标识
user_id记忆所属用户
memory_type记忆类型,例如偏好或任务摘要
content记忆内容
source记忆来源,例如用户明确表达
created_at创建时间
updated_at最近更新时间

例如,在健康报告 Agent 中,报告原文、解析结果和指标记录应该优先保存为可追踪的业务数据,而不是全部塞进一段模糊的用户记忆。

因为业务数据需要明确的主键、数据关系、权限控制和更新规则。把重要业务事实只保存在大模型生成的摘要里,会增加数据不一致和无法核验的风险。

3. 使用向量数据库保存语义检索信息

向量(Vector)在这里指的是一组数字,可以用来表示文本等内容的语义特征。

嵌入(Embedding)是将文本转换成向量的过程。

例如,以下两句话用词不同,但表达的意思相近:

  • “我不喜欢太早出门。”
  • “旅行时尽量安排晚一点开始的活动。”

经过合适的嵌入模型处理后,这些文本可能得到语义上接近的向量。

向量数据库(Vector Database)可以保存向量,并根据向量之间的相似程度检索相关内容。

这适合处理“我之前说过什么类似的话”之类的问题。

但要注意:向量相似不等于事实正确,也不等于两条信息完全相同。检索结果仍然需要结合原始文本、时间、来源和业务规则进行判断。

此外,并不是所有记忆都需要向量数据库。

如果只需要根据 user_id 查询用户的语言偏好,用普通关系数据库往往更简单、更可靠。

四、RAG 是什么?它和 Memory 有什么区别?

现在,我们再来理解检索增强生成(Retrieval-Augmented Generation,RAG)。

这个名称可以拆成三部分:

  • 检索(Retrieval):从外部数据源中查找相关内容。
  • 增强(Augmented):把检索结果补充到模型的输入中。
  • 生成(Generation):让大模型根据当前问题和提供的内容生成答案。

1. 为什么需要 RAG?

假设用户上传了几十份体检报告,并提出:“比较我最近两次报告中的血糖指标。”

如果把几十份报告全部塞进模型输入,不仅可能占用大量上下文空间,还可能引入无关信息。

更合理的方式是:

用户问题   ↓确定需要查询的指标与时间范围   ↓从数据库中检索相关报告   ↓取得最近两次报告中的血糖记录   ↓将相关数据提供给模型或业务分析模块   ↓生成比较结果

这里的重点是:先找到相关数据,再组织回答。

如果数据是结构化的,例如报告日期、指标名称和测量值,那么直接使用数据库查询通常比语义检索更合适。

如果数据是大量非结构化文本,例如报告解释、知识文档和长篇资料,语义检索就可能发挥作用。

2. RAG 的基本流程

一个常见的文本 RAG 流程可以分为两个阶段。

阶段一:准备知识库。

原始文档   ↓文本提取   ↓文本分块(Chunking)   ↓生成文本向量(Embedding)   ↓保存文本、元数据与向量

文本分块(Chunking)是把长文档切分成较小片段的过程。

这样做是为了让系统更容易检索到与问题相关的部分,而不是每次都返回整篇文档。

元数据(Metadata)是描述文档的信息,例如文档编号、所属用户、创建时间、标题和来源。

这些信息能够帮助系统过滤不相关的数据,并追踪检索结果来自哪里。

阶段二:回答用户问题。

用户问题   ↓理解问题并生成检索条件   ↓检索相关片段   ↓过滤和整理检索结果   ↓构建模型输入   ↓生成回答

这里的检索可以使用关键词、向量相似度或混合检索等方式。

混合检索(Hybrid Search)是把不同的检索方式结合起来。例如,关键词检索擅长匹配明确的指标名称,向量检索则可能更擅长寻找语义相近的表达。

检索方式应根据数据类型和实际效果选择,而不是默认所有项目都必须使用向量数据库。

3. Memory 和 RAG 的区别

这两个概念经常被混为一谈,但可以从它们的主要目的来区分。

对比维度Memory(记忆)RAG(检索增强生成)
主要目的保存并复用过去的交互、偏好或任务经历从外部知识源检索与当前问题相关的内容
常见数据用户偏好、对话摘要、任务结果文档、知识库、业务资料
信息来源用户交互、任务执行和应用状态文档库、数据库或其他外部数据源
关键问题什么值得记住?什么时候使用?应该检索哪些内容?怎样提高相关性?
是否必须使用向量数据库不需要也不一定需要
典型场景记住用户偏好,恢复任务进度从大量资料中查找答案依据

二者可以组合使用。

例如,用户问:“结合我之前的偏好,推荐一个适合我的旅行方案。”

系统可以:

  1. 从 Memory 中读取用户的旅行偏好。
  2. 从 RAG 知识库中检索景点、交通和活动资料。
  3. 将用户偏好、检索资料和当前请求一起交给规划模块。
  4. 生成符合约束的旅行方案。

这说明 Memory 和 RAG 可以协作,但它们不是同一个模块。

五、上下文工程:不是信息越多越好

现在,我们已经知道如何保存记忆,也知道如何检索知识。

接下来还有一个重要问题:

当我们找到了很多信息,究竟应该把哪些内容交给大模型?

这就是上下文工程(Context Engineering)要解决的问题。

1. 什么是上下文工程?

上下文工程是指围绕当前任务,有目的地选择、组织、压缩和安排模型输入信息。

可以把它理解为:不仅要让模型“有信息可用”,还要让它“在正确的位置获得完成任务所需的信息”。

例如,用户提出:“帮我比较北京和上海明天的天气。”

系统可能获得以下信息:

  • 当前用户的问题。
  • 用户几个月前讨论过的旅行计划。
  • 用户喜欢樱花的偏好。
  • 北京天气查询工具的定义。
  • 上海天气查询工具的定义。
  • 当前日期。
  • 与天气无关的历史对话。
  • 本次任务已经完成的查询结果。

这些内容并不都同样重要。

如果把所有信息都塞进模型输入,不仅浪费上下文空间,还可能干扰模型判断。

合理的做法是:根据当前任务,挑选真正有用的信息。

2. 上下文通常包括什么?

在 Agent 应用中,可以把上下文划分成几个部分:

当前模型输入├── 系统指令与开发者指令├── 当前用户请求├── 当前任务与执行状态├── 相关用户记忆├── 检索得到的知识├── 可用工具定义├── 必要的工具执行结果└── 必要的历史对话

不同任务需要的上下文不一样。

例如:

  • 天气查询需要城市、日期和天气工具定义。
  • 体检报告趋势分析需要报告日期、指标名称、数值和单位。
  • 旅行规划可能需要预算、偏好、景点资料和交通信息。

因此,上下文工程不应被实现成一个固定的“把所有内容都加进去”的模板,而应该根据任务类型进行组装。

3. 上下文排序(Context Ordering)

上下文排序指的是决定不同信息在模型输入中如何组织。

例如:

1. 任务目标与必须遵守的约束2. 当前任务状态3. 相关工具定义4. 可信的检索结果5. 必要的历史对话6. 当前需要模型完成的具体操作

这只是一个示例顺序,不是所有模型和任务都适用的固定规则。

不同模型对位置和格式的敏感程度不同,因此关键是通过实际测试确定合适的输入组织方式。

对于 Agent 开发者来说,至少应该保证:

  • 当前任务目标清晰。
  • 重要约束没有被遗漏。
  • 工具定义与当前操作相关。
  • 工具结果能够与对应任务关联。
  • 历史信息不会覆盖当前的明确要求。

4. 上下文预算(Context Budget)

上下文预算是指为模型输入中的不同信息分配有限的空间。

假设某个应用为一次请求设置了二十四千个词元的输入预算。

词元(Token)是模型处理文本时使用的基本单位,它不等于一个汉字或一个英文单词。实际占用量取决于模型的分词方式。

可以把预算暂时划分为:

内容示例预算
系统指令和任务要求3,000
当前任务与状态2,000
工具定义3,000
检索结果8,000
历史对话摘要3,000
输出预留及其他空间5,000
合计24,000

这些数字仅用于演示预算规划,并不代表所有模型都应该采用这样的分配。

实际系统还需要考虑模型对输入和输出的限制,以及系统指令、工具定义、消息格式等内容占用的空间。

为什么要做预算?

因为上下文空间有限,而不同信息的重要程度不同。

如果检索结果已经占用了大量空间,系统就可能需要:

  • 减少不相关的文档片段。
  • 压缩较早的对话历史。
  • 只保留当前任务需要的工具定义。
  • 删除已经完成且不再有用的中间信息。
  • 优先保留影响任务正确性的约束和执行结果。

这里最重要的不是死记某个数字,而是形成一个工程习惯:每一类上下文都应该有明确的用途、优先级和容量限制。

5. 上下文压缩(Context Compression)

上下文压缩是将较长的信息整理成更短、但仍然保留关键信息的表示方式。

例如,原始对话可能是:

用户:我想去东京玩三天。用户:我喜欢樱花和安静的地方。用户:我不喜欢早起。用户:预算大约一千元。用户:希望每天不要安排太多景点。

可以将它压缩成:

travel_preferences = {    "destination": "东京",    "duration_days": 3,    "interests": ["樱花", "安静的地方"],    "avoid": ["早起"],    "budget": 1000,    "pace": "慢节奏"}

对于结构明确的信息,使用结构化数据往往比自由文本摘要更容易验证和更新。

但压缩并不意味着可以随意丢弃细节。

例如,健康报告中的具体数值、日期、单位和参考范围,不能为了节省上下文而被随意改写或省略。需要进行精确计算的数据,应尽量保留结构化原始记录。

六、把 Memory、RAG 和上下文工程连接起来

现在,我们把三个概念放在同一个 Agent 中理解。

假设用户提出:

“根据我以前的旅行偏好,帮我规划一次东京三日游。”

系统可以采用下面的流程:

用户请求   ↓识别当前任务   ↓读取 Memory取得用户偏好、预算等信息   ↓使用 RAG 检索景点与交通资料   ↓检查资料来源、相关性与时间有效性   ↓Context Engineering组织目标、约束、记忆和检索结果   ↓Planner生成旅行任务计划   ↓Scheduler选择当前可以执行的任务   ↓Executor调用搜索、天气或日历工具   ↓更新 AgentState   ↓判断是否需要继续执行或重新规划   ↓生成最终结果

注意,这不是说 Memory、RAG 必须位于 Agent Runtime 的某个固定位置。

在不同架构中,它们可以作为独立服务、工具、图节点或运行时中的辅助模块。

重要的是明确每个模块的职责,以及它们怎样把信息交给后续步骤。

1. 在 AgentState 中怎样体现?

回顾第四篇文章,我们设计了 AgentState 来保存当前任务的执行信息。

现在,可以在此基础上加入与上下文组装有关的字段:

from typing import Anyfrom dataclasses import dataclass, field@dataclassclass AgentState:    goal: str    tasks: list[dict] = field(default_factory=list)    # 当前任务的执行状态    current_task: str | None = None    tool_results: list[dict] = field(default_factory=list)    # 从用户记忆中读取的相关信息    retrieved_memories: list[dict] = field(default_factory=list)    # 从知识库中检索到的资料    retrieved_documents: list[dict] = field(default_factory=list)    # 当前为模型组织好的输入信息    context: dict[str, Any] = field(default_factory=dict)    final_result: str | None = None

这里的 retrieved_memories 保存本次任务读取到的相关记忆,retrieved_documents 保存知识检索结果,context 保存经过筛选和整理后准备交给模型的信息。

还要注意:AgentState 中保存这些字段,并不意味着数据已经永久写入数据库。

运行时状态(Runtime State)与持久化存储(Persistence)是两个不同的概念。

如果希望程序重启后仍然能够恢复记忆或任务,就需要使用数据库、文件存储或其他持久化机制。

2. 一个最小的上下文组装函数

我们先不引入复杂的记忆框架,而是用 Python 体现核心思路:

def assemble_context(    goal: str,    memories: list[dict],    documents: list[dict],    tool_results: list[dict],) -> dict:    return {        "goal": goal,        "relevant_memories": memories,        "relevant_documents": documents,        "tool_results": tool_results,    }

这个函数做的事情很简单:把已经筛选好的信息组织成统一的数据结构。

真实项目还需要在调用之前完成:

  • 根据用户身份过滤数据。
  • 根据当前任务筛选相关记忆。
  • 检查文档是否属于当前用户有权访问的范围。
  • 限制检索结果数量和总长度。
  • 删除不必要的历史内容。
  • 保留来源和时间等可追踪信息。

因此,assemble_context() 不是完整的上下文工程实现,而是其中的一个小步骤。

真正的难点通常不在于把字典拼起来,而在于判断哪些信息应该进入字典。

七、结合健康报告 Agent:为什么不能只依赖记忆?

我们用一个实际的业务例子收尾。

假设我们正在开发一个健康报告 Agent。它需要支持:

  1. 上传体检报告。
  2. 解析报告中的指标。
  3. 保存结构化指标数据。
  4. 查询历史报告。
  5. 比较指标变化。
  6. 向用户解释结果。

当用户问:

“最近两次报告里的某个指标有什么变化?”

系统应该怎样处理?

1. 先查询业务数据库

报告日期、指标名称、测量值、单位和参考范围,都属于应该有明确记录的业务数据。

因此,系统应优先通过数据库查询取得准确记录,而不是依赖模型对历史聊天的回忆。

例如:

history = [    {        "report_date": "2026-06-01",        "indicator": "示例指标",        "value": 5.2,        "unit": "示例单位"    },    {        "report_date": "2026-09-01",        "indicator": "示例指标",        "value": 5.8,        "unit": "示例单位"    }]

这里的数据仅用于说明数据结构,不代表任何真实医学指标或临床判断。

2. 再由程序完成确定性计算

如果只是计算两个数值之间的差值,可以由 Python 完成:

old_value = history[0]["value"]new_value = history[1]["value"]difference = new_value - old_value

这属于确定性计算(Deterministic Computation):在输入相同且计算逻辑不变时,程序会得到相同的计算结果。

相比让大模型自行计算,使用明确的程序逻辑更容易测试和验证。

当然,实际健康指标还需要确认单位一致、检测方法可比、日期正确,并遵循适用的专业规则。不能仅凭数值变大或变小,就直接推导疾病诊断。

3. 再使用记忆和上下文工程

如果用户以前明确表示希望用通俗语言解释报告,系统可以读取这一偏好。

随后,系统将以下内容组合起来:

  • 当前问题。
  • 最近两次报告的真实记录。
  • 计算后的数值变化。
  • 相关指标的专业解释资料。
  • 用户的表达偏好。
  • 必须遵守的安全限制。

这些信息共同构成当前任务的上下文。

最后,大模型可以负责把经过验证的数据与专业资料组织成易懂的解释。

这个设计把不同类型的工作分配给合适的模块:

  • 数据库负责保存和查询业务事实。
  • Python 负责明确的计算规则。
  • RAG 负责检索相关资料。
  • Memory 负责复用合适的历史偏好或任务信息。
  • 上下文工程负责组织当前模型需要的信息。
  • 大模型负责语言理解、解释和生成。
  • Agent Runtime 负责组织整个执行过程。

这就是一个更容易测试、追踪和维护的 Agent 应用。

学AI大模型的正确顺序,千万不要搞错了

🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!

有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!

就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

在这里插入图片描述

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇

学习路线:

✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经

以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!

我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐