diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs index 2c0aa15ed..3c93976bd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingContext.cs @@ -3,7 +3,8 @@ namespace BotSharp.Abstraction.Routing; public interface IRoutingContext { string GetCurrentAgentId(); - string PreviousAgentId(); + string FirstGoalAgentId(); + bool ContainsAgentId(string agentId); string OriginAgentId { get; } string ConversationId { get; } string MessageId { get; } @@ -13,6 +14,7 @@ public interface IRoutingContext int AgentCount { get; } void Push(string agentId, string? reason = null); void Pop(string? reason = null); + void PopTo(string agentId, string reason); void Replace(string agentId, string? reason = null); void Empty(string? reason = null); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 7df5fa02a..eb21fb31c 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -14,6 +14,13 @@ public class AgentPlugin : IBotSharpPlugin public SettingsMeta Settings => new SettingsMeta("Agent"); + public string[] AgentIds => new string[] + { + "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", + "01e2fc5c-2c89-4ec7-8470-7688608b496c", + "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b" + }; + public object GetNewSettingsInstance() => new AgentSettings(); diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index ece7daa7d..873fa93ae 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -46,6 +46,9 @@ + + + @@ -67,6 +70,15 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs new file mode 100644 index 000000000..d0c26be03 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/HumanInterventionNeededFn.cs @@ -0,0 +1,29 @@ +using BotSharp.Abstraction.Functions; + +namespace BotSharp.Core.Routing.Functions; + +public class HumanInterventionNeededFn : IFunctionCallback +{ + public string Name => "human_intervention_needed"; + + private readonly IServiceProvider _services; + + public HumanInterventionNeededFn(IServiceProvider services) + { + _services = services; + } + + public async Task Execute(RoleDialogModel message) + { + var hooks = _services.GetServices() + .OrderBy(x => x.Priority) + .ToList(); + + foreach (var hook in hooks) + { + await hook.OnHumanInterventionNeeded(message); + } + + return true; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index 8221cfb5d..2450a6fc0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -51,7 +51,7 @@ public async Task Execute(RoleDialogModel message) var originalAgent = db.GetAgents(filter).FirstOrDefault(); if (originalAgent != null) { - _context.Push(originalAgent.Id, $"user goal agent{(correctToOriginalAgent ? " & is corrected" : "")}"); + _context.Push(originalAgent.Id, $"user goal agent{(correctToOriginalAgent ? " " + originalAgent.Name + " & is corrected" : "")}"); } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs deleted file mode 100644 index 4aca2c3a8..000000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/HumanInterventionNeededHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BotSharp.Abstraction.Routing.Settings; - -namespace BotSharp.Core.Routing.Handlers; - -public class HumanInterventionNeededHandler : RoutingHandlerBase, IRoutingHandler -{ - public string Name => "human_intervention_needed"; - - public string Description => "Reach out to human customer service."; - - public List Parameters => new List - { - new ParameterPropertyDef("reason", "why need customer service"), - new ParameterPropertyDef("summary", "the whole conversation summary with important information"), - new ParameterPropertyDef("response", "asking user whether to connect with customer service representative") - }; - - public HumanInterventionNeededHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) - : base(services, logger, settings) - { - - } - - public async Task Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message) - { - var response = RoleDialogModel.From(message, - role: AgentRole.Assistant, - content: inst.Response); - - _dialogs.Add(response); - - var hooks = _services.GetServices() - .OrderBy(x => x.Priority) - .ToList(); - - foreach (var hook in hooks) - { - await hook.OnHumanInterventionNeeded(response); - } - - return true; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 87e55840a..4d8eba43d 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Abstraction.Utilities; namespace BotSharp.Core.Routing; @@ -137,7 +138,18 @@ await hook.OnAgentDequeued(agentId, currentAgentId, reason: reason) } } - public string PreviousAgentId() + public void PopTo(string agentId, string reason) + { + var currentAgentId = GetCurrentAgentId(); + while (!string.IsNullOrEmpty(currentAgentId) && + currentAgentId != agentId) + { + Pop(reason); + currentAgentId = GetCurrentAgentId(); + } + } + + public string FirstGoalAgentId() { if (_stack.Count == 1) { @@ -151,6 +163,11 @@ public string PreviousAgentId() return string.Empty; } + public bool ContainsAgentId(string agentId) + { + return _stack.ToArray().Contains(agentId); + } + public void Replace(string agentId, string? reason = null) { var fromAgent = agentId; diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json new file mode 100644 index 000000000..d32e46800 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/agent.json @@ -0,0 +1,11 @@ +{ + "id": "01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b", + "name": "Human Support", + "description": "Reach out to human customer service representative.", + "type": "task", + "createdDateTime": "2024-04-22T10:00:00Z", + "updatedDateTime": "2024-04-22T10:00:00Z", + "disabled": false, + "isPublic": true, + "profiles": [ "human" ] +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json new file mode 100644 index 000000000..240f5b7c3 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/functions.json @@ -0,0 +1,20 @@ +[ + { + "name": "human_intervention_needed", + "description": "If user wants to speak to human customer service.", + "parameters": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "description": "why customer needs customer service." + }, + "summary": { + "type": "string", + "description": "the whole conversation summary with important information" + } + }, + "required": [ "reason", "summary" ] + } + } +] diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid new file mode 100644 index 000000000..3b1aab426 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid @@ -0,0 +1,2 @@ +You are a human customer service connection program. +When other AI customer service cannot solve user problems, you know how to call the API to transfer users to human customer service for answers. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid index 3a332350a..f7388be6c 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.naive.liquid @@ -10,5 +10,3 @@ Expected user goal agent is {{ expected_user_goal_agent }}. {%- else -%} User goal agent is inferred based on user initial request. {%- endif %} -If user wants to speak to human customer service, use function human_intervention_needed. -If user wants to or is processing with a specific task that can be handled by agents, respond in appropriate output format defined to let proper agent to handle the task.