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

[MAF预定义的AIContextProvider-12]FileMemoryProvider:为Agent提供可解释、可回溯的记忆能力

这个系列先后介绍了ChatHistoryMemoryProvider和Mem0Provider,接下来我们再介绍一个与记忆相关的AIContextProvider,那就是基于文件存储的FileMemoryProvider。我们很容易根据命名将它们划分错误地划分为同一类,但实际上它们之间有着本质的区别,不仅体现针对基类的存储(文件),更核心的区别在于:前两个是跨Session的记忆,而FileMemoryProvider是针对单个Session的记忆。

1. 为什么需要基于Session的记忆?

FileMemoryProvider并不是长期用户记忆,而是会话级工作记忆的工程化实现。它的生命周期与Session绑定,但相比LLM的上下文窗口,它提供了更稳定、更结构化、更可控的记忆空间。可以将它视为在LLM上下文和跨Session长期记忆之间的记忆类型,从生命周期角度来看,由于它是基于Session存储的,所以依然属于短期记忆。Session 的状态如果只依赖上下文窗口,会有几个致命问题:

  • 容量有限:上下文窗口再大,也是有限的。对话一长,系统就会开始做截断或者摘要,这意味着早期的关键信息可能被压缩甚至丢失;中间计算结果、细节、推理过程,很容易消失。对于长任务(几百轮对话),上下文根本撑不住。就算通过摘要方式最大限度地保留了语义,但是有时候我们需要当时生成的原始数据;
  • 无结构,难以检索:上下文本质上是一串token或者一堆拼接的消息,它没有文件边界、版本概念、命名空间和类型结构,这样的结构无法支持检索。LLM上下文这种无结构的文本流,不适合作为工程级状态存储;
  • 不可控,不可审计,不可复现:上下文是动态生成,还会可能被截断或者摘要,所以我们不能精确知道某一轮模型到底“看到了哪些信息”,也无法精确复现某一次推理(因为上下文可能已经变了)。更重要的是,我们无法审计模型的“记忆内容”(它记住了什么?忘了什么?),这些针对企业级应用都是大问题;

Session级文件记忆要解决的,其实是工程问题,不模型问题FileMemoryProvider做的事情可以概括成一句话:给Agent提供一个基于Session的、文件化的、可读写的工作记忆空间,用来存放那些不适合塞进上下文、但又必须跨轮次保留的东西。它解决的是工程问题,包括但不限于:

  • 长任务的中间状态
  • 工具链的中间结果
  • 用户上传的文件/下载的网页 / API 响应
  • 计划、草稿、总结、偏好
  • 可审计的推理过程
  • 这些东西如果只放在上下文里,要么会被截断,要么会变成一坨不可控的文本

具体来看,FileMemoryProvider相比“只用LLM上下文”,多了如下这些核心价值:

  • 易失缓存持久工作区:长任务、复杂任务、阶段性结果、下载的数据,都可以安全地放在文件里,而不是赌“上下文别刚好被截断”;
  • 无结构文本命名文件 + 描述 + 索引:记忆变成了可寻址、可检索、可更新的结构化资源,而不是一坨“你自己回想一下”的文本;
  • 黑盒记忆可审计、可回放的记忆:在企业、合规、调试场景下,记忆不再是黑盒,而是可审计的资产;
  • 单轮对话状态多轮工具链状态:Agent不再只是“对话机器人”,而是可以执行多阶段、有状态的任务流水线;
  • 不可控的遗忘显式的记忆管理:记忆不再是“系统自动帮你丢东西”,而是“Agent 自己管理自己的工作区”;

2. 使用FileMemoryProvider保留中间状态

我们通拓如下这个程序来演示如何利用FileMemoryProvider将中间推理过程中生成的原始数据保存在文件中。如代码片段所示,当我们根据创建的OpenAIClient构建Agent时,在ChatClientAgentOptions里配置了一个FileMemoryProviderFileMemoryProvider中所谓的文件并不限于本地磁盘上的某个文件,而是通过通过AgentFileStore表示的抽象文件系统中的某个文件。我们通过指定的FileSystemAgentFileStore对象来使用本地磁盘作为文件存储系统,并且指定了文件的存储路径为当前工作目录。基于Session的记忆隔离通过为每个Session创建一个独立的工作目录来实现,构建FileMemoryProvider时,我们指定了一个指向静态方法InitializeMemoryState的委托函数作为stateInitializer参数的值,这个静态方法的核心目的就是为了设置每个Session独立的工作目录名称。

usingAzure;usingdotenv.net;usingMicrosoft.Agents.AI;usingMicrosoft.Extensions.AI;usingOpenAI;DotEnv.Load();varmodel=Environment.GetEnvironmentVariable("MODEL")!;varapiKey=Environment.GetEnvironmentVariable("API_KEY")!;varendpoint=Environment.GetEnvironmentVariable("OPENAI_URL")!;varagent=newOpenAIClient(credential:newAzureKeyCredential(apiKey),options:newOpenAIClientOptions{Endpoint=newUri(endpoint)}).GetResponsesClient().AsIChatClient(defaultModelId:model).AsAIAgent(options:newChatClientAgentOptions{AIContextProviders=[newFileMemoryProvider(fileStore:newFileSystemAgentFileStore(Environment.CurrentDirectory),stateInitializer:InitializeMemoryState)],ChatOptions=newChatOptions{Tools=[newHostedWebSearchTool()]}});varsession=awaitagent.CreateSessionAsync();Console.Write("请输入你的命令: ");stringprompt=Console.ReadLine()!;while(true){varresponse=awaitagent.RunAsync(prompt,session);Console.WriteLine($"AI助手:{response}");Console.Write("\n$: ");prompt=Console.ReadLine()!;}staticFileMemoryStateInitializeMemoryState(AgentSession?session)=>new(){WorkingFolder=DateTime.UtcNow.ToString("yyyyMMdd_HHmmss")+"_"+Guid.NewGuid()};

由于演示需要使用到网络搜索功能,所以我们在ChatOptions里配置了HostedWebSearchTool工具。接下来我们开启一个对话循环,并在启动后完成了如下这轮三段式的对话:

请输入你的命令: 获取过去三年手机三大品牌 三星、苹果和小米 在全球各地区销量,整理成表格根据年份存储,直接返回存储的文件名 AI助手: smartphone_sales_2023.md smartphone_sales_2024.md smartphone_sales_2025.md $: 读取销售数据文件,生成一份简单的销售报告,要求在500字以内。以文件存储后,直接返回文件名 AI助手: smartphone_sales_report_2023_2025.md $: 读取销售报告内容 AI助手: # 2023–2025年三星、苹果、小米全球销售简报 2023年至2025年,全球智能手机市场逐步复苏,总出货量由约11.65亿台增长至12.85亿台,呈现温和回升趋势。 从品牌表现看,苹果在北美始终保持绝对领先,且逐年增长(95→102→108百万台),高端市场优势明显;三星在欧洲、亚太(除中国)及新兴市场基础稳固,整体出货量持续提升;小米在中国、亚太及中东非洲表现突出,三年内在多个区域实现稳定增长。 区域结构方面,亚太(除中国)为三大品牌最重要的增量市场;中国市场苹果与小米竞争激烈;拉美与中东非洲市场规模较小但增速平稳。整体来看,行业已走出低谷,高端化与新兴市场扩张成为主要增长动力。

我们首先让Agent根据用户的命令去获取过去三年三星、苹果和小米在全球各地区的销量数据,并将这些数据整理成表格存储在文件里。Agent的回复里直接返回了生成的文件名,单从文件命名可以看出,Agent确实将数据按照年份存储在了不同的文件里。接着我们让AI助手读取这些销售数据文件,生成一份简单的销售报告并存储在另一个文件里。Agent同样直接返回了生成的报告文件名。最后我们让AI助手读取销售报告内容,Agent成功地将报告内容返回给了用户。

3. 查看记忆文件

在当前目录下,确实有一个按照我们前面定义的命名规则创建的文件夹(20260607_113556_1534ae08-6f4d-4026-8848-eab918eef870),文件夹里有如下所示的9个文件,

3.1 记忆文件

smartphone_sales_2023.mdsmartphone_sales_2024.mdsmartphone_sales_2025.md分别是Agent根据用户的命令生成的包含三星、苹果和小米在全球各地区销量数据的文件;smartphone_sales_2023.md文件具有如下的内容:

# 2023年全球智能手机销量(按地区与品牌) 数据来源:IDC、Omdia、Counterpoint(2023年全年数据汇总) 单位:百万台(Million Units) | 地区 | 三星 | 苹果 | 小米 | |------|------|------|------| | 北美 | 52 | 95 | 3 | | 欧洲 | 58 | 42 | 32 | | 中国 | 4 | 50 | 39 | | 亚太(除中国) | 71 | 28 | 45 | | 拉丁美洲 | 32 | 8 | 18 | | 中东与非洲 | 29 | 6 | 28 | > 全球总出货量约 1,164.7 百万台

smartphone_sales_report_2023_2025.md是Agent根据前面生成的销量数据文件生成的销售报告文件,具有如下的内容:

# 2023–2025年三星、苹果、小米全球销售简报 2023年至2025年,全球智能手机市场逐步复苏,总出货量由约11.65亿台增长至12.85亿台,呈现温和回升趋势。 从品牌表现看,苹果在北美始终保持绝对领先,且逐年增长(95→102→108百万台),高端市场优势明显;三星在欧洲、亚太(除中国)及新兴市场基础稳固,整体出货量持续提升;小米在中国、亚太及中东非洲表现突出,三年内在多个区域实现稳定增长。 区域结构方面,亚太(除中国)为三大品牌最重要的增量市场;中国a市场苹果与小米竞争激烈;拉美与中东非洲市场规模较小但增速平稳。整体来看,行业已走出低谷,高端化与新兴市场扩张成为主要增长动力。

3.2 描述文件

smartphone_sales_2023_description.mdsmartphone_sales_2024_description.mdsmartphone_sales_2025_description.mdsmartphone_sales_report_2023_2025_description.md分别是四个记忆文件对应的描述文件,smartphone_sales_2023_description.md文件具有如下的内容:

Samsung, Apple, Xiaomi regional smartphone shipments for 2023 based on IDC and market reports.

3.3 索引文件

memories.mdFileMemoryProvider为当前Session生成的索引文件,记录了当前Session里所有记忆文件的基本描述。它具有如下的内容:

# Memory Index - **smartphone_sales_2023.md**: Samsung, Apple, Xiaomi regional smartphone shipments for 2023 based on IDC and market reports. - **smartphone_sales_2024.md**: Samsung, Apple, Xiaomi regional smartphone shipments for 2024 based on IDC annual data. - **smartphone_sales_2025.md**: Samsung, Apple, Xiaomi regional smartphone shipments for 2025 aggregated from IDC quarterly releases. - **smartphone_sales_report_2023_2025.md**: Brief sales report (under 500 Chinese characters) summarizing Samsung, Apple, Xiaomi regional performance from 2023 to 2025.

4. 配置选项和状态维护

FileMemoryProvider的实现原理其实很简单,不外乎就是注册了几个工具函数用来完成基于文件的记忆读写,在辅以相应的系统指令来指导LLM如何使用这些工具函数来管理它的工作记忆。这个系统指令可以通过FileMemoryProviderOptions里的Instructions属性进行设置。

publicsealedclassFileMemoryProviderOptions{publicstring?Instructions{get;set;}}

如果创建FileMemoryProvider时没有利用提供的FileMemoryProviderOptions对系统指令进行显式设置,会默认采用如下所示的指令文本:

## File Based Memory You have access to a session-scoped, file-based memory system via the `FileMemory_*` tools for storing and retrieving information across interactions. These files act as your working memory for the current session and are isolated from other sessions. Use these tools to store plans, memories, processing results, or downloaded data. - Use descriptive file names (e.g., "projectarchitecture.md", "userpreferences.md"). - Include a description when saving a file to help with future discovery. - Before starting new tasks, use FileMemory_ListFiles and FileMemory_SearchFiles to check for relevant existing memories to avoid duplicate work. - Keep memories up-to-date by overwriting files when information changes. - When you receive large amounts of data (e.g., downloaded web pages, API responses, research results), save them to files if they will be required later, so that they are not lost when older context is compacted or truncated. This ensures important data remains accessible across long-running sessions.

FileMemoryProvider需要为每一个不同的Session选择存放记忆文件的工作目录,所以它的状态FileMemoryState里包含了一个WorkingFolder属性来记录当前Session的工作目录名称。构造函数通过参数stateInitializer指定的委托就是为了设置这个工具目录。有人可能会问,为什么不自动使用Session的ID来创建工作目录名称呢?这是因为针对一般意义上的AIAgent对应的AISession来说,并没有ID这个属性,ChatClientAgent对应的ChatClientAgentSessionConversationId也可能为空。

publicsealedclassFileMemoryState{[JsonPropertyName("workingFolder")]publicstringWorkingFolder{get;set;}=string.Empty;}[Experimental("MAAI001")]publicsealedclassFileMemoryProvider:AIContextProvider,IDisposable{privatereadonlyProviderSessionState<FileMemoryState>_sessionState;privatereadonlyAgentFileStore_fileStore;publicFileMemoryProvider(AgentFileStorefileStore,Func<AgentSession?,FileMemoryState>?stateInitializer=null,FileMemoryProviderOptions?options=null);}

5. 工具注册

FileMemoryProvider用于读写记忆文件的工具通过重写的ProvideToolsAsync方法进行注册,这些工具最终利用的自然是构造函数里传入的AgentFileStore对象来完成文件的读写。

publicsealedclassFileMemoryProvider:AIContextProvider,IDisposable{protectedoverrideasyncValueTask<AIContext>ProvideAIContextAsync(InvokingContextcontext,CancellationTokencancellationToken=default);}

我们可以利用如下这个自定义的AIContextProvider来查看FileMemoryProvider注册的工具函数。ToolTrackingProvider在重写的InvokingCoreAsync方法里会将当前AIContext里的工具函数的名称、描述和参数结构打印出来。

classToolTrackingProvider:AIContextProvider{protectedoverrideasyncValueTask<AIContext>InvokingCoreAsync(InvokingContextcontext,CancellationTokencancellationToken=default){varindex=1;varaiContext=awaitbase.InvokingCoreAsync(context,cancellationToken);foreach(vartoolinaiContext.Tools??[]){if(toolisAIFunctionfunction){Console.WriteLine($"""{newstring('-',30)}Tool{index++}{newstring('-',30)}Name:{function.Name}Description:{function.Description}JsonSchema:{JsonSerializer.Serialize(function.JsonSchema,newJsonSerializerOptions{WriteIndented=true})}""");}}returnaiContext;}}

我们将它应用到如下这个演示程序中。如代码片段所示,创建的Agent先后注册了FileMemoryProviderToolTrackingProvider两个AIContextProvider

usingAzure;usingdotenv.net;usingMicrosoft.Agents.AI;usingMicrosoft.Extensions.AI;usingOpenAI;usingSystem.Text.Json;DotEnv.Load();varmodel=Environment.GetEnvironmentVariable("MODEL")!;varapiKey=Environment.GetEnvironmentVariable("API_KEY")!;varendpoint=Environment.GetEnvironmentVariable("OPENAI_URL")!;varfileMemoryProvider=newFileMemoryProvider(fileStore:newFileSystemAgentFileStore(Environment.CurrentDirectory),stateInitializer:InitializeMemoryState);vartrackingProvider=newToolTrackingProvider();varagent=newOpenAIClient(credential:newAzureKeyCredential(apiKey),options:newOpenAIClientOptions{Endpoint=newUri(endpoint)}).GetResponsesClient().AsIChatClient(defaultModelId:model).AsAIAgent(options:newChatClientAgentOptions{AIContextProviders=[fileMemoryProvider,trackingProvider],ChatOptions=newChatOptions{Tools=[newHostedWebSearchTool()]}});awaitagent.RunAsync("傅斯年在五四运动所起的什么作用?");

程序运行后,我们可以在控制台里看到FileMemoryProvider注册的工具函数的名称、描述和参数结构,如下所示:

------------------------------Tool 1------------------------------ Name: FileMemory_SaveFile Description: Save a memory file with the given name and content. Overwrites the file if it already exists. Include a description for large files to provide a summary that helps with discovery. JsonSchema: { "type": "object", "properties": { "fileName": { "type": "string" }, "content": { "type": "string" }, "description": { "type": [ "string", "null" ], "default": null } }, "required": [ "fileName", "content" ] } ------------------------------Tool 2------------------------------ Name: FileMemory_ReadFile Description: Read the content of a memory file by name. Returns the file content or a message indicating the file was not found. JsonSchema: { "type": "object", "properties": { "fileName": { "type": "string" } }, "required": [ "fileName" ] } ------------------------------Tool 3------------------------------ Name: FileMemory_DeleteFile Description: Delete a memory file by name. Also removes its companion description file if one exists. JsonSchema: { "type": "object", "properties": { "fileName": { "type": "string" } }, "required": [ "fileName" ] } ------------------------------Tool 4------------------------------ Name: FileMemory_ListFiles Description: List all memory files with their descriptions (if available). Description files are not shown separately. JsonSchema: { "type": "object", "properties": {} } ------------------------------Tool 5------------------------------ Name: FileMemory_SearchFiles Description: Search memory file contents using a regular expression pattern (case-insensitive). Optionally filter which files to search using a glob pattern (e.g., "*.md", "research*"). Returns matching file names, content snippets, and matching lines with line numbers. JsonSchema: { "type": "object", "properties": { "regexPattern": { "type": "string" }, "filePattern": { "type": [ "string", "null" ], "default": null } }, "required": [ "regexPattern" ] }

从输出可以看出,FileMemoryProvider注册了五个工具函数,分别是:

  • FileMemory_SaveFile:用于保存记忆文件的工具函数,接受文件名、内容和可选的描述作为参数,如果文件已经存在则会覆盖;
  • FileMemory_ReadFile:用于读取记忆文件内容的工具函数,接受文件名作为参数,如果文件存在则返回文件内容,否则返回未找到的消息;
  • FileMemory_DeleteFile:用于删除记忆文件的工具函数,接受文件名作为参数,如果文件存在则删除该文件以及它的描述文件(如果有的话);
  • FileMemory_ListFiles:用于列出所有记忆文件的工具函数,不接受任何参数,返回所有记忆文件的文件名和描述(如果有的话);
  • FileMemory_SearchFiles:用于搜索记忆文件内容的工具函数,接受一个正则表达式模式和一个可选的文件名模式作为参数,在匹配的文件中搜索内容并返回匹配的文件名、内容片段以及带行号的匹配行。

6. 消息注入

除了设置系统指令和注册上述五个工具函数之外,所过记忆索引文件memories.md不为空,重写的ProvideAIContextAsync方法返回的AIContext中,还会额外注册一个角色为UserChatMessage。消息的内容如下。

The following is your memory index — a list of files you have previously saved. You can read any of these files using the FileMemory_ReadFile tool. {indexContent}

这个消息旨在告诉LLM目前已经有哪些记忆文件存在,并且提醒它可以使用FileMemory_ReadFile工具函数来读取这些文件的内容。由于这个消息是基于当前Session的记忆索引文件生成的,所以它能够为LLM提供一个动态更新的、基于文件的记忆概览,让LLM能够更好地管理和利用它的工作记忆。

http://www.cnnetsun.cn/news/2821584.html

相关文章:

  • 如何快速掌握dex2jar:Android逆向分析终极指南
  • 从ExoPlayer 2.X到Media3:技术架构升级的3大战略优势与迁移实施指南
  • HsMod:炉石传说的55个隐藏功能解锁器,重塑你的卡牌对战体验
  • 蓝桥杯B组Java选手看过来:用这几道真题带你拆解省奖拿分套路
  • 【IF-SAFE-07】SMU故障管理 - 双域架构与FSP
  • angular-webpack-starter高级配置:DLL插件与性能优化的实战技巧
  • NLP生产实战:10个高频接口的选型、调优与避坑指南
  • Three.js ShaderMaterial实战:用两张贴图轻松实现酷炫墙体流光动画
  • 生产环境机器学习监控:从数据漂移到业务影响的四级穿透体系
  • 告别抓包失败:手把手教你用Charles搞定iOS 17+的HTTPS流量(含SSL Proxying规则配置)
  • 软件工程师岗位全景解析:从技术栈到职业路径的深度指南
  • eBay账户安全机制揭秘:为什么你的购买会被临时限制?如何主动预防与快速解封
  • 给电机装上‘智能大脑’:手把手教你用扩展卡尔曼滤波(EKF)估算PMSM转速与位置
  • 零样本分类性能预测:基于生成图像的多模态评估方法
  • HDRNet高级技巧:数据pipeline优化与性能提升策略终极指南
  • 告别手动编译!用Docker Compose一键拉起RuoYi-flowable+MySQL+Redis全家桶
  • 如何快速配置GlosSI:3步实现全局Steam输入和系统级控制器支持
  • 用Python+OpenCV玩转Apriltag:从打印到姿态估计的保姆级实战(附完整代码)
  • Plotly实现印度数字体系(Lac/Cr)数据可视化
  • Fortnite-External-Cheat-2026常见问题解答:从安装失败到功能失效的全面解决方案
  • PyTorch超参优化实战:用Optuna实现高效、可复现的贝叶斯搜索
  • Kallax迁移系统完全指南:数据库版本控制的正确姿势
  • 机器学习模型生产化部署:Kubernetes+ONNX服务化实战
  • Unity游戏翻译终极指南:XUnity.AutoTranslator完全使用教程
  • 三分钟完成黑苹果配置:OpCore-Simplify让PC变Mac不再是梦
  • VC6平台下可直接运行的算符优先法C语言计算器工程包(含源码、编译结果与调试文件)
  • OpenCore Legacy Patcher终极指南:5步让旧Mac显卡重获新生并优化系统性能
  • Data-Centric AI:数据驱动的AI工程化范式转型
  • 别只当查看器用!Meshlab隐藏的‘清洁与修复’滤镜实战:处理3D打印坏模型
  • MGF概率放大镜:用矩生成函数解析数据分布本质