refine function filtering

This commit is contained in:
Jicheng Lu 2025-08-14 10:47:07 -05:00
parent 938656b1f8
commit 7f42bc7296
14 changed files with 92 additions and 72 deletions

View file

@ -37,10 +37,13 @@ public interface IAgentService
FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def);
(string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null);
IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null);
IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null);
bool RenderVisibility(string? visibilityExpression, Dictionary<string, object> dict);
/// <summary>
/// Get agent detail without trigger any hook.
/// </summary>

View file

@ -13,7 +13,7 @@ public partial class AgentService
var conv = _services.GetRequiredService<IConversationService>();
// merge instructions
var instructions = new List<string> { agent.Instruction };
var instructions = new List<string> { 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<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
public (string, IEnumerable<FunctionDef>) 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<string>(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<FunctionDef> 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<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
{
comparer = comparer ?? StringComparer.OrdinalIgnoreCase;
var matches = Regex.Matches(instruction, @"\b[A-Za-z0-9_-]+\b");
var words = new HashSet<string>(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<IConversationService>();

View file

@ -104,16 +104,14 @@ public class ChatCompletionProvider : IChatCompletion
private (string, MessageParameters) PrepareOptions(Agent agent, List<RoleDialogModel> conversations,
LlmModelSetting settings)
{
var instruction = "";
var agentService = _services.GetRequiredService<IAgentService>();
renderedInstructions = [];
var agentService = _services.GetRequiredService<IAgentService>();
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<IRoutingService>();
@ -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()

View file

@ -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;

View file

@ -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;

View file

@ -188,21 +188,21 @@ public class GeminiChatCompletionProvider : IChatCompletion
AutoCallFunction = false
};
// Assembly messages
// Assemble messages
var contents = new List<Content>();
var tools = new List<Tool>();
var funcDeclarations = new List<FunctionDeclaration>();
var systemPrompts = new List<string>();
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
var funcPrompts = new List<string>();
// 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<string>();
var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent);
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;

View file

@ -96,23 +96,20 @@ public class PalmChatCompletionProvider : IChatCompletion
private (string, List<PalmChatMessage>, bool) PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
{
var prompt = "";
var agentService = _services.GetRequiredService<IAgentService>();
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
{
prompt += agentService.RenderInstruction(agent);
renderedInstructions.Add(prompt);
}
var routing = _services.GetRequiredService<IRoutingService>();
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";

View file

@ -484,21 +484,21 @@ public class GoogleRealTimeProvider : IRealTimeCompletion
var googleSettings = _settings;
renderedInstructions = [];
// Assembly messages
// Assemble messages
var contents = new List<Content>();
var tools = new List<Tool>();
var funcDeclarations = new List<FunctionDeclaration>();
var systemPrompts = new List<string>();
if (!string.IsNullOrEmpty(agent.Instruction) || !agent.SecondaryInstructions.IsNullOrEmpty())
var funcPrompts = new List<string>();
// 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<string>();
var functions = agentService.FilterFunctions(renderedInstructions.FirstOrDefault(), agent);
foreach (var function in functions)
{
if (!agentService.RenderFunction(agent, function)) continue;

View file

@ -93,9 +93,10 @@ public class ChatCompletionProvider : IChatCompletion
List<FunctionTool> toolcalls = new List<FunctionTool>();
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);

View file

@ -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<ChatMessage> messages = [];
var agentService = _services.GetRequiredService<IAgentService>();
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))

View file

@ -321,17 +321,14 @@ public class ChatCompletionProvider : IChatCompletion
var messages = new List<ChatMessage>();
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)
{

View file

@ -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<ChatMessage>();
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;

View file

@ -234,16 +234,19 @@ public class ChatCompletionProvider : IChatCompletion
var messages = new List<ChatMessage>();
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));
}

View file

@ -56,11 +56,21 @@ namespace BotSharp.Plugin.Google.Core
return true;
}
public (string, IEnumerable<FunctionDef>) PrepareInstructionAndFunctions(Agent agent, StringComparer? comparer = null)
{
return (string.Empty, []);
}
public IEnumerable<FunctionDef> FilterFunctions(string instruction, Agent agent, StringComparer? comparer = null)
{
return [];
}
public IEnumerable<FunctionDef> FilterFunctions(string instruction, IEnumerable<FunctionDef> functions, StringComparer? comparer = null)
{
return [];
}
public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def)
{
return def.Parameters;