From 5ff707947089443797c27b44f2fd73210a4de79e Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 2 May 2025 20:02:37 -0500 Subject: [PATCH] Optimize InstructLoop --- .../Routing/IRoutingService.cs | 4 +-- .../ConversationService.SendMessage.cs | 26 +++++++-------- .../BotSharp.Core/Routing/RoutingContext.cs | 2 +- .../Routing/RoutingService.InstructLoop.cs | 4 +-- .../BotSharp.Core/Routing/RoutingService.cs | 33 +++++++++++-------- .../Templating/TemplateRender.cs | 2 +- .../BotSharp.Logger/Hooks/VerboseLogHook.cs | 6 ++-- .../Controllers/TwilioInboundController.cs | 24 +++++++++++++- .../Models/ConversationalVoiceRequest.cs | 5 ++- 9 files changed, 67 insertions(+), 39 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index d98dd495..97be8874 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -32,7 +32,7 @@ public interface IRoutingService Task InvokeAgent(string agentId, List dialogs); Task InvokeFunction(string name, RoleDialogModel messages); - Task InstructLoop(RoleDialogModel message, List dialogs); + Task InstructLoop(Agent agent, RoleDialogModel message, List dialogs); /// /// Talk to a specific Agent directly, bypassing the Router @@ -40,7 +40,7 @@ public interface IRoutingService /// /// /// - Task InstructDirect(Agent agent, RoleDialogModel message); + Task InstructDirect(Agent agent, RoleDialogModel message, List dialogs); Task GetConversationContent(List dialogs, int maxDialogCount = 100); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 6a844c41..5ec1ae76 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -38,17 +38,6 @@ public partial class ConversationService var routing = _services.GetRequiredService(); routing.Context.SetMessageId(_conversationId, message.MessageId); - // Check the routing mode - var states = _services.GetRequiredService(); - var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard"); - routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false); - - if (routingMode == "lazy") - { - message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId); - routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false); - } - // Save payload in order to assign the payload before hook is invoked if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload)) { @@ -91,11 +80,22 @@ public partial class ConversationService if (agent.Type == AgentType.Routing) { - response = await routing.InstructLoop(message, dialogs); + // Check the routing mode + var states = _services.GetRequiredService(); + var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager"); + routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false); + + if (routingMode == "lazy") + { + message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId); + routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false); + } + + response = await routing.InstructLoop(agent, message, dialogs); } else { - response = await routing.InstructDirect(agent, message); + response = await routing.InstructDirect(agent, message, dialogs); } routing.Context.ResetRecursiveCounter(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 4cba2e2d..1c4ca518 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -294,7 +294,7 @@ public class RoutingContext : IRoutingContext // Set next handling agent for lazy routing mode var states = _services.GetRequiredService(); - var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard"); + var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager"); if (routingMode == "lazy") { var agentId = GetCurrentAgentId(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs index 20aa16b6..76cf5047 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs @@ -7,7 +7,7 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { - public async Task InstructLoop(RoleDialogModel message, List dialogs) + public async Task InstructLoop(Agent agent, RoleDialogModel message, List dialogs) { RoleDialogModel response = default; @@ -15,8 +15,6 @@ public partial class RoutingService var convService = _services.GetRequiredService(); var storage = _services.GetRequiredService(); - _router = await agentService.LoadAgent(message.CurrentAgentId); - var states = _services.GetRequiredService(); var executor = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 253e7b54..3e45fd63 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -26,31 +26,36 @@ public partial class RoutingService : IRoutingService _logger = logger; } - public async Task InstructDirect(Agent agent, RoleDialogModel message) + public async Task InstructDirect(Agent agent, RoleDialogModel message, List dialogs) { var conv = _services.GetRequiredService(); var storage = _services.GetRequiredService(); storage.Append(conv.ConversationId, message); - var dialogs = conv.GetDialogHistory(); + dialogs.Add(message); Context.SetDialogs(dialogs); - var inst = new FunctionCallFromLlm - { - Function = "route_to_agent", - Question = message.Content, - NextActionReason = message.Content, - AgentName = agent.Name, - OriginalAgent = agent.Name, - ExecutingDirectly = true - }; + var routing = _services.GetRequiredService(); + routing.Context.Push(agent.Id, "instruct directly"); + var agentId = routing.Context.GetCurrentAgentId(); - message.Instruction = inst; - var result = await InvokeFunction("route_to_agent", message); + // Update next action agent's name + var agentService = _services.GetRequiredService(); + + if (agent.Disabled) + { + var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; + + message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content); + dialogs.Add(message); + } + else + { + var ret = await routing.InvokeAgent(agentId, dialogs); + } var response = dialogs.Last(); response.MessageId = message.MessageId; - response.Instruction = inst; return response; } diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index c16a8c4b..24e5d8e2 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -44,7 +44,7 @@ public class TemplateRender : ITemplateRender } else { - _logger.LogWarning(error); + _logger.LogError(error); return template; } } diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs index fb3c37a2..391db1a9 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/VerboseLogHook.cs @@ -27,7 +27,7 @@ public class VerboseLogHook : IContentGeneratingHook if (dialog != null) { var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>"; - _logger.LogInformation(log); + _logger.LogDebug(log); } await Task.CompletedTask; @@ -44,7 +44,7 @@ public class VerboseLogHook : IContentGeneratingHook $"[{agent?.Name}]: {message.Indication} {message.FunctionName}({message.FunctionArgs})" : $"[{agent?.Name}]: {message.Content}" + $" <== [msg_id: {message.MessageId}]"; - _logger.LogInformation(tokenStats.Prompt); - _logger.LogInformation(log); + _logger.LogDebug(tokenStats.Prompt); + _logger.LogDebug(log); } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs index e82908a0..74134a97 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioInboundController.cs @@ -146,7 +146,7 @@ public class TwilioInboundController : TwilioController AgentId = request.AgentId, Channel = ConversationChannel.Phone, ChannelId = request.CallSid, - Title = $"Incoming phone call from {request.From}", + Title = request.Intent ?? $"Incoming phone call from {request.From}", Tags = [], }; @@ -161,6 +161,15 @@ public class TwilioInboundController : TwilioController new("twilio_call_sid", request.CallSid), }; + var requestStates = ParseStates(request.States); + foreach (var s in requestStates) + { + if (!states.Any(x => x.Key == s.Key)) + { + states.Add(new MessageState(s.Key, s.Value)); + } + } + if (request.InitAudioFile != null) { states.Add(new("init_audio_file", request.InitAudioFile)); @@ -173,7 +182,20 @@ public class TwilioInboundController : TwilioController { states.Add(new(StateConst.ROUTING_MODE, agent.Mode)); } + convService.SetConversationId(conversation.Id, states); + + if (!string.IsNullOrEmpty(request.Intent)) + { + var storage = _services.GetRequiredService(); + + storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent) + { + CurrentAgentId = conversation.Id, + CreatedAt = DateTime.UtcNow + }); + } + convService.SaveStates(); // reload agent rendering with states diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs index df08df0a..344ddc06 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/ConversationalVoiceRequest.cs @@ -17,7 +17,10 @@ public class ConversationalVoiceRequest : VoiceRequest public int AIResponseWaitTime { get; set; } = 0; public string? AIResponseErrorMessage { get; set; } = string.Empty; - public string Intent { get; set; } = string.Empty; + /// + /// Initial intent when incoming call connected + /// + public string? Intent { get; set; } [FromQuery(Name = "init-audio-file")] public string? InitAudioFile { get; set; }