From 7f42bc729697ff8e3ac5f32ab28bd12b7ed75982 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 14 Aug 2025 10:47:07 -0500 Subject: [PATCH] refine function filtering --- .../Agents/IAgentService.cs | 3 ++ .../Agents/Services/AgentService.Rendering.cs | 34 +++++++++++++------ .../Providers/ChatCompletionProvider.cs | 13 +++---- .../Providers/Chat/ChatCompletionProvider.cs | 6 ++-- .../Providers/Chat/ChatCompletionProvider.cs | 10 +++--- .../Chat/GeminiChatCompletionProvider.cs | 12 +++---- .../Chat/PalmChatCompletionProvider.cs | 17 ++++------ .../Realtime/RealTimeCompletionProvider.cs | 12 +++---- .../Providers/ChatCompletionProvider.cs | 6 ++-- ...osoftExtensionsAIChatCompletionProvider.cs | 11 +++--- .../Providers/Chat/ChatCompletionProvider.cs | 13 +++---- .../Realtime/RealTimeCompletionProvider.cs | 7 ++-- .../Providers/ChatCompletionProvider.cs | 10 +++--- .../Core/TestAgentService.cs | 10 ++++++ 14 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index e8fc01e3..e2905976 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -37,10 +37,13 @@ public interface IAgentService FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def); + (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null); IEnumerable FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null); + IEnumerable FilterFunctions(string instruction, IEnumerable functions, StringComparer? comparer = null); bool RenderVisibility(string? visibilityExpression, Dictionary dict); + /// /// Get agent detail without trigger any hook. /// diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index ca93e201..9675c5e6 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -13,7 +13,7 @@ public partial class AgentService var conv = _services.GetRequiredService(); // merge instructions - var instructions = new List { agent.Instruction }; + var instructions = new List { agent.Instruction ?? string.Empty }; var secondaryInstructions = agent.SecondaryInstructions?.Where(x => !string.IsNullOrWhiteSpace(x)).ToList() ?? []; instructions.AddRange(secondaryInstructions); @@ -107,22 +107,36 @@ public partial class AgentService return parameterDef; } - public IEnumerable FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null) + public (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null) { - var functions = agent.Functions.AsEnumerable(); - - if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction)) + var text = string.Empty; + if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) { - comparer = comparer ?? StringComparer.OrdinalIgnoreCase; - var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_]+\b"); - var words = new HashSet(matches.Select(m => m.Value), comparer); - functions = functions.Where(x => words.Contains(x.Name, comparer)); + text = RenderInstruction(agent); } - functions = functions.Concat(agent.SecondaryFunctions ?? []); + var functions = FilterFunctions(text, agent, comparer); + return (text, functions); + } + + public IEnumerable FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null) + { + var functions = agent.Functions.Concat(agent.SecondaryFunctions ?? []); + if (agent.FuncVisMode.IsEqualTo(AgentFuncVisMode.Auto) && !string.IsNullOrWhiteSpace(instruction)) + { + functions = FilterFunctions(instruction, functions, comparer); + } return functions; } + public IEnumerable FilterFunctions(string instruction, IEnumerable functions, StringComparer? comparer = null) + { + comparer = comparer ?? StringComparer.OrdinalIgnoreCase; + var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b"); + var words = new HashSet(matches.Select(m => m.Value), comparer); + return functions.Where(x => words.Contains(x.Name, comparer)); + } + public string RenderTemplate(Agent agent, string templateName) { var conv = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index f2cef397..31c1e230 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -104,16 +104,14 @@ public class ChatCompletionProvider : IChatCompletion private (string, MessageParameters) PrepareOptions(Agent agent, List conversations, LlmModelSetting settings) { - var instruction = ""; + var agentService = _services.GetRequiredService(); renderedInstructions = []; - var agentService = _services.GetRequiredService(); - - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var text = agentService.RenderInstruction(agent); - instruction += text; - renderedInstructions.Add(text); + renderedInstructions.Add(instruction); } /*var routing = _services.GetRequiredService(); @@ -211,7 +209,6 @@ public class ChatCompletionProvider : IChatCompletion ReferenceHandler = ReferenceHandler.IgnoreCycles, }; - var functions = agentService.FilterFunctions(instruction, agent); foreach (var fn in functions) { /*var inputschema = new InputSchema() diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs index a213176b..697ad604 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -359,14 +359,14 @@ public class ChatCompletionProvider : IChatCompletion MaxOutputTokenCount = maxTokens }; - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var instruction = agentService.RenderInstruction(agent); renderedInstructions.Add(instruction); messages.Add(new SystemChatMessage(instruction)); } - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { if (!agentService.RenderFunction(agent, function)) continue; diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs index 600ab7ba..ed0908cf 100644 --- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs @@ -331,14 +331,14 @@ public class ChatCompletionProvider : IChatCompletion MaxOutputTokenCount = maxTokens }; - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var text = agentService.RenderInstruction(agent); - renderedInstructions.Add(text); - messages.Add(new SystemChatMessage(text)); + renderedInstructions.Add(instruction); + messages.Add(new SystemChatMessage(instruction)); } - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { if (!agentService.RenderFunction(agent, function)) continue; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs index fe6c04c2..c65f5870 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs @@ -188,21 +188,21 @@ public class GeminiChatCompletionProvider : IChatCompletion AutoCallFunction = false }; - // Assembly messages + // Assemble messages var contents = new List(); var tools = new List(); var funcDeclarations = new List(); - var systemPrompts = new List(); - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + var funcPrompts = new List(); + + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var instruction = agentService.RenderInstruction(agent); renderedInstructions.Add(instruction); systemPrompts.Add(instruction); } - var funcPrompts = new List(); - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { if (!agentService.RenderFunction(agent, function)) continue; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs index 57f1c8bf..509ed5ba 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs @@ -96,23 +96,20 @@ public class PalmChatCompletionProvider : IChatCompletion private (string, List, bool) PrepareOptions(Agent agent, List conversations) { - var prompt = ""; - var agentService = _services.GetRequiredService(); - - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) - { - prompt += agentService.RenderInstruction(agent); - renderedInstructions.Add(prompt); - } - var routing = _services.GetRequiredService(); var router = routing.Router; + // Prepare instruction and functions + var (prompt, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(prompt)) + { + renderedInstructions.Add(prompt); + } + var messages = conversations.Select(c => new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI")) .ToList(); - var functions = agentService.FilterFunctions(prompt, agent); if (!functions.IsNullOrEmpty()) { prompt += "\r\n\r\n[Functions] defined in JSON Schema:\r\n"; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs index 5a402e34..b8501af8 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -484,21 +484,21 @@ public class GoogleRealTimeProvider : IRealTimeCompletion var googleSettings = _settings; renderedInstructions = []; - // Assembly messages + // Assemble messages var contents = new List(); var tools = new List(); var funcDeclarations = new List(); - var systemPrompts = new List(); - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + var funcPrompts = new List(); + + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var instruction = agentService.RenderInstruction(agent); renderedInstructions.Add(instruction); systemPrompts.Add(instruction); } - var funcPrompts = new List(); - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { if (!agentService.RenderFunction(agent, function)) continue; diff --git a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs index bf8045b9..20f59db2 100644 --- a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs @@ -93,9 +93,10 @@ public class ChatCompletionProvider : IChatCompletion List toolcalls = new List(); renderedInstructions = []; - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var instruction = agentService.RenderInstruction(agent); renderedInstructions.Add(instruction); messages.Add(new MessageItem("system", instruction)); } @@ -113,7 +114,6 @@ public class ChatCompletionProvider : IChatCompletion new MessageItem("assistant", message.Content)); } - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { var functionTool = ConvertToFunctionTool(function); diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs index a0e7ef86..3394b514 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using static System.Net.Mime.MediaTypeNames; namespace BotSharp.Plugin.MicrosoftExtensionsAI; @@ -71,14 +72,14 @@ public sealed class MicrosoftExtensionsAIChatCompletionProvider : IChatCompletio List messages = []; var agentService = _services.GetRequiredService(); - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var text = agentService.RenderInstruction(agent); - renderedInstructions.Add(text); - messages.Add(new(ChatRole.System, text)); + renderedInstructions.Add(instruction); + messages.Add(new(ChatRole.System, instruction)); } - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); foreach (var function in functions) { if (agentService.RenderFunction(agent, function)) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index 1940eb03..5a1c4a37 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -321,17 +321,14 @@ public class ChatCompletionProvider : IChatCompletion var messages = new List(); var options = InitChatCompletionOption(agent); - // Render instructions - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var text = agentService.RenderInstruction(agent); - renderedInstructions.Add(text); - messages.Add(new SystemChatMessage(text)); + renderedInstructions.Add(instruction); + messages.Add(new SystemChatMessage(instruction)); } - // Filter functions - var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); - // Render functions foreach (var function in functions) { diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 8ae4227f..d8a59727 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -555,7 +555,6 @@ public class RealTimeCompletionProvider : IRealTimeCompletion var settings = settingsService.GetSetting(Provider, _model); var allowMultiModal = settings != null && settings.MultiModal; - var instruction = string.Empty; var messages = new List(); var temperature = float.Parse(state.GetState("temperature", "0.0")); @@ -569,13 +568,13 @@ public class RealTimeCompletionProvider : IRealTimeCompletion MaxOutputTokenCount = maxTokens }; - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - instruction = agentService.RenderInstruction(agent); messages.Add(new SystemChatMessage(instruction)); } - var functions = agentService.FilterFunctions(instruction, agent); foreach (var function in functions) { if (!agentService.RenderFunction(agent, function)) continue; diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs index 2180af7a..b8191c56 100644 --- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs @@ -234,16 +234,19 @@ public class ChatCompletionProvider : IChatCompletion var messages = new List(); renderedInstructions = []; - if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty()) + // Prepare instruction and functions + var (instruction, agentFunctions) = agentService.PrepareInstructionAndFunctions(agent); + if (!string.IsNullOrWhiteSpace(instruction)) { - var instruction = agentService.RenderInstruction(agent); renderedInstructions.Add(instruction); messages.Add(ChatMessage.FromSystem(instruction)); } + if (!string.IsNullOrEmpty(agent.Knowledges)) { messages.Add(ChatMessage.FromSystem(agent.Knowledges)); } + var samples = ProviderHelper.GetChatSamples(agent.Samples); foreach (var message in samples) { @@ -252,8 +255,7 @@ public class ChatCompletionProvider : IChatCompletion ChatMessage.FromAssistant(message.Content)); } - var agentFuncs = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent); - foreach (var function in agentFuncs) + foreach (var function in agentFunctions) { functions.Add(ConvertToFunctionDef(function)); } diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs index 6fac9271..d46b7ca4 100644 --- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs +++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs @@ -56,11 +56,21 @@ namespace BotSharp.Plugin.Google.Core return true; } + public (string, IEnumerable) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null) + { + return (string.Empty, []); + } + public IEnumerable FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null) { return []; } + public IEnumerable FilterFunctions(string instruction, IEnumerable functions, StringComparer? comparer = null) + { + return []; + } + public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def) { return def.Parameters;