add: 解析语法树和对话机器人
This commit is contained in:
parent
9c27d534ce
commit
ccaefdc6cc
|
@ -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[]
|
||||||
{
|
{
|
||||||
"暂时不支持该功能,请联系维护人员。"
|
"啊对对对。"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
22
Katheryne/Exceptions/GrammarException.cs
Normal file
22
Katheryne/Exceptions/GrammarException.cs
Normal 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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
54
Katheryne/KatheryneChatRobot.cs
Normal file
54
Katheryne/KatheryneChatRobot.cs
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
109
Katheryne/Models/GrammarTree.cs
Normal file
109
Katheryne/Models/GrammarTree.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Katheryne/ServiceCollectionExtensions.cs
Normal file
14
Katheryne/ServiceCollectionExtensions.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
51
Katheryne/Services/KatheryneChatRobotFactory.cs
Normal file
51
Katheryne/Services/KatheryneChatRobotFactory.cs
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user