add: 解析语法树和对话机器人

This commit is contained in:
jackfiled 2023-10-13 15:49:24 +08:00
parent 9c27d534ce
commit ccaefdc6cc
6 changed files with 256 additions and 5 deletions

View File

@ -12,15 +12,16 @@ public class DefaultChatRobot : IChatRobot
_logger = logger; _logger = logger;
} }
public string RobotName => "凯瑟琳"; public string RobotName => "Default";
public IEnumerable<string> OnChatStart() public IEnumerable<string> OnChatStart()
{ {
_logger.LogDebug("Start default chat robot."); _logger.LogDebug("Start default chat robot.");
return new[] return new[]
{ {
"向着星辰与深渊!", "坏了,被你发现默认机器人了。",
"欢迎来到冒险家协会。" "使用这个粪机器人,怎么能得高分呢?",
"必须要出重拳!"
}; };
} }
@ -29,7 +30,7 @@ public class DefaultChatRobot : IChatRobot
_logger.LogDebug("End default chat robot."); _logger.LogDebug("End default chat robot.");
return new[] return new[]
{ {
"再见,感谢您对协会做出的贡献,冒险家。" "我不到啊。"
}; };
} }
@ -38,7 +39,7 @@ public class DefaultChatRobot : IChatRobot
_logger.LogDebug("Robot receive message: \"{}\".", input); _logger.LogDebug("Robot receive message: \"{}\".", input);
return new[] return new[]
{ {
"暂时不支持该功能,请联系维护人员。" "啊对对对。"
}; };
} }
} }

View File

@ -0,0 +1,22 @@
namespace Katheryne.Exceptions;
/// <summary>
/// 语法中存在的错误
/// </summary>
public class GrammarException : Exception
{
public GrammarException() : base()
{
}
public GrammarException(string message) : base(message)
{
}
public GrammarException(string message, Exception innerException) : base(message, innerException)
{
}
}

View File

@ -0,0 +1,54 @@
using Katheryne.Abstractions;
using Katheryne.Models;
using Katheryne.Services;
using Microsoft.Extensions.Logging;
using YamlDotNet.Serialization;
namespace Katheryne;
public class KatheryneChatRobot : IChatRobot
{
private readonly ILogger<KatheryneChatRobot> _logger;
private readonly GrammarTree _grammarTree;
private string _currentStage;
public KatheryneChatRobot(GrammarTree grammarTree, ILogger<KatheryneChatRobot> logger,
string beginStage, string robotName)
{
_logger = logger;
_grammarTree = grammarTree;
_currentStage = beginStage;
RobotName = robotName;
}
public string RobotName { get; }
public IEnumerable<string> OnChatStart()
{
return new[]
{
_grammarTree[_currentStage]
};
}
public IEnumerable<string> OnChatStop()
{
return new[]
{
"再见。"
};
}
public IEnumerable<string> ChatNext(string input)
{
_logger.LogDebug("Receive input {} on stage {}.", input, _currentStage);
(_currentStage, string answer) = _grammarTree.NextStage(_currentStage, input);
_logger.LogDebug("Change stage to {}.", _currentStage);
return new[]
{
answer
};
}
}

View File

@ -0,0 +1,109 @@
using System.Text.RegularExpressions;
using Katheryne.Exceptions;
namespace Katheryne.Models;
public class GrammarTree
{
private readonly Dictionary<string, InnerStage> _stages = new();
public GrammarTree(LexicalModel model)
{
foreach (Stage stage in model.Stages)
{
_stages[stage.Name] = new InnerStage(stage);
}
if (!Validate(model))
{
throw new GrammarException("使用了未声明的阶段名");
}
}
/// <summary>
/// 获得下一个阶段
/// </summary>
/// <param name="currentStage">当前所在阶段</param>
/// <param name="input">用户输入</param>
/// <returns>元组,第一个参数是下一个阶段名称 第二个参数是机器人回答</returns>
/// <exception cref="GrammarException"></exception>
public (string, string) NextStage(string currentStage, string input)
{
List<InnerTransformer> transformers = _stages[currentStage].Transformers;
foreach (InnerTransformer transformer in transformers)
{
Match match = transformer.Pattern.Match(input);
if (match.Success)
{
return (_stages[transformer.NextStage].Name,
_stages[transformer.NextStage].Answer);
}
}
throw new GrammarException("Failed to get next stage.");
}
public string this[string index] => _stages[index].Answer;
/// <summary>
/// 主要验证语法的两个特点
/// 1. 起始阶段是否被定义
/// 2. 每个Transformer的下一个阶段是否被定义
/// </summary>
/// <returns></returns>
private bool Validate(LexicalModel model)
{
if (!_stages.ContainsKey(model.BeginStageName))
{
return false;
}
return model.Stages.All((stage) =>
{
return stage.Transformers.All(t => _stages.ContainsKey(t.NextStageName));
});
}
private class InnerStage
{
public string Name { get; }
public List<InnerTransformer> Transformers { get; } = new();
public string Answer { get; }
public InnerStage(Stage stage)
{
Name = stage.Name;
Answer = stage.Answer;
foreach (Transformer transformer in stage.Transformers)
{
Transformers.Add(new InnerTransformer(transformer));
}
}
}
private class InnerTransformer
{
public Regex Pattern { get; }
public string NextStage { get; }
public InnerTransformer(Transformer transformer)
{
NextStage = transformer.NextStageName;
try
{
Pattern = new Regex(transformer.Pattern);
}
catch (ArgumentException e)
{
throw new GrammarException($"Failed to Parse regex:{transformer.Pattern}.", e);
}
}
}
}

View File

@ -0,0 +1,14 @@
using Katheryne.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Katheryne;
public static class ServiceCollectionExtensions
{
public static void AddYamlDeserializerFactory(this IServiceCollection collection)
{
collection.AddSingleton<YamlDeserializerFactory>();
collection.AddSingleton<DefaultChatRobot>();
collection.AddScoped<KatheryneChatRobotFactory>();
}
}

View File

@ -0,0 +1,51 @@
using Katheryne.Abstractions;
using Katheryne.Models;
using Microsoft.Extensions.Logging;
using YamlDotNet.Serialization;
namespace Katheryne.Services;
public class KatheryneChatRobotFactory
{
private readonly YamlDeserializerFactory _deserializerFactory;
private readonly ILogger<KatheryneChatRobotFactory> _factoryLogger;
private readonly ILogger<KatheryneChatRobot> _robotLogger;
private readonly DefaultChatRobot _defaultChatRobot;
private Grammar? _grammar;
public KatheryneChatRobotFactory(YamlDeserializerFactory deserializerFactory,
ILogger<KatheryneChatRobotFactory> factoryLogger,
ILogger<KatheryneChatRobot> robotLogger,
DefaultChatRobot defaultChatRobot)
{
_deserializerFactory = deserializerFactory;
_factoryLogger = factoryLogger;
_robotLogger = robotLogger;
_defaultChatRobot = defaultChatRobot;
}
public void SetGrammar(string grammarText)
{
_factoryLogger.LogInformation("Receive new grammar: {}.", grammarText);
IDeserializer deserializer = _deserializerFactory.GetDeserializer();
LexicalModel model = deserializer.Deserialize<LexicalModel>(grammarText);
_grammar = new Grammar(new GrammarTree(model), model.RobotName, model.BeginStageName);
}
public IChatRobot GetRobot()
{
if (_grammar is null)
{
_factoryLogger.LogDebug("Get default chat robot.");
return _defaultChatRobot;
}
_factoryLogger.LogDebug("Get chat robot: {}.", _grammar.RobotName);
return new KatheryneChatRobot(_grammar.GrammarTree, _robotLogger,
_grammar.BeginStage, _grammar.RobotName);
}
private record Grammar(GrammarTree GrammarTree, string RobotName, string BeginStage);
}