Optimize InstructLoop

This commit is contained in:
Haiping Chen 2025-05-02 20:02:37 -05:00
parent 4298e466db
commit 5ff7079470
9 changed files with 67 additions and 39 deletions

View file

@ -32,7 +32,7 @@ public interface IRoutingService
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
Task<bool> InvokeFunction(string name, RoleDialogModel messages);
Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<RoleDialogModel> InstructLoop(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs);
/// <summary>
/// Talk to a specific Agent directly, bypassing the Router
@ -40,7 +40,7 @@ public interface IRoutingService
/// <param name="agent"></param>
/// <param name="message"></param>
/// <returns></returns>
Task<RoleDialogModel> InstructDirect(Agent agent, RoleDialogModel message);
Task<RoleDialogModel> InstructDirect(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<string> GetConversationContent(List<RoleDialogModel> dialogs, int maxDialogCount = 100);

View file

@ -38,17 +38,6 @@ public partial class ConversationService
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(_conversationId, message.MessageId);
// Check the routing mode
var states = _services.GetRequiredService<IConversationStateService>();
var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard");
routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
if (routingMode == "lazy")
{
message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
}
// Save payload in order to assign the payload before hook is invoked
if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload))
{
@ -91,11 +80,22 @@ public partial class ConversationService
if (agent.Type == AgentType.Routing)
{
response = await routing.InstructLoop(message, dialogs);
// Check the routing mode
var states = _services.GetRequiredService<IConversationStateService>();
var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
routing.Context.Push(agent.Id, reason: "request started", updateLazyRouting: false);
if (routingMode == "lazy")
{
message.CurrentAgentId = states.GetState(StateConst.LAZY_ROUTING_AGENT_ID, message.CurrentAgentId);
routing.Context.Push(message.CurrentAgentId, reason: "lazy routing", updateLazyRouting: false);
}
response = await routing.InstructLoop(agent, message, dialogs);
}
else
{
response = await routing.InstructDirect(agent, message);
response = await routing.InstructDirect(agent, message, dialogs);
}
routing.Context.ResetRecursiveCounter();

View file

@ -294,7 +294,7 @@ public class RoutingContext : IRoutingContext
// Set next handling agent for lazy routing mode
var states = _services.GetRequiredService<IConversationStateService>();
var routingMode = states.GetState(StateConst.ROUTING_MODE, "hard");
var routingMode = states.GetState(StateConst.ROUTING_MODE, "eager");
if (routingMode == "lazy")
{
var agentId = GetCurrentAgentId();

View file

@ -7,7 +7,7 @@ namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
public async Task<RoleDialogModel> InstructLoop(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
@ -15,8 +15,6 @@ public partial class RoutingService
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();

View file

@ -26,31 +26,36 @@ public partial class RoutingService : IRoutingService
_logger = logger;
}
public async Task<RoleDialogModel> InstructDirect(Agent agent, RoleDialogModel message)
public async Task<RoleDialogModel> InstructDirect(Agent agent, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var conv = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
storage.Append(conv.ConversationId, message);
var dialogs = conv.GetDialogHistory();
dialogs.Add(message);
Context.SetDialogs(dialogs);
var inst = new FunctionCallFromLlm
{
Function = "route_to_agent",
Question = message.Content,
NextActionReason = message.Content,
AgentName = agent.Name,
OriginalAgent = agent.Name,
ExecutingDirectly = true
};
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.Push(agent.Id, "instruct directly");
var agentId = routing.Context.GetCurrentAgentId();
message.Instruction = inst;
var result = await InvokeFunction("route_to_agent", message);
// Update next action agent's name
var agentService = _services.GetRequiredService<IAgentService>();
if (agent.Disabled)
{
var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent.";
message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content);
dialogs.Add(message);
}
else
{
var ret = await routing.InvokeAgent(agentId, dialogs);
}
var response = dialogs.Last();
response.MessageId = message.MessageId;
response.Instruction = inst;
return response;
}

View file

@ -44,7 +44,7 @@ public class TemplateRender : ITemplateRender
}
else
{
_logger.LogWarning(error);
_logger.LogError(error);
return template;
}
}

View file

@ -27,7 +27,7 @@ public class VerboseLogHook : IContentGeneratingHook
if (dialog != null)
{
var log = $"{dialog.Role}: {dialog.Content} [msg_id: {dialog.MessageId}] ==>";
_logger.LogInformation(log);
_logger.LogDebug(log);
}
await Task.CompletedTask;
@ -44,7 +44,7 @@ public class VerboseLogHook : IContentGeneratingHook
$"[{agent?.Name}]: {message.Indication} {message.FunctionName}({message.FunctionArgs})" :
$"[{agent?.Name}]: {message.Content}" + $" <== [msg_id: {message.MessageId}]";
_logger.LogInformation(tokenStats.Prompt);
_logger.LogInformation(log);
_logger.LogDebug(tokenStats.Prompt);
_logger.LogDebug(log);
}
}

View file

@ -146,7 +146,7 @@ public class TwilioInboundController : TwilioController
AgentId = request.AgentId,
Channel = ConversationChannel.Phone,
ChannelId = request.CallSid,
Title = $"Incoming phone call from {request.From}",
Title = request.Intent ?? $"Incoming phone call from {request.From}",
Tags = [],
};
@ -161,6 +161,15 @@ public class TwilioInboundController : TwilioController
new("twilio_call_sid", request.CallSid),
};
var requestStates = ParseStates(request.States);
foreach (var s in requestStates)
{
if (!states.Any(x => x.Key == s.Key))
{
states.Add(new MessageState(s.Key, s.Value));
}
}
if (request.InitAudioFile != null)
{
states.Add(new("init_audio_file", request.InitAudioFile));
@ -173,7 +182,20 @@ public class TwilioInboundController : TwilioController
{
states.Add(new(StateConst.ROUTING_MODE, agent.Mode));
}
convService.SetConversationId(conversation.Id, states);
if (!string.IsNullOrEmpty(request.Intent))
{
var storage = _services.GetRequiredService<IConversationStorage>();
storage.Append(conversation.Id, new RoleDialogModel(AgentRole.User, request.Intent)
{
CurrentAgentId = conversation.Id,
CreatedAt = DateTime.UtcNow
});
}
convService.SaveStates();
// reload agent rendering with states

View file

@ -17,7 +17,10 @@ public class ConversationalVoiceRequest : VoiceRequest
public int AIResponseWaitTime { get; set; } = 0;
public string? AIResponseErrorMessage { get; set; } = string.Empty;
public string Intent { get; set; } = string.Empty;
/// <summary>
/// Initial intent when incoming call connected
/// </summary>
public string? Intent { get; set; }
[FromQuery(Name = "init-audio-file")]
public string? InitAudioFile { get; set; }