resolve conflicts

This commit is contained in:
Jicheng Lu 2024-09-04 11:04:19 -05:00
commit c2b870b655
6 changed files with 83 additions and 170 deletions

View file

@ -18,6 +18,7 @@ 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>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
@ -28,16 +29,14 @@ public class PrimaryStagePlanFn : IFunctionCallback
// Get knowledge from vectordb
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp; ;
var knowledges = await knowledgeService.SearchVectorKnowledge(task.Question, collectionName, new VectorSearchOptions
var knowledges = await knowledgeService.SearchVectorKnowledge(task.Requirements, collectionName, new VectorSearchOptions
{
Confidence = 0.1f
});
message.Content = string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
var agentService = _services.GetRequiredService<IAgentService>();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
// Send knowledge to AI to refine and summarize the primary planning
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var firstPlanningPrompt = await GetFirstStagePlanPrompt(task, message);
var plannerAgent = new Agent
{

View file

@ -5,11 +5,13 @@ namespace BotSharp.Plugin.Planner.Functions;
public class SummaryPlanFn : IFunctionCallback
{
public string Name => "plan_summary";
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private object aiAssistant;
public SummaryPlanFn(IServiceProvider services, ILogger<PrimaryStagePlanFn> logger)
private readonly IServiceProvider _services;
private readonly ILogger<SummaryPlanFn> _logger;
public SummaryPlanFn(
IServiceProvider services,
ILogger<SummaryPlanFn> logger)
{
_services = services;
_logger = logger;
@ -17,32 +19,51 @@ public class SummaryPlanFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
//debug
var fn = _services.GetRequiredService<IRoutingService>();
var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
state.SetState("max_tokens", "4096");
var task = state.GetState("requirement_detail");
// summarize and generate query
var summaryPlanningPrompt = await GetPlanSummaryPrompt(task, message);
_logger.LogInformation(summaryPlanningPrompt);
// Get DDL
var steps = message.Content.JsonArrayContent<SecondStagePlan>();
// Get all the related tables
var allTables = new List<string>();
foreach (var step in steps)
{
allTables.AddRange(step.Tables);
}
message.Data = allTables.Distinct().ToList();
// Get table DDL and stores in content
var msgCopy = RoleDialogModel.From(message);
await fn.InvokeFunction("get_table_definition", msgCopy);
var ddlStatements = msgCopy.Content;
// Summarize and generate query
var summaryPlanPrompt = await GetPlanSummaryPrompt(task, message.Content, ddlStatements);
_logger.LogInformation($"Summary plan prompt:\r\n{summaryPlanPrompt}");
var plannerAgent = new Agent
{
Id = BuiltInAgentId.Planner,
Name = "planner_summary",
Instruction = summaryPlanningPrompt,
TemplateDict = new Dictionary<string, object>()
Name = "Planner Summary",
Instruction = summaryPlanPrompt,
LlmConfig = currentAgent.LlmConfig
};
var response_summary = await GetAiResponse(plannerAgent);
message.Content = response_summary.Content;
var summary = await GetAiResponse(plannerAgent);
message.Content = summary.Content;
message.StopCompletion = true;
return true;
}
private async Task<string> GetPlanSummaryPrompt(string task, RoleDialogModel message)
private async Task<string> GetPlanSummaryPrompt(string task, string knowledge, string ddlStatement)
{
// save to knowledge base
var agentService = _services.GetRequiredService<IAgentService>();
@ -58,9 +79,9 @@ public class SummaryPlanFn : IFunctionCallback
return render.Render(template, new Dictionary<string, object>
{
{ "table_structure", message.SecondaryContent }, ////check
{ "task_description", task},
{ "relevant_knowledges", message.Content },
{ "table_structure", ddlStatement },
{ "task_description", task },
{ "relevant_knowledges", knowledge },
{ "response_format", responseFormat }
});
}
@ -69,17 +90,17 @@ public class SummaryPlanFn : IFunctionCallback
var conv = _services.GetRequiredService<IConversationService>();
var wholeDialogs = conv.GetDialogHistory();
//add "test" to wholeDialogs' last element
// Add "test" to wholeDialogs' last element
if (plannerAgent.Name == "planner_summary")
{
//add "test" to wholeDialogs' last element in a new paragraph
// 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
// 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.";
}

View file

@ -1,3 +1,3 @@
Use the TwoStagePlanner approach to plan the overall implementation steps, call plan_primary_stage.
If need_additional_information is true, call plan_secondary_stage for the specific primary stage.
Call plan_summary to summarize the final planning steps.
You must Call plan_summary as the last step to summarize the final planning steps.

View file

@ -1,122 +0,0 @@
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Infrastructures;
using MySqlConnector;
using static Dapper.SqlMapper;
using BotSharp.Abstraction.Agents.Enums;
using Microsoft.Extensions.Logging;
namespace BotSharp.Plugin.Planner.Functions;
public class AddDatabaseKnowledgeFn : IFunctionCallback
{
public string Name => "add_database_knowledge";
private readonly IServiceProvider _services;
private readonly ILogger<AddDatabaseKnowledgeFn> _logger;
public AddDatabaseKnowledgeFn(
IServiceProvider services,
ILogger<AddDatabaseKnowledgeFn> logger)
{
_services = services;
_logger = logger;
}
public async Task<bool> Execute(RoleDialogModel message)
{
var agentService = _services.GetRequiredService<IAgentService>();
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
var fn = _services.GetRequiredService<IRoutingService>();
var settings = _services.GetRequiredService<SqlDriverSetting>();
var allTables = new HashSet<string>();
using var connection = new MySqlConnection(settings.MySqlConnectionString);
var sql = $"select table_name from information_schema.tables;";
var results = connection.Query(sql, new Dictionary<string, object>());
foreach (var item in results)
{
if (item == null) continue;
allTables.Add(item.TABLE_NAME);
}
message.Data = allTables.ToList();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var errorNote = string.Empty;
foreach (var table in allTables)
{
message.Data = new List<string> { table };
await fn.InvokeFunction("get_table_definition", message);
var planningPrompt = await GetPrompt(message);
var plannerAgent = new Agent
{
Id = string.Empty,
Name = "Database Knowledge",
Instruction = planningPrompt,
LlmConfig = currentAgent.LlmConfig
};
try
{
var response = await GetAiResponse(plannerAgent);
var knowledges = response.Content.JsonArrayContent<ExtractedKnowledge>();
foreach (var k in knowledges)
{
try
{
message.FunctionArgs = JsonSerializer.Serialize(new ExtractedKnowledge
{
Question = k.Question,
Answer = k.Answer
});
await fn.InvokeFunction("memorize_knowledge", message);
message.SecondaryContent += $"Table: {table}, Question: {k.Question}, {message.Content}\r\n";
}
catch (Exception e)
{
var note = $"Error processing table {table}: {e.Message}\r\n{e.InnerException}";
errorNote += note;
_logger.LogWarning(note);
}
}
}
catch (Exception e)
{
errorNote += $"Error processing table {table}: {e.Message}\r\n{e.InnerException}\r\n";
_logger.LogWarning(errorNote);
}
}
return true;
}
private async Task<RoleDialogModel> GetAiResponse(Agent plannerAgent)
{
var conv = _services.GetRequiredService<IConversationService>();
var wholeDialogs = conv.GetDialogHistory();
var completion = CompletionProvider.GetChatCompletion(_services,
provider: plannerAgent.LlmConfig.Provider,
model: plannerAgent.LlmConfig.Model);
return await completion.GetChatCompletions(plannerAgent, wholeDialogs);
}
private async Task<string> GetPrompt(RoleDialogModel message)
{
var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>();
var aiAssistant = await agentService.GetAgent(BuiltInAgentId.AIAssistant);
var template = aiAssistant.Templates.FirstOrDefault(x => x.Name == "database_knowledge")?.Content ?? string.Empty;
return render.Render(template, new Dictionary<string, object>
{
{ "table_structure", message.Content }
});
}
}

View file

@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using MySqlConnector;
using static Dapper.SqlMapper;
@ -7,10 +8,14 @@ public class GetTableDefinitionFn : IFunctionCallback
{
public string Name => "get_table_definition";
private readonly IServiceProvider _services;
private readonly ILogger<GetTableDefinitionFn> _logger;
public GetTableDefinitionFn(IServiceProvider services)
public GetTableDefinitionFn(
IServiceProvider services,
ILogger<GetTableDefinitionFn> logger)
{
_services = services;
_logger = logger;
}
public async Task<bool> Execute(RoleDialogModel message)
@ -20,37 +25,47 @@ public class GetTableDefinitionFn : IFunctionCallback
var settings = _services.GetRequiredService<SqlDriverSetting>();
// Get table DDL from database
var tables = message.Data as List<string>;
if (tables.IsNullOrEmpty()) return false;
var tableDdls = new List<string>();
using var connection = new MySqlConnection(settings.MySqlConnectionString);
connection.Open();
foreach (var table in (List<string>)message.Data)
foreach (var table in tables)
{
var escapedTableName = MySqlHelper.EscapeString(table);
var sql = $"select * from information_schema.tables where table_name = @tableName";
var result = connection.QueryFirstOrDefault(sql, new
try
{
tableName = escapedTableName
});
var escapedTableName = MySqlHelper.EscapeString(table);
if (result == null) continue;
var sql = $"select * from information_schema.tables where table_name = @tableName";
var result = connection.QueryFirstOrDefault(sql, new
{
tableName = escapedTableName
});
sql = $"SHOW CREATE TABLE `{escapedTableName}`";
using var command = new MySqlCommand(sql, connection);
using var reader = command.ExecuteReader();
if (reader.Read())
{
result = reader.GetString(1);
tableDdls.Add(result);
if (result == null) continue;
sql = $"SHOW CREATE TABLE `{escapedTableName}`";
using var command = new MySqlCommand(sql, connection);
using var reader = command.ExecuteReader();
if (reader.Read())
{
result = reader.GetString(1);
tableDdls.Add(result);
}
reader.Close();
command.Dispose();
}
catch (Exception ex)
{
_logger.LogWarning($"Error when getting ddl statement of table {table}.");
}
reader.Close();
command.Dispose();
}
connection.Close();
message.Content = string.Join("\r\n", tableDdls);
message.Content = string.Join("\r\n\r\n", tableDdls);
return true;
}
}

View file

@ -94,7 +94,7 @@ public class DbKnowledgeService
using var connection = new MySqlConnection(settings.MySqlConnectionString);
connection.Open();
var result = string.Empty;
var ddl = string.Empty;
var escapedTableName = MySqlHelper.EscapeString(table);
var sql = $"SHOW CREATE TABLE `{escapedTableName}`";
@ -102,13 +102,13 @@ public class DbKnowledgeService
using var reader = command.ExecuteReader();
if (reader.Read())
{
result = reader.GetString(1);
ddl = reader.GetString(1);
}
reader.Close();
command.Dispose();
connection.Close();
return result;
return ddl;
}
private async Task<string> GetPrompt(string content)