Merge pull request #416 from hchen2020/master

Refactor human intervention needed.
This commit is contained in:
C. Oceania 2024-04-22 16:02:03 -05:00 committed by GitHub
commit 8e9aa4acf6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 103 additions and 48 deletions

View file

@ -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);
}

View file

@ -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();

View file

@ -46,6 +46,9 @@
</PropertyGroup>
<ItemGroup>
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\instruction.liquid" />
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\agent.json" />
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\instruction.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\agent.json" />
@ -67,6 +70,15 @@
</ItemGroup>
<ItemGroup>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View file

@ -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<bool> Execute(RoleDialogModel message)
{
var hooks = _services.GetServices<IConversationHook>()
.OrderBy(x => x.Priority)
.ToList();
foreach (var hook in hooks)
{
await hook.OnHumanInterventionNeeded(message);
}
return true;
}
}

View file

@ -51,7 +51,7 @@ public partial class RouteToAgentFn : IFunctionCallback
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" : "")}");
}
}

View file

@ -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<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{
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<HumanInterventionNeededHandler> logger, RoutingSettings settings)
: base(services, logger, settings)
{
}
public async Task<bool> 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<IConversationHook>()
.OrderBy(x => x.Priority)
.ToList();
foreach (var hook in hooks)
{
await hook.OnHumanInterventionNeeded(response);
}
return true;
}
}

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Utilities;
namespace BotSharp.Core.Routing;
@ -137,7 +138,18 @@ public class RoutingContext : IRoutingContext
}
}
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 class RoutingContext : IRoutingContext
return string.Empty;
}
public bool ContainsAgentId(string agentId)
{
return _stack.ToArray().Contains(agentId);
}
public void Replace(string agentId, string? reason = null)
{
var fromAgent = agentId;

View file

@ -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" ]
}

View file

@ -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" ]
}
}
]

View file

@ -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.

View file

@ -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.