news 2026/4/17 14:19:18

使用Microsoft Agent Framework链接外部存储资源

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用Microsoft Agent Framework链接外部存储资源

在构建生成式 AI 应用时,我们经常面临一个关键挑战:“记忆管理(Memory Management)”。在简单的 Demo 中,我们通常把聊天记录(Chat History)直接存在内存的List<ChatMessage>中,这很容易。但在实际的生产环境,尤其是构建无状态(Stateless)的 Web API 时,这种方式就完全不够用了:

  • 服务器重启,内存里的数据丢失

  • 负载均衡导致请求落在不同服务器,导致上下文无法共享

  • 用户刷新浏览器,session 消失

  • 多终端(App / Web)无法共享对话历史

因此,我们需要将“记忆”托管到一个外部存储中,例如:

  • 向量数据库(Azure AI Search / pgvector)

  • Redis

  • Cosmos DB

  • SQL / NoSQL 数据库

  • 任意持久化服务

本节我们将使用 Microsoft Agent Framework 来演示如何通过实现自定义的ChatMessageStore,将 AI 的记忆托管给外部存储。示例中我们采用 InMemory VectorStore(仅用于演示),你可以替换为任意数据库。

引用包

  • 需要的 NuGet 包:

    • Azure.AI.OpenAI (2.1.0)

    • Azure.Identity (1.18.0-beta.2)

    • Microsoft.Agents.AI.OpenAI (1.0.0-preview.251125.1)

    • Microsoft.Extensions.AI.OpenAI (10.0.1-preview.1.25571.5)

    • Microsoft.SemanticKernel.Connectors.InMemory (1.67.1-preview)

可选:使用命令行安装

dotnet add package Azure.AI.OpenAI --version 2.1.0 dotnet add package Azure.Identity --version 1.18.0-beta.2 dotnet add package Microsoft.Agents.AI.OpenAI --version 1.0.0-preview.251125.1 dotnet add package Microsoft.Extensions.AI.OpenAI --version 10.0.1-preview.1.25571.5 dotnet add package Microsoft.SemanticKernel.Connectors.InMemory --version 1.67.1-preview

我们这一节中使用Microsoft.SemanticKernel.Connectors.InMemory包来实现一个简单的内存存储。关于更多的第三方存储实现,可以参考: Semantic Kernel与Postgres向量存储

引用外部包后,我们就可以开始编写代码了。老生常谈,基础配置请参考:使用 Microsoft Agent Framework 构建你的第一个 Agent 应用

组装 Agent:注入自定义 ChatMessageStore

在创建 Agent 时,通过ChatMessageStoreFactory参数,告诉框架如何为每个AgentThread创建消息存储器。

AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new AzureCliCredential()) .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { Instructions = "你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。", Name = "Joker", ChatMessageStoreFactory = ctx => { return new VectorChatMessageStore(vectorStore, ctx.SerializedState, ctx.JsonSerializerOptions); } });

接着我们就可以使用这个 Agent 来进行对话了。

// 创建线程并运行对话 AgentThread thread = agent.GetNewThread(); // 运行代理,传入线程以存储对话历史记录在向量存储中。 Console.WriteLine(await agent.RunAsync("给我讲一个发生在茶馆里的段子,轻松一点的那种。", thread)); // 序列化线程状态,以便稍后使用。 JsonElement serializedThread = thread.Serialize(); Console.WriteLine("\n--- Serialized thread ---\n"); Console.WriteLine(JsonSerializer.Serialize(serializedThread, new JsonSerializerOptions { WriteIndented = true })); // 反序列化线程状态以恢复对话。 AgentThread resumedThread = agent.DeserializeThread(serializedThread); // 继续与代理对话,传入恢复的线程以访问以前的对话历史记录。 Console.WriteLine(await agent.RunAsync("现在把这个段子加上一些表情符号,并用说书人的语气再讲一遍。", resumedThread)); // 我们能够通过线程的 GetService 方法访问 VectorChatMessageStore,如果我们需要读取存储线程的键。 var messageStore = resumedThread.GetService<VectorChatMessageStore>()!; Console.WriteLine($"\n线程唯一ID存储在向量数据库中: {messageStore.ThreadDbKey}"); Console.WriteLine("\n--- 完成 ---\n");

接下来我们定义VectorChatMessageStore来实现存储逻辑。

internal sealedclassVectorChatMessageStore : ChatMessageStore { privatereadonly VectorStore _vectorStore; public VectorChatMessageStore(VectorStore vectorStore, JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null) { this._vectorStore = vectorStore ?? thrownew ArgumentNullException(nameof(vectorStore)); if (serializedStoreState.ValueKind is JsonValueKind.String) { this.ThreadDbKey = serializedStoreState.Deserialize<string>(); } } publicstring? ThreadDbKey { get; privateset; } public override async Task AddMessagesAsync(IEnumerable<ChatMessage> messages, CancellationToken cancellationToken = default) { this.ThreadDbKey ??= Guid.NewGuid().ToString("N"); var collection = this._vectorStore.GetCollection<string, ChatHistoryItem>("ChatHistory"); await collection.EnsureCollectionExistsAsync(cancellationToken); await collection.UpsertAsync(messages.Select(x => new ChatHistoryItem() { Key = this.ThreadDbKey + x.MessageId, Timestamp = DateTimeOffset.UtcNow, ThreadId = this.ThreadDbKey, SerializedMessage = JsonSerializer.Serialize(x), MessageText = x.Text }), cancellationToken); } publicoverrideasync Task<IEnumerable<ChatMessage>> GetMessagesAsync(CancellationToken cancellationToken = default) { var collection = this._vectorStore.GetCollection<string, ChatHistoryItem>("ChatHistory"); await collection.EnsureCollectionExistsAsync(cancellationToken); var records = await collection .GetAsync( x => x.ThreadId == this.ThreadDbKey, 10, new() { OrderBy = x => x.Descending(y => y.Timestamp) }, cancellationToken) .ToListAsync(cancellationToken); var messages = records.ConvertAll(x => JsonSerializer.Deserialize<ChatMessage>(x.SerializedMessage!)!); messages.Reverse(); return messages; } public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) => JsonSerializer.SerializeToElement(this.ThreadDbKey); /// <summary> /// /// </summary> privatesealedclassChatHistoryItem { [VectorStoreKey] publicstring? Key { get; set; } [VectorStoreData] publicstring? ThreadId { get; set; } [VectorStoreData] public DateTimeOffset? Timestamp { get; set; } [VectorStoreData] publicstring? SerializedMessage { get; set; } [VectorStoreData] publicstring? MessageText { get; set; } } }

实现存储逻辑

需要继承ChatMessageStore并重写关键方法。

  • 存(AddMessagesAsync):不存内存,直接写库。

  • 取(GetMessagesAsync):通过 ID 去库里查,按时间排序。

  • 序列化(Serialize):当系统要求 Agent “序列化当前状态”时,只返回 ID。

public override JsonElement Serialize(JsonSerializerOptions? options = null) => // 哪怕聊了 100 句,序列化结果也只是一个轻量级的 ID 字符串 JsonSerializer.SerializeToElement(this.ThreadDbKey);

代码执行逻辑序列

由于图片过大,不太适合在手机上查看,请在PC上点击放大查看

总结

通过解耦“计算”(Agent)与“存储”(VectorStore),让 AI 应用更健壮。

  • 扩展性:可替换底层存储(Redis、CosmosDB、Postgres),可以使用不同的连接器。

  • 轻量化:前端或客户端只需保存一个极小的 Thread ID。

  • 云原生友好:无状态的服务端设计,便于水平扩展。

源代码地址

https://github.com/bingbing-gui/aspnetcore-developer/tree/master/src/09-AI-Agent/Agent-Framework/07-StorageConversations

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 3:25:54

15、数据类型转换函数与元素提取详解

数据类型转换函数与元素提取详解 1. 输入值与格式规范中的分隔符 虽然建议输入值和格式规范使用相同的分隔符(这样更清晰),但 Oracle 对此要求并不严格,分隔符甚至在间距方面都可以不同。以下两个示例均能正常工作: select TO_DATE(15.10.2020, DD/MM/YYYY) from dual…

作者头像 李华
网站建设 2026/4/17 5:18:01

LLama-Factory集成HuggingFace镜像,加速模型下载提升训练效率

LLama-Factory集成HuggingFace镜像&#xff0c;加速模型下载提升训练效率 在大语言模型&#xff08;LLM&#xff09;快速发展的今天&#xff0c;微调已成为将通用预训练模型转化为行业专用智能体的核心手段。然而&#xff0c;现实中的开发者常常面临两个“拦路虎”&#xff1a;…

作者头像 李华
网站建设 2026/4/17 10:21:50

AutoGPT与Zapier集成可能性分析:连接上千种SaaS服务的桥梁

AutoGPT与Zapier集成&#xff1a;构建智能自动化代理的实践路径 在企业系统日益碎片化的今天&#xff0c;一个常见的困境是&#xff1a;我们拥有强大的AI语言模型&#xff0c;能写出流畅报告、设计学习计划&#xff0c;甚至模拟决策逻辑&#xff0c;却难以让它真正“动手”——…

作者头像 李华
网站建设 2026/4/16 18:05:20

中国科学技术大学论文模板参考文献格式调整深度解析与实用指南

中国科学技术大学论文模板参考文献格式调整深度解析与实用指南 【免费下载链接】ustcthesis LaTeX template for USTC thesis 项目地址: https://gitcode.com/gh_mirrors/us/ustcthesis 中国科学技术大学论文模板&#xff08;ustcthesis&#xff09;近期针对参考文献格式…

作者头像 李华
网站建设 2026/4/17 17:23:41

GitHub镜像同步更新:LLama-Factory支持100+主流大模型高效微调

GitHub镜像同步更新&#xff1a;LLama-Factory支持100主流大模型高效微调 在AI技术快速迭代的今天&#xff0c;越来越多企业和开发者希望基于大语言模型构建专属应用——无论是医疗问答系统、金融客服机器人&#xff0c;还是教育领域的智能辅导工具。然而&#xff0c;面对动辄数…

作者头像 李华
网站建设 2026/4/16 14:39:27

《2026年春节出境游趋势》显示,十大热搜目的地中新西兰与挪威热度持续冲高 | 美通社头条

、美通社消息&#xff1a;史上最长春节假期全面点燃中国旅行者的出境游热情。Airbnb爱彼迎发布《2026年春节出境游趋势》显示&#xff0c;中国旅行者计划在春节假期前后出境游的搜索热度达到去年同期的两倍左右&#xff0c;延续了国庆黄金周的强劲势头。同时&#xff0c;越来越…

作者头像 李华