Skip to content

Commit 316d7d9

Browse files
authored
Merge pull request #107 from hchen2020/master
Add maxRecursiveDepth to avoid keeping calling LLM.
2 parents c1da960 + 187710c commit 316d7d9

File tree

5 files changed

+237
-198
lines changed

5 files changed

+237
-198
lines changed

src/Infrastructure/BotSharp.Core/Agents/Services/AgentHookBase.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using BotSharp.Abstraction.Agents.Models;
2-
using BotSharp.Abstraction.Conversations.Models;
32
using Fluid;
43

54
namespace BotSharp.Core.Agents.Services;
@@ -67,9 +66,4 @@ public virtual bool OnSamplesLoaded(ref string samples)
6766
public virtual void OnAgentLoaded(Agent agent)
6867
{
6968
}
70-
71-
public virtual bool OnAgentRouting(RoleDialogModel message, ref string id)
72-
{
73-
return true;
74-
}
7569
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using BotSharp.Abstraction.Conversations.Models;
2+
using BotSharp.Abstraction.Functions;
3+
4+
namespace BotSharp.Core.Conversations.Services;
5+
6+
public partial class ConversationService
7+
{
8+
private async Task CallFunctions(RoleDialogModel msg)
9+
{
10+
var hooks = _services.GetServices<IConversationHook>().ToList();
11+
12+
// Invoke functions
13+
var functions = _services.GetServices<IFunctionCallback>()
14+
.Where(x => x.Name == msg.FunctionName)
15+
.ToList();
16+
17+
if (functions.Count == 0)
18+
{
19+
msg.Content = $"Can't find function implementation of {msg.FunctionName}.";
20+
_logger.LogError(msg.Content);
21+
return;
22+
}
23+
24+
foreach (var fn in functions)
25+
{
26+
// Before executing functions
27+
foreach (var hook in hooks)
28+
{
29+
await hook.OnFunctionExecuting(msg);
30+
}
31+
32+
// Execute function
33+
await fn.Execute(msg);
34+
35+
// After functions have been executed
36+
foreach (var hook in hooks)
37+
{
38+
await hook.OnFunctionExecuted(msg);
39+
}
40+
}
41+
}
42+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using BotSharp.Abstraction.Agents.Enums;
2+
using BotSharp.Abstraction.Agents.Models;
3+
using BotSharp.Abstraction.Conversations.Models;
4+
using BotSharp.Abstraction.MLTasks;
5+
6+
namespace BotSharp.Core.Conversations.Services;
7+
8+
public partial class ConversationService
9+
{
10+
const int maxRecursiveDepth = 3;
11+
int currentRecursiveDepth = 0;
12+
13+
private async Task<bool> GetChatCompletionsAsyncRecursively(IChatCompletion chatCompletion,
14+
string conversationId,
15+
Agent agent,
16+
List<RoleDialogModel> wholeDialogs,
17+
Func<RoleDialogModel, Task> onMessageReceived,
18+
Func<RoleDialogModel, Task> onFunctionExecuting)
19+
{
20+
currentRecursiveDepth++;
21+
if (currentRecursiveDepth > maxRecursiveDepth)
22+
{
23+
_logger.LogError($"Exceed max current recursive depth.");
24+
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, "System has exception, please try later.")
25+
{
26+
CurrentAgentId = agent.Id
27+
}, onMessageReceived);
28+
return false;
29+
}
30+
31+
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
32+
{
33+
await HandleAssistantMessage(msg, onMessageReceived);
34+
35+
// Add to dialog history
36+
_storage.Append(conversationId, agent.Id, msg);
37+
}, async fn =>
38+
{
39+
var preAgentId = agent.Id;
40+
41+
await HandleFunctionMessage(fn, onFunctionExecuting);
42+
43+
// Function executed has exception
44+
if (fn.ExecutionResult == null)
45+
{
46+
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, fn.Content)
47+
{
48+
CurrentAgentId = fn.CurrentAgentId
49+
}, onMessageReceived);
50+
return;
51+
}
52+
53+
fn.Content = fn.ExecutionResult;
54+
55+
// Agent has been transferred
56+
if (fn.CurrentAgentId != preAgentId)
57+
{
58+
var agentSettings = _services.GetRequiredService<AgentSettings>();
59+
var agentService = _services.GetRequiredService<IAgentService>();
60+
agent = await agentService.LoadAgent(fn.CurrentAgentId);
61+
62+
// Set state to make next conversation will go to this agent directly
63+
// var state = _services.GetRequiredService<IConversationStateService>();
64+
// state.SetState("agentId", fn.CurrentAgentId);
65+
}
66+
67+
// Add to dialog history
68+
_storage.Append(conversationId, preAgentId, fn);
69+
70+
// After function is executed, pass the result to LLM to get a natural response
71+
wholeDialogs.Add(fn);
72+
73+
await GetChatCompletionsAsyncRecursively(chatCompletion, conversationId, agent, wholeDialogs, onMessageReceived, onFunctionExecuting);
74+
});
75+
76+
return result;
77+
}
78+
79+
private async Task HandleAssistantMessage(RoleDialogModel msg, Func<RoleDialogModel, Task> onMessageReceived)
80+
{
81+
var hooks = _services.GetServices<IConversationHook>().ToList();
82+
83+
// After chat completion hook
84+
foreach (var hook in hooks)
85+
{
86+
await hook.AfterCompletion(msg);
87+
}
88+
89+
await onMessageReceived(msg);
90+
}
91+
92+
private async Task HandleFunctionMessage(RoleDialogModel msg, Func<RoleDialogModel, Task> onFunctionExecuting)
93+
{
94+
// Save states
95+
SaveStateByArgs(msg.FunctionArgs);
96+
97+
// Call functions
98+
await onFunctionExecuting(msg);
99+
await CallFunctions(msg);
100+
}
101+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using BotSharp.Abstraction.Conversations.Models;
2+
using BotSharp.Abstraction.MLTasks;
3+
4+
namespace BotSharp.Core.Conversations.Services;
5+
6+
public partial class ConversationService
7+
{
8+
public async Task<bool> SendMessage(string agentId, string conversationId,
9+
RoleDialogModel lastDialog,
10+
Func<RoleDialogModel, Task> onMessageReceived,
11+
Func<RoleDialogModel, Task> onFunctionExecuting)
12+
{
13+
var converation = await GetConversation(conversationId);
14+
15+
// Create conversation if this conversation not exists
16+
if (converation == null)
17+
{
18+
var sess = new Conversation
19+
{
20+
Id = conversationId,
21+
AgentId = agentId
22+
};
23+
converation = await NewConversation(sess);
24+
}
25+
26+
// conversation state
27+
var stateService = _services.GetRequiredService<IConversationStateService>();
28+
stateService.SetConversation(conversationId);
29+
stateService.Load();
30+
31+
var router = _services.GetRequiredService<IAgentRouting>();
32+
var agent = await router.LoadCurrentAgent();
33+
34+
_logger.LogInformation($"[{agent.Name}] {lastDialog.Role}: {lastDialog.Content}");
35+
36+
lastDialog.CurrentAgentId = agent.Id;
37+
_storage.Append(conversationId, agent.Id, lastDialog);
38+
39+
var wholeDialogs = GetDialogHistory(conversationId);
40+
41+
// Get relevant domain knowledge
42+
/*if (_settings.EnableKnowledgeBase)
43+
{
44+
var knowledge = _services.GetRequiredService<IKnowledgeService>();
45+
agent.Knowledges = await knowledge.GetKnowledges(new KnowledgeRetrievalModel
46+
{
47+
AgentId = agentId,
48+
Question = string.Join("\n", wholeDialogs.Select(x => x.Content))
49+
});
50+
}*/
51+
52+
var hooks = _services.GetServices<IConversationHook>().ToList();
53+
54+
// Before chat completion hook
55+
foreach (var hook in hooks)
56+
{
57+
hook.SetAgent(agent)
58+
.SetConversation(converation);
59+
60+
await hook.OnDialogsLoaded(wholeDialogs);
61+
await hook.BeforeCompletion();
62+
}
63+
64+
var chatCompletion = GetChatCompletion();
65+
var result = await GetChatCompletionsAsyncRecursively(chatCompletion,
66+
conversationId,
67+
agent,
68+
wholeDialogs,
69+
onMessageReceived,
70+
onFunctionExecuting);
71+
72+
return result;
73+
}
74+
75+
private void SaveStateByArgs(string args)
76+
{
77+
var stateService = _services.GetRequiredService<IConversationStateService>();
78+
var jo = JsonSerializer.Deserialize<object>(args);
79+
if (jo is JsonElement root)
80+
{
81+
foreach (JsonProperty property in root.EnumerateObject())
82+
{
83+
stateService.SetState(property.Name, property.Value.ToString());
84+
}
85+
}
86+
}
87+
88+
public IChatCompletion GetChatCompletion()
89+
{
90+
var completions = _services.GetServices<IChatCompletion>();
91+
return completions.FirstOrDefault(x => x.GetType().FullName.EndsWith(_settings.ChatCompletion));
92+
}
93+
}

0 commit comments

Comments
 (0)