【问题标题】:Most effective way to match commands when developing a chatbot?开发聊天机器人时匹配命令的最有效方法是什么?
【发布时间】:2018-02-10 17:09:34
【问题描述】:

我正在通过 Messenger 平台 API 创建一个简单的聊天机器人,但我几乎不知道如何有效地识别机器人可以响应的一组命令。我目前正在使用 switch 语句来检测以感叹号开头的命令(例如 !showlist; !additem <single/set of parameter(s)>)。

这是我目前拥有的:

switch(true){
            case stristr($receivedMsg,'!additem'):
....
}

在代码的任何匹配阶段,要么执行一组语句,要么先推断最终参数,然后使用它们执行一些语句。

我在上述设置中遇到的问题如下:

  • 在无参数命令的情况下,即使命令拼写错误,也可以获得相关代码来执行。例如。 !additem#$%% 仍将调用 switch 语句中的实际命令代码。
  • 对于带有参数的命令,在检索这些参数时使用以下语句:

    $item=str_replace('!additem', '', $receivedMsg);
    

    在参数中包含不需要的文本很容易;您可以使用trim() 处理空格,或者暗示总会有空格并编辑上述语句以将其包含在函数中。例如。

    $item=str_replace('!additem ', '', $receivedMsg);
    

    但这会在尝试将命令与参数分开时出现其他问题。

我知道解决方案可以使用系统的字符串操作函数进行硬编码,但这对我来说似乎不正确。在这种情况下人们会怎么做?难道没有一种特定的方法可以精确匹配命令并安全地发现最终用户的拼写错误吗?

【问题讨论】:

  • 参数是否用引号括起来?还是空格字符是分隔符?
  • @revo 没有。我希望单个空格字符作为分隔符(例如,在命令之后,所以 !additem{space}param)。然后如果有多个参数,我希望逗号作为分隔符。
  • 为什么要为错位的命令烦恼?这会导致问题。
  • @Havenard 你所说的“错位命令”是什么意思?
  • @MarcoDufal 没关系,我想我看错了那里的东西,无论如何你不应该为拼写错误的命令而烦恼,如果我们要开始尝试猜测用户到底想用他的无能做什么为了提供具有正确语法的命令,我们将需要进入人工智能领域。这绝对不是机器人的操作方式。

标签: php regex string string-matching chatbot


【解决方案1】:

您没有在自己的解决方案中使用正则表达式,但对其进行了正确标记。通过stristr() 函数,我发现您不是在寻找更多即将到来的命令,因此我将相同的逻辑应用于 RegEx:

$msg = 'Gonna add it !additem param1,param2';
preg_match('~(!\S+)\s*(.*)~', $msg, $match);
$command = $match[1];
$parameters = preg_split('~\s*,\s*~', $match[2]);

我尝试过单线,但后来认为这样会更干净。顺便说一句,我想知道 switch 语句。

正则表达式细分:

~   # regex delimiter
    (   # Start of Capturing Group (1)
        !\S+    # Match non-space characters that start with !
    )   # End of CG1
    \s* # Any number of white-sapce characters
    (   # Start of CG2
        .*  # Match up to end
    )   # End of CG2
~   # regex delimiter

preg_split 也接收一个正则表达式作为它的第一个参数并尝试对其进行拆分,几乎是一个带有正则表达式的explode\s*,\s* 表示可以包含在任意数量的空格中的逗号。

【讨论】:

  • 出于某种原因,我已经避免正则表达式太久了。我认为是时候让我了解更多关于它们的信息了……好吧,我给你解决了,但我当然不太了解正则表达式,但暂时不要担心这个。我是否正确地说删除~ 只会匹配以! 开头的文本?另外,如果我想在逗号后面留一个空格怎么办?我应该严格定义命令还是允许某些变体,例如可能的额外空格等?
  • 感谢您的分解!您是否正在使用捕获组,因为您已将 $match 作为参数传递?还有为什么需要~
【解决方案2】:
if ($receivedMsg[0] == '!')
    switch (strtolower(substr(reset(explode(' ', $receivedMsg)), 1)))
    {
        case 'additem':
            // do this
            break;
        case 'delitem':
            // do that
            break;
        default:
            echo 'Command not recognized.';
    }

嗯,这是一种方法。您还可以使用处理每个命令的函数声明一个数组,例如:

$handles = [
    'additem' = function ($args) { /* add something */ },
    'delitem' = function ($args) { /* del something */ },
    // ...
];

if ($receivedMsg[0] == '!')
{
    $args = explode(' ', $receivedMsg);
    $cmd  = strtolower(substr($args[0], 1));
    if (isset($handles[$cmd]))
        $handles[$cmd]($args);
    else
        echo 'Command not recognized.';
}

【讨论】:

  • 第一个代码sn-p就像欢迎大家Notices!
  • 我更喜欢第二种解决方案。似乎比我的 switch 方法更整洁,它也使用函数式编程方面!
  • @revo 我明白你的意思了......谢谢你们俩。你给了我两个不止有效的解决方案!我认为在某种程度上对我有用的是两者的混合......
  • @revo 好吧,我想在测试第一个字符是否为“!”之前,您必须确保消息长度不为零,除此之外我不确定您的意思。
【解决方案3】:

根据@Havenhard 和@revo 提供的答案,我编写了以下非常适合我的解决方案:

$this->senderId = $messaging['sender']['id'];
$command_handlers = [
        'additem' => "addItemCommand",
        'showlist' => "showListCommand",
        'rngroup' => "renameGroupCommand"
    ];

    $actionCompletedOrh = new OutRequestHandler($this->senderId);

    if(!empty($messaging['message']) && empty($messaging['message']['quick_reply'])){
        $receivedMsg = $messaging['message']['text'];
        $replyMsg = "";
        $this->performSenderAction(0);
        //isCommand uses this regex to perform the evaluation 
        //(^!\w+\s+([\w,\s]*$)|^!\w+$)"
        if($this->isCommand($receivedMsg)){
            //regex matching to get params in raw form
            preg_match("~(^!\w+\s+([\w,\s]*$)|^!\w+$)~",$receivedMsg,$match);
            //regex matching to get the command
            preg_match("~^!\w+~",$match[0],$_match); 
            $command = strtolower($_match[0]);
            $params = null; 
            if(count($match)>2){
                //the function below uses preg_split as in @revo's example
                $params = $this->getCommandParams($match[2]);
            }
            if(array_key_exists(substr($command,1), $command_handlers)){
                $func = $command_handlers[substr($command,1)];
                $replyMsg=$this->$func($params,$connection);
            }
            else{
                $replyMsg=$this->getPromptMessage("cmer1");
            }
        }
        else{
            //All other messages - possibly processed with NPL
        }
        $this->performSenderAction(2);
        $replyMsg = json_encode($replyMsg);
        $actionCompletedOrh->sendJustTextMessage($replyMsg,$access_token);
    }

你觉得我有什么可以改进的吗?请让我知道 cmets 中的内容和原因!

【讨论】:

    猜你喜欢
    • 2021-04-15
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    • 2017-07-08
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    • 2016-08-05
    相关资源
    最近更新 更多