diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs index c04a1e99..bb28514e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionCallFromLlm.cs @@ -15,13 +15,13 @@ public class FunctionCallFromLlm : RoutingArgs { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; - if (string.IsNullOrEmpty(Answer)) + if (string.IsNullOrEmpty(Response)) { return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question}"; } else { - return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question} => {Answer}"; + return $"[{Function} {route} {JsonSerializer.Serialize(Arguments)}]: {Question} => {Response}"; } } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index 0dfa5118..c9a2bb0f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -5,29 +5,41 @@ public class RoutingArgs [JsonPropertyName("function")] public string Function { get; set; } + /// + /// The reason why you select this function or agent + /// [JsonPropertyName("reason")] - public string Reason { get; set; } = "the reason why you select this function or agent"; + public string Reason { get; set; } = string.Empty; - [JsonPropertyName("answer")] - public string Answer { get; set; } = "the content of response to user"; + /// + /// The content of replying to user + /// + [JsonPropertyName("response")] + public string Response { get; set; } + /// + /// Agent for next action based on user latest response + /// [JsonPropertyName("next_action_agent")] - public string AgentName { get; set; } = "agent for next action based on user latest response"; + public string AgentName { get; set; } + /// + /// Agent who can achieve user original goal + /// [JsonPropertyName("user_goal_agent")] - public string OriginalAgent { get; set; } = "agent who can achieve user original goal"; + public string OriginalAgent { get; set; } public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; - if (string.IsNullOrEmpty(Answer)) + if (string.IsNullOrEmpty(Response)) { return $"[{Function} {route}]"; } else { - return $"[{Function} {route}] => {Answer}"; + return $"[{Function} {route}] => {Response}"; } } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs index 0ce49cd6..bbbc6ec8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ConversationEndRoutingHandler.cs @@ -8,7 +8,7 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler { public string Name => "conversation_end"; - public string Description => "User wants to end the conversation."; + public string Description => "User completed his task and wants to end the conversation."; public bool IsReasoning => false; @@ -19,11 +19,11 @@ public class ConversationEndRoutingHandler : RoutingHandlerBase, IRoutingHandler public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Answer) + var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, - StopCompletion = true + ExecutionData = inst }; return result; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs index 4e021616..b28e4d47 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/ResponseToUserRoutingHandler.cs @@ -20,10 +20,11 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst) { - var result = new RoleDialogModel(AgentRole.Assistant, inst.Answer) + var result = new RoleDialogModel(AgentRole.Assistant, inst.Response) { CurrentAgentId = _settings.RouterId, FunctionName = inst.Function, + ExecutionData = inst, StopCompletion = true }; return result; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs index a3d81fe0..81435fc4 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RetrieveDataFromAgentRoutingHandler.cs @@ -34,7 +34,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH var record = db.Agents.First(x => x.Name.ToLower() == inst.AgentName.ToLower()); var response = await routing.InvokeAgent(record.Id); - inst.Answer = response.Content; + inst.Response = response.Content; /*_dialogs.Add(new RoleDialogModel(AgentRole.Assistant, inst.Parameters.Question) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs index 3a0d623a..3bb71e94 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetNextInstruction.cs @@ -1,6 +1,8 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Routing.Models; +using BotSharp.Abstraction.Templating; using System.Drawing; +using System.IO; using System.Text.RegularExpressions; namespace BotSharp.Core.Routing; @@ -9,17 +11,7 @@ public partial class RoutingService { public async Task GetNextInstruction() { - var prompt = "Which is the next step based on the CONVERSATION? Or you can handle without asking specific agent."; - var responseFormat = _settings.EnableReasoning ? - JsonSerializer.Serialize(new FunctionCallFromLlm()) : - JsonSerializer.Serialize(new RoutingArgs - { - Function = "route_to_agent" - }); - var content = $"{prompt} Response must be in JSON format {responseFormat}."; - content += " Set function as conversation_end with courtesy greeting if user is willing to end conversation"; - - var state = _services.GetRequiredService(); + var content = GetNextStepPrompt(); RoleDialogModel response = default; var args = new FunctionCallFromLlm(); @@ -50,7 +42,7 @@ public partial class RoutingService { _logger.LogError($"{ex.Message}: {response.Content}"); args.Function = "response_to_user"; - args.Answer = ex.Message; + args.Response = ex.Message; args.AgentName = "Router"; content += "\r\nPlease response in JSON format."; } @@ -86,7 +78,7 @@ public partial class RoutingService { _logger.LogError($"{ex.Message}: {response.Content}"); args.Function = "response_to_user"; - args.Answer = ex.Message; + args.Response = ex.Message; args.AgentName = "Router"; content += "\r\nPlease response in JSON format."; } @@ -135,4 +127,24 @@ public partial class RoutingService return args; } + +#if !DEBUG + [MemoryCache(60 * 60)] +#endif + private string GetNextStepPrompt() + { + var agentService = _services.GetRequiredService(); + var agentSettings = _services.GetRequiredService(); + var filePath = Path.Combine(agentService.GetAgentDataDir(_routerInstance.AgentId), $"next_step_prompt.{agentSettings.TemplateFormat}"); + var template = File.ReadAllText(filePath); + + // If enabled reasoning + // JsonSerializer.Serialize(new FunctionCallFromLlm()); + + var render = _services.GetRequiredService(); + return render.Render(template, new Dictionary + { + { "enabled_reasoning", false } + }); + } } diff --git a/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/next_step_prompt.liquid b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/next_step_prompt.liquid new file mode 100644 index 00000000..562672e6 --- /dev/null +++ b/src/WebStarter/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/next_step_prompt.liquid @@ -0,0 +1,10 @@ +What is the next step based on the CONVERSATION? Or you can handle without asking specific agent. + +Response must be in JSON format +{% if enabled_reasoning -%} +{"function":"route_to_agent"} +{%- else -%} +{"function":"route_to_agent","reason":"the reason why you select this function or agent","response":"content of replying to user","next_action_agent":"agent for next action based on user latest response","user_goal_agent":"agent who can achieve user original goal"} +{%- endif %} + +If the user has no other tasks need help with, set function as conversation_end with reason and reply user courteously. \ No newline at end of file