写点什么

智能问答机器人

作者:DS小龙哥
  • 2022 年 3 月 16 日
  • 本文字数:7299 字

    阅读完需:约 24 分钟

1. 前言

问答机器人现在很多场合都有使用,比如:网页智能客服、微信公众号智能回复、淘宝的售后客服,QQ 聊天机器人等等。有了这些客户机器人就能帮我们回答很多预置的一些问题,帮助用户解决常见问题,还可以进行自主训练,得到一个适合自己使用的机器人。机器人也可以关联很多其他的技能,玩小游戏,查询天气、查询节假日、查询很多其他的信息,非常方便。


这篇文章就采用华为云提供的智能问答机器人设计一个小软件,采用华为云提供的 API 接口完成数据交互,与机器人进行问答交互,通过这个例子可以了解到智能问答机器人的基本功能、使用场景、使用方法等等。



华为云的智能问答机器人特点介绍


提供问答引擎、机器人管理平台来方便客户快速、低成本构建智能问答服务。智能问答能满足用户快速上线、高度定制、数据可控的需求,具有问答准确率高、自主学习等特点。能够帮助企业节省客服人力,大大降低客服响应时间。

具备如下优势点:

  • 智能的问答管理

  • 热点问题、趋势、知识自动分析统计。

  • 支持未知问题自动聚类,匹配相似问答,辅助人工不断扩充知识库。

  • 支持问答调测,点对点的监测智能应答过程。

  • 支持领域知识挖掘,提供易用的标注工具挖掘领域词。

  • 全面的对话管理

  • 支持自然语言多能力融合,智能对话中控。

  • 灵活的知识库管理,支持对知识的批量操作。

  • 支持嵌入多轮对话技能,满足复杂的任务型对话场景。

  • 高效训练部署

  • 基于 modelarts 的底层算法能力,提供更快的模型训练、部署能力。

  • 支持多算法模型效果验证,验证不同数据、参数、模型对问法效果的影响。

  • 支持模型最优参数组合推荐,保证问答效果。

2. 使用问答机器人服务

2.1 开通服务

地址: https://www.huaweicloud.com/product/cbsqa.html


点击立即使用会进入到购买页面,可以免费体验 14 天,对于技术评估,场景测试已经足够。





2.2 配置机器人

(1)机器人购买之后,点击进入管理页面,对机器人的属性、技能进行配置,训练。




(2)可以添加预置的技能,还可以添加自定义技能


预置的技能有查询天气、成语接龙、查星座、查节日、猜数字游戏等等。也可以自己自定义技能标注训练发布。



2.3 对话体验

在管理页面右上角可以在线体验与机器人对话,可以快速调试问答效果。



2.4 接口调试

地址: https://support.huaweicloud.com/api-cbs/cbs_03_0115.html


在调用 API 测试之前,可以先使用在线调试接口测试,了解请求如何发出,有哪些必填参数,请求参数怎么填,返回的结果格式是怎样的。


2.5 API 请求总结

(1)请求的 URL 格式


请求的URL格式: POST /v1/{project_id}/qabots/{qabot_id}/chat其中参数介绍: project_id  是项目ID。qabot_id 是机器人标识符,qabot编号,UUID格式。如:303a0a00-c88a-43e3-aa2f-d5b8b9832b02。登录对话机器人服务控制台,在智能问答机器人列表中就可以查看到abot_id。    最终拼接的URL格式: https://cbs-ext.cn-north-4.myhuaweicloud.com/v1/0e5957be8a00f53c2fa7c0045e4d8fbf/qabots/5c889415-6834-4ada-aa51-ea5000941e25/chat    
复制代码



(2)请求头与请求参数总结


请求头:  "X-Auth-Token": "------------",  这是API接口鉴权用的,所有的API请求都要这个参数"Content-Type": "application/json" 请求体: { "question": "北京天气"  这是给机器人提交的问题,随后机器人会返回答案}
响应结果:{ "request_id": "e3ab440c-0bb2-455b-aff8-07e4cc4115f4", "reply_type": 1, "taskbot_answers": { "answer": "当前北京天气晴,最高8摄氏度,最低-5摄氏度,日间南风≤3级,夜间南风≤3级。", "skill_id": "22a20348-aa8b-44d2-96df-dcae1b8d92c2", "skill_responses": [ { "frame": { "intention": "weather_query", "confidence": 1, "reply": "当前北京天气晴,最高8摄氏度,最低-5摄氏度,日间南风≤3级,夜间南风≤3级。", "intention_alias": "查天气", "candidate_words": [], "task_complete": true, "flow_complete": true, "current_slots": [ { "slot_id": "a9ee29df-8f60-4ff1-863e-60e9412a1f95", "slot_name": "地点", "slot_identification": "loc", "slot_values": [ { "word": "北京", "norm_word": "北京", "begin_position": 0, "end_position": 1 } ] } ], "history_slots": [] }, "candidate": { "candidate_confidence": 0 }, "skill_id": "22a20348-aa8b-44d2-96df-dcae1b8d92c2", "skill_version": "v50", "locked": false, "related_intentions": [ { "intention": "weather_query", "confidence": 1 } ] }, { "frame": { "confidence": 0, "reply": "你太难理解了,我需要一些信息才能知道呢,哼!", "candidate_words": [], "task_complete": true, "flow_complete": true, "current_slots": [], "history_slots": [] }, "candidate": { "candidate_confidence": 0 }, "skill_id": "8b71d740-aedb-4c01-8948-460dab64fd22", "skill_version": "v67", "locked": false, "related_intentions": [ { "intention": "constellation", "confidence": 0.513 } ] }, { "frame": { "confidence": 0, "reply": "对不起,我没明白,请再多教我一些吧", "candidate_words": [], "task_complete": true, "flow_complete": true, "current_slots": [], "history_slots": [] }, "candidate": { "candidate_intention": "chengyu", "candidate_confidence": 0.507154 }, "skill_id": "9d2aa6d4-8461-4ca7-9db8-af32fdbfde57", "skill_version": "v12", "locked": true, "related_intentions": [ { "intention": "chengyu", "confidence": 0.507 } ] }, { "frame": { "confidence": 0, "reply": "对不起,我没明白,请再多教我一些吧", "candidate_words": [], "task_complete": true, "flow_complete": true, "current_slots": [], "history_slots": [] }, "candidate": { "candidate_confidence": 0 }, "skill_id": "4a93acd4-5a29-4188-b033-9fffd932e5df", "skill_version": "v31", "locked": true, "related_intentions": [ { "intention": "sys.other", "confidence": 0.555 } ] }, { "frame": { "confidence": 0, "reply": "对不起,我没明白,请再多教我一些吧", "candidate_words": [], "task_complete": true, "flow_complete": true, "current_slots": [], "history_slots": [] }, "candidate": { "candidate_confidence": 0 }, "skill_id": "25ad99ee-8a13-40a2-8fa1-0a18370e2ef5", "skill_version": "v34", "locked": false, "related_intentions": [ { "intention": "sys.other", "confidence": 0 } ] } ] }, "session_id": "4b105ca2-28e2-4ec8-bd4b-87c8d7c6a322"}
复制代码


请求头里的 X-Auth-Token 字段在之前的文章已经介绍过,获取方法看这里: https://bbs.huaweicloud.com/blogs/317759 翻到 2.3 小节。


(3)请求参数介绍


详细的参数可以看官方文档介绍: https://support.huaweicloud.com/api-cbs/cbs_03_0115.html


请求参数里一般主要填下面两个字段:
question 这是必填的参数,填用户的问题。如:查天气。长度为1~512。
session_id 填会话标识符,UUID格式。如:c04e6f7b-61d7-4a2d-a0c8-f9ecd2f62359。每次对话开启,机器人创建会话id,下次请求中传入该id表示继续该轮对话,每轮会话有效时间为2分钟。若传入的会话id已过期或者为空,则机器人会重新创建新的会话id(重新创建会话id会消耗一定时间)。比如: 玩成语接龙游戏,就需要填会话标识ID,这样才可以接着上一次的对话继续问答。
复制代码


(4)响应参数介绍


reply_type 表示当前回答的类型      0 知识库回复。      1 技能回复。      2 闲聊回复。      3 图谱回复。      4 文档回复。      5 表格回复。
session_id 这是当前的会话id,每次对话开启,机器人创建会话id,下次请求中传入该id表示继续该对话,每轮会话有效时间为2分钟。
以技能回复为例: "taskbot_answers": {"answer": "当前北京天气晴,最高8摄氏度,最低-5摄氏度,日间南风≤3级,夜间南风≤3级。",}
复制代码

3. 实现效果与案例代码

3.1 实现效果

(1)成语接龙



(2)天气查询



(3)查星座




(4)猜数字游戏


3.2 核心代码


#include "widget.h"#include "ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget){ ui->setupUi(this);
this->setWindowTitle("智能问答机器人");

//读取之前保存的token数据 QString data_token=ReadDataFile(); if(!data_token.isEmpty()) { Token=data_token.toUtf8(); qDebug()<<"读取到之前的数据:"<<Token; }
/*网络请求设置*/ manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
//机器人 ui->listWidget->addItem(new QListWidgetItem(QIcon(QObject::tr(":/res/2.ico")), QObject::tr("您好,很高兴为你服务.")));
}

Widget::~Widget(){ delete ui;}
/*功能: 保存数据到文件*/void Widget::SaveDataToFile(QString text){ /*保存数据到文件,方便下次加载*/ QString file; file=QCoreApplication::applicationDirPath()+"/"+ConfigFile; QFile filesrc(file); filesrc.open(QIODevice::WriteOnly); QDataStream out(&filesrc); out << text; //序列化写字符串 filesrc.flush(); filesrc.close();}
/*功能: 从文件读取数据*/QString Widget::ReadDataFile(void){ //读取配置文件 QString text,data; text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
//判断文件是否存在 if(QFile::exists(text)) { QFile filenew(text); filenew.open(QIODevice::ReadOnly); QDataStream in(&filenew); // 从文件读取序列化数据 in >> data; //提取写入的数据 filenew.close(); } return data; //返回值读取的值}
/*功能: 获取token*/void Widget::GetToken(){ //表示获取token function_select=3;
QString requestUrl; QNetworkRequest request;
//设置请求地址 QUrl url;
//获取token请求地址 requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens") .arg(SERVER_ID);
//自己创建的TCP服务器,测试用 //requestUrl="http://10.0.0.6:8080";
//设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
//构造请求 url.setUrl(requestUrl);
request.setUrl(url);
QString text =QString("{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":" "{\"user\":{\"domain\": {" "\"name\":\"%1\"},\"name\": \"%2\",\"password\": \"%3\"}}}," "\"scope\":{\"project\":{\"name\":\"%4\"}}}}") .arg(MAIN_USER) .arg(IAM_USER) .arg(IAM_PASSWORD) .arg(SERVER_ID);
//发送请求 manager->post(request, text.toUtf8());}

//解析反馈结果void Widget::replyFinished(QNetworkReply *reply){ QString displayInfo=""; int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
//读取所有数据 QByteArray replyData = reply->readAll();
qDebug()<<"状态码:"<<statusCode; qDebug()<<"反馈的数据:"<<QString(replyData);
//更新token if(function_select==3) { displayInfo="token 更新失败."; //读取HTTP响应头的数据 QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs(); qDebug()<<"HTTP响应头数量:"<<RawHeader.size(); for(int i=0;i<RawHeader.size();i++) { QString first=RawHeader.at(i).first; QString second=RawHeader.at(i).second; if(first=="X-Subject-Token") { Token=second.toUtf8(); displayInfo="token 更新成功.";
//保存到文件 SaveDataToFile(Token); break; } } QMessageBox::information(this,"提示",displayInfo,QMessageBox::Ok,QMessageBox::Ok); return; }
//判断状态码 if(200 != statusCode) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QString error_str=""; QJsonObject obj = document.object(); QString error_code; //解析错误代码 if(obj.contains("error_code")) { error_code=obj.take("error_code").toString(); error_str+="错误代码:"; error_str+=error_code; error_str+="\n"; } if(obj.contains("error_msg")) { error_str+="错误消息:"; error_str+=obj.take("error_msg").toString(); error_str+="\n"; }
//显示错误代码 QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok); } } return; }
//对话返回 if(function_select==0) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QString error_str=""; QJsonObject obj = document.object(); QString answer;
//解析对话ID if(obj.contains("session_id")) { session_id=obj.take("session_id").toString(); qDebug()<<"持续对话ID: "<<session_id; }
//解析答案 if(obj.contains("taskbot_answers")) { QJsonObject obj1=obj.take("taskbot_answers").toObject(); if(obj1.contains("answer")) { answer=obj1.take("answer").toString(); } } qDebug()<<"答案:"<<answer;
//机器人 ui->listWidget->addItem(new QListWidgetItem(QIcon(QObject::tr(":/res/2.ico")), answer));
} } }}

//获取对话结果void Widget::getProblemResult(QString session_id,QString send_text){ //表示获取对话结果 function_select=0;
QString requestUrl; QNetworkRequest request;
//设置请求地址 QUrl url;
//获取token请求地址 requestUrl = QString("https://cbs-ext.%1.myhuaweicloud.com/v1/%2/qabots/%3/chat") .arg(SERVER_ID) .arg(PROJECT_ID) .arg(ROBOT_ID);
//设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
//设置token request.setRawHeader("X-Auth-Token",Token);
//构造请求 url.setUrl(requestUrl);
request.setUrl(url);
QString text =QString("{\"question\": \"%1\",\"session_id\":\"%2\"}").arg(send_text).arg(session_id);
//发送请求 manager->post(request, text.toUtf8());}
//发送问答void Widget::on_pushButton_send_clicked(){ QString text=ui->lineEdit->text(); if(text.isEmpty()) { return; }
QListWidgetItem *item; item=new QListWidgetItem(text); item->setTextAlignment(Qt::AlignRight); item->setTextColor(QColor("#FF1493")); ui->listWidget->addItem(item);
getProblemResult(session_id,text);}
//更新tokenvoid Widget::on_pushButton_token_clicked(){ GetToken();}
复制代码


发布于: 刚刚阅读数: 2
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
智能问答机器人_智能问答机器人_DS小龙哥_InfoQ写作平台