[MAF预定义Agent中间件-02]MessageAIContextProviderAgent——利用MessageAIContextProvider实现输入增强
作为MAF Agent管道上极其重要的一环注册的AIContextProvider不仅可能通过对请求消息、工具集和系统指令的更新实现对LLM输入的增强还可以通过对LLM返回的结果在加工实现对输出的增强。作为它的子类MessageAIContextProvider主要用来定制请求消息列表原则上它可以添加、修改和替换原始的请求消息但是大部分场景下我们会利用它注入的消息来提供补充的上下文。MessageAIContextProviderAgent这个中间件的作用就是利用指定的MessageAIContextProvider来动态注入请求消息。1. 利用MessageAIContextProviderAgent实现RAGRAG是最典型的动态注入上下文的应用场景。MAF主要利用TextSearchProvider这个MessageAIContextProvider实现RAG。它根据请求检索上下文并根据检索结果创建消息作为注入的上下文。我们可以结合MessageAIContextProviderAgent和TextSearchProvider来实现RAG功能。如下是一个简单的示例在根据OpenAIClient将AIAgent构建出来后我们调用扩展方法AsBuilder生成构建Agent管道的AIAgentBuilder对象然后调用UseAIContextProviders方法注册了MessageAIContextProviderAgent中间件并提供了一个TextSearchProvider对象来提供动态注入的消息。usingdotenv.net;usingMicrosoft.Agents.AI;usingMicrosoft.Extensions.AI;usingOpenAI;usingSystem.ClientModel;usingstaticMicrosoft.Agents.AI.TextSearchProvider;DotEnv.Load();varmodelEnvironment.GetEnvironmentVariable(MODEL)!;varapiKeyEnvironment.GetEnvironmentVariable(API_KEY)!;varopenAIUrlEnvironment.GetEnvironmentVariable(OPENAI_URL)!;vartextSearchProvidernewTextSearchProvider(SearchAsync);varagentnewOpenAIClient(newApiKeyCredential(apiKey),newOpenAIClientOptions{EndpointnewUri(openAIUrl)}).GetChatClient(model:model).AsIChatClient().AsAIAgent().AsBuilder().UseAIContextProviders(providers:textSearchProvider).Build();varresponseawaitagent.RunAsync(2026年斯诺克世界赛冠军是谁);Console.WriteLine(response);staticTaskIEnumerableTextSearchResultSearchAsync(stringquery,CancellationTokencancellationToken){if(query.Contains(2026)query.Contains(斯诺克)query.Contains(世界赛)){varnews 北京时间2026年5月5日英国谢菲尔德克鲁斯堡剧院决胜局最后一颗黑球落袋后00后中国球员吴宜泽挥拳庆祝。18:17吴宜泽击败肖恩·墨菲拿下2026年斯诺克世锦赛冠军。 社交平台上“吴宜泽夺冠”迅速登上热搜不少球迷将这场胜利形容为“中国斯诺克新的接力时刻”。这是继2025年赵心童夺冠之后中国选手再次问鼎这项赛事最高荣誉。这也是在经历2023年前后相关禁赛与争议事件后中国球员重新回到世界顶级竞争序列的重要节点。 相比十多年前丁俊晖在斯诺克领域的单点突破中国近几年开始稳定涌现世界级斯诺克选手。从个体突破到群体崛起这项运动在中国已经进入新的发展阶段。;varresultnewTextSearchResult{RawRepresentationnews,SourceLinkhttps://baijiahao.baidu.com/s?id1864605028122769594,SourceName每日经济新闻,Textnews};returnTask.FromResultIEnumerableTextSearchResult([result]);}else{returnTask.FromResultIEnumerableTextSearchResult([]);}}作为演示我们询问一个LLM目前无法回答的问题2026年斯诺克世界赛冠军是谁。所以我们将这个问题的检索实现在静态方法SearchAsync中为MessageAIContextProviderAgent中间件提供的TextSearchProvider正是利用这个方法对应的委托对象实现上下文检索。所以最终我们能够得到希望的响应结果根据《每日经济新闻》的报道2026年斯诺克世锦赛的冠军是中国“00后”球员**吴宜泽**。 在2026年5月5日的决赛中他在决胜局击败了肖恩·墨菲最终以18比17的比分夺冠。这也是继2025年赵心童夺冠后中国选手连续第二年问鼎这项赛事的最高荣誉。 参考来源《每日经济新闻》([链接](https://baijiahao.baidu.com/s?id1864605028122769594))2. 另一种解法其实这个例子换成如下的编程方式也能达到相同的效果。虽然两种最终都能将检索的上下文提供给LLM但是检索的时机是不同的。前者是请求流经Agent中间件管道时完成的后者时请求流经AIContextProvider管道时完成的。vartextSearchProvidernewTextSearchProvider(SearchAsync);varagentnewOpenAIClient(newApiKeyCredential(apiKey),newOpenAIClientOptions{EndpointnewUri(openAIUrl)}).GetChatClient(model:model).AsIChatClient().AsAIAgent(options:newChatClientAgentOptions{AIContextProviders[textSearchProvider]});3. MessageAIContextProviderAgentMessageAIContextProviderAgent是MAF中的一个内置internalAgent中间件类型我们构建该对象时需要指定一组MessageAIContextProvider。在重写的RunCoreAsync和RunCoreStreamingAsync方法中MessageAIContextProviderAgent会利用这些MessageAIContextProvider根据当前上下文来提供新的消息列表。internalsealedclassMessageAIContextProviderAgent:DelegatingAIAgent{publicMessageAIContextProviderAgent(AIAgentinnerAgent,IReadOnlyListMessageAIContextProviderproviders);protectedoverrideasyncTaskAgentResponseRunCoreAsync(IEnumerableChatMessagemessages,AgentSession?sessionnull,AgentRunOptions?optionsnull,CancellationTokencancellationTokendefault);protectedoverrideasyncIAsyncEnumerableAgentResponseUpdateRunCoreStreamingAsync(IEnumerableChatMessagemessages,AgentSession?sessionnull,AgentRunOptions?optionsnull,[EnumeratorCancellation]CancellationTokencancellationTokendefault);}以重写的RunCoreAsync方法为例它的执行流程如下根据方法提供的消息列表、AgentSession和AgentRunOptions创建一个MessageAIContextProvider.InvokingContext上下文并将其作为参数依次调用每一个MessageAIContextProvider的InvokingAsync方法。最后一个MessageAIContextProvider.InvokingAsync方法返回的就是传递给内层Agent的消息列表。针对内层Agent的RunAsync方法调用结束之后如果调用成功会利用利用自身表示的Agent对象、Session、请求消息和响应消息创建一个MessageAIContextProvider.InvokedContext上下文并将其作为参数依次调用每一个MessageAIContextProvider的InvokedAsync方法。如果调用失败会利用自身表示的Agent对象、Session、请求消息和异常对象创建一个MessageAIContextProvider.InvokedContext上下文并将其作为参数依次调用每一个MessageAIContextProvider的InvokedAsync方法。大部分MessageAIContextProvider类型都是为了向LLM提供额外的上下文这一个目的所以它们并不会重写LLM输出增强的相关方法。RunCoreStreamingAsync方法的执行流程与RunCoreAsync方法类似差异在于它会对内层Agent的RunCoreStreamingAsync方法的返回的异步迭代器进行迭代如果迭代过程中抛出了异常它会利用注册的MessageAIContextProvider来处理异常。4. UseMessageAIContextProvider方法用于注册MessageAIContextProviderAgent中间件的方法UseMessageAIContextProvider并非AIAgentBuilder的扩展方法而是一个实例方法。它接受一个或一组MessageAIContextProvider对象作为参数并将它们封装到一个MessageAIContextProviderAgent对象中注册到Agent管道上。publicsealedclassAIAgentBuilder{publicAIAgentBuilderUseAIContextProviders(paramsMessageAIContextProvider[]providers)Use((innerAgent,_)newMessageAIContextProviderAgent(innerAgent,providers));}