最近借用了女友的公号,感受若是只是用来发文章,太浪费微信给提供的这些功能了。想了想,先从最简单的开始,作一个聊天机器人吧。html
使用Python实现聊天机器人的方案有多种:AIML、chatterBot以及图灵聊天机器人和微软小冰等。python
考虑到之后可能会作一些定制化的需求,这里我选择了chatterBot
(github 项目地址:https://github.com/gunthercox/ChatterBot)。git
chatterbot是一款python接口的,基于一系列规则和机器学习算法完成的聊天机器人。具备结构清晰,可扩展性好,简单实用的特色。程序员
chatterBot 的工做流程如图:github
输入模块(input adapter)从终端或者API等输入源获取数据web
输入源会被指定的逻辑处理模块(logic Adapter)分别处理,逻辑处理模块会匹配训练集中已知的最接近输入数据句子A,而后根据句子A去找到相关度最高的结果B,若是有多个逻辑处理模块返回了不一样的结果,会返回一个相关度最高的结果。算法
输出模块(output adapter)将匹配到的结果返回给终端或者API。mongodb
值得一说的是chatterBot 是一个模块化的项目,分为 input Adapter、logic Adapter、storage Adapter、output Adapter以及Trainer 模块。json
logic Adapter是一个插件式设计,主进程在启动时会将用户定义的全部逻辑处理插件添加到logic context中,而后交MultiLogicAdapter 进行处理,MultiLogicAdapter 依次调用每一个 logic Adapter,logic Adapter 被调用时先执行can_process 方式判断输入是否能够命中这个逻辑处理插件。好比”今每天气怎么样“这样的问题显然须要命中天气逻辑处理插件,这时时间逻辑处理插件的can_process 则会返回False。在命中后logic Adapter 负责计算出对应的回答(Statement对象)以及可信度(confidence),MultiLogicAdapter会取可信度最高的回答,并进入下一步。浏览器
下面咱们来看下 chatterBot 如何使用
chatterBot 是使用Python编写的,可使用 pip 安装:
pip install chatterbot
chatterBot 的中文对话要求Python3 以上版本,建议在Python3.x 环境下开发
打开iPython,输入测试一下
In [1]: from chatterbot import ChatBot # import ChatBot In [2]: momo = ChatBot('Momo', trainer='chatterbot.trainers.ChatterBotCorpusTrainer') /Users/gs/.virtualenvs/py3/lib/python3.6/site-packages/chatterbot/storage/jsonfile.py:26: UnsuitableForProductionWarning: The JsonFileStorageAdapter is not recommended for production environments. self.UnsuitableForProductionWarning # 这里storage adapter 默认使用的是 json 格式存储数据的,若是想在服务端部署,应该避免使用这种格式,由于实在是太慢了 In [3]: momo.train("chatterbot.corpus.chinese") # 指定训练集,这里咱们使用中文 # 下边是对话结果 In [4]: momo.get_response('你好') Out[4]: <Statement text:你好> In [5]: momo.get_response('怎么了') Out[5]: <Statement text:没什么.> In [6]: momo.get_response('你知道它的全部内容吗?') Out[6]: <Statement text:优美胜于丑陋.> In [7]: momo.get_response('你是一个程序员吗?') Out[7]: <Statement text:我是个程序员> In [8]: momo.get_response('你使用什么语言呢?') Out[8]: <Statement text:我常用 Python, Java 和 C++ .>
这时你已经能够和机器人对话了,不过如今因为训练数据太少,机器人只能返回简单的对话。
这里是默认的中文对话训练数据 中文训练数据地址:https://github.com/gunthercox/chatterbot-corpus/tree/master/chatterbot_corpus/data/chinese。
那么咱们怎么添加训练数据呢?
chatterBot 内置了training class,自带的方法有两种,一种是使用经过输入list 来训练,好比 ["你好", "我很差"],后者是前者的回答,另外一种是经过导入Corpus 格式的文件来训练。也支持自定义的训练模块,不过最终都是转为上述两种类型。
chatterBot 经过调用 train() 函数训练,不过在这以前要先用 set_trainer() 来进行设置。例如:
In [12]: from chatterbot.trainers import ListTrainer # 导入训练模块的 ListTrainer 类 In [13]: momo.get_response('你叫什么?') # 如今是答非所问,由于在这以前咱们并无训练过 Out[13]: <Statement text:我在烤蛋糕.> In [14]: momo.set_trainer(ListTrainer) # 指定训练方式 In [15]: momo.train(['你叫什么?', '我叫魔魔!']) # 训练 In [16]: momo.get_response('你叫什么?') # 如今机器人已经能够回答了 Out[16]: <Statement text:我叫魔魔!>
训练好的数据默认存在 ./database.db,这里使用的是 jsondb。
对 chatterBot 的介绍先到这里,具体用法能够参考文档:ChatterBot Tutorial:http://chatterbot.readthedocs.io/en/stable/tutorial.html
接下来,介绍如何在项目中使用 chatterBot。
Sanic 是一个和类Flask 的基于Python3.5+的web框架,它编写的代码速度特别快。
除了像Flask 之外,Sanic 还支持以异步请求的方式处理请求。这意味着你可使用新的 async/await 语法,编写非阻塞的快速的代码。
对 Sanic 不了解的能够参考我以前的一篇文章: python web 框架 Sanci 快速入门,能够在公号输入 【sanic】获取文章地址。
这里之因此使用 Sanic 是由于他和Flask 很是像,以前我一直使用Flask,而且它也是专门为Python3.5 写的,使用到了协程。
首先建个项目,这里项目我已经建好了,项目结构以下:
. ├── LICENSE ├── README.md ├── manage.py # 运行文件 启动项目 使用 python manage.py 命令 ├── momo │ ├── __init__.py │ ├── app.py # 建立app 模块 │ ├── helper.py │ ├── settings.py # 应用配置 │ └── views │ ├── __init__.py │ ├── hello.py # 测试模块 │ └── mweixin.py # 微信消息处理模块 ├── requirements.txt └── supervisord.conf
源码我已经上传到github,有兴趣的能够看一下,也能够直接拉下来测试。
项目代码地址
咱们先重点看下 hello.py
文件 和 helper.py
。
# hello.py # -*- coding: utf-8 -*- from sanic import Sanic, Blueprint from sanic.views import HTTPMethodView from sanic.response import text from momo.helper import get_momo_answer # 导入获取机器人回答获取函数 blueprint = Blueprint('index', url_prefix='/') class ChatBot(HTTPMethodView): # 聊天机器人 http 请求处理逻辑 async def get(self, request): ask = request.args.get('ask') # 先获取url 参数值 若是没有值,返回 '你说啥' if ask: answer = get_momo_answer(ask) return text(answer) return text('你说啥?') blueprint.add_route(ChatBot.as_view(), '/momo')
# helper.py from chatterbot import ChatBot momo_chat = ChatBot( 'Momo', # 指定存储方式 使用mongodb 存储数据 storage_adapter='chatterbot.storage.MongoDatabaseAdapter', # 指定 logic adpater 这里咱们指定三个 logic_adapters=[ "chatterbot.logic.BestMatch", "chatterbot.logic.MathematicalEvaluation", # 数学模块 "chatterbot.logic.TimeLogicAdapter", # 时间模块 ], input_adapter='chatterbot.input.VariableInputTypeAdapter', output_adapter='chatterbot.output.OutputAdapter', database='chatterbot', read_only=True ) def get_momo_answer(content): # 获取机器人返回结果函数 response = momo_chat.get_response(content) if isinstance(response, str): return response return response.text
运行命令 python manage.py
启动项目。
在浏览器访问url: http://0.0.0.0:8000/momo?ask=你是程序员吗
到这里,咱们已经启动了一个web 项目,能够经过访问url 的方式和机器人对话,是时候接入微信公号了!
拥有一个可使用的微信公众号(订阅号服务号均可以,若是没有,可使用微信提供的测试帐号)
拥有一个外网能够访问的服务器(vps 或公有云均可以 aws 新用户无偿使用一年,能够试试)
服务器配置了python3 环境,(建议使用 virtualenvwrapper 配置虚拟环境)
登陆微信公众号: https://mp.weixin.qq.com
查看公号开发信息:
设置请求url,这里是你配置的url(须要外网可访问,只能是80或443端口)
填写token和EncodingAESKey,这里我选择的是兼容模式,既有明文方便调试,又有信息加密。
详细配置能够参考官方文档:接入指南
若是你的 服务器地址
已经配置完成,如今点击提交应该就成功了。若是没有成功咱们接下来看怎么配置服务器地址。
先看下 微信请求的视图代码:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from six import StringIO import re import xmltodict from chatterbot.trainers import ListTrainer from sanic import Blueprint from sanic.views import HTTPMethodView from sanic.response import text from sanic.exceptions import ServerError from weixin import WeixinMpAPI from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt from momo.settings import Config blueprint = Blueprint('weixin', url_prefix='/weixin') class WXRequestView(HTTPMethodView): def _get_args(self, request): # 获取微信请求参数,加上token 拼接为完整的请求参数 params = request.raw_args if not params: raise ServerError("invalid params", status_code=400) args = { 'mp_token': Config.WEIXINMP_TOKEN, 'signature': params.get('signature'), 'timestamp': params.get('timestamp'), 'echostr': params.get('echostr'), 'nonce': params.get('nonce'), } return args def get(self, request): # 微信验证服务器这一步是get 请求,参数可使用 request.raw_args 获取 args = self._get_args(request) weixin = WeixinMpAPI(**args) # 这里我使用了 第三方包 python-weixin 能够直接实例化一个WeixinMpAPI对象 if weixin.validate_signature(): # 验证参数合法性 # 若是参数争取,咱们将微信发过来的echostr参数再返回给微信,不然返回 fail return text(args.get('echostr') or 'fail') return text('fail') blueprint.add_route(WXRequestView.as_view(), '/request')
这里处理微信请求我使用的是 我用python 写的 微信SDK python-weixin,可使用 pip 安装:
pip install python-weixin
这个包最新版本对Python3 加密解密有点问题,能够直接从github 安装:
pip install git+https://github.com/zongxiao/python-weixin.git@py3
而后更新 app.py 文件:
# -*- coding: utf-8 -*- from sanic import Sanic from momo.settings import Config def create_app(register_bp=True, test=False): # 建立app app = Sanic(__name__) if test: app.config['TESTING'] = True # 从object 导入配置 app.config.from_object(Config) register_blueprints(app) return app def register_blueprints(app): from momo.views.hello import blueprint as hello_bp from momo.views.mweixin import blueprint as wx_bp app.register_blueprint(hello_bp) # 注册 wx_bp app.register_blueprint(wx_bp)
详细代码参考github: 微信聊天机器人 momo
如今咱们公号已经接入了本身的服务,是时候接入微信聊天机器人。
微信聊天机器人的工做流程以下:
看咱们消息逻辑处理代码:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from six import StringIO import re import xmltodict from chatterbot.trainers import ListTrainer from sanic import Blueprint from sanic.views import HTTPMethodView from sanic.response import text from sanic.exceptions import ServerError from weixin import WeixinMpAPI from weixin.reply import TextReply from weixin.response import WXResponse as _WXResponse from weixin.lib.WXBizMsgCrypt import WXBizMsgCrypt from momo.settings import Config from momo.helper import validate_xml, smart_str, get_momo_answer from momo.media import media_fetch blueprint = Blueprint('weixin', url_prefix='/weixin') appid = smart_str(Config.WEIXINMP_APPID) token = smart_str(Config.WEIXINMP_TOKEN) encoding_aeskey = smart_str(Config.WEIXINMP_ENCODINGAESKEY) # 关注后自动返回的文案 AUTO_REPLY_CONTENT = """ Hi,朋友! 这是我妈四月的公号,我是魔魔,我能够陪你聊天呦! 我还能"记帐",输入"记帐"会有惊喜呦! <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzAwNjI5MjAzNw==&scene=124#wechat_redirect">历史记录</a> """ class ReplyContent(object): _source = 'value' def __init__(self, event, keyword, content=None, momo=True): self.momo = momo self.event = event self.content = content self.keyword = keyword if self.event == 'scan': pass @property def value(self): if self.momo: answer = get_momo_answer(self.content) return answer return '' class WXResponse(_WXResponse): auto_reply_content = AUTO_REPLY_CONTENT def _subscribe_event_handler(self): # 关注公号后的处理逻辑 self.reply_params['content'] = self.auto_reply_content self.reply = TextReply(**self.reply_params).render() def _unsubscribe_event_handler(self): # 取关后的处理逻辑,取关我估计会哭吧 pass def _text_msg_handler(self): # 文字消息处理逻辑 聊天机器人的主要逻辑 event_key = 'text' content = self.data.get('Content') reply_content = ReplyContent('text', event_key, content) self.reply_params['content'] = reply_content.value self.reply = TextReply(**self.reply_params).render() class WXRequestView(HTTPMethodView): def _get_args(self, request): params = request.raw_args if not params: raise ServerError("invalid params", status_code=400) args = { 'mp_token': Config.WEIXINMP_TOKEN, 'signature': params.get('signature'), 'timestamp': params.get('timestamp'), 'echostr': params.get('echostr'), 'nonce': params.get('nonce'), } return args def get(self, request): args = self._get_args(request) weixin = WeixinMpAPI(**args) if weixin.validate_signature(): return text(args.get('echostr') or 'fail') return text('fail') def _get_xml(self, data): post_str = smart_str(data) # 验证xml 格式是否正确 validate_xml(StringIO(post_str)) return post_str def _decrypt_xml(self, params, crypt, xml_str): # 解密消息 nonce = params.get('nonce') msg_sign = params.get('msg_signature') timestamp = params.get('timestamp') ret, decryp_xml = crypt.DecryptMsg(xml_str, msg_sign, timestamp, nonce) return decryp_xml, nonce def _encryp_xml(self, crypt, to_xml, nonce): # 加密消息 to_xml = smart_str(to_xml) ret, encrypt_xml = crypt.EncryptMsg(to_xml, nonce) return encrypt_xml def post(self, request): # 获取微信服务器发送的请求参数 args = self._get_args(request) weixin = WeixinMpAPI(**args) if not weixin.validate_signature(): # 验证参数合法性 raise AttributeError("Invalid weixin signature") xml_str = self._get_xml(request.body) # 获取form data crypt = WXBizMsgCrypt(token, encoding_aeskey, appid) decryp_xml, nonce = self._decrypt_xml(request.raw_args, crypt, xml_str) # 解密 xml_dict = xmltodict.parse(decryp_xml) xml = WXResponse(xml_dict)() or 'success' # 使用WXResponse 根据消息获取机器人返回值 encryp_xml = self._encryp_xml(crypt, xml, nonce) # 加密消息 return text(encryp_xml or xml) # 回应微信请求 blueprint.add_route(WXRequestView.as_view(), '/request')
能够看到,我处理微信请求返回结果比较简单,也是使用的 python-weixin 包封装的接口,
主要的处理逻辑是 WXResponse。
这里须要注意的是,若是服务器在5秒内没有响应微信服务器会重试。为了加快响应速度,不要在服务器 将 chatterBot 的 storage adapter 设置为使用 jsondb。
上边这些就是,微信聊天机器人的主要处理逻辑,咱们运行服务,示例以下:
能够看到这里聊天机器人也能够作简单的数学运算和报时,是由于我在上边指定处理逻辑的时候添加了数学模块和时间模块:
momo_chat = ChatBot( 'Momo', # 指定存储方式 使用mongodb 存储数据 storage_adapter='chatterbot.storage.MongoDatabaseAdapter', # 指定 logic adpater 这里咱们指定三个 logic_adapters=[ "chatterbot.logic.BestMatch", "chatterbot.logic.MathematicalEvaluation", # 数学模块 "chatterbot.logic.TimeLogicAdapter", # 时间模块 ], input_adapter='chatterbot.input.VariableInputTypeAdapter', output_adapter='chatterbot.output.OutputAdapter', database='chatterbot', read_only=True )
到这里,微信机器人的搭建就完成了,详细代码已经长传到了 github: https://github.com/gusibi/momo/tree/chatterbot,感兴趣的能够参考一下。
ChatterBot Tutorial:http://chatterbot.readthedocs.io/en/stable/tutorial.html
基于Python-ChatterBot搭建不一样adapter的聊天机器人:https://ask.hellobi.com/blog/guodongwei1991/7626
擁有自動學習的 Python 機器人 - ChatterBot:https://kantai235.github.io/2017/03/16/ChatterBotTeaching/
使用 ChatterBot构建聊天机器人:https://www.biaodianfu.com/chatterbot.html
这里,聊天机器人仍是比较简单的只能回复简单的对话,下一篇将要结束如何在公号训练机器人以及一个更实用的功能,如何让公号变成一个博客写做助手。
最后,感谢女友支持。
欢迎关注(April_Louisa) | 请我喝芬达 |
---|---|
![]() |
![]() |