拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Semantic Kernel 基于上下文的函数选择(Context-Based Function Selection)设计解析:从全量广告到动态检索的四种候选方案与最终决策

Semantic Kernel 基于上下文的函数选择Context-Based Function Selection设计解析从全量广告到动态检索的四种候选方案与最终决策【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel导读当应用注册的函数数量变多时把所有函数一次性广告advertise给 AI 模型会导致模型选择困难、误调无关函数甚至整个场景失败。本文以 0072-context-based-function-selection.md 这份已接受的架构决策记录ADR为骨架逐条剖析 Semantic KernelSK为基于对话上下文动态挑选要广告给模型的函数所评估的四种候选方案及其变体并结合仓库内 PluginSelectionWithFilters.cs 示例与 FunctionChoiceBehavior.cs 源码说明每种方案的接线方式、适用范围与最终决策结果。读完你将理解为什么最终选择通过AIContextBehavior为 Agent 实现基于 RAG 的函数选择以及 Option 2M.E.AI ChatClient 装饰器为何被留作后续扩展路径。背景与问题陈述Semantic Kernel 目前会向 AI 模型广告全部函数无论这些函数来自已注册的插件还是通过配置函数选择行为Function Choice Behavior时直接提供。当函数数量不多时这种做法工作良好模型可以轻松选出正确的函数。但当可用的函数非常多时AI 模型会难以在众多候选中挑出合适的函数导致模型陷入选择困难产生困惑调用与当前上下文或对话无关的函数整个场景scenario失败。这份 ADR 的目标是为 SK Agent、聊天补全服务chat completion services以及 M.E.AI 聊天客户端chat clients这三大类组件评估基于上下文的函数选择与广告机制的可行方案。决策驱动力Decision Drivers方案必须满足三个核心约束动态性能够根据对话上下文动态决定要广告哪些函数无缝集成能与 SK 和 M.E.AI 的 AI 连接器connectors以及 SK Agent 顺畅集成低侵入在获取上下文和函数时不需要复杂的管道plumbing。明确排除的范围Out of ScopeADR 明确不讨论函数选择算法的具体实现——无论是 RAG 还是其他检索算法都不在本次决策范围内。本文后续所有代码示例中出现的向量检索都只是演示如何用 RAG 充当选择算法而非决策本身的一部分。Option 1外部向量化与搜索External Vectorization and Search这是最手工但也最通用的方案完全在 SK 之外编排函数向量化 → 语义搜索 → 函数广告三步然后把筛选出的相关函数通过FunctionChoiceBehavior注入执行设置。ADR 中给出的核心流程如下// Register services IKernelBuilder builder Kernel.CreateBuilder(); builder.Services.AddInMemoryVectorStore(); builder.Services.AddSingletonIFunctionProvider, FunctionProvider(); builder.Services.AddSingletonIPluginStore, PluginStore(); // Register plugins Kernel kernel builder.Build(); kernel.ImportPluginFromTypeTimePlugin(); kernel.ImportPluginFromTypeWeatherPlugin(); // Vectorize all functions in the kernel IPluginStore pluginStore kernel.GetRequiredServiceIPluginStore(); await pluginStore.SaveAsync(collectionName: functions, kernel.Plugins); const string Prompt Provide latest headlines; // Do RAG to find the relevant function for the prompt IFunctionProvider functionProvider kernel.GetRequiredServiceIFunctionProvider(); KernelFunction[] relevantFunctions await functionProvider.GetRelevantFunctionsAsync(collectionName: functions, Prompt, kernel.Plugins, numberOfFunctions: 1); // Set the relevant functions to be advertised to the AI model executionSettings.FunctionChoiceBehavior FunctionChoiceBehavior.Auto(relevantFunctions); // Do the chat completion var chatHistory new ChatHistory(); chatHistory.AddUserMessage(Prompt); var chatCompletionService kernel.GetRequiredServiceIChatCompletionService(); var result await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings, kernel); Console.WriteLine(result);仓库内的完整落地实现ADR 引用的示例在仓库中真实存在dotnet/samples/Concepts/Optimization/PluginSelectionWithFilters.cs。示例比 ADR 中的伪代码更完整值得关注以下几个实现细节PluginStore.SaveAsync将内核中每个函数编码为文本Plugin name: {PluginName}. Function name: {Name}. Description: {Description}用IEmbeddingGeneratorstring, Embeddingfloat生成向量后以函数键插件名-函数名作为主键 upsert 进向量集合见FunctionRecord其向量维度为 1536FunctionProvider.GetBestFunctionsAsync先为用户请求生成 embedding再在集合上执行SearchAsync(requestEmbedding, top: numberOfBestFunctions)得到最相似的函数键最后在内核已注册插件中反查并返回对应的KernelFunction实例调用方式先await pluginStore.SaveAsync(CollectionName, kernel.Plugins)完成向量化可只做一次复用再对每个请求执行检索效果对比示例中同一请求Provide latest headlines无过滤器时全部函数共享给模型约消耗 250 tokens启用选择后只共享 1 个函数约 150 tokens——这是控制 token 成本的最直观收益。调用粒度该方案按操作operation粒度调用而非按AI 模型请求粒度一次操作调用在模型进行函数调用function calling时可能引发多次 AI 模型请求。优点可用于所有 AI 组件——SK 聊天补全服务、SK Agent、M.E.AI 聊天客户端都能用。缺点各部分函数向量化、函数搜索、函数广告需要复杂地集成在一起不支持在提示词模板prompt templates中配置的函数选择行为。Option 1A函数调用过滤器Function Invocation Filter这是 Option 1 的变体向量化部分完全一致但函数选择部分改由**函数调用过滤器function invocation filter**承担。过滤器拦截对InvokePromptAsync的调用识别与提示词相关的函数并通过执行设置将其设为要向模型广告的内容。// Register services IKernelBuilder builder Kernel.CreateBuilder(); builder.Services.AddInMemoryVectorStore(); builder.Services.AddSingletonIFunctionProvider, FunctionProvider(); builder.Services.AddSingletonIPluginStore, PluginStore(); // Register plugins Kernel kernel builder.Build(); kernel.ImportPluginFromTypeTimePlugin(); kernel.ImportPluginFromTypeWeatherPlugin(); // Vectorize all functions in the kernel IPluginStore pluginStore kernel.GetRequiredServiceIPluginStore(); await pluginStore.SaveAsync(collectionName: functions, kernel.Plugins); // Register function invocation filter IFunctionProvider functionProvider kernel.GetRequiredServiceIFunctionProvider(); kernel.FunctionInvocationFilters.Add(new PluginSelectionFilter(functionProvider: functionProvider, collectionName: functions)); // Do the chat completion KernelArguments kernelArguments new(executionSettings) { [Request] Provide latest headlines }; await kernel.InvokePromptAsync({{$Request}}, kernelArguments); // Function invocation filter class PluginSelectionFilter(IFunctionProvider functionProvider, string collectionName) { public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, FuncFunctionInvocationContext, Task next) { string request context.Arguments[Request]; if (context.Function.Name.Contains(nameof(KernelExtensions.InvokePromptAsync)) !string.IsNullOrWhiteSpace(request)) { var functions await functionProvider.GetRelevantFunctionsAsync(collectionName, request, plugins, numberOfFunctions); context.Arguments.ExecutionSettings.FunctionChoiceBehavior FunctionChoiceBehavior.Auto(functions); } await next(context); } }仓库示例中的PluginSelectionFilter实现与 ADR 一致仅当被触发的是InvokePromptAsync且参数Request非空时才执行函数检索并改写OpenAIPromptExecutionSettings.FunctionChoiceBehavior见 PluginSelectionWithFilters.cs 中 PluginSelectionFilter 的实现。优点无ADR 中该选项的 Pros 一栏为空。缺点强依赖InvokePromptAsync入口除使用kernel.InvokePromptAsync的场景外全部不可用不支持提示词模板中配置的函数选择行为。Option 2M.E.AI ChatClient 装饰器Decorator该方案假设存在一个M.E.AI.IChatClient接口的实现例如ContextFunctionSelectorChatClient类它会对GetResponseAsync/GetResponseStreamAsync的ChatOptions参数中的全部函数做向量化再基于传入的聊天消息列表即上下文检索相关函数public class ContextFunctionSelectorChatClient : DelegatingChatClient { protected ContextFunctionSelectorChatClient(IChatClient innerClient) : base(innerClient) { } public override async TaskChatResponse GetResponseAsync(IEnumerableChatMessage messages, ChatOptions? options null) { ChatOptions? targetOptions options; if (options?.Tools?.Any() ?? false) { targetOptions options.Clone(); AITool[] functionsToAdvertise await this.GetRelevantFunctions(options, messages).ConfigureAwait(false); targetOptions.Tools functionsToAdvertise; } return await base.GetResponseAsync(messages, targetOptions, ct).ConfigureAwait(false); } private async TaskAITool[] GetRelevantFunctions(ChatOptions options, IEnumerableChatMessage messages) { // 1. Vectorize all the functions form the options.Tool collection, if not already vectorized. // 2. Vectorize the context represented by the messages collection. // 3. Search for and return the most relevant functions using the vectorized context. } public override IAsyncEnumerableChatResponseUpdate GetStreamingResponseAsync(IEnumerableChatMessage messages, ChatOptions? options null) { // similar to GetResponseAsync, but for streaming } }它的核心技巧是克隆ChatOptions不修改调用方传入的原始 options而是在克隆体上把Tools替换为检索出的相关函数再交给内层 client。装饰器的两种接法如下// Usage with M.E.AI chat client ChatClient chatClient new(model, api-key); IChatClient client chatClient.AsIChatClient() .AsBuilder() .UseFunctionInvocation() .UseContextFunctionSelector() .Build(); // Usage with SK chat completion service IChatCompletionService chatCompletionService new OpenAIChatCompletionService(model-id, api-key); IChatClient client chatCompletionService.AsChatClient() .AsBuilder() .UseContextFunctionSelector() .Build();装饰器同样按操作粒度调用。优点与 SK 聊天补全服务和 M.E.AI 聊天客户端无缝配合接线方式与 M.E.AI 的初始化模式一致非常轻量无需新增抽象易于添加新的函数选择器并且可以链式组合多个选择器。缺点仅适用于聊天补全型 Agent不适用于不使用聊天补全服务的 SK Agent不支持提示词模板中配置的函数选择行为。Option 3函数广告过滤器Function Advertisement Filter该方案引入一种新的过滤器类型——函数广告过滤器专门负责基于对话上下文选择要广告给模型的相关函数// Register plugins Kernel kernel new Kernel(); kernel.ImportPluginFromTypeTimePlugin(); kernel.ImportPluginFromTypeWeatherPlugin(); // Register function advertisement filter kernel.FunctionAdvertisementFilters.Add(new ContextFunctionSelectorFilter()); // Do the chat completion await kernel.InvokePromptAsync(Provide latest headlines); // Function invocation filter class ContextFunctionSelectorFilter() { public async Task OnFunctionsAdvertisementAsync(FunctionAdvertisementContext context, FuncFunctionAdvertisementContext, Task next); { // 1. Vectorize all the functions form the context.Functions collection, if not already vectorized. // 2. Vectorize the context represented by the context.ChatHistory collection. // 3. Search for and assign the most relevant functions using the vectorized context to context.Functions property. } }与 Option 1/1A/2 不同该过滤器既可以按操作粒度调用也可以按AI 模型请求粒度调用——这意味着即使一次操作引发多次模型请求模型多次执行函数调用每次请求前都能重新按最新上下文选择函数。优点对 SK 用户而言是熟悉的概念filter 机制适用于聊天补全服务同时适用于聊天补全型与非聊天补全型的SKAgent——前提是它们能把上下文提供给过滤器。缺点需要新增抽象需要扩展Kernel的公开 API 面所有 AI 组件SK Agent、聊天补全服务、M.E.AI 聊天客户端适配器都需要更新以触发该过滤器。Option 4FunctionChoiceBehavior 回调Callback该方案扩展现有的AutoFunctionChoiceBehavior、RequiredFunctionChoiceBehavior和NoneFunctionChoiceBehavior三个类新增一个接收**函数选择器function selector**参数的构造函数由选择器基于上下文决定要向模型广告哪些函数// Register services IKernelBuilder builder Kernel.CreateBuilder(); builder.Services.AddInMemoryVectorStore(); builder.Services.AddSingletonIFunctionProvider, FunctionProvider(); builder.Services.AddSingletonIPluginStore, PluginStore(); // Register plugins Kernel kernel builder.Build(); kernel.ImportPluginFromTypeTimePlugin(); kernel.ImportPluginFromTypeWeatherPlugin(); // Set the relevant functions to be advertised to the AI model executionSettings.FunctionChoiceBehavior FunctionChoiceBehavior.Auto(FunctionSelector); // Do the chat completion var chatHistory new ChatHistory(); chatHistory.AddUserMessage(Provide latest headlines); var chatCompletionService kernel.GetRequiredServiceIChatCompletionService(); var result await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings, kernel); Console.WriteLine(result); async TaskIListKernelFunction FunctionSelector(FunctionChoiceBehaviorConfigurationContext context) { // Vectorize all the functions form the context.Functions collection IPluginStore pluginStore context.Kernel.GetRequiredServiceIPluginStore(); await pluginStore.SaveAsync(collectionName: functions, context.Kernel.Plugins); // Search for the most relevant functions using the vectorized context IFunctionProvider functionProvider kernel.GetRequiredServiceIFunctionProvider(); IListKernelFunction relevantFunctions await functionProvider.GetRelevantFunctionsAsync(collectionName: functions, context.ChatHistory, kernel.Plugins, numberOfFunctions: 1); return relevantFunctions; }从 FunctionChoiceBehavior.cs 的基类源码可以看到FunctionChoiceBehavior.Auto(...)/Required(...)/None(...)工厂方法都支持传入函数集合且GetConfiguration(FunctionChoiceBehaviorConfigurationContext context)是所有行为子类必须实现的核心方法——AI 连接器正是通过它拿到本次应向模型提供哪些函数、是否自动调用的配置。Option 4 的思路就是把这个配置过程从静态传入函数列表升级为通过回调按上下文动态计算。该方案同样可按操作或按模型请求粒度调用。优点无ADR 中该选项的 Pros 一栏为空。缺点不支持提示词模板中配置的函数选择行为只能被使用FunctionChoiceBehavior的组件使用SK 聊天补全服务和聊天补全型 Agent。方案适用性总览ADR 用一张表总结了五种方案对不同组件的适用性Scope列中 Operation 表示按操作粒度、Op Req 表示同时支持按操作和按模型请求粒度OptionScopeOpenAI AzureAI AgentsBedrock AgentChat Completion AgentSK Chat Completion ServiceM.E.AI Chat Client1.External Vectorization SearchOperationYes¹,²Yes¹,³Yes¹,²⁾⁴Yes¹,²⁾⁴Yes¹1A.Function Invocation FilterOperationNo⁵No⁵No⁵No⁵No2.M.E.AI ChatClient DecoratorOperationNoNoYes⁶Yes⁶Yes3.Function Advertisement FilterOp ReqYesNo³YesYesYes⁷4.FunctionChoiceBehavior CallbackOp ReqNo⁸,⁹No⁸YesYesYes⁷各脚注解释了关键限制需要手工编排函数向量化、函数搜索、函数广告、Agent/聊天补全服务的调用需要手动串联。该方案今天即可实现但管道复杂。动态插件注入成本高要为每次 Agent/聊天补全服务调用提供相关函数必须先把内核中所有插件移除再通过kernel.Plugins.AddFromFunctions(dynamicPlugin, [relevantFunctions])注册含相关函数的新插件或者新建内核——但那样也必须新建 Agent 实例。此外相关函数脱离了原插件重新打包可能引入函数名冲突、丢失原插件附带的上下文信息等问题。Bedrock Agent 需每次新建实例因为 Agent 使用的是AgentDefinition.Tools集合中定义的函数而该集合只在 Agent 初始化时生效一次。通过 FunctionChoiceBehavior 注入编排功能需要借助*FunctionChoiceBehavior类的新实例及其functions参数即executionSettings.FunctionChoiceBehavior new AutoFunctionChoiceBehavior(functions)。1A 的致命局限过滤器只有在被kernel.InvokePromptAsync触发时才执行函数选择与广告其他函数调用触发时什么都不做因此除InvokePromptAsync场景外全部不可用。需要适配器M.E.AI Chat Client 需通过 SK 的ChatClientChatCompletionService适配器适配为IChatCompletionService接口。需要装饰器配合M.E.AI Chat Client 需要被装饰装饰器由 SK 提供装饰器才能访问函数广告过滤器/函数选择行为来取得相关函数。Agent 不用 FunctionChoiceBehaviorOpenAI、AzureAI 和 Bedrock Agent 都不使用函数选择行为来广告函数。为它们扩展该机制没有意义因为它们除了 auto 之外不支持任何其他函数选择行为。避免第四个函数来源目前 Agent 的函数可来自三处——agent definition、agent constructor 和 kernel。再为 OpenAI/AzureAI Agent 增加从函数选择行为获取相关函数的第四来源会让开发体验更加混乱。补充说明Notes对于在服务端维护线程thread的 Agent若不先从服务器加载整个线程就无法获取完整上下文——这既不高效也可能不被 Agent 支持。不过Agent 调用时传入的消息本身可能已足够用作函数选择的上下文。与 Agent 记忆Agent Memory的集成AIContextBehaviorADR 的另一个关键部分是函数选择与 0072-agents-with-memory.md 提出的Agent 记忆模型如何衔接。记忆模型由以下类表示public sealed class AIContextPart { public string? Instructions { get; set; } public ListAIFunction AIFunctions { get; set; } new(); } public abstract class AIContextBehavior { public virtual Task OnThreadCreatedAsync(string? threadId, CancellationToken ct) {...} public virtual Task OnNewMessageAsync(string? threadId, ChatMessage newMessage, CancellationToken ct) {...} public virtual Task OnThreadDeleteAsync(string? threadId, CancellationToken ct) {...} public abstract TaskAIContextPart OnModelInvokeAsync(ICollectionChatMessage newMessages, CancellationToken ct); public virtual Task OnSuspendAsync(string? threadId, CancellationToken ct) {...} public virtual Task OnResumeAsync(string? threadId, CancellationToken ct) {...} } public sealed class AIContextBehaviorsManager { public AIContextBehaviorsManager(IEnumerableAIContextBehavior aiContextBehaviors) {...} public void Add(AIContextBehavior aiContextBehavior) {...} public void AddFromServiceProvider(IServiceProvider serviceProvider) {...} public async Task OnThreadCreatedAsync(string? threadId, CancellationToken ct) {...} public async Task OnThreadDeleteAsync(string threadId, CancellationToken ct) {...} public async Task OnNewMessageAsync(string? threadId, ChatMessage newMessage, CancellationToken ct) {...} public async TaskAIContextPart OnModelInvokeAsync(ICollectionChatMessage newMessages, CancellationToken ct) {...} public async Task OnSuspendAsync(string? threadId, CancellationToken ct) {...} public async Task OnResumeAsync(string? threadId, CancellationToken ct) {...} }可以看出AIContextPart同时承载**指令Instructions和函数AIFunctions**两类上下文——这正好为基于上下文的函数选择提供了天然的落点一个AIContextBehavior实现可以在OnModelInvokeAsync时基于消息上下文检索相关函数并通过AIContextPart.AIFunctions把它们提供给模型。示例用法如下// Create a kernel and register plugins Kernel kernel this.CreateKernelWithChatCompletion(); kernel.Plugins.AddFromTypeFinancePlugin(); // Create Mem0Behavior Mem0Behavior mem0Behavior new(...); await mem0Behavior.ClearStoredMemoriesAsync(); // Create a chat completion agent ChatCompletionAgent agent new(kernel, ...); // Create agent thread and add Mem0Behavior to it ChatHistoryAgentThread agentThread new(); agentThread.AIContextBehaviors.Add(mem0Behavior); // Prompt the agent string userMessage Please retrieve my company report; ChatMessageContent message await agent.InvokeAsync(userMessage, agentThread).FirstAsync();ADR 还特别指出当非 Agent 场景如聊天补全服务或聊天客户端也需要复用某个已有的 AI context behavior 来缩小函数列表时有两种做法——把该 behavior 适配到前面某个方案所需的模型或者更好的是复用同一套向量化与语义搜索组件同时实现 AI context behavior 与某个方案所需的模型避免重复造轮子。决策结果Decision Outcome在 ADR 评审会议上最终决策如下优先为 Agent 实现基于上下文的函数选择通过实现一个AIContextBehavior对 Agent 的函数执行 RAG检索增强生成来选择相关函数。后续按需扩展收到请求后再把同样的能力通过Option 2M.E.AI ChatClient 装饰器扩展到聊天补全服务和 M.E.AI 聊天客户端。也就是说首选路径是复用记忆模型中的AIContextBehavior机制Agent 场景这与 Agent 的线程、消息生命周期天然集成扩展路径是 Option 2 的装饰器方案聊天补全服务 / M.E.AI 聊天客户端场景因为它接线轻量、与 M.E.AI 初始化模式一致、无需新增抽象。实践指引如何应用这套机制综合 ADR 与仓库示例落地基于上下文的函数选择可以参考以下路线小规模应用函数数量不多时无需任何选择机制保持默认的FunctionChoiceBehavior.Auto()即可默认把内核全部插件函数提供给模型见 FunctionChoiceBehavior.cs。大规模应用且使用kernel.InvokePromptAsync入口直接参考 PluginSelectionWithFilters.cs 中的UsingVectorSearchWithKernelAsync实现IPluginStore/IFunctionProviderIFunctionInvocationFilter对应 Option 1A并在KernelArguments中传入Request参数。示例代码展示了 token 消耗从约 250 降到约 150 的实际效果。大规模应用且直接使用IChatCompletionService参考同一示例中的UsingVectorSearchWithChatCompletionAsync在调用前手动执行向量化 → 检索 →FunctionChoiceBehavior.Auto(bestFunctions)对应 Option 1。Agent 场景遵循决策结果实现AIContextBehavior并挂到ChatHistoryAgentThread.AIContextBehaviors上让函数选择与线程生命周期、消息通知、挂起/恢复suspend/resume机制协同工作。关注约束无论选择哪种方案都要注意两个共性问题——提示词模板中配置的函数选择行为不受支持按操作粒度调用时一次操作内的多次模型请求可能无法每次都重新选择函数Option 3 与 Option 4 支持按请求粒度调用但代价是需要新增抽象或扩展 API 面。【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门