2018-10-03 01:44:11 +00:00
|
|
|
|
using BotSharp.Core.Engines;
|
|
|
|
|
|
using BotSharp.Platform.Abstraction;
|
2018-09-28 22:25:55 +00:00
|
|
|
|
using BotSharp.Platform.Models;
|
2018-10-24 21:57:48 +00:00
|
|
|
|
using BotSharp.Platform.Models.AiRequest;
|
|
|
|
|
|
using BotSharp.Platform.Models.AiResponse;
|
2018-10-25 04:54:26 +00:00
|
|
|
|
using BotSharp.Platform.Models.Entities;
|
2018-10-25 22:17:51 +00:00
|
|
|
|
using BotSharp.Platform.Models.Intents;
|
2018-10-04 21:18:46 +00:00
|
|
|
|
using BotSharp.Platform.Models.MachineLearning;
|
2018-10-01 17:15:17 +00:00
|
|
|
|
using DotNetToolkit;
|
2018-10-03 01:44:11 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2018-10-25 22:17:51 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2018-09-28 22:25:55 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2018-10-03 01:44:11 +00:00
|
|
|
|
using System.IO;
|
2018-10-01 17:15:17 +00:00
|
|
|
|
using System.Linq;
|
2018-10-25 22:17:51 +00:00
|
|
|
|
using System.Net.Http;
|
2018-09-28 22:25:55 +00:00
|
|
|
|
using System.Text;
|
2018-10-04 21:18:46 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2018-09-28 22:25:55 +00:00
|
|
|
|
|
|
|
|
|
|
namespace BotSharp.Core
|
|
|
|
|
|
{
|
2018-10-01 17:15:17 +00:00
|
|
|
|
public abstract class PlatformBuilderBase<TAgent> where TAgent : AgentBase
|
2018-09-28 22:25:55 +00:00
|
|
|
|
{
|
2018-10-24 21:57:48 +00:00
|
|
|
|
public TAgent Agent { get; set; }
|
|
|
|
|
|
|
2018-10-01 17:15:17 +00:00
|
|
|
|
public IAgentStorage<TAgent> Storage { get; set; }
|
|
|
|
|
|
|
2018-10-11 13:24:50 +00:00
|
|
|
|
private readonly IAgentStorageFactory<TAgent> agentStorageFactory;
|
2018-10-13 04:12:21 +00:00
|
|
|
|
private readonly IPlatformSettings settings;
|
2018-09-28 22:25:55 +00:00
|
|
|
|
|
2018-10-13 04:12:21 +00:00
|
|
|
|
public PlatformBuilderBase(IAgentStorageFactory<TAgent> agentStorageFactory, IPlatformSettings settings)
|
2018-10-05 06:39:39 +00:00
|
|
|
|
{
|
|
|
|
|
|
this.agentStorageFactory = agentStorageFactory;
|
2018-10-13 04:12:21 +00:00
|
|
|
|
this.settings = settings;
|
2018-10-05 06:39:39 +00:00
|
|
|
|
}
|
2018-09-28 22:25:55 +00:00
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
public async Task<List<TAgent>> GetAllAgents()
|
2018-09-28 22:25:55 +00:00
|
|
|
|
{
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await GetStorage();
|
2018-10-01 17:15:17 +00:00
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
return await Storage.Query();
|
2018-09-28 22:25:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
public async Task<TAgent> LoadAgentFromFile<TImporter>(string dataDir) where TImporter : IAgentImporter<TAgent>, new()
|
2018-10-03 01:44:11 +00:00
|
|
|
|
{
|
2018-10-22 03:47:51 +00:00
|
|
|
|
Console.WriteLine($"Loading agent from folder {dataDir}");
|
|
|
|
|
|
|
2018-10-03 01:44:11 +00:00
|
|
|
|
var meta = LoadMeta(dataDir);
|
2018-10-05 06:39:39 +00:00
|
|
|
|
var importer = new TImporter
|
|
|
|
|
|
{
|
|
|
|
|
|
AgentDir = dataDir
|
|
|
|
|
|
};
|
2018-10-03 01:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Load agent summary
|
2018-10-05 06:39:39 +00:00
|
|
|
|
var agent = await importer.LoadAgent(meta);
|
2018-10-03 01:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Load user custom entities
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await importer.LoadCustomEntities(agent);
|
2018-10-03 01:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Load agent intents
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await importer.LoadIntents(agent);
|
2018-10-03 01:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
// Load system buildin entities
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await importer.LoadBuildinEntities(agent);
|
2018-10-03 01:44:11 +00:00
|
|
|
|
|
2018-10-22 03:47:51 +00:00
|
|
|
|
Console.WriteLine($"Loaded agent: {agent.Name} {agent.Id}");
|
|
|
|
|
|
|
2018-10-24 21:57:48 +00:00
|
|
|
|
Agent = agent;
|
|
|
|
|
|
|
2018-10-03 06:00:07 +00:00
|
|
|
|
return agent;
|
2018-10-03 01:44:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private AgentImportHeader LoadMeta(string dataDir)
|
|
|
|
|
|
{
|
|
|
|
|
|
// load meta
|
|
|
|
|
|
string metaJson = File.ReadAllText(Path.Combine(dataDir, "meta.json"));
|
|
|
|
|
|
|
|
|
|
|
|
return JsonConvert.DeserializeObject<AgentImportHeader>(metaJson);
|
|
|
|
|
|
}
|
2018-09-28 22:25:55 +00:00
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
public async Task<TAgent> GetAgentById(string agentId)
|
2018-09-28 22:25:55 +00:00
|
|
|
|
{
|
2018-10-01 17:15:17 +00:00
|
|
|
|
GetStorage();
|
|
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
return await Storage.FetchById(agentId);
|
2018-09-28 22:25:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
public async Task<TAgent> GetAgentByName(string agentName)
|
2018-09-28 22:25:55 +00:00
|
|
|
|
{
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await GetStorage();
|
2018-10-01 17:15:17 +00:00
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
return await Storage.FetchByName(agentName);
|
2018-09-28 22:25:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-04 21:18:46 +00:00
|
|
|
|
public virtual async Task<ModelMetaData> Train(TAgent agent, TrainingCorpus corpus, BotTrainOptions options)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (String.IsNullOrEmpty(options.AgentDir))
|
|
|
|
|
|
{
|
|
|
|
|
|
options.AgentDir = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Projects", agent.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (String.IsNullOrEmpty(options.Model))
|
|
|
|
|
|
{
|
|
|
|
|
|
options.Model = "model_" + DateTime.UtcNow.ToString("yyyyMMdd");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-24 21:57:48 +00:00
|
|
|
|
ModelMetaData meta = null;
|
|
|
|
|
|
|
|
|
|
|
|
// train by contexts
|
|
|
|
|
|
corpus.UserSays.GroupBy(x => x.ContextHash).Select(g => new
|
|
|
|
|
|
{
|
|
|
|
|
|
Context = g.Key,
|
|
|
|
|
|
Corpus = new TrainingCorpus
|
|
|
|
|
|
{
|
|
|
|
|
|
Entities = corpus.Entities,
|
|
|
|
|
|
UserSays = corpus.UserSays.Where(x => x.ContextHash == g.Key).ToList()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.ToList()
|
|
|
|
|
|
.ForEach(async c =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var trainer = new BotTrainer(settings);
|
|
|
|
|
|
agent.Corpus = c.Corpus;
|
|
|
|
|
|
|
|
|
|
|
|
meta = await trainer.Train(agent, new BotTrainOptions
|
|
|
|
|
|
{
|
|
|
|
|
|
AgentDir = options.AgentDir,
|
|
|
|
|
|
Model = options.Model + $"{Path.DirectorySeparatorChar}{c.Context}"
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
meta.Pipeline.Clear();
|
|
|
|
|
|
meta.Model = options.Model;
|
|
|
|
|
|
|
|
|
|
|
|
return meta;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual async Task<TResult> TextRequest<TResult>(AiRequest request)
|
|
|
|
|
|
{
|
|
|
|
|
|
string contexts = String.Join("_", request.Contexts);
|
|
|
|
|
|
string contextHash = contexts.GetMd5Hash();
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"TextRequest: {request.Text}, {contexts}, {request.SessionId}");
|
|
|
|
|
|
|
|
|
|
|
|
// Load agent
|
|
|
|
|
|
var projectPath = Path.Combine(AppDomain.CurrentDomain.GetData("DataPath").ToString(), "Projects", request.AgentId);
|
|
|
|
|
|
var model = Directory.GetDirectories(projectPath).Where(x => x.Contains("model_")).Last().Split(Path.DirectorySeparatorChar).Last();
|
|
|
|
|
|
var modelPath = Path.Combine(projectPath, model);
|
|
|
|
|
|
request.AgentDir = projectPath;
|
|
|
|
|
|
request.Model = model + $"{Path.DirectorySeparatorChar}{contextHash}";
|
2018-10-04 21:18:46 +00:00
|
|
|
|
|
2018-10-24 21:57:48 +00:00
|
|
|
|
Agent = await GetAgentById(request.AgentId);
|
2018-10-04 21:18:46 +00:00
|
|
|
|
|
2018-10-24 21:57:48 +00:00
|
|
|
|
var preditor = new BotPredictor();
|
|
|
|
|
|
var doc = await preditor.Predict(Agent, request);
|
|
|
|
|
|
|
2018-10-25 22:17:51 +00:00
|
|
|
|
var predictedIntent = doc.Sentences[0].Intent;
|
|
|
|
|
|
|
|
|
|
|
|
if (predictedIntent.Confidence < Agent.MlConfig.MinConfidence)
|
2018-10-24 21:57:48 +00:00
|
|
|
|
{
|
2018-10-26 02:30:59 +00:00
|
|
|
|
predictedIntent = await FallbackResponse(request);
|
2018-10-24 21:57:48 +00:00
|
|
|
|
|
2018-10-26 02:30:59 +00:00
|
|
|
|
predictedIntent.Confidence = Agent.MlConfig.MinConfidence;
|
|
|
|
|
|
predictedIntent.Label = "fallback";
|
|
|
|
|
|
|
|
|
|
|
|
Agent.Intents.Add(new Intent
|
2018-10-25 22:17:51 +00:00
|
|
|
|
{
|
2018-10-26 02:30:59 +00:00
|
|
|
|
Name = predictedIntent.Label,
|
|
|
|
|
|
Responses = new List<IntentResponse>
|
2018-10-25 22:17:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
new IntentResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
IntentName = predictedIntent.Label,
|
|
|
|
|
|
Messages = new List<IntentResponseMessage>
|
|
|
|
|
|
{
|
|
|
|
|
|
new IntentResponseMessage
|
|
|
|
|
|
{
|
2018-10-26 02:30:59 +00:00
|
|
|
|
Speech = "\"" + predictedIntent.Text + "\"",
|
2018-10-25 22:17:51 +00:00
|
|
|
|
Type = AIResponseMessageType.Text
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-10-26 02:30:59 +00:00
|
|
|
|
});
|
2018-10-25 22:17:51 +00:00
|
|
|
|
}
|
2018-10-24 21:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
var aiResponse = new AiResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
ResolvedQuery = request.Text,
|
|
|
|
|
|
Score = predictedIntent.Confidence,
|
|
|
|
|
|
Source = predictedIntent.Classifier,
|
2018-10-25 04:54:26 +00:00
|
|
|
|
Intent = predictedIntent.Label,
|
|
|
|
|
|
Entities = doc.Sentences[0].Entities
|
2018-10-24 21:57:48 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"TextResponse: {aiResponse.Intent}, {request.SessionId}");
|
|
|
|
|
|
|
|
|
|
|
|
return await AssembleResult<TResult>(aiResponse);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-26 02:30:59 +00:00
|
|
|
|
public virtual async Task<TextClassificationResult> FallbackResponse(AiRequest request)
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = new
|
|
|
|
|
|
{
|
|
|
|
|
|
token = "openbot",
|
|
|
|
|
|
info = request.Text
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
using (var client = new HttpClient())
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = await client.PostAsync(
|
|
|
|
|
|
"https://api.ownthink.com/bot",
|
|
|
|
|
|
new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"));
|
|
|
|
|
|
var content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
var result = JsonConvert.DeserializeObject<JObject>(content);
|
|
|
|
|
|
|
|
|
|
|
|
return new TextClassificationResult
|
|
|
|
|
|
{
|
|
|
|
|
|
Classifier = "ownthink",
|
|
|
|
|
|
Text = result["text"].ToString()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-24 21:57:48 +00:00
|
|
|
|
public virtual async Task<TResult> AssembleResult<TResult>(AiResponse response)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new NotImplementedException();
|
2018-10-04 21:18:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
public virtual async Task<bool> SaveAgent(TAgent agent)
|
2018-09-28 22:25:55 +00:00
|
|
|
|
{
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await GetStorage();
|
2018-10-01 17:15:17 +00:00
|
|
|
|
|
2018-09-28 22:25:55 +00:00
|
|
|
|
// default save agent in FileStorage
|
2018-10-05 06:39:39 +00:00
|
|
|
|
await Storage.Persist(agent);
|
2018-09-28 22:25:55 +00:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2018-10-01 17:15:17 +00:00
|
|
|
|
|
2018-10-05 06:39:39 +00:00
|
|
|
|
protected async Task<IAgentStorage<TAgent>> GetStorage()
|
2018-10-01 17:15:17 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (Storage == null)
|
|
|
|
|
|
{
|
2018-10-11 13:24:50 +00:00
|
|
|
|
Storage = await agentStorageFactory.Get();
|
2018-10-01 17:15:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
return Storage;
|
|
|
|
|
|
}
|
2018-09-28 22:25:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|