From 96bf9d2fa084068d37d9f1c8a9955d66977cc8e8 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Tue, 5 Mar 2024 11:36:48 -0600 Subject: [PATCH] Add user_goal_description to router. Improve stream log. --- .../Functions/Models/FunctionCallFromLlm.cs | 2 ++ .../Routing/IRoutingHook.cs | 18 +++++++++++++----- .../Routing/Models/RoutingArgs.cs | 8 +++++++- .../Routing/Functions/RouteToAgentFn.cs | 15 ++++----------- .../Handlers/RouteToAgentRoutingHandler.cs | 16 +++++++++++++++- .../Routing/Planning/NaivePlanner.cs | 1 + .../BotSharp.Core/Routing/RoutingContext.cs | 15 +++++++++++++++ .../Hooks/StreamingLogHook.cs | 14 ++++++++++++-- 8 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs index b0276e15..26d9ecef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs @@ -6,6 +6,7 @@ namespace BotSharp.Abstraction.Functions.Models; public class FunctionCallFromLlm : RoutingArgs { [JsonPropertyName("question")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Question { get; set; } [JsonPropertyName("args")] @@ -29,6 +30,7 @@ public class FunctionCallFromLlm : RoutingArgs /// Conversation summary /// [JsonPropertyName("summary")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Summary { get; set; } public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs index 9fb25a63..c60bf4f1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingHook.cs @@ -10,13 +10,21 @@ public interface IRoutingHook /// routing instruction /// message /// - Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message); + Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message) + => Task.CompletedTask; - Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null); + Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message) + => Task.CompletedTask; - Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null); + Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null) + => Task.CompletedTask; - Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null); + Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null) + => Task.CompletedTask; - Task OnAgentQueueEmptied(string agentId, string? reason = null); + Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null) + => Task.CompletedTask; + + Task OnAgentQueueEmptied(string agentId, string? reason = null) + => Task.CompletedTask; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index bca8018e..ebeac256 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -8,13 +8,15 @@ public class RoutingArgs /// /// The reason why you select this function or agent /// - [JsonPropertyName("reason")] + [JsonPropertyName("next_action_reason")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Reason { get; set; } = string.Empty; /// /// The content of replying to user /// [JsonPropertyName("response")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Response { get; set; } /// @@ -31,6 +33,10 @@ public class RoutingArgs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string OriginalAgent { get; set; } + [JsonPropertyName("user_goal_description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string UserGoal { get; set; } + public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index bca532f0..476611d9 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -58,15 +58,8 @@ public class RouteToAgentFn : IFunctionCallback // Push next action agent if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32) { - var db = _services.GetRequiredService(); - var filter = new AgentFilter { AgentName = args.AgentName }; - var actionAgent = db.GetAgents(filter).FirstOrDefault(); - if (actionAgent != null) - { - _context.Push(actionAgent.Id, args.Reason); - } - - states.SetState("last_action_agent", args.AgentName, isNeedVersion: true); + _context.Push(args.AgentName, args.Reason); + states.SetState("next_action_agent", args.AgentName, isNeedVersion: true); } if (string.IsNullOrEmpty(args.AgentName)) @@ -93,7 +86,7 @@ public class RouteToAgentFn : IFunctionCallback if (missingfield && message.CurrentAgentId != agentId) { // Stack redirection agent - _context.Push(agentId, reason: $"redirection: {message.Content}"); + _context.Push(agentId, reason: $"REDIRECTION {message.Content}"); } } @@ -155,7 +148,7 @@ public class RouteToAgentFn : IFunctionCallback { // Add field to args message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields); - message.Content = $"missing some information: {string.Join(',', missingFields)}"; + message.Content = $"missing some information: {string.Join(", ", missingFields)}"; // Handle redirect var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field)); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index ae440748..b21250db 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -10,7 +10,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler public List Parameters => new List { - new ParameterPropertyDef("reason", "why route to agent") + new ParameterPropertyDef("next_action_reason", "the reason why route to this agent") { Required = true }, @@ -18,6 +18,10 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler { Required = true }, + new ParameterPropertyDef("user_goal_description", "user original goal") + { + Required = true + }, new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal") { Required = true @@ -38,6 +42,16 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler message.FunctionArgs = JsonSerializer.Serialize(inst); var ret = await routing.InvokeFunction(message.FunctionName, message); + var states = _services.GetRequiredService(); + var goalAgent = states.GetState("user_goal_agent"); + if (!string.IsNullOrEmpty(goalAgent)) + { + inst.OriginalAgent = goalAgent; + } + await HookEmitter.Emit(_services, async hook => + await hook.OnRoutingInstructionRevised(inst, message) + ); + var agentId = routing.Context.GetCurrentAgentId(); // Update next action agent's name diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs index 37277b3c..bca5105c 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Planning/NaivePlanner.cs @@ -102,6 +102,7 @@ public class NaivePlanner : IPlaner else { context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}"); + // context.Push(inst.OriginalAgent, "Push user goal agent"); } return true; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index db8900b4..96cd95e6 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -61,8 +61,23 @@ public class RoutingContext : IRoutingContext return _stack.Peek(); } + /// + /// Push agent + /// + /// Id or Name + /// public void Push(string agentId, string? reason = null) { + // Convert id to name + if (!Guid.TryParse(agentId, out _)) + { + var agentService = _services.GetRequiredService(); + agentId = agentService.GetAgents(new AgentFilter + { + AgentName = agentId + }).Result.Items.First().Id; + } + if (_stack.Count == 0 || _stack.Peek() != agentId) { var preAgentId = _stack.Count == 0 ? agentId : _stack.Peek(); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 303f6dd3..3a166e2b 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -41,7 +41,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, AllowTrailingCommas = true, - WriteIndented = true + WriteIndented = true, }; } @@ -66,6 +66,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR public override async Task OnFunctionExecuted(RoleDialogModel message) { + if (message.FunctionName == "route_to_agent") + { + return; + } + var conversationId = _state.GetConversationId(); var agent = await _agentService.LoadAgent(message.CurrentAgentId); message.FunctionArgs = message.FunctionArgs ?? "{}"; @@ -128,6 +133,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR log += $"\r\n```json\r\n{richContent}\r\n```"; } + if (!string.IsNullOrEmpty(message.FunctionName)) + { + log += $"\r\n\r\n**{message.FunctionName}**"; + } + var input = new ContentLogInputModel(conv.ConversationId, message) { Name = agent?.Name, @@ -223,7 +233,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input)); } - public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message) + public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message) { var conversationId = _state.GetConversationId(); var agent = await _agentService.LoadAgent(message.CurrentAgentId);