很多 AI 应用看起来拥有记忆,实际上只是把历史消息反复传给大模型。在同一个会话中,模型能够看到之前的聊天内容,因此显得“记得用户”。但只要创建一个新的 Session,没有重新传入历史消息,模型就会像第一次见到用户一样。
在真实业务中,这种记忆能力通常是不够的。招聘助手需要记住候选人的技术方向、工作地点和办公方式,购物助手需要记住用户喜欢的品牌和预算,企业内部助手则可能需要记住用户所在的项目、岗位和工作习惯。这些信息不应该只存在于某一次聊天中,而应该能够跨会话长期保存。
Microsoft Agent Framework 中的 FileMemoryProvider,就是一种基于文件实现长期记忆的方案。它可以让 Agent 把有价值的信息写入文件,并在之后的新会话中重新读取这些文件,从而实现真正的持久化记忆。
FileMemoryProvider 是什么
FileMemoryProvider 本质上是一个 AIContextProvider。它被挂载到 Agent 后,会向模型提供一组文件记忆工具,让模型可以创建、读取、修改、搜索、列出和删除记忆文件。
从模型的角度看,这些工具和普通的 Function Calling 工具没有本质区别。职位查询工具用来搜索岗位,天气工具用来查询天气,而文件记忆工具则用来管理用户的长期信息。
因此,FileMemoryProvider 并不是简单地自动保存所有聊天记录,而是给 Agent 提供了一套“记笔记”的能力。至于什么时候记、记什么内容,通常由模型根据系统提示词和当前对话自行判断。
它和聊天历史有什么区别
聊天历史保存的是完整对话,例如用户问了什么,助手回答了什么。这类信息通常只在当前 Session 中使用,主要作用是让上下文保持连贯。
长期记忆保存的则是经过提炼后、未来仍然有价值的信息。例如候选人有 5 年 .NET 后端开发经验、希望远程办公、工作地点倾向上海,并且关注 AI 应用方向。这些信息不需要依赖原始聊天记录,也不应该随着 Session 结束而消失。
可以把聊天历史理解为当前对话的临时工作区,而 FileMemory 则更像 Agent 的长期笔记本。
记忆最终保存在哪里
FileMemoryProvider 本身不直接绑定具体存储,它通过 AgentFileStore 访问底层文件系统。你的 Demo 使用的是 FileSystemAgentFileStore,因此记忆会直接写入本地磁盘。
var memoryRoot = Path.Combine(AppContext.BaseDirectory, "agent-memory");var fileStore = new FileSystemAgentFileStore(memoryRoot);
这段代码会在程序目录下创建一个 agent-memory 文件夹,之后由 Agent 生成的记忆文件都会保存在这个目录中。
这种方式非常适合本地开发和功能验证,因为文件内容可以直接打开查看,调试起来也很直观。进入生产环境后,也可以替换成其他 AgentFileStore 实现,例如内存存储、Azure Blob Storage,或者企业自己的远程文件服务。
因此,FileMemory 更准确的说法是“基于文件模型的记忆”,而不只是“本地文件记忆”。
WorkingFolder 决定记忆作用域
创建 FileMemoryProvider 时,需要通过 FileMemoryState 设置工作目录。
using var fileMemoryProvider = new FileMemoryProvider(fileStore,_ => new FileMemoryState{WorkingFolder = workingFolder});
你的 Demo 中,工作目录按照用户 ID 划分。
const string UserId = "user00001";var workingFolder = $"users/{UserId}";
最终文件大致会保存在下面的路径中:
agent-memory/users/user00001这个目录非常关键,因为它决定了哪些会话可以共享同一组记忆。只要多个 Session 使用相同的 WorkingFolder,它们就可以访问同一批文件。即使 Session 是新创建的,之前保存的用户信息仍然存在。
如果每次创建 Session 时都生成不同的文件夹,那么记忆就只能在单个 Session 中使用,无法实现跨会话共享。
在多用户系统中,通常需要为每个用户设置独立目录,否则不同用户的记忆可能混在一起。实际项目中还可以进一步加入应用 ID、租户 ID 等信息,例如:
applications/{applicationId}/tenants/{tenantId}/users/{userId}这样可以建立更清晰的数据隔离边界。
Agent 是如何读取记忆的
当记忆文件越来越多时,不适合每次都把所有文件内容完整放进模型上下文。这样不仅会浪费 Token,也会让模型难以判断哪些信息与当前问题真正相关。
FileMemoryProvider 的思路是先向 Agent 提供记忆文件的索引或描述。模型根据当前问题判断哪些文件可能有用,再调用读取工具获取具体内容。
例如用户询问三个不同职位中哪一个更适合自己,Agent 可以先查看已有记忆,发现候选人是一名有 5 年经验的 .NET 后端工程师,希望在上海工作,倾向远程办公,并且关注 AI 应用方向。之后,模型就可以结合这些偏好,对职位进行匹配和排序。
在 Demo 给出的三个职位中,北京现场办公的 Java 工程师,在技术栈、工作地点和办公方式上都不符合候选人偏好;上海混合办公的 .NET 工程师符合技术栈和地点要求,但没有完全满足远程办公和 AI 方向;上海远程办公的 .NET AI 应用工程师,则同时符合技术方向、办公方式、工作地点和职业发展兴趣,因此最适合推荐。
这个过程很像招聘顾问查阅候选人档案。顾问不需要每次都重新询问候选人的所有求职条件,而是在匹配职位时,先查看之前保存的偏好,再给出有针对性的建议。
对于比较大的文件,FileMemoryProvider 还可以配合描述文件使用。主文件保存完整内容,描述文件只保存简短摘要。模型先通过摘要判断文件是否相关,再决定是否读取正文,从而减少不必要的上下文消耗。
Demo 验证了什么
你的 Demo 主要验证的是 FileMemoryProvider 最核心的能力:在一个 Session 中写入记忆,在另一个全新的 Session 中继续读取。
程序首先创建本地文件存储,并为用户 UID1 设置固定的工作目录。之后,将 FileMemoryProvider 加入 Agent 的 AIContextProviders。
AIContextProviders = [fileMemoryProvider]这一步完成后,招聘顾问就具备了文件记忆工具,可以在对话过程中自行读写候选人的求职偏好。
第一次对话中,候选人告诉 Agent:
"我是一名有 5 年经验的 .NET 后端工程师,希望寻找支持远程办公、工作地点在上海、重视 AI 应用方向的职位。请记住我的求职偏好。"
这句话包含了几条长期有效的信息:候选人有 5 年 .NET 后端开发经验,希望在上海工作,倾向远程办公,并且关注 AI 应用方向。这些偏好会持续影响未来的职位推荐,因此很适合被保存为长期记忆。
第一次对话结束后,程序枚举用户目录下的文件。
foreach (var file in Directory.EnumerateFiles(Path.Combine(memoryRoot, workingFolder))){Console.WriteLine(Path.GetFileName(file));}
这一步可以直观验证 Agent 是否真正调用了文件写入工具。只要目录中出现了新文件,就说明所谓的“记住”不是模型口头上的回应,而是已经形成了持久化数据。
接下来,程序创建了一个完全新的 Session。
AgentSession secondSession = await agent.CreateSessionAsync();这个 Session 不包含第一次对话的聊天历史。随后,用户向 Agent 提供三个职位,并要求结合之前的求职偏好进行推荐。
"目前有三个职位:北京现场办公的 Java 工程师、上海混合办公的 .NET 工程师,以及上海远程办公的 .NET AI 应用工程师。请结合我的求职偏好推荐最合适的职位,并说明理由。"
如果没有长期记忆,Agent 只能根据当前消息中列出的岗位信息进行简单比较,并不知道候选人的技术背景、工作地点要求和办公方式偏好。
如果 Agent 能够主动推荐“上海远程办公的 .NET AI 应用工程师”,并明确说明该职位同时符合 .NET 技术方向、上海工作地点、远程办公和 AI 应用兴趣,就说明第一次会话中的信息已经成功跨 Session 保留下来。
这正是整个 Demo 想要验证的结果:记忆不再依赖聊天历史,而是持久化在用户目录中。
FileMemoryProvider 会自动保存所有信息吗
答案是否定的。
FileMemoryProvider 只是把文件操作能力提供给 Agent,并不会无条件保存每一句话。模型仍然需要判断哪些内容值得长期记忆。
例如“我有 5 年 .NET 后端开发经验,希望寻找上海的远程岗位”通常具有长期价值,而“我今天下午两点方便面试”可能只是一次性信息,不一定适合长期保存。
如果所有内容都被保存,记忆文件会迅速膨胀,同时混入大量过期和无关信息。招聘场景还涉及较多个人信息,如果没有明确限制,模型可能会误将联系方式、身份证号或者详细住址写入文件,带来隐私和安全风险。
因此,在生产环境中,应该通过提示词明确记忆标准,只保存长期有效的技术背景、职位方向、地点偏好和办公方式,不保存临时安排、敏感身份信息和没有后续价值的内容。
对于特别关键的信息,也可以由业务代码主动控制写入,而不是完全依赖模型判断。
FileMemory 适合哪些场景
FileMemoryProvider 比较适合保存自然语言形式的长期信息,例如用户偏好、项目背景、常用规则、学习进度和工作习惯。
招聘助手可以记住候选人的技术栈、工作年限、期望地点、办公方式和职业方向;旅行助手可以记住饮食限制、住宿预算和宠物出行需求;购物助手可以记住品牌偏好、尺码和价格范围;开发助手可以记住项目技术栈、代码规范和目录结构。
它的优势是实现简单、内容直观、调试方便。开发者可以直接查看文件,确认 Agent 保存了什么,也可以手动修改或删除不正确的记忆。
生产环境中的安全问题
本地文件存储很适合 Demo,但生产环境还需要考虑数据隔离、权限控制、加密和删除机制。
用户 ID 不应该未经处理就直接用于拼接路径,否则可能产生路径穿越等安全问题。不同候选人的文件目录必须严格隔离,敏感信息也不能在缺少授权的情况下自动保存。
招聘场景尤其需要重视个人信息保护。系统应明确禁止保存身份证号、电话号码、邮箱地址、家庭住址等敏感信息,并对已经保存的记忆提供查看、修改和删除能力。
因为这些文件不仅是 Agent 的辅助数据,也是候选人的个人数据。好的记忆系统并不是保存得越多越好,而是只保存真正有助于职位匹配的信息,并且让用户始终拥有控制权。
总结
FileMemoryProvider 通过 AIContextProvider 将文件记忆工具提供给 Agent,再通过 AgentFileStore 抽象底层存储。FileMemoryState.WorkingFolder 决定记忆保存在哪个目录,也决定哪些 Session 可以共享这些记忆。
你的 Demo 使用固定的用户目录,先在第一次会话中保存候选人的 .NET 技术背景、上海工作地点、远程办公和 AI 应用方向偏好,再创建一个不包含历史消息的新 Session,验证 Agent 是否能够重新读取这些信息并用于职位推荐。
如果第二次回答能够推荐“上海远程办公的 .NET AI 应用工程师”,并说明它同时符合候选人的技术栈、工作地点、办公方式和职业方向,就说明 FileMemory 已经实现了真正的跨会话持久化。
它的实现并不复杂,却解决了 Agent 应用中非常关键的问题:让 AI 不再只是记住当前聊天,而是能够随着使用逐渐了解候选人,并给出越来越匹配的职位建议。
夜雨聆风