在产品开发过程中,我们经常遇到一些问答交互场景,例如体检推荐场景中询问用户的健康信息和需求,在智能导诊和预问诊场景中询问患者的不适症状和相关信息,以及我们在很多产品的引导入口,经常遇到询问基本信息和爱好的地方,也属于问答交互场景。如下图所示分别为**人寿蛮牛健康的引导页面、**平安好医生的导诊页面
在传统开发中,大多数此类产品功能的开发都采用硬编码的方式,即产品经理设计好状态图(或流程图),前端开发和后端开发按状态图进行硬编码。这样后期每一次需求调整,产品经理要修改设计,前端开发和后端开发要做对应代码开发。在实际场景中,需求是多变的,难免会多次修改需求;若每次需求的微调都会让前后端开发介入,会导致沟通协调的工作量巨大。
在我们提炼的技术框架中,可以直接把产品经理的状态图(或流程图)作为输入自动转换为代码,带来了工作模式的调整,大幅提高了需求的响应速度和响应效率。
而且在技术框架中采用了全面的模块化技术,大幅提高了开发效率,降低了后期的维护成本。
下面分别从两个方面进行介绍:状态机技术和模块化技术
1、用户打开界面,向后端发送api请求
- flag=start
2、后端产生问题,返回问句,主要字段为:
- flag=contine
- question_seq: 问题编码
- question_type:问题类型:多选项、单选项、输入项、身高、体重、症状列表,发生频率、持续时间
- question_content: 问题内容
- question_option:数组,前端显示的候选项列表
- default_value: 默认值
3、用户对问题进行回答后,返回后端内容,主要字段为:
- question_seq: 问题编码
- question_anwser: 数组,用户填写或者选择的问题答案
- all_answer: 字典,所有问题答案累加在answer里,作为最终问答结果
4、当后端收到用户回答后,计算下一个状态,若继续问句,则继续第2步;如果计算流程结束,则跳到第5步
5、结束流程,结束问答,此时的主要字段为:
- flag=end
- flow_retsult_type:流程结果类型,例如"预问诊结果"、"导诊结果"、"体检结果"
- flow_result_detail:流程结果详细,当flag=end时,返回flow_retsult_type和flow_result_detail,前端根据type显示不同结果页面
整个问答引擎的核心只有一个API(例如路径为api/p/qa),前端发起事件获取后端响应,即用户回答问题给后端,后端计算返回下一个问题或者终点结果。上述前后端交互过程中的API字段定义如下表:
入参:
question_seq: 当前的问题编号,如果是第一个问题,为空
question_anwser: 当前问题的答案,为int或者string或者list或者对象
all_anwser: 累计的问题答案
flow_name: 流程名
extend: 扩展内容,例如token等,用于登录用户信息验证等
出参:
# 场景1、后端产生问题,返回问句,主要字段为:
flag: contine
question_seq: 问题编码
question_type: 问题类型:多选项、单选项、输入项、身高、体重、症状列表,发生频率、持续时间
question_content: 问题内容
question_option: 数组,前端显示的候选项列表
default_value: 默认值
# 场景2、结束流程,结束问答,此时的主要字段为:
flag: end
flow_retsult_type: 流程结果类型,例如"预问诊结果"、"导诊结果"、"体检结果"
flow_result_detail: 流程结果详细,当flag=end时,返回flow_retsult_type和flow_result_detail,前端根据type显示不同结果页面
状态图控制器是问答引擎的核心,其处理过程如下:
- 业务经理或者产品经理在processon上绘制完成业务流程图后,导出pos文件上传到程序指定目录(pos格式是ProcessOn的一种基于Json的开放格式,也是ProcessOn自己的格式,可以导出和导入使用)
- 产品经理也可以使用其他的流程图制作工具,例如visio,导出VisioXML文档类似格式
- 状态图控制器解析状态图的pos文件,提取节点、节点内信息、节点之间的连线、连线描述
- 每个节点对应一个问题状态
- 节点内的信息描述了该问题的内容、主键、类型(前端控件)、(后端)业务处理等
- 节点之间的连线表示了问题之间的跳转关系
- 连线描述表示跳转的逻辑表达式,支持and、or not 等逻辑表达
- 状态图控制器侦听前端的请求,根据当前状态和事件切换到下一状态
- 状态图控制器能够解析的状态图需要按照“描述规范”进行编写
- 开始状态采用"Flowchat流程图"中的”开始/结束“,命名方式为"开始:流程名",其中‘流程名’是个变量,可以支持子流程调用
- 结束状态采用"Flowchat流程图"中的”开始/结束“,命名方式为"结束:终点名",其中‘终点名’是个变量,表示采用的是哪个终点状态结果进行处理
- 中间状态采用"Flowchat流程图"中的”流程“表示,格式如下:
- 第一行为问句内容,例如"你的$性别$为?"、”请问您描述的症状发生多久了?“,其中$围起来的变量表示这个状态的主键
- 第二行表示前端显示控件,例如 "单选项:X1、X2",“输入项:”,“自定义项:身高”等
- 除第一行和第二行外,下面内容为可选项:
- 主键:状态的主键名称,如果再第一行中用$标识,则不需要再单独定义
- 前置处理 :相似症状推导,后端的业务处理模块,产生问题前调用
- 后置处理:后端的业务处理模块,收到问答后调用,一般是进行值转换
- 默认值: 前端显示时的默认值
- 状态与状态之间的跳转逻辑,直接在中间的连线上用表达式描述
- 变量名用 $围起来
- 支持、 、=、=、=、contains等比较运算符
- 支持and、or、not等逻辑运算符
- 若状态与状态之间连线上没有条件,则表示默认跳转,为低优先级
下图是一个“健康APP引导流程”样例
- 整个问答引擎采用彻底的模块化原理
- 前端显示问题内容,采用模块化前端显示控件,如:多选项、单选项、输入项、症状列表、症状时间等
- 每个问题的后端逻辑,采用模块化业务处理,如:相似症状、年龄段处理
- 所有问答结束后,后端采用模块化结果计算,前端进行模块化结果展示,如:预问诊结果、导诊结果、体检结果
- 与第三方对接,支持模块化接口扩展,如:仁济掌医接口、健康静安接口、上海互联网总医院接口
前端控件举例:
- 单选项
- 多选选
- 输入项
- 身高
- 体重
- 日期选择
后端业务处理细分为两种类型:前置处理和后置处理,分别表示产生问题前调用和回答结果后调用。在后端业务处理模块中,可以灵活引入各种算法模型,从而提供整个系统的智能化!
- 年龄段处理:为了减少用户输入,让用户选择年龄范围,后台要把范围采样为某个年龄值
- 相似症状推导模型:根据用户先前输入的症状和基础信息,通过算法推导出相似症状
- 身高估算模型:根据年龄、性别等已知条件估算身高,尽量让用户少调整数值
- 体重估算模型:根据年龄、性别、身高等已知条件估算体重,尽量让用户少调整数值
所有问答结束后,后端采用模块化进行终点状态结果结算,前端进行模块化结果展示,如:
- 预问诊结果
- 导诊结果
- 体检结果
- 个性化推荐结果
在真实场景中,存在下面场景:在进入问答系统之前,已经从第三方系统获取了用户基本信息,如姓名、年龄、身份证号、挂号流水号等信息,这些信息可以通过extend字段进行传递
上海仁济医院的extend定义:
extend_type: 上海仁济医院
sigh: 对身份证号、挂号流水号的加密信息
上海互联网总医院的extend定义:
extend_type: 上海互联网总医院
姓名: xx
性别: xx
本模块用到的关键技术点:
- 流程图解析与流程执行引擎
- 表达式执行、规则执行引擎
- 模块化封装、业务解耦、函数注册
当前人工智能的现状是呼声高,落地少,类似“只见人工,不见智能“,”有多少人工就有多少智能”这样的吐槽,不管行业内大家外笑而不语。
毛主席能称之为伟人,就在于其伟大的**,一个持久战**就可以应用在太多地方,在我们人工智能领域也一样适用。按其《论持久战》的观点,对于人工智能解决问题的能力,有两种观点都是需要纠正的,一种是"亡国论"认为人工智能解决不了啥问题,对其拒绝接受,远离之;另一种是"速胜论"认为人工智能啥都能解决,盲目制定目标,然后撞个头破血流。
对于一个问题的解决,既不能亡国论(不要讲什么智能、算法这些忽悠人的东西,只要手工给我做完就行),也不能速胜论(人都没想清楚的情况下,只要采用某个先进的算法就能短时间解决所有问题)。正确做法是要采用持久战的**,算法只是用来提高效率的工具,人要想清楚做什么,怎么做,人能做哪些东西,算法能做哪些东西,这样循序渐进人机配合,才可以真正解决问题,把事情做好。
在我们基于状态机的智能问答引擎中,前半部分的状态图(或流程图)可以帮助产品经理想清楚人可以做哪些工作,机器和算法可以做哪些工作;后半部分的模块化可以把各种智能算法有机融合到系统中,比如相似症状推导模型可以采用词向量、关联矩阵、SVD算法等,个性化推荐结果采用协同过滤、聚类、深度学习等,中间一些小的估算模型也可以很好提高用户的智能体验。
这样人工做一部分工作、机器和算法做一部分,整体组合起来,就是一个很好用的人工智能产品。
参考文档: