任务简介

上一篇博文中,我们介绍了聆思的大模型平台。本篇博文我们将介绍如何使用在线编排能力,实现语音控制电梯的功能。在我们设计的智能电梯应用中,用户可以通过语音控制电梯所到的楼层,比如说“到4层”,电梯就会自动运行到4层。而这之中语音识别、语音合成以及对用户的语言的理解都是在云平台上完成的。开发板只是做语音的采集、上传、接收云平台的处理结果,并播放语音和显示结果。

流程设计

我们首先按照官方的文档搭建一个语音控制的基本流程:产品应用搭建 | 聆思文档中心 (listenai.com)。在这个例子中官方提供了上一篇博文文件,这个文件是用来实现官方示例的。不过这个文件只包括整体流程,不包括提示词和代码,这些内容要参照官方介绍自己输入。示例工程所对应的处理流程如下图所示。

这个流程可以分为两个主要部分。前面的部分是进行语音交互,将用户的语音输入识别为文字后利用第一个提示词(我称之为分类提示词)将用户语音命令进行意图分类。

后面一部分是根据前面的意图识别结果进行多分支处理。画图的分支是调用讯飞星火的文生图功能生成图片。控制修改背景的分支是再次使用提示词(我称之为背景设置提示词)调用讯飞星火大模型从用户语音内容中提取关键的控制信息,如颜色。还有个闲聊分支是完成基本的语音问答功能的。

我们在原有的大模型处理的三个分支基础上增加一个分支,也就是我们的电梯控制分支。

在落域控制部分也需要加上相应的分支选项。

增加电梯控制分支

为了让大家看得更清楚,我们将分支部分放大一下。这里我们的电梯控制分支是完全复制背景设置分支的。其中“前置处理”后两个分支:一个去TTS初始化,一个去提示词。我刚开始忘记画TTS初始化分支,导致在处理相关内容时无法给出用户语音提示。“后置处理”后也有两个分支:一个去TTS合成,一个去语音结果下发。TTS合成部分主要是负责合成“请稍等”、“处理完成”之类的语音提示。

整个处理流程必须严格按照图示进行连接,否则功能可能不正常。聆思平台最大的缺点是不方便调试,如果有问题,只能自己细心检查。

提示词设计

在与大模型交互时,提示词能控制大模型更好地达到你所期望的结果。设计好的提示词非常关键。

分类提示词

分类提示词是用于对用户提示词进行分类的,根据不同的分类来完成不同的任务。我们所使用的提示词如下:

你是一个分类专家,你需要将用户的句子归为“电梯控制”、“画画”、“设置背景”、“闲聊”中的一类,下面是一些例子:

用户:一幅海马进食
助手:画画

用户:把背景设置为蓝色
助手:设置背景

用户:电梯到第五层
助手:电梯控制

用户:到首层
助手:电梯控制

按照提示词的一般设计原则,首先要对大模型进行角色设定(“你是一个分类专家”)。然后对其进行任务设定(“你需要将用户的句子归为“电梯控制”、“画画”、“设置背景”、“闲聊”中的一类”)。接下来就是给大模型一些例子进行学习理解以提高分类的准确性。

电梯控制提示词

电梯控制提示词的作用是从用户的命令中提取我们感兴趣的参数,也就是目的楼层。用户的输入可能是各种各样的,采用自然语言处理比采用通配符匹配显然有更好的识别效果。我采用的电梯控制提示词如下:

你是一个电梯控制专家,我会给你一个设置电梯的要求,请你告诉要设置的楼层是什么,请用十进制数值表示结果,以下是一些例子:

用户:电梯到达五层
助手:5

用户:电梯到达首层
助手:1

用户:电梯到达地下2层
助手:-2

你只需要输出目标楼层的十进制表示,我的要求是:
用户:{{content}}
助手:

最后的识别结果会以一个有符号数来表示,后续的处理就简单了。

添加处理代码

需要添加的代码包括前置代码和后置代码,前置代码完全照抄背景设置分支即可。这里只介绍后置代码分支。

完整的代码如下:


//设置楼层成功的语音提示
const floorSucc = "已完成设置";
//设置楼层失败的语音提示,留空表示使用大模型的回复
const floorFail = "很抱歉,我可能还无法理解你想要的楼层";
//设置楼层异常的语音提示
const floorErr = "很抱歉,我暂时无法完成该操作";

//数据模版,不建议直接修改
const intentTemplate = {
    "text": "",
    "rc": 0,
    "data": {
        "result": [{
            "id": "xxx",
            "type": "Decimal",
            "value": "1"
        }]
    },
    "answer": {
        "text": "已完成设置",
        "type": "T"
    },
    "service": "floor",
    "service_pkg": "media",
    "category": "LISTENAI.floor"
}

//大模型回复内容
let content = msg.payload.choices[0]?.message?.content || '';

//正则匹配十进制值
if (/^[-+]?\d+$/.test(content)){
    //正则表达式提取十进制值
    let match = content.match(/^[-+]?\d+/);
    let matchFloor;

    if (match) {
        matchFloor = match[0]; // 提取第一个匹配的十进制数字序列  
    } else {
        matchFloor = null; // 如果没有找到匹配项,则设置为null  
    }
    printInfo("匹配到楼层:", matchFloor);
    intentTemplate.data.result[0].id = msg.payload.id;
    intentTemplate.data.result[0].value = matchFloor;
    intentTemplate.text = msg._asrResult;

    //构造tts合成文本
    let ttsMsg = RED.util.cloneMessage(msg);
    ttsMsg.payload = {
        text: floorSucc,
        stream: true,
        is_last: true
    };

    //构造设置楼层的数据帧给设备
    let nluMsg = RED.util.cloneMessage(msg);
    nluMsg.payload = {
        type: "CUSTOM",
        intent: intentTemplate
    };

    node.send([nluMsg, ttsMsg]);

} else {
    printErr("匹配不到楼层:" , content);

    //构造tts合成文本
    let ttsMsg = RED.util.cloneMessage(msg);
    ttsMsg.payload = {
        text: floorFail || content || floorErr,
        stream: true,
        is_last: true
    };

    //若匹配不到楼层,只下发语音提示
    node.send([null, ttsMsg]);
}


return;


/**
 * 日志打印
 */
function printInfo(tips, data) {
    let date = new Date();
    let message = `[INFO] SID[${msg.queryParams?.sid || ''}] App[${msg.queryParams?.llmApp}] Device[${msg.queryParams.deviceId || ''}] Time[${`${date.getHours() + 8}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}]`} ${tips}`;
    if (data) {
        if (typeof data === 'object') {
            console.log(message, JSON.stringify(data))
        } else {
            console.log(message, data)
        }
    } else {
        console.log(message)
    }
}

/**
 * 日志打印
 */
function printErr(tips, err) {
    let date = new Date();
    let message = `[ERR ] SID[${msg.queryParams?.sid || ''}] App[${msg.queryParams?.llmApp}] Device[${msg.queryParams.deviceId || ''}] Time[${`${date.getHours() + 8}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}]`} ${tips}`;
    console.error(message, err);
}

代码并不复杂,仅就关键点进行解释。正常情况下,节点将产生两个数据,其中nluMsg是一个JSON字符串,发送给开发板进行数据处理,其格式是由intentTemplate变量定义的,修改时要注意不要出错。而ttsMsg是发给TTS合成语音节点的,生成语音数据发送给开发板直接进行播放。

应用部署

完成配置后,点击右上角部署后返回应用界面,打开刚才创建的应用,点击部署生产。

有关开发板程序的修改,将在下篇博文中介绍。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐