很久没写了,本来打算就“谢邀”,写点爬虫的内容,但似乎这块比较敏感,就不单独开篇写了,以后写其他内容或群里遇到讨论,就瞎聊两句算了。而这篇,也是源自群友一个偶然的“谢邀”(调百度接口,指定用Flask做web服务器做个展示),于是趁周日有空,瞎写一篇,顺便聊聊“架构”、“工具选择”,以及学习方向和学习成本的问题。

首先,直接上作业题,给代码,然后再聊废话。

作业题:调用一个免费外部接口,NLP或图像方面的都行,然后用Flask做一个服务器,语言当然是用python,把接口返回的东西展示出来。

需求给的很明确,那咱就挑个简单的,聊天机器人呗,百度、黄小鸭、图灵都有接口,当然是选“大厂”百度了。如果没有百度账号,就注册一个,进百度AI平台:https://ai.baidu.com/,登录好账号之后,在控制台界面,寻找并点击这个红框里面的东西:智能对话定制与服务平台UNIT。

创建应用,拿到API Key和Secret Key:

点击上面图片中的“UNIT配置平台”,并点击下个页面中的“进入UNIT”

然后新建一个机器人,记住ID号:

点击刚刚新建的机器人,在技能管理里面,添加技能,在列表中选取免费的(大部分免费),然后绑定给机器人

之前已经绑定了2个技能了,现在新增一个“电影”技能

选择技能,添加给机器人,实现绑定

技能列表中已经出现新技能,电影,这些技能的ID号要记住,调用接口的时候要用。

准备工作做完了,现在进入写代码环节,一个Flask主程序作为后端,一个前端(使用Flask官方定义的目录结构,放置静态页面和JS脚本,以及CSS样式)。

数据流程是这样的:

1、用JS的ajax技术,发请求(本例请求中主要是对话内容)给Flask后端;

2、再由Flask后端调用百度的API;

3、Flask后端收到百度API的返回数据后,返回给前端的ajax;

4、再由ajax渲染到静态页面上。

这是一个数据流程。N轮及多用户对话,就是重复这个流程。

整个项目的文件结构如下:

jquery-3.5.1.min.js需要自己下载,网上很多资源,下载好了直接用就行,本例用它是因为要用ajax提交请求,否则用form提交的话,稍微有点low,实在拿不出手……,放在static/JS目录下

sytle.css放在static/CSS目录下,文件内容如下:

@charset "utf-8";
body { margin:0; padding:0; width:100%;}
html { padding:0; margin:0;}.shadow-box {width: 100%;height: 100%;position: fixed;top: 0;left: 0;background: rgba(0,0,0,0.3);z-index: 9999;}
.shadow {position: absolute;top: 50%;left: 50%;margin: -380px 0 0 -400px;}
.shadow-bg {width: 800px;height: 800px;position: absolute;background: #ffffff;border-radius: 5px;padding: 5px 5px 5px 5px;}
.shadow-bg .echart_bar{position: relative;border: 1px solid #CCC;margin: 10px 0 0 10px;padding-top: 0px;}
.shadow-bg .echart_block_1{position: relative;border: 1px solid #CCC;margin: 10px 0 0 10px;padding-top: 0px;float: left;width: 286px;height:440px;}
.shadow-bg .echart_block_1 ul { text-align:right; padding:0; margin:0; list-style:none; border:0;}
.shadow-bg .echart_block_1 ul li { float:left; margin:0; padding:0 5px; border:0; height:30px;font-size:12px;}
.shadow-bg .echart_block_2{position: relative;border: 1px solid #CCC;margin: 10px 0 0 10px;padding-top: 0px;float: left;width: 784px;height:300px;}
.shadow-bg .title{float: left;width: 800px;height: 30px;line-height: 30px;position: relative;}.shadow-assis {position: absolute;top: 50%;left: 50%;margin: -180px 0 0 -200px;}
.shadow-bg-assis {width: 400px;height: 360px;position: absolute;background: #ffffff;border-radius: 5px;padding: 5px 5px 5px 5px;}
.shadow-bg-assis .title{float: left;width: 400px;height: 30px;line-height: 30px;position: relative;}
/* .assis-box {position: relative;width: 300px;height: 310px;margin: 10px 0 0 10px;border: 1px solid cornflowerblue;padding-top: 20px;} */
/* .shadow-bg-assis .assis-box {position: relative;width: 300px;height: 310px;border: 1px solid #CCC;margin: 10px 0 0 10px;padding-top: 0px;} */
.shadow-bg-assis .assis-box01 {width: 380px;height: 250px;overflow: auto;margin: 10px;border: 1px solid #CECECE;padding-top: 5px;}
.shadow-bg-assis .assis-txt {width: 330px;height: 18px;margin-left: 10px;}

如果不怎么讲究的话,完全可以不用CSS,都是样式,没啥好说的

index.html文件,放置在templates目录下,内容如下:

<html><head><title>proj_1_flask</title><link rel="stylesheet" type="text/css" href="/CSS/style.css"/><script src="/JS/jquery-3.5.1.min.js"></script> </head><body><!-- 百度UNIT调用 --><div class="shadow-box" id="assistantArea" style="display: block;"><div class="shadow-assis"><div class="shadow-bg-assis" id="containerArea"><div class="title-assis"><span class="t_txt" id="assistantArea_title">百度UNIT调用</span></div><div id="box01" class="assis-box01"></div><input type="text" id="txt" class="assis-txt" placeholder="ctrl+Enter发送" /><input type="button" id="btn" value="发送" /></div></div></div><script type="text/javascript">var oBox = document.getElementById("box01");var oTxt = document.getElementById("txt");var oBtn = document.getElementById("btn");function msg() {var val = oTxt.value;// oBox.innerHTML += '<p>' + val + '</p>'// oBox.scrollTop = oBox.scrollHeight; //让滚动条一直处于底部oTxt.value=""; //发送完成侯情况输入框$.ajax({url: "/Baidu_Unit_Chat",type: "POST", dataType: "json",async: true,data: {"sender_name": 'your_name', "send_text": val},beforeSend: function(XMLHttpRequest){// Handle the beforeSend eventvar p = document.createElement("p");p.setAttribute("style", "color:#0080C1;");  p.innerHTML = 'From me:' + valoBox.appendChild(p); oBox.scrollTop = oBox.scrollHeight; //让滚动条一直处于底部console.time('global');},success: function(res){console.log(res);var str = res.text;var p = document.createElement("p");// p.setAttribute("style", "color:#0080C1;");  p.innerHTML = 'From AI:' + stroBox.appendChild(p);oBox.scrollTop = oBox.scrollHeight; //让滚动条一直处于底部console.timeEnd('global');}});}oBtn.onclick = msg;document.onkeydown = function(e) {var e = e || window.event;if (e.ctrlKey && e.keyCode == 13 ||e.keyCode ==13) {msg();}}
</script></body>
</html>

这段里面,ajax的部分稍微讲讲,对ajax不熟的人可以了解一下

红框和线条画出来的地方注意一下:

冒号前面的,是ajax内置的属性,不能改,照写上去。

url是待会提交给Flask处理的地址

type是post,这是百度api要求的

async: true,代表是“异步”提交请求。效果是你说的话,提交后马上显示在页面上,百度api返回的话,等它返回之后再显示在页面上。

如果是同步,效果就是,你要等百度返回值之后,把你说的话和百度返回的话,一起显示出来。

data就是你提交的一个字典结构,可以定义你想定义的所有内容进去,这里只定义2个作为示例,而且主要是send_text有用,send_name提交后并未做处理,就直接返回了,如果要做用户管理和鉴权,需要用到这个,可以自己扩充一下。

beforeSend和async: true配合,就是上面说的那个效果。

success是返回后发生的事,参数res就是Flask处理后,返回的内容。

最后,就是Flask了,这里的文件是Flask_Main.py,代码如下:

# coding:utf-8import requests
import json
from flask import Flask, render_template, request
app = Flask(__name__, static_url_path='')class UNIT:def __init__(self, api_key, api_secret):self.access_token = Noneself.url = Noneself.set_access_token(api_key, api_secret)def set_access_token(self, api_key, api_secret):host = 'https://aip.baidubce.com/oauth/2.0/token?' \'grant_type=client_credentials&' \'client_id={0}&' \'client_secret={1}'.format(api_key, api_secret)response = requests.post(host)if response:self.access_token = response.json()['access_token']def query(self, query_text):self.url = 'https://aip.baidubce.com/rpc/2.0/unit/service/chat?access_token=' + self.access_token'''提交字段说明:详情见百度AI开发中心UNIT文档中的【请求参数详细说明】"log_id": "UNITTEST_10000",  # 随便写"version": "2.0",  # 必须写2.0"service_id": "Sxxxx",  # 填你自己百度AI平台申请UNIT的机器人id"skill_ids": ["1076657", "1076658"],  # 申请机器人的技能,把技能id写这里,这两个技能是尬聊和问候,需要其他技能自己去百度拿,大部分免费"session_id": "",  # ssession保存机器人的历史会话信息,由机器人创建,客户端从上轮应答中取出并直接传递,不需要了解其内容。如果为空,则表示清空session"request": {"query": "%s",  # 你的输入"user_id": "88888"  # 与技能对话的用户id},"dialog_state": {"contexts": {"SYS_REMEMBERED_SKILLS": ["1076657"]}  # dialog_state.contexts["SYS_REMEMBERED_SKILLS"]中设定一个技能ID列表,避免处于列表中的技能session被清空,就可以基于历史会话信息与机器人进行多轮对话,而无需接触复杂的原始session。}'''post_data = """{"log_id": "UNITTEST_10000","version": "2.0","service_id": "Sxxxx","skill_ids": ["1076657", "1076658"],"session_id": "","request": {"query": "%s","user_id": "88888"},"dialog_state": {"contexts": {"SYS_REMEMBERED_SKILLS": ["1076657"]}}}""" % query_textpost_data = post_data.encode('utf-8')headers = {'content-type': 'application/x-www-form-urlencoded'}response = requests.post(self.url, data=post_data, headers=headers)print(response.json())if response:return response.json()['result']['response_list'][0]['action_list'][0]['say']unit = UNIT('your_api_key', 'your_api_secret')@app.route('/')
def index():return render_template('index.html')@app.route('/Baidu_Unit_Chat', methods=['post'])
def ai_talk():rst_dict = {}send_text = request.form.get('send_text')sender_name = request.form.get('sender_name')out_str = unit.query(send_text)  # 调用api的返回值if out_str is None:rst_dict['status'] = 'failure'else:rst_dict['status'] = 'success'rst_dict['from_whom'] = sender_namerst_dict['text'] = out_strai_say = json.dumps(rst_dict)return ai_sayif __name__ == '__main__':app.run(debug=True)

注释都写得很清楚了,需要找百度拿的就是下面几个值

类UNIT内的函数:

your_api_key和your_api_secret,文章开头有怎么拿的方法

函数set_access_token在UNIT类实例化的时候执行,根据上面两个值,生成token,这个token下面要用,这个token值用函数获取,是实时变化的。

函数query的参数query_text,就是前端ajax送过来的对话信息

类UNIT在Flask运行之初即实例化加载,然后提供持久服务。

类UNIT外的函数:

index函数不用说了

ai_talk函数,处理前端提交到/Baidu_Unit_Chat的请求,本例中,是和ajax配合工作。该函数中send_text = request.form.get('send_text')获得ajax送过来的对话信息,交给unit.query(send_text)处理,并赋值给out_str,返回的字典经过json打包后,发送给前端ajax,并由ajax解析后渲染到页面上。

至此,完成一个数据流程。(对应看文章前半部分关于流程的描述,观察程序实现的过程)。

最后效果放一个,用图片形式偷偷说下“爬虫”,悟一下……

因为加了电影技能,试了下,然后……百度api的效果怎样,就不讨论了……

Flask上的提示

至此,正文就完了,下面开始讲废话:

简单说说这个nlp的制作原理,我也做过内核部分,流程类似,算是番外篇吧,有兴趣就了解一下:

1、设置一些列场景,就是百度定义的“技能”,在每个场景中,找打量相关语料,进行训练,每个场景一个对话模型

2、对输入的文本,先进行场景判断,场景的数量是有限的,用文本分类做模型,判断输入文本属于哪个场景,就调用哪个场景的对话模型

3、多轮对话方面,设置session保存,同一个用户,保存前几轮的对话内容,以便判断聊天主题需要转换,也可以提供同一用户在停止聊天后,保持上一轮的语境,以便下次聊天时迅速切入,或判断是否需要转换话题

4、以上是用模型进行的选择和对话,我称之为“软切换”

5、设计“词槽”,做关键字匹配,我称之为“硬切换”,这个可以迅速提升效果,缺点是,太low……

6、还有其他很多办法,各自开脑洞

……

差不多就这样

1、Flask是不是一个好web服务器?

是个好web服务器,但由于和python绑定太深,缺点也很多,比如如果一个项目是由多人合作完成,项目组内有人用java,有人用jsp,有人用C/C++,Flask就不是一个好的选择了,而且速度方面,一直是个问题。当然,有人觉得,对于学习来说,无所谓,可以用就行。嗯,学习阶段是可以用,但熟练以后,我相信自然就会放弃Flask,至少是主框架不会再用Flask。

2、前面我重点介绍的其实是数据流这个问题,无论是对这种小demo,还是非常大的项目,都要习惯用数据流来思考架构问题,把复杂的问题,拆解成简单问题,然后每个问题,用最适合的工具去处理,最后就形成了所谓的架构

3、在选用工具方面,要尽量的选择通用性较强的工具,学习新工具,也要学习通用性强的工具,长期坚持下去,自己的工具箱才会强大,才会在处理问题的时候,信手拈来,搭建靠谱的架构。因为,无论是通用性强还是弱,对于工程问题来说,学习成本差别不会太大。

废话也讲完了,打完收工。

用一个Flask的小例子来聊聊架构和工具选择相关推荐

  1. Java基础。public,private,static变量!以及一个实例化的小例子 以及方法

    public,适用范围最广! private,仅仅方法内部可以使用!创建出来的对象也是不可以使用的! 代码: public class mmm12333 {public int a = 1111;pr ...

  2. 决策树分析例题经典案例_决策树原理及一个简单的小例子

    首先通过两个图来引入什么是决策树. 是否学习的决策过程 决策树是仿树结构来进行决策的,例如上图来说,我们要对'是否学习'这个问题进行决策时,通常伴随一系列的子决策.先看是否有'对象',有的话是否需要' ...

  3. 模拟一个火车站售票小例子

    1.车票 属性:起始站.终点站.票价 2.系统12306 属性:集合 方法 3.窗口window 多线程 package system12306;/*** @author liuxian*/ publ ...

  4. 一个有趣的小例子,带你入门协程模块-asyncio

    上篇文章写了关于yield from的用法,简单的了解异步模式,[上次的内容链接]这次让我们通过一个有趣例子带大家了解asyncio基本使用. 目标效果图 在控制台中显示一个由ASCII字符" ...

  5. Hadoop中RPC协议小例子报错java.lang.reflect.UndeclaredThrowableException解决方法

    最近在学习传智播客吴超老师的Hadoop视频,里面他在讲解RPC通信原理的过程中给了一个RPC的小例子,但是自己编写的过程中遇到一个小错误,整理如下: log4j:WARN No appenders ...

  6. win32 实现死锁的小例子

    死锁的一种情况是两个线程竞争两个锁,需要同时拿到两个锁才能执行,然后出现了两个线程各拿一个锁的情况,这样两个线程就都无法继续执行,称为死锁. 避免死锁的方法有很多,有预防死锁,出现死锁后通过某些方法释 ...

  7. JointJS简单小例子

    JointJS是一个HTML5的JavaScript库,用于创建完全互动式的图表,它极易上手且操作简单,并且支持所有的现代浏览器,对于时间紧迫的我们非常有利.我们可以使用JointJS已提供的图元素绘 ...

  8. 简单的 Java 导出 Excel 表格 小例子《一抹茶CSDN》

    Java 导出 Excel 为什么要有导出Excel表格的功能呢? 因为我们在使用软件时会有,一些数据需要导出来,进行留存,大多数人使用的都是office的办公软件,就会使用常用的Excel表格.因此 ...

  9. OOM问题小例子及思考

    目录 一.背景介绍 二.思路&方案 三.过程 四.总结 五.升华 一.背景介绍 前段时间朋友新进入一个公司,遇到线上系统cpu飙高,内存占满情况:和他交流过后有了本篇总结,该博文会以一个简单的 ...

最新文章

  1. python笔记基础-python学习笔记之基础一(第一天)
  2. Bitbucket Pipelines在Atlassian的Bitbucket云上提供持续交付功能
  3. 【NLP-语义匹配】详解深度语义匹配模型DSSM
  4. Windows7 IIS7.5 HTTP Error 503 The service is unavailable 另类解决方案
  5. MySQL 8.0 数据字典有哪些变化?
  6. lintcode 734. 形式为a^i b^j c^k的子序列数量 题解
  7. jetpack的camerax_Android开发-Jetpack组件CameraX
  8. 从零开始的全栈工程师——underscore
  9. 2019 蓝桥杯省赛 B 组模拟赛(一) J. 程序设计:蒜厂年会 环形连续子序列求和问题
  10. 解放双手,基于github travis-ci docker自动化部署java项目
  11. 『TensorFlow』批处理类
  12. React-Native Fetch使用Promise封装(一)
  13. marvell raid linux,华硕P7F-M (-MARVELL 88SE6145 SATA RAID)主板驱动-版下载,适用于win7,Win7-64,winxp-驱动精灵...
  14. pmp 第六版 模拟卷1疑难问题
  15. Javashop 支持全业务模式电商系统
  16. STM32单片机PT100温度采集控制系统
  17. 给自定义Dialog加入保留对话框值的功能
  18. java espresso车架,只爱钢架公路:Casati Espresso
  19. NR PRACH (七)Type 2(2-step) RA 参数及相关规定
  20. Geoserver 发布wmts服务,以及cesium加载发布的wmts服务

热门文章

  1. 【码蚁君】AR实战-圣诞礼物EasyAR+Unity
  2. ESP8266SmartConfig——一键配网
  3. Java开发两年挑战阿里P6,面试经历分享
  4. 字符 字符集 编码 以及乱码
  5. 2021年国考中国文联面试公告
  6. Android8.0 Media系统(一)
  7. Google浏览器上传文件卡死状态问题解决方案
  8. java jconsole 远程服务器_JConsole监控远程Tomcat服务器 遇到的坑
  9. 【附源码】Python计算机毕业设计怦然心动网上服装商城
  10. C语言--有符号16进制转换