190 lines
6.5 KiB
C#
190 lines
6.5 KiB
C#
using BotSharp.Core.Agents;
|
|
using BotSharp.Core.Entities;
|
|
using BotSharp.Core.Intents;
|
|
using BotSharp.Core.Models;
|
|
using EntityFrameworkCore.BootKit;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace BotSharp.Core.Engines
|
|
{
|
|
/// <summary>
|
|
/// Bot engine/ platform base class
|
|
/// </summary>
|
|
public abstract class BotEngineBase
|
|
{
|
|
protected Database dc;
|
|
|
|
public AIConfiguration AiConfig { get; set; }
|
|
|
|
protected Agent agent { get; set; }
|
|
|
|
public String DbInitializerPath { get; private set; }
|
|
|
|
public BotEngineBase()
|
|
{
|
|
dc = new DefaultDataContextLoader().GetDefaultDc();
|
|
DbInitializerPath = $"{Database.ContentRootPath}App_Data{Path.DirectorySeparatorChar}DbInitializer{Path.DirectorySeparatorChar}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load Agent
|
|
/// </summary>
|
|
/// <param name="id">agentId, clientAccessToken, developerAccessToken</param>
|
|
/// <returns></returns>
|
|
public Agent LoadAgent(string id)
|
|
{
|
|
if (agent == null)
|
|
{
|
|
agent = dc.Table<Agent>()
|
|
.Include(x => x.Intents).ThenInclude(x => x.Contexts)
|
|
.Include(x => x.Entities).ThenInclude(x => x.Entries).ThenInclude(x => x.Synonyms)
|
|
.Include(x => x.MlConfig)
|
|
.FirstOrDefault(x => x.Id == id ||
|
|
x.ClientAccessToken == id ||
|
|
x.DeveloperAccessToken == id);
|
|
}
|
|
else
|
|
{
|
|
return agent;
|
|
}
|
|
|
|
return agent;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore a agent instance from backup json files
|
|
/// </summary>
|
|
/// <param name="importer"></param>
|
|
/// <param name="agentHeader"></param>
|
|
/// <param name="dataDir"></param>
|
|
/// <returns></returns>
|
|
public bool RestoreAgent<TAgentImporter>(AgentImportHeader agentHeader) where TAgentImporter : IAgentImporter, new()
|
|
{
|
|
var importer = new TAgentImporter();
|
|
|
|
string dataDir = $"{DbInitializerPath}Agents{Path.DirectorySeparatorChar}";
|
|
|
|
int row = dc.DbTran(() => {
|
|
|
|
// Load agent summary
|
|
agent = importer.LoadAgent(agentHeader, dataDir);
|
|
|
|
// Load user custom entities
|
|
importer.LoadCustomEntities(agent, dataDir);
|
|
|
|
// Load agent intents
|
|
importer.LoadIntents(agent, dataDir);
|
|
|
|
// Load system buildin entities
|
|
importer.LoadBuildinEntities(agent, dataDir);
|
|
|
|
SaveAgent();
|
|
});
|
|
|
|
return row > 0;
|
|
}
|
|
|
|
public String SaveAgent()
|
|
{
|
|
var existedAgent = dc.Table<Agent>().FirstOrDefault(x => x.Id == agent.Id || x.Name == agent.Name);
|
|
if (existedAgent == null)
|
|
{
|
|
dc.Table<Agent>().Add(agent);
|
|
return agent.Id;
|
|
}
|
|
else
|
|
{
|
|
agent.Id = existedAgent.Id;
|
|
return existedAgent.Id;
|
|
}
|
|
}
|
|
|
|
public TrainingCorpus GetIntentExpressions()
|
|
{
|
|
TrainingCorpus corpus = new TrainingCorpus()
|
|
{
|
|
UserSays = new List<TrainingIntentExpression<TrainingIntentExpressionPart>>(),
|
|
Entities = new List<TrainingEntity>()
|
|
};
|
|
|
|
var expressParts = new List<IntentExpressionPart>();
|
|
|
|
var intents = dc.Table<Intent>()
|
|
.Include(x => x.Contexts)
|
|
.Include(x => x.UserSays).ThenInclude(say => say.Data)
|
|
.Where(x => x.AgentId == agent.Id && x.UserSays.Count > 0)
|
|
.ToList();
|
|
|
|
intents.ForEach(intent =>
|
|
{
|
|
intent.UserSays.ForEach(exp =>
|
|
{
|
|
var say = new TrainingIntentExpression<TrainingIntentExpressionPart>
|
|
{
|
|
Intent = intent.Name,
|
|
Text = String.Join("", exp.Data.OrderBy(x => x.UpdatedTime).Select(x => x.Text)),
|
|
ContextHash = intent.ContextHash
|
|
};
|
|
|
|
// convert entity format
|
|
exp.Data.Where(x => !String.IsNullOrEmpty(x.Meta))
|
|
.ToList()
|
|
.ForEach(x =>
|
|
{
|
|
int start = say.Text.IndexOf(x.Text);
|
|
|
|
var part = new TrainingIntentExpressionPart
|
|
{
|
|
Value = x.Text,
|
|
Entity = $"{x.Meta}:{x.Alias}",
|
|
Start = start,
|
|
End = start + x.Text.Length
|
|
};
|
|
|
|
if (say.Entities == null) say.Entities = new List<TrainingIntentExpressionPart>();
|
|
say.Entities.Add(part);
|
|
|
|
// assemble entity synonmus
|
|
/*if (!trainingData.Entities.Any(y => y.EntityType == x.Alias && y.EntityValue == x.Text))
|
|
{
|
|
var allSynonyms = (from e in dc.Table<EntityType>()
|
|
join ee in dc.Table<EntityEntry>() on e.Id equals ee.EntityId
|
|
join ees in dc.Table<EntrySynonym>() on ee.Id equals ees.EntityEntryId
|
|
where e.Name == x.Alias && ee.Value == x.Text & ees.Synonym != x.Text
|
|
select ees.Synonym).ToList();
|
|
|
|
var te = new TrainingEntity
|
|
{
|
|
EntityType = $"{x.Meta}:{x.Alias}",
|
|
EntityValue = x.Text,
|
|
Synonyms = allSynonyms
|
|
};
|
|
|
|
trainingData.Entities.Add(te);
|
|
}*/
|
|
});
|
|
|
|
corpus.UserSays.Add(say);
|
|
});
|
|
});
|
|
|
|
// remove Default Fallback Intent
|
|
corpus.UserSays = corpus.UserSays.Where(x => x.Intent != "Default Fallback Intent").ToList();
|
|
|
|
return corpus;
|
|
}
|
|
|
|
public virtual void Train()
|
|
{
|
|
var trainer = new BotTrainer(agent.Id, dc);
|
|
trainer.Train(agent);
|
|
}
|
|
|
|
}
|
|
}
|