[微信机器人_05]会话管理

上一篇已经说明了自然语言处理的简单实现方法,在此基础上,如何实现一个机器人与我们交流,是这篇文章需要讨论的问题。希望这个机器人不算太笨。^_^


微信的交互方式是触发式的,由用户发起会话,一问一答。所有的设计都需要基于这个模式。

 

会话管理机制

想想平时交流时的行为,我们会根据对方的每句话来逐渐建立起一个语境,即对话上下文。根据不同的语境,即使是同一个问题,也会有不同的答案。

基于这个想法,设计一个简单的会话管理机制,即每个用户有一个独立的对话上下文context,每个context会有一个状态来标志当前的语境。

1、会话状态管理

  • 为使每个会话拥有独立的语境,每个上下文拥有自己的状态。
  • 根据用户不同的请求,将会话切换为不同的状态。
  • 根据当前的状态,机器人选择不同的回答方式或答案。
  • 当前的实现比较简单,只有如下五种状态

public enumContextStateEnum {

         WAIT_QUERY,                    //等待询问

         WAIT_LEARN,                    //等待学习

         WAIT_CONFIRM,               //等待确认

         WAIT_WEATHER_CITY,  //等待指定需要查询天气的城市

         WAIT_MUSIC_INFO;        //等待指定需要搜索的歌曲信息

}


2、创建与删除会话

  • 当用户发起会话时,创建一个属于该用户的上下文。
  • 考虑到系统负载,每个会话拥有超时时间,并且设定一个最大会话数。
  • 当会话数达到上限时,删除超时的会话。如果没有超时的会话,则不接受新的会话请求。



3、会话处理流程

有了这个基本思路,再来看看每个会话的处理流程。

我将用户的问题分为两类,非功能性问题和功能性问题:

  1. 非功能性:这类问题的答案由用户调教获得,存储在知识库中,查询本地数据库即可回答,用户可通过调教修改问题答案。
  2. 功能性:这类问题的答案通过访问外部API获得,比如查询天气,搜索音乐。Context状态中,WAIT_WEATHER_CITY, WAIT_MUSIC_INFO两个状态属于功能性问题的状态。

 Context的状态切换如下图:


  • 会话创建时,状态初始化为WAIT_QUERY。
  • 用户发起请求后,如果处于WAIT_QUERY状态则首先要判断是否是功能性问题(通过匹配关键字来判断)。
  • FUNC_WAIT代表功能性问题的状态,如WAIT_WEATHER_CITY, WAIT_MUSIC_INFO。
  • 对于非功能性问题,用户通过调教可以修改答案,其流程如下图。

                                


Context模块与其他模块的关系

  • Context模块提供会话接口给微信适配层调用,用于处理用户请求。
  • 处理非功能性问题时就需要调用自然语言处理模块的接口。
  • 处理功能性问题时就调用外部API适配层提供的接口。
  • 还有一些会话信息需要保存到数据库,调用DAO模块的接口。

 

一些扩展的设想

当前的实现比较简单,如果继续扩展,可以加入一个解析模块。解析模块负责分析会话中的聊天记录,context模块根据分析结果切换会话状态,比如机器人可以有开心、愤怒、悲伤等状态,在不同的状态下,即使是同一个问题机器人也可以选择不同的回复。当然,这需要把知识库中的答案进行归类,机器人才能根据状态选择对应的答案。


实现过程中碰到的问题

问题:分布式服务器导致机器人状态机混乱

最初设计context保存在内存中,定义一个map容器,以用户ID作为key,context结构体作为value。

本地运行没有问题,但是发布到BAE服务器后,问题来了,通过微信与奇迹蛋robot对话,发现奇迹蛋的context状态机混乱,行为非预期。并且问题发生具有一定的随机性。

开始一直以为是多线程操作map引起的问题,在程序中又加了单例,满怀期待的发布了,结果还是不行。

折腾了挺久,最后在网上找到了一个比较靠谱的解释,参考:http://blog.csdn.net/ostrichmyself/article/details/8098119,“3.1 静态变量无法常驻内存”。

简单来说,由于BAE是分布式服务器,我们部署在上面的web应用会被放在多个容器里执行,而这些容器的内存是非共享的,如下图:


  • 用户发起会话A,机器人程序在web容器A中被执行,此时判断为一个新用户访问,申请内存创建MapA,保存本次会话信息。
  • 同一个用户,继续发起会话B,此时分布式服务器把机器人程序放在web容器B中执行,该容器中并没有MapA,该用户再次被作为一个新用户处理,创建MapB。
  • 这样用户会话的状态不能有效的保持,导致状态机混乱。

弄清楚了原因就好办了,有两种解决方法:

1. 使用BAE的cache服务,创建共享内存

    研究了一下,要调用BAE的API,本地调试不方便,加上BAE的文档看的蛋疼,放弃了。

2. 使用数据库来保存

    放弃使用内存的方式,用mysql来保存context map。尽管增加了数据库操作,不过用户也没几个,无所谓了。

 

奇迹蛋的主要设计思路基本就介绍完了,整个系统比较简单,有些功能设计了但是没有去实现。主要目的还是学习一下java web的开发方法,也顺便了解了解时下最火的微信APP。

Have fun~

 

下篇文章会聊一些代码实现中碰到的问题,并符上源码。


谢谢关注奇迹蛋~扫一下&调戏之


猜你喜欢

转载自blog.csdn.net/elcarim/article/details/19765627