ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Semantic Kernel Agent 记忆机制深度解析:从 ADR 0072 到 AIContextProvider 落地实现

Semantic Kernel Agent 记忆机制深度解析:从 ADR 0072 到 AIContextProvider 落地实现 Semantic Kernel Agent 记忆机制深度解析从 ADR 0072 到 AIContextProvider 落地实现【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel本文基于 docs/decisions/0072-agents-with-memory.md 这一架构决策记录ADR系统讲解 Semantic Kernel 如何为各类 Agent 提供带记忆能力从记忆的作用域模型、内存组件Memory Component的设计思想到挂起/恢复Suspend/Resume机制再到当前仓库中AIContextProvider接口族的实际落地代码。读完本文你将理解 AgentThread 上如何挂载上下文提供器、AggregateAIContextProvider如何聚合多个组件、以及如何把用户事实记忆与 RAG 检索组件接入 Agent 会话。背景为什么要给 Agent 加记忆Agent 类型的两大差异维度Semantic Kernel 目前支持多种 Agent 类型它们各有不同的运行特性见 dotnet/src/Agents 目录下的A2A、AzureAI、Bedrock、Copilot、OpenAI、Core等子项目进程内in-processvs 远程remote进程内 Agent 由宿主应用直接运行模型调用远程 Agent 则委托给外部服务如 Azure AI、Bedrock、OpenAI Assistant执行。服务端维护会话状态 vs 调用方提供会话状态部分远程 Agent 会在服务端保存并维护会话线程Thread而ChatHistoryAgentThread这类本地线程则要求调用方在每次调用时提供会话状态。无论哪种 Agent都存在记住本次对话中学到的信息并在本次或后续对话中复用的需求——这就是本文讨论的Memory记忆的定义。ADR 0072 的核心目标正是为上述所有 Agent 类型统一地支持高级记忆能力。记忆的作用域Memory Scope与隐私边界文档特别强调记忆是有作用域的。大多数 Agent 实现都配有指令instructions和技能skills但 Agent 本身并不绑定某个单一对话——每次调用时调用方会告诉 Agent 它要参与哪个会话。由此衍生出两类不同性质的记忆用户级 / 会话级记忆从某个用户的一段对话中提取并在与同一用户的相同或后续对话中被召回。这类记忆往往包含用户不希望与其他用户共享的私人信息。跨用户 / 跨会话记忆例如 Agent 学会了如何做某件事从而能在与不同用户的许多对话中复用该能力。文档明确指出第二类记忆存在在不同用户之间泄漏个人信息的风险这是设计时必须防范的重点。这个作用域模型直接决定了后续命名空间namespace概念的出现——在示例代码中记忆被存储于user/12345、group/g2这样的命名空间下正是为了在存储层面隔离不同用户/群体的数据。打包记忆能力将组件挂载到 AgentThread三件套机制ADR 0072 提出上述所有记忆类型都可以通过把软件组件挂载到会话线程conversation threads上来支持其实现机制非常简单只有两件事检视并使用消息消息在传入/传出 Agent 时组件可以读取并学习。按调用附加上下文每次调用 Agent 时额外向其传入上下文。借助现有的AgentThread实现当 Agent 被调用时所有输入/输出消息都会经过AgentThread因此可以被挂载在AgentThread上的任何组件感知。对于远程/外部 Agent服务端管理会话状态把消息传给AgentThread可能不会影响服务端的真实线程——这没有关系因为服务端在远程调用期间已经更新了线程但我们仍然可以通过挂载组件订阅到这些消息。对于按调用获取额外上下文这一需求Agent 可以向传给它的线程发起询问线程再去询问其上挂载的每个组件请它们提供要传给 Agent 的上下文。这样组件就能按需把自己保存的记忆提供给 Agent。每个记忆组件的能力特征文档为记忆组件Memory Component定义了三项可选能力可以存储一些上下文在每次调用时提供给 Agent可以检视会话中的消息从对话中学习并构建自己的上下文可以注册插件plugins让 Agent 能直接存储、检索、更新或清除记忆。不同的记忆能力可以通过独立的组件组合实现例如一个组件专门提取用户事实User Facts另一个组件专门做 RAG 检索互不干扰。Suspend / Resume有状态体验背后的无状态服务构建托管 Agent 的服务面临一个经典难题有状态服务难以构建但服务消费者期望从外部看起来是有状态的。例如每次调用时用户都期望服务能继续他们正在进行的对话。这意味着当服务暴露的是一个使用本地会话状态如ChatHistory的本地 Agent 时会话状态必须在每次服务调用时被加载和持久化。同理任何带有进程内in-memory状态的记忆组件也需要随之加载与持久化。为此文档设计了OnSuspend/OnResume两个通知方法前者通知组件保存状态后者通知组件重载状态具体在哪里、以何种方式保存/加载状态由每个组件自行决定——这正是组件可插拔的体现。例如在客户端应用中挂起可能发生在用户关闭聊天窗口时恢复则发生在用户重新打开窗口继续对话时。提议的接口与当前仓库的落地实现ADR 提议通用命名的 AIContextBehaviorADR 0072 指出记忆组件所需的事件类型并不仅限于记忆场景还可以用来打包其他能力甚至用于非 Agent 场景。因此它建议创建一个更通用的类型且该类型应位于Microsoft.SemanticKernel.AbstractionsNuGet 包中。文档给出了如下提议接口节选namespace Microsoft.SemanticKernel; public abstract class AIContextBehavior { public virtual IReadOnlyCollectionAIFunction AIFunctions Array.EmptyAIFunction(); public virtual Task OnThreadCreatedAsync(string? threadId, CancellationToken cancellationToken default); public virtual Task OnThreadDeleteAsync(string? threadId, CancellationToken cancellationToken default); public virtual Task OnThreadCheckpointAsync(string? threadId, CancellationToken cancellationToken default); public virtual Task OnNewMessageAsync(string? threadId, ChatMessage newMessage, CancellationToken cancellationToken default); public abstract Taskstring OnModelInvokeAsync(ICollectionChatMessage newMessages, CancellationToken cancellationToken default); public virtual Task OnSuspendAsync(string? threadId, CancellationToken cancellationToken default); public virtual Task OnResumeAsync(string? threadId, CancellationToken cancellationToken default); }注意几点OnThreadCheckpointAsync在文档中被明确标注为初始版本不包含可能未来加入OnModelInvokeAsync是唯一抽象方法由组件返回要传给 Agent 的上下文文本接口中的AIFunctions属性即为注册插件能力的接口级体现让 Agent 可以直接操作记忆。源码落地AIContextProvider 接口族从当前仓库源码看该提议已落地为AIContextProvider抽象类位于 dotnet/src/SemanticKernel.Abstractions/Memory/AIContextProvider.cs命名空间为Microsoft.SemanticKernel并标记为实验性特性[Experimental(SKEXP0130)]。其方法集与 ADR 提议一一对应方法名做了微调例如OnModelInvokeAsync→ModelInvokingAsyncOnNewMessageAsync→MessageAddingAsync生命周期阶段ADR 提议名落地实现名AIContextProvider语义线程/会话创建后OnThreadCreatedAsyncConversationCreatedAsync新会话创建后触发如检查长期存储中与本会话相关的数据消息加入前OnNewMessageAsyncMessageAddingAsync任何参与者向聊天添加新消息前触发组件可据此学习并更新上下文线程/会话删除前OnThreadDeleteAsyncConversationDeletingAsync会话删除前触发如把上下文保存到长期存储模型调用前OnModelInvokeAsync抽象ModelInvokingAsync抽象加载额外上下文并返回要传给模型/Agent 的上下文返回类型为AIContext挂起OnSuspendAsyncSuspendingAsync会话暂时挂起、需要保存状态时触发恢复OnResumeAsyncResumingAsync会话恢复、需要恢复状态时触发从源码结构看落地实现相比 ADR 有两处演进一是抽象方法的返回类型从Taskstring细化为TaskAIContext为上下文传递提供了更结构化的载体二是类名从AIContextBehavior更名为AIContextProvider但通用命名、可复用于非 Agent 场景的设计初衷保持不变。基础实现的默认行为在单元测试中有明确验证dotnet/src/SemanticKernel.UnitTests/Memory/AIContextProviderTests.cs 中ConversationCreatedAsyncBaseImplementationSucceeds、MessageAddingAsyncBaseImplementationSucceeds、ConversationDeletingAsyncBaseImplementationSucceeds、SuspendingAsyncBaseImplementationSucceeds、ResumingAsyncBaseImplementationSucceeds等用例确认所有虚方法的基类默认实现均安全返回已完成任务Task.CompletedTask子类只需按需覆写。管理多个组件AggregateAIContextProvider单个会话上往往需要挂载多个组件如用户事实组件 RAG 检索组件。ADR 提议用一个容器类统一注册组件并把新消息通知、AI 调用请求等委托给容器内的各个组件。源码中该容器落地为AggregateAIContextProvider同样位于 dotnet/src/SemanticKernel.Abstractions/Memory/AggregateAIContextProvider.cs并配套提供了扩展方法 AggregateAIContextProviderExtensions.cs。其行为特征如下通过Providers属性暴露已注册的IReadOnlyListAIContextProvider组件列表覆写了ConversationCreatedAsync、ConversationDeletingAsync、MessageAddingAsync、ModelInvokingAsync、SuspendingAsync、ResumingAsync等全部生命周期方法逐一委托给内部所有组件当多个组件在ModelInvokingAsync中返回上下文时聚合器负责把各组件返回的上下文合并形成传给 Agent 的组合上下文。对应测试位于 dotnet/src/SemanticKernel.UnitTests/Memory/AggregateAIContextProviderTests.cs 与 AggregateAIContextProviderExtensionsTests.cs。与 Agent 的集成AgentThread.AIContextProvidersADR 提议将行为管理器加入AgentThread类。在源码中这一设计落实为AgentThread上的AIContextProviders属性类型为AggregateAIContextProvider标记实验特性SKEXP0110参见 dotnet/src/Agents/Abstractions/AgentThread.cs#L30-L34[Experimental(SKEXP0110)] public virtual AggregateAIContextProvider AIContextProviders { get; init; } new AggregateAIContextProvider();AgentThread基类自身集成了上下文提供器的生命周期调用线程创建时CreateAsync在拿到线程 Id 后调用AIContextProviders.ConversationCreatedAsyncAgentThread.cs#L96-L98线程删除时DeleteAsync先调用ConversationDeletingAsync再执行真正的删除AgentThread.cs#L119-L121新消息到达时OnNewMessageAsync调用MessageAddingAsync通知所有组件AgentThread.cs#L150-L152挂起/恢复OnSuspendAsync与OnResumeAsync分别委托给SuspendingAsync/ResumingAsync其中OnResumeAsync还会校验线程未被删除且已创建AgentThread.cs#L46-L74。当 Agent 被调用时会通过线程向各个组件发出模型调用前的上下文请求并汇总。这与 ADR 中给出的内部调用示意一致var additionalInstructions await currentAgentThread.OnModelInvokeAsync(messages, cancellationToken).ConfigureAwait(false);整个过程对用户透明用户不需要关心上下文是如何收集与注入的。实战示例示例一多个线程共享同一个记忆组件ADR 给出的第一个场景是用户事实记忆组件从对话中抽取用户信息如名字存入向量存储并在后续调用中作为附加指令提供给 Agent。关键点在于同一个userFacts组件可以被挂载到多个不同的线程上从而让 Agent 在不同会话间记住同一用户的信息。// 创建向量存储用于存放记忆。 var vectorStore new InMemoryVectorStore(); // 创建与向量存储中 Memories 集合绑定、并以 user/12345 命名空间存储记忆的文本记忆存储。 using var textMemoryStore new VectorDataTextMemoryStorestring( vectorStore, textEmbeddingService, Memories, user/12345, 1536); // 创建记忆组件从对话中抽取用户事实存入向量存储 // 并在调用时作为附加指令传给 Agent。 var userFacts new UserFactsMemoryComponent(this.Fixture.Agent.Kernel, textMemoryStore); // 创建线程并挂载记忆组件。 var agentThread1 new ChatHistoryAgentThread(); agentThread1.ThreadExtensionsManager.Add(userFacts); var asyncResults1 agent.InvokeAsync(Hello, my name is Caoimhe., agentThread1); // 创建第二个线程并挂载同一个记忆组件。 var agentThread2 new ChatHistoryAgentThread(); agentThread2.ThreadExtensionsManager.Add(userFacts); var asyncResults2 agent.InvokeAsync(What is my name?., agentThread2); // 期望的响应中包含 Caoimhe。要点解读命名空间隔离记忆存储绑定在user/12345命名空间下不同用户的数据天然隔离对应前文跨用户泄漏风险的防范跨线程复用第二个线程虽然是一个全新会话但由于共享同一个userFacts组件Agent 依然能回忆起此前对话中学习到的用户姓名嵌入维度1536是文本嵌入向量的维度参数需与所用嵌入模型的实际输出维度一致。需要说明的是示例中的VectorDataTextMemoryStoreT、UserFactsMemoryComponent属于 ADR 设计时使用的演示性类型在当前仓库源码中抽象层已统一演化为AIContextProvider及其聚合器如AgentThread.AIContextProviders组件的挂载入口也随之变化——例如 AgentThread.cs#L34 所示通过向AIContextProviders聚合器注册组件即可实现同样的多线程共享记忆效果。示例二使用 RAG 组件第二个场景是 RAG检索增强生成把企业文档切片存入向量存储Agent 在回答问题时先检索相关片段再基于检索结果生成答案。这样既避免把全部文档塞进上下文又能让 Agent 引用最新的事实数据。// 创建向量存储与 RAG 存储/组件。 var vectorStore new InMemoryVectorStore(); using var ragStore new TextRagStorestring( vectorStore, textEmbeddingService, Memories, 1536, group/g2); var ragComponent new TextRagComponent(ragStore, new TextRagComponentOptions()); // 把文档 Upsert 进向量存储。 await ragStore.UpsertDocumentsAsync( [ new TextRagDocument(The financial results of Contoso Corp for 2023 is as follows:\nIncome EUR 174 000 000\nExpenses EUR 152 000 000) { SourceName Contoso 2023 Financial Report, SourceReference https://www.consoso.com/reports/2023.pdf, Namespaces [group/g2] } ]); // 创建新的 Agent 线程并注册 RAG 组件。 var agentThread new ChatHistoryAgentThread(); agentThread.ThreadExtensionsManager.RegisterThreadExtension(ragComponent); // 调用 Agent。 var asyncResults1 agent.InvokeAsync(What was the income of Contoso for 2023, agentThread); // 期望的响应中包含文档中 1.74 亿欧元的收入数据。要点解读文档元数据TextRagDocument除正文外还携带SourceName来源名称与SourceReference来源引用便于回答时给出可溯源出处命名空间过滤文档与 RAG 存储均使用group/g2命名空间检索时只在同一命名空间内进行从存储层保证不同用户组的文档互不可见组合使用RAG 组件与用户事实组件可以同时挂载到同一线程分别承担外部知识检索与对话内用户记忆两类互补的记忆职责。决策记录为什么这样命名与放置ADR 0072 最后记录了三个关键决策这些决策对后续实现有直接约束1. 扩展基类命名候选方案包括ConversationStateExtension——过长MemoryComponent——过于具体无法涵盖非记忆场景AIContextBehavior——通用性强。决定采用 3AIContextBehavior。落地时类名进一步演化为AIContextProvider见 AIContextProvider.cs但通用化命名、可复用于记忆之外的场景这一原则被完整保留。2. 抽象层基类位置候选方案Microsoft.SemanticKernel.baseclass即 Abstractions 包根命名空间Microsoft.SemanticKernel.Memory.baseclassMicrosoft.SemanticKernel.Memory.baseclass放在独立 NuGet 包。决定采用 1。因为这些组件不仅服务于 Agent也可被其他系统使用放根命名空间并置于Microsoft.SemanticKernel.Abstractions程序集便于最大范围复用。源码中该决策已兑现——AIContextProvider与AggregateAIContextProvider均位于 dotnet/src/SemanticKernel.Abstractions/Memory 下。3. 记忆组件实现的位置候选方案每个组件一个 NuGet 包Microsoft.SemanticKernel.CoreNuGetMicrosoft.SemanticKernel.MemoryNuGetMicrosoft.SemanticKernel.ConversationStateExtensionsNuGet。决定采用 2Microsoft.SemanticKernel.Core NuGet——组件实现随核心包分发降低使用方的引用复杂度。小结回顾 ADR 0072 与当前仓库源码可以总结出 Semantic Kernel Agent 记忆能力设计的四条主线记忆是一个可插拔的组件化能力通过AIContextProvider抽象类 AggregateAIContextProvider聚合容器任何 Agent 线程都可以挂载任意数量的上下文组件生命周期全埋点会话创建、消息加入、模型调用前、会话删除、挂起/恢复等关键节点均有钩子组件可以在正确的时机学习、注入、保存与恢复状态作用域隔离优先用户级/会话级与跨用户级记忆必须通过命名空间等手段隔离防止个人信息在不同用户间泄漏服务端无状态、体验有状态通过SuspendingAsync/ResumingAsync让携带内存状态的组件随每次服务调用持久化与恢复支撑对外连续对话的体验。如果你正在构建需要长期记忆能力的 Agent 应用建议从阅读 AgentThread.cs、AIContextProvider.cs 与 AggregateAIContextProvider.cs 入手参考 AIContextProviderTests.cs 中的用例理解各生命周期钩子的默认行为再按需实现自己的上下文提供器并挂载到AgentThread.AIContextProviders上。【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表