Merge branch 'SciSharp:master' into master

This commit is contained in:
hchen2020 2024-09-05 10:04:31 -05:00 committed by GitHub
commit 90932fa2dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 54 additions and 83 deletions

View file

@ -55,8 +55,6 @@ public class ConversationPlugin : IBotSharpPlugin
services.AddScoped<IExecutor, InstructExecutor>();
services.AddScoped<IInstructService, InstructService>();
services.AddScoped<ITokenStatistics, TokenStatistics>();
services.AddScoped<IKnowledgeService, KnowledgeService>();
}
public bool AttachMenu(List<PluginMenuDef> menu)

View file

@ -1,5 +1,6 @@
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Settings;
using BotSharp.Core.Knowledges.Services;
using BotSharp.Plugin.KnowledgeBase.Converters;
using BotSharp.Plugin.KnowledgeBase.Hooks;
using Microsoft.Extensions.Configuration;
@ -24,6 +25,8 @@ public class KnowledgeBasePlugin : IBotSharpPlugin
services.AddSingleton<IPdf2TextConverter, PigPdf2TextConverter>();
services.AddScoped<IAgentUtilityHook, KnowledgeBaseUtilityHook>();
services.AddScoped<IAgentHook, KnowledgeBaseAgentHook>();
services.AddScoped<IKnowledgeService, KnowledgeService>();
}
public bool AttachMenu(List<PluginMenuDef> menu)

View file

@ -1,8 +1,3 @@
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Knowledges;
using BotSharp.Abstraction.Knowledges.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Plugin.Planner.TwoStaging.Models;
namespace BotSharp.Plugin.Planner.Functions;
@ -22,7 +17,6 @@ public class PrimaryStagePlanFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
// Debug
var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
@ -31,26 +25,23 @@ public class PrimaryStagePlanFn : IFunctionCallback
state.SetState("max_tokens", "4096");
var task = JsonSerializer.Deserialize<PrimaryRequirementRequest>(message.FunctionArgs);
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
//get knowledge from vectordb
// Get knowledge from vectordb
var knowledges = new List<string>();
foreach (var question in task.Questions)
{
var retrievalMessage = new RoleDialogModel(AgentRole.User, question)
var list = await knowledgeService.SearchVectorKnowledge(question, collectionName, new VectorSearchOptions
{
FunctionArgs = JsonSerializer.Serialize(new ExtractedKnowledge
{
Question = question
}),
Content = ""
};
await fn.InvokeFunction("knowledge_retrieval", retrievalMessage);
knowledges.Add(retrievalMessage.Content);
Confidence = 0.2f
});
knowledges.Add(string.Join("\r\n\r\n=====\r\n", list.Select(x => x.ToQuestionAnswer())));
}
// Send knowledge to AI to refine and summarize the primary planning
// Get first stage planning prompt
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var firstPlanningPrompt = await GetFirstStagePlanPrompt(task, knowledges);
var firstPlanningPrompt = await GetFirstStagePlanPrompt(task.Requirements, knowledges);
var plannerAgent = new Agent
{
Id = BuiltInAgentId.Planner,
@ -65,21 +56,22 @@ public class PrimaryStagePlanFn : IFunctionCallback
return true;
}
private async Task<string> GetFirstStagePlanPrompt(PrimaryRequirementRequest task, List<string> relevantKnowledges)
private async Task<string> GetFirstStagePlanPrompt(string taskDescription, List<string> relevantKnowledges)
{
var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>();
var knowledgeHooks = _services.GetServices<IKnowledgeHook>();
var aiAssistant = await agentService.GetAgent(BuiltInAgentId.Planner);
var template = aiAssistant.Templates.FirstOrDefault(x => x.Name == "two_stage.1st.plan")?.Content ?? string.Empty;
var agent = await agentService.GetAgent(BuiltInAgentId.Planner);
var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.1st.plan")?.Content ?? string.Empty;
var responseFormat = JsonSerializer.Serialize(new FirstStagePlan
{
Parameters = [JsonDocument.Parse("{}")],
Results = [""]
Parameters = [ JsonDocument.Parse("{}") ],
Results = [ string.Empty ]
});
// Get global knowledges
var globalKnowledges = new List<string>();
var knowledgeHooks = _services.GetServices<IKnowledgeHook>();
foreach (var hook in knowledgeHooks)
{
var k = await hook.GetGlobalKnowledges();
@ -88,7 +80,7 @@ public class PrimaryStagePlanFn : IFunctionCallback
return render.Render(template, new Dictionary<string, object>
{
{ "task_description", task.Requirements },
{ "task_description", taskDescription },
{ "global_knowledges", globalKnowledges },
{ "relevant_knowledges", relevantKnowledges },
{ "response_format", responseFormat }
@ -100,19 +92,8 @@ public class PrimaryStagePlanFn : IFunctionCallback
var conv = _services.GetRequiredService<IConversationService>();
var wholeDialogs = conv.GetDialogHistory();
//add "test" to wholeDialogs' last element
if(plannerAgent.Name == "planner_summary")
{
//add "test" to wholeDialogs' last element in a new paragraph
wholeDialogs.Last().Content += "\n\nIf the table structure didn't mention auto incremental, the data field id needs to insert id manually and you need to use max(id) instead of LAST_INSERT_ID function.\nFor example, you should use SET @id = select max(id) from table;";
wholeDialogs.Last().Content += "\n\nTry if you can generate a single query to fulfill the needs";
}
if (plannerAgent.Name == "planning_1st")
{
//add "test" to wholeDialogs' last element in a new paragraph
wholeDialogs.Last().Content += "\n\nYou must analyze the table description to infer the table relations.";
}
// Append text
wholeDialogs.Last().Content += "\n\nYou must analyze the table description to infer the table relations.";
var completion = CompletionProvider.GetChatCompletion(_services,
provider: plannerAgent.LlmConfig.Provider,

View file

@ -18,12 +18,13 @@ public class SecondaryStagePlanFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
var fn = _services.GetRequiredService<IRoutingService>();
var agentService = _services.GetRequiredService<IAgentService>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
var msgSecondary = RoleDialogModel.From(message);
var taskPrimary = JsonSerializer.Deserialize<PrimaryRequirementRequest>(message.FunctionArgs);
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
msgSecondary.FunctionArgs = JsonSerializer.Serialize(new SecondaryBreakdownTask
{
@ -33,6 +34,7 @@ public class SecondaryStagePlanFn : IFunctionCallback
var taskSecondary = JsonSerializer.Deserialize<SecondaryBreakdownTask>(msgSecondary.FunctionArgs);
var items = msgSecondary.Content.JsonArrayContent<FirstStagePlan>();
// Search knowledgebase
foreach (var item in items)
{
if (!item.NeedAdditionalInformation) continue;
@ -44,17 +46,15 @@ public class SecondaryStagePlanFn : IFunctionCallback
message.Content += string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
}
// load agent
var agentService = _services.GetRequiredService<IAgentService>();
// Get second stage planning prompt
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var secondPlanningPrompt = await GetSecondStagePlanPrompt(taskSecondary, message);
var secondPlanningPrompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, message);
_logger.LogInformation(secondPlanningPrompt);
var plannerAgent = new Agent
{
Id = string.Empty,
Name = "test",
Name = "planning_2nd",
Instruction = secondPlanningPrompt,
TemplateDict = new Dictionary<string, object>(),
LlmConfig = currentAgent.LlmConfig
@ -65,27 +65,29 @@ public class SecondaryStagePlanFn : IFunctionCallback
_logger.LogInformation(response.Content);
return true;
}
private async Task<string> GetSecondStagePlanPrompt(SecondaryBreakdownTask task, RoleDialogModel message)
private async Task<string> GetSecondStagePlanPrompt(string taskDescription, RoleDialogModel message)
{
var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>();
var planner = await agentService.GetAgent(message.CurrentAgentId);
var template = planner.Templates.FirstOrDefault(x => x.Name == "two_stage.2nd.plan")?.Content ?? string.Empty;
var agent = await agentService.GetAgent(message.CurrentAgentId);
var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.2nd.plan")?.Content ?? string.Empty;
var responseFormat = JsonSerializer.Serialize(new SecondStagePlan
{
Tool = "tool name if task solution provided",
Parameters = new JsonDocument[] { JsonDocument.Parse("{}") },
Results = new string[] { "" }
Parameters = [ JsonDocument.Parse("{}") ],
Results = [ string.Empty ]
});
return render.Render(template, new Dictionary<string, object>
{
{ "task_description", task.TaskDescription },
{ "task_description", taskDescription },
{ "primary_plan", new[]{ message.Content } },
{ "response_format", responseFormat }
});
}
private async Task<RoleDialogModel> GetAiResponse(Agent plannerAgent)
{
var conv = _services.GetRequiredService<IConversationService>();

View file

@ -23,15 +23,12 @@ public class SummaryPlanFn : IFunctionCallback
var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
state.SetState("max_tokens", "4096");
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var taskRequirement = state.GetState("requirement_detail");
var task = state.GetState("requirement_detail");
// Get DDL
// Get table names
var steps = message.Content.JsonArrayContent<SecondStagePlan>();
// Get all the related tables
var allTables = new List<string>();
foreach (var step in steps)
{
@ -39,14 +36,15 @@ public class SummaryPlanFn : IFunctionCallback
}
message.Data = allTables.Distinct().ToList();
// Get table DDL and stores in content
// Get table DDL statements
var msgCopy = RoleDialogModel.From(message);
await fn.InvokeFunction("get_table_definition", msgCopy);
var ddlStatements = msgCopy.Content;
var relevantKnowledge = message.Content;
message.Data = null;
// Summarize and generate query
var summaryPlanPrompt = await GetPlanSummaryPrompt(task, message.Content, ddlStatements);
var summaryPlanPrompt = await GetSummaryPlanPrompt(taskRequirement, relevantKnowledge, ddlStatements);
_logger.LogInformation($"Summary plan prompt:\r\n{summaryPlanPrompt}");
var plannerAgent = new Agent
@ -64,14 +62,13 @@ public class SummaryPlanFn : IFunctionCallback
return true;
}
private async Task<string> GetPlanSummaryPrompt(string task, string knowledge, string ddlStatement)
private async Task<string> GetSummaryPlanPrompt(string taskDescription, string relevantKnowledge, string ddlStatement)
{
// save to knowledge base
var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>();
var aiAssistant = await agentService.GetAgent(BuiltInAgentId.Planner);
var template = aiAssistant.Templates.FirstOrDefault(x => x.Name == "two_stage.summarize")?.Content ?? string.Empty;
var agent = await agentService.GetAgent(BuiltInAgentId.Planner);
var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.summarize")?.Content ?? string.Empty;
var responseFormat = JsonSerializer.Serialize(new FirstStagePlan
{
Parameters = [JsonDocument.Parse("{}")],
@ -81,8 +78,8 @@ public class SummaryPlanFn : IFunctionCallback
return render.Render(template, new Dictionary<string, object>
{
{ "table_structure", ddlStatement },
{ "task_description", task },
{ "relevant_knowledges", knowledge },
{ "task_description", taskDescription },
{ "relevant_knowledges", relevantKnowledge },
{ "response_format", responseFormat }
});
}
@ -91,19 +88,9 @@ public class SummaryPlanFn : IFunctionCallback
var conv = _services.GetRequiredService<IConversationService>();
var wholeDialogs = conv.GetDialogHistory();
// Add "test" to wholeDialogs' last element
if (plannerAgent.Name == "planner_summary")
{
// Add "test" to wholeDialogs' last element in a new paragraph
wholeDialogs.Last().Content += "\n\nIf the table structure didn't mention auto incremental, the data field id needs to insert id manually and you need to use max(id) instead of LAST_INSERT_ID function.\nFor example, you should use SET @id = select max(id) from table;";
wholeDialogs.Last().Content += "\n\nTry if you can generate a single query to fulfill the needs";
}
if (plannerAgent.Name == "planning_1st")
{
// Add "test" to wholeDialogs' last element in a new paragraph
wholeDialogs.Last().Content += "\n\nYou must analyze the table description to infer the table relations.";
}
// Append text
wholeDialogs.Last().Content += "\n\nIf the table structure didn't mention auto incremental, the data field id needs to insert id manually and you need to use max(id) instead of LAST_INSERT_ID function.\nFor example, you should use SET @id = select max(id) from table;";
wholeDialogs.Last().Content += "\n\nTry if you can generate a single query to fulfill the needs";
var completion = CompletionProvider.GetChatCompletion(_services,
provider: plannerAgent.LlmConfig.Provider,

View file

@ -25,7 +25,7 @@ public class GetTableDefinitionFn : IFunctionCallback
var settings = _services.GetRequiredService<SqlDriverSetting>();
// Get table DDL from database
var tables = message.Data as List<string>;
var tables = message.Data as IEnumerable<string>;
if (tables.IsNullOrEmpty()) return false;
var tableDdls = new List<string>();
@ -36,9 +36,9 @@ public class GetTableDefinitionFn : IFunctionCallback
{
try
{
var sql = $"select * from information_schema.tables where table_name = @tableName";
var escapedTableName = MySqlHelper.EscapeString(table);
var sql = $"select * from information_schema.tables where table_name = @tableName";
var result = connection.QueryFirstOrDefault(sql, new
{
tableName = escapedTableName
@ -60,7 +60,7 @@ public class GetTableDefinitionFn : IFunctionCallback
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting ddl statement of table {table}.");
_logger.LogWarning($"Error when getting ddl statement of table {table}. {ex.Message}\r\n{ex.InnerException}");
}
}