两种记忆实现怎么选:微软 AI Agent 入门课第 13 课的外部记忆层
ai-agents-for-beginners 的第 13 课有个容易被忽略的细节:这一课给了两份可以单独跑的 notebook,13-agent-memory/13-agent-memory.ipynb 和 13-agent-memory/13-agent-memory-cognee.ipynb。很多人翻到第一份跑通了就走了,然后在自己项目里照着那套写法把「长期记忆」搭出来,过两个月发现召回不上来。
这两份 notebook 讲的是同一件事——工作记忆之外怎么把东西存住——但它们把边界划在完全不同的位置。下面按依赖、数据模型、召回能力三条线对一遍,只对照两份文件里都写明的东西。
相同的那一半:先划清楚哪里没差别
先把不用纠结的部分排掉,省得后面误判。
两份 notebook 的工作记忆是同一套机制:都用 agent.create_session() 拿到 AgentSession,把同一个 session 传给连续几次 agent.run(),agent 就能看到这一段对话的完整历史。两份文件也都各有一个 cell 专门开一个新 session 再问一遍,用来演示 session 边界——只是演示的落点不同:13-agent-memory.ipynb 那份的 markdown 说的是新 session 里 agent 没有前一段对话的记忆,Cognee 那份的 markdown 说的是新 session 清掉了工作记忆、但知识图谱还在。
两份的接入方式也一样:都从 agent_framework.foundry 导入 FoundryChatClient,用 project_endpoint、model、credential 三个参数构造,再由它 as_agent(...) 出 agent;环境变量都是 AZURE_AI_PROJECT_ENDPOINT 与 AZURE_AI_MODEL_DEPLOYMENT_NAME。
工具注册的写法也一致:都是 from agent_framework import tool,函数上挂 @tool(approval_mode="never_require"),参数用 Annotated[str, "..."] 带描述。注意 approval_mode="never_require" 这个值——它是仓库示例里的写法,意思是这些工具调用不走审批,教学场景下省事,搬到自己项目里要不要保留是另一回事。
所以真正的分岔只在一个位置:长期记忆那一层挂的是什么。
依赖:一条只要云端,一条要先起本地服务
13-agent-memory.ipynb 的安装 cell 是:
%pip install agent-framework azure-ai-projects azure-identity python-dotenv -q
前置条件那段 markdown 列的是:一个已部署聊天模型的 Microsoft Foundry 项目、az login、以及上面那两个环境变量。没有本地服务。
13-agent-memory-cognee.ipynb 的安装 cell 多了一项,是带 extra 并且钉死版本的 cognee[redis](具体版本号以仓库当前文件为准,别照抄本文)。它的 Prerequisites 在前一份那几条之外还多列了几项:一个 Python 版本下限、本地跑一个 Redis 用于 session 管理(命令逐字给的是 docker run -d -p 6379:6379 redis)、.env 里要有 LLM_API_KEY、.env 里要 CACHING=true 并注明这是 Cognee session 所需。代码 cell 里对应地有 os.environ["CACHING"] = os.getenv("CACHING", "true")。
这里有个 Windows 侧必须先想清楚的问题:那条 Redis 命令是 Docker 命令,在 Windows 上意味着你得先有 Docker Desktop(或 WSL2 里的 Docker),公司机器上这一步能不能过往往不由你决定。Linux/macOS 上装 Docker 基本没有门槛,Windows 上它是一个真实的准入条件。课程仓里没有给 Windows 下不用 Docker 的替代方案,这一点别指望它。
还有一处细节差异值得留意:两份 notebook 用的凭据类不是同一个。13-agent-memory.ipynb 里是 from azure.identity import DefaultAzureCredential,Cognee 那份是 from azure.identity import AzureCliCredential,虽然两份的前置条件都写了 az login。这两个类的行为差别属于 azure-identity 的范畴,不在这个课程仓里,我们不替它下结论——但你把两份代码来回复制粘贴时,这一行是会跟着跑的。
另外,Cognee 那份还要配 LLM_API_KEY,也就是说这条路线上同时存在两套凭据:Foundry 走 Azure 身份认证,Cognee 自己走 API key。这在企业环境里通常是要单独走审批的。
数据模型:一个 dict,一个图
13-agent-memory.ipynb 的长期记忆就一行:
preference_store: dict[str, list[str]] = {}
注释写着 Persistent preference store (simulated) —— simulated,这个词是仓库自己写的,不是我加的。写入靠 save_preference(user_id, preference),实现是 preference_store.setdefault(user_id, []).append(preference)。所以数据模型就是:按 user_id 分桶,每桶一个字符串列表,只追加。没有去重、没有更新、没有删除、没有时间戳,条目之间也没有任何关系。notebook 的 markdown 里明说了生产环境应该换成数据库或向量索引,末尾的 Next Steps 第一条也是「把这个 dict 换掉」。
Cognee 那份的数据模型是在 ingest 阶段建起来的:await cognee.add(text, node_set=[...]) 把原始文本塞进去,再 await cognee.cognify() 把它变成图。notebook 里分了两个 node_set:开发者自述和历史对话进 developer_data,Python 原则那段进 principles_data。之后还有一步 await cognee.memify(),notebook 的 markdown 对它的描述是分析知识图谱、生成规则——具体生成什么形态的规则,这份 notebook 里没有展开,我们不替它补。
存储位置是显式配的:cognee.config.data_root_directory(...) 和 cognee.config.system_root_directory(...),notebook 里分别指向 .data_storage 和 .cognee_system 两个用 Path(...).resolve() 解析出来的目录。重置用 await cognee.prune.prune_data() 和 await cognee.prune.prune_system(metadata=True)——这两行在 notebook 里是每次运行都执行的,你要是在本地反复跑,得知道自己每次都在清库。另外它还有一步 visualize_graph('./cognee_graph.html'),会往当前目录写一个 HTML 出来。
召回能力:字符串匹配 vs 图检索
这是两条路线差得最远的地方,也是最容易在项目做到一半才咬到你的地方。
13-agent-memory.ipynb 的读取只有两种:
get_preferences(user_id):把这个用户桶里的全部条目拼成一段字符串返回,没有筛选、没有排序。桶空时返回一句No saved preferences for ...。search_hotels(query):把 query 转小写,在写死的酒店列表里做子串匹配——匹 name、匹 location、或者匹 tags 里任意一项。匹不到时,代码走的是matches = hotels[:3],也就是兜底返回前三条。
这个兜底行为要特别注意:它不会告诉你「没匹上」,它会给你三条看起来像结果的结果。教学 notebook 里这么写没问题,抄进自己项目就是个静默的坑。
Cognee 那份的读取包在两个 async def 工具里,核心都是 await cognee.search(...):
results = await cognee.search(
query_text=query,
query_type=SearchType.GRAPH_COMPLETION,
)
SearchType 从 cognee.modules.search.types 导入。第二个工具 search_principles 在同样的调用上多传了两个参数做子集限定:
from cognee.modules.engine.models.node_set import NodeSet
results = await cognee.search(
query_text=query,
query_type=SearchType.GRAPH_COMPLETION,
node_type=NodeSet,
node_name=["principles_data"],
)
以上两段按 notebook 里的接口语义抄录,未经实测,以仓库最新代码为准。
对照点很清楚:Cognee 这条路线有「只在某个子集里检索」这个概念(node_name 对应 ingest 时打的 node_set),dict 那条路线没有——get_preferences 只有 user_id 一个维度,要么全给要么没有。
反过来也有一处:dict 那条路线天然带用户隔离,key 就是 user_id(notebook 的 instructions 里把它写死成了一个示例用户 id)。Cognee 那份 notebook 里的 node_set 分的是数据类别而不是用户,我们在这份 notebook 里没有找到按用户隔离检索的说明,这一点不比。
至于检索质量、耗时、成本这些——两份 notebook 都没有给任何依据,我们也没有跑过,一律不比。
顺手记下三处对不上的地方
按仓库文件读,第 13 课内部有几处前后不一致,你踩到时不用怀疑自己:
一、README 说的实现和 notebook 里的实现不是一回事。 13-agent-memory/README.md 的 Available Implementations 一节写 13-agent-memory.ipynb 是用 Mem0 和 Azure AI Search 实现的,但打开这份 notebook,从头到尾没有 Mem0 或 Azure AI Search 的 import,长期记忆就是上面那个 dict。README 里关于 Mem0 两阶段流水线、Azure AI Search 做 Structured RAG 的段落是概念介绍,不是这份 notebook 的实现说明。按 README 的目录去找代码会扑空。
二、术语归属两边不一样。 README 在 Short Term Memory 一节里明说,在 Microsoft Agent Framework 的 Python SDK 示例中这一层对应 AgentSession(由 agent.create_session() 创建);而 notebook 的「Types of Agent Memory」那个 cell 把 agent.create_session() 归在 Working Memory 底下,短期记忆另指「一次任务中累积起来的事实」。同一个 API,两处挂在不同的名字下。你跟同事讨论时最好直接说 create_session(),别说「短期记忆」。
三、instructions 里提到了没注册的工具。 13-agent-memory.ipynb 里 travel_agent = client.as_agent(tools=[save_preference, get_preferences], ...),只传了两个工具;但同一个调用的 instructions 第 3 步写的是「Use search_hotels() to find suitable options」。search_hotels 定义了、也带了 @tool 装饰器,就是没进 tools 列表。这两处在同一个文件里,摆在一起看就是对不上的——这类「提示词里让它调一个它拿不到的工具」的写法,在自己项目里会表现成模型一本正经地编数据,值得记一下这个形态。
那到底该选哪个
不排优劣,只按你的处境倒推:
你要存的是「用户说过的、下次别再问的偏好」,条目彼此独立。 那么第一份 notebook 的形态就够了——按 key 取回全量、塞进上下文。你要做的不是换成图,而是把那个 dict 换成真数据库,并且补上它没有的东西:去重、更新、删除、过期。notebook 自己在 Next Steps 里也是这么说的。
你要回答的是「我这段实现符合哪几条原则」「这两件事之间什么关系」这类跨来源、要串联的问题。 这时 get_preferences 那种全量拼接就不成立了,Cognee 那份提供的 GRAPH_COMPLETION 检索和 node_name 子集限定才对得上你的问题形态。
你的环境起不了本地服务,或者不能再引入第二套 API key。 那 Cognee 这条路线在前置条件那一步就断了(Redis + LLM_API_KEY + CACHING=true 三条都是它写明的必需项),不用往下看数据模型。
你要做多用户隔离。 dict 那条有 user_id 维度可以直接扩,Cognee 那份 notebook 里没写这一层,你得自己去 Cognee 的文档确认,别按这份 notebook 推。
最后提醒一句:这两份 notebook 的示例都会把内容发到云端服务,Cognee 那条还要额外配一套 key,密钥与数据边界自己评估。课程仓持续更新,文中的文件路径、依赖写法、参数名与目录名都随版本变动,动手前请以仓库最新内容为准。
本文依据 github.com/microsoft/ai-agents-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与 Agent 实际表现的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。
本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。