diff --git a/docs/agent/intro.md b/docs/agent/intro.md index 7507ef2e..00484fe8 100644 --- a/docs/agent/intro.md +++ b/docs/agent/intro.md @@ -2,7 +2,7 @@ An agent helps you process user sentences (unstructure data) into structure data that you can use to return an appropriate response. -Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. +Agent is a collection that contains prompt words and function Json Schema definitions, few-shot examples and knowledge base data. You can create multiple different Agents to perform specific operations in specific domains. BotSharp has built-in maintenance for Agents, including creating, updating and deleting, importing and exporting. Agents are divided into task agents and routing (non-task) agents. Business domain agents belong to task agents, and routers belong to non-task agents. ## My Agent After creating the platform account, you can start to enter the steps of creating the Agent. diff --git a/docs/architecture/routing.md b/docs/architecture/routing.md index a4bbb6ea..8732cd59 100644 --- a/docs/architecture/routing.md +++ b/docs/architecture/routing.md @@ -14,4 +14,8 @@ For simple questions raised by users, the ordinary routing function can already ![routing with reasoning](./assets/routing-reasoner.png) -For more **Routing** related information, please go to [Agent Routing](../agent/router.md). \ No newline at end of file +For more **Routing** related information, please go to [Agent Routing](../agent/router.md). + +## Profile + +There is an array field called `Profile` in the Agent data model, which is used to store the current profiles. When this attribute is set in the `Router`, only matching Task Agents can be included in the routing candidate Agents list, which means that the Task Agent also To set the same profile name. Profiles allows you to enter multiple profiles, and the system will automatically combine them for processing. \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 1280c865..6a134cef 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -60,6 +60,9 @@ public class Agent [JsonIgnore] public bool IsRouter { get; set; } + [JsonIgnore] + public bool IsHost { get; set; } + [JsonIgnore] public PluginDef Plugin { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index 17027652..c9f40fdc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Routing; public interface IRoutingService { Agent Router { get; } - RoutingItem[] GetRoutingItems(); + RoutableAgent[] GetRoutableAgents(List profiles); RoutingRule[] GetRulesByName(string name); RoutingRule[] GetRulesByAgentId(string id); List GetHandlers(); @@ -20,5 +20,5 @@ public interface IRoutingService /// /// /// - Task ExecuteDirectly(Agent agent, RoleDialogModel message); + Task InstructDirect(Agent agent, RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs similarity index 84% rename from src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs rename to src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs index b08edfed..14fd6826 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutableAgent.cs @@ -2,7 +2,7 @@ using BotSharp.Abstraction.Functions.Models; namespace BotSharp.Abstraction.Routing.Models; -public class RoutingItem +public class RoutableAgent { [JsonPropertyName("agent_id")] public string AgentId { get; set; } = string.Empty; @@ -13,6 +13,10 @@ public class RoutingItem [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; + [JsonPropertyName("profiles")] + public List Profiles { get; set; } + = new List(); + [JsonPropertyName("required_fields")] public List RequiredFields { get; set; } = new List(); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 486c756b..7e7493d1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -21,6 +21,13 @@ public partial class AgentService agent.Plugin = GetPlugin(agent.Id); } + // Set IsHost + var agentSetting = _services.GetRequiredService(); + foreach (var agent in agents) + { + agent.IsHost = agentSetting.HostAgentId == agent.Id; + } + agents = agents.Where(x => x.Installed).ToList(); var pager = filter?.Pager ?? new Pagination(); return new PagedItems diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index fd427562..eb46d5ec 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -61,7 +61,7 @@ public partial class ConversationService response = settings.AgentIds.Contains(agentId) ? await routing.InstructLoop(message) : - await routing.ExecuteDirectly(agent, message); + await routing.InstructDirect(agent, message); routing.ResetRecursiveCounter(); } diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index d7e33451..c7e34ff8 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -93,7 +93,7 @@ public class NaivePlanner : IPlaner var unmatchedAgentId = context.GetCurrentAgentId(); // Exclude the wrong routed agent - var agents = router.TemplateDict["routing_agents"] as RoutingItem[]; + var agents = router.TemplateDict["routing_agents"] as RoutableAgent[]; router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray(); // Handover to Router; diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 47cfcc38..84de68b0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -24,7 +24,8 @@ public class RoutingAgentHook : AgentHookBase dict["router"] = _agent; var routing = _services.GetRequiredService(); - dict["routing_agents"] = routing.GetRoutingItems(); + var agents = routing.GetRoutableAgents(_agent.Profiles); + dict["routing_agents"] = agents; dict["routing_handlers"] = routing.GetHandlers(); return base.OnInstructionLoaded(template, dict); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index d70cc15f..888a9eb5 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -32,7 +32,7 @@ public partial class RoutingService : IRoutingService _logger = logger; } - public async Task ExecuteDirectly(Agent agent, RoleDialogModel message) + public async Task InstructDirect(Agent agent, RoleDialogModel message) { var handlers = _services.GetServices(); @@ -147,22 +147,13 @@ public partial class RoutingService : IRoutingService return x.RoutingRules; }).ToArray(); - // Filter agents by profile - var state = _services.GetRequiredService(); - var channel = state.GetState("channel"); - var specifiedProfile = agents.FirstOrDefault(x => x.Profiles.Contains(channel)); - if (specifiedProfile != null) - { - records = records.Where(x => specifiedProfile.Profiles.Contains(channel)).ToArray(); - } - return records; } #if !DEBUG [MemoryCache(10 * 60)] #endif - public RoutingItem[] GetRoutingItems() + public RoutableAgent[] GetRoutableAgents(List profiles) { var db = _services.GetRequiredService(); @@ -171,12 +162,14 @@ public partial class RoutingService : IRoutingService Disabled = false, AllowRouting = true }; + var agents = db.GetAgents(filter); - return agents.Select(x => new RoutingItem + var routableAgents = agents.Select(x => new RoutableAgent { AgentId = x.Id, Description = x.Description, Name = x.Name, + Profiles = x.Profiles, RequiredFields = x.RoutingRules .Where(p => p.Required) .Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type) @@ -190,6 +183,17 @@ public partial class RoutingService : IRoutingService Required = p.Required }).ToList() }).ToArray(); + + // Handle profile. + // Router profile must match the agent profile + if (routableAgents.Length > 0 && profiles.Count > 0) + { + routableAgents = routableAgents.Where(x => x.Profiles != null && + x.Profiles.Exists(x1 => profiles.Exists(y => x1 == y))) + .ToArray(); + } + + return routableAgents; } public RoutingRule[] GetRulesByName(string name) diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index 3aba223f..62d5c3e0 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -25,7 +25,7 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); - _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 663dfd4a..6a41a66b 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -22,12 +22,19 @@ public class AgentViewModel [JsonPropertyName("is_router")] public bool IsRouter { get; set; } + [JsonPropertyName("is_host")] + public bool IsHost { get; set; } + [JsonPropertyName("allow_routing")] public bool AllowRouting { get; set; } + public bool Disabled { get; set; } + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } + public List Profiles { get; set; } + = new List(); [JsonPropertyName("routing_rules")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -59,10 +66,11 @@ public class AgentViewModel Samples = agent.Samples, IsPublic= agent.IsPublic, IsRouter = agent.IsRouter, + IsHost = agent.IsHost, Disabled = agent.Disabled, IconUrl = agent.IconUrl, AllowRouting = agent.AllowRouting, - Profiles = agent.Profiles, + Profiles = agent.Profiles ?? new List(), RoutingRules = agent.RoutingRules, LlmConfig = agent.LlmConfig, Plugin = agent.Plugin, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 18fe98d6..15a2406b 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -210,6 +210,7 @@ public partial class MongoRepository var builder = Builders.Filter; var filters = new List>() { builder.Empty }; + if (!string.IsNullOrEmpty(filter.Id)) filters.Add(builder.Eq(x => x.Id, filter.Id)); if (!string.IsNullOrEmpty(filter.AgentId)) filters.Add(builder.Eq(x => x.AgentId, filter.AgentId)); if (!string.IsNullOrEmpty(filter.Status)) filters.Add(builder.Eq(x => x.Status, filter.Status)); if (!string.IsNullOrEmpty(filter.Channel)) filters.Add(builder.Eq(x => x.Channel, filter.Channel)); diff --git a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj index 041ba8e5..77ba7e96 100644 --- a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj +++ b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -35,13 +35,9 @@ - - - - @@ -91,27 +87,15 @@ PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json index 6b2a2771..09c08cb6 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/agent.json @@ -6,6 +6,7 @@ "id": "b284db86-e9c2-4c25-a59e-4649797dd130", "allowRouting": true, "isPublic": true, + "profiles": [ "pizza" ], "routingRules": [ { "field": "order_number", diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json index bed04b3f..23323f63 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json @@ -5,5 +5,6 @@ "updatedDateTime": "2023-07-26T02:29:25.123274Z", "id": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd", "allowRouting": true, - "isPublic": true + "isPublic": true, + "profiles": [ "pizza" ] } \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/10d12798-08fb-4aa6-977b-5dd94d82dbfe/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json deleted file mode 100644 index 9ed093a2..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/456e35c5-caf0-4d45-9084-b44a8ca717e4/agents.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "userId": "456e35c5-caf0-4d45-9084-b44a8ca717e4", - "agentId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", - "updatedTime": "2023-08-14T18:14:11.6833783Z", - "createdTime": "2023-08-14T18:14:11.6829767Z", - "editable": true, - "id": "1273379c-4419-460a-b0a2-5695afd097f5" - } -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/d0e6680d-03d5-4ed8-bdcd-aa7d86f2a1bc/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file diff --git a/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json b/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json deleted file mode 100644 index 32960f8c..00000000 --- a/tests/BotSharp.Plugin.PizzaBot/data/users/e465af5f-044f-414b-b670-92834929b96c/agents.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] \ No newline at end of file