开发聊天机器人时匹配命令的最有效方法?

Most effective way to match commands when developing a chatbot?

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

这是我目前拥有的:

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

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

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

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

您没有在自己的解决方案中使用正则表达式,但正确标记了它。通过 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* 表示逗号可以包含在任意数量的空格中。

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.';
}

我的解决方案基于@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);
    }

你觉得我有什么可以改进的地方吗?请在评论中告诉我内容和原因!