实现 AI 语音助手
声网提供 AIGCService SDK,借助三方 STT(语音转)、TTS(文本转语音)和 LLM(大型语言模型)的能力,能在 App 中实现语音识别、自然语言理解、智能对话等功能。
本文介绍 AI 语音助手场景的实现流程,以及如何集成 AIGCService SDK,并调用核心 API 来实现这个流程。
业务流程
AI 语音助手的实现流程如下图所示:
上图中,采集和播放音频数据是使用声网 RTC SDK 的原始音频数据功能实现的。你也可以选用其他 RTC SDK 的相应功能实现。
前提条件
开始前,请确保满足如下要求:
- Git
- Java Development Kit
- Android Studio 4.1 以上版本
- Android API 级别 23 及以上
- Android 6.0 或以上版本的移动设备。声网推荐使用真机运行项目,部分模拟机可能存在功能缺失或者性能问题
- 参考开通服务创建项目、获取 App ID、App 证书、临时 Token 并开通 AIGCService 服务
集成 SDK
参考如下步骤获取 AIGCService SDK,并将 SDK 集成到你的项目中。
如果你本地尚无 Android 项目,可以参考 Android 官方文档 Create a project 创建。
-
联系 sales@shengwang.cn 获取 AIGCService SDK。
-
将获取到的
aar
文件放入app/libs
路径下,然后在/app/build.gradle
文件中引入该文件:Javaimplementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
注意AIGCService SDK 仅支持 arm64-v8a 和 armeabi-v7a 架构。
项目设置
权限申请
在项目的 /app/src/main/AndroidManifest.xml
文件中,添加如下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
环境配置
在项目的 /app/build.gradle
文件的 defaultConfig
字段中添加如下行:
minSdk 23
targetSdk 30
compileSdk 31
防止代码混淆
在 /app/proguard-rules.pro
文件中添加如下行:
-keep class io.agora.** { *; }
-keepnames class io.agora.* { *; }
-keepnames interface io.agora.* { *; }
-dontwarn io.agora.**
-keep class com.microsoft.cognitiveservices.speech.** { *; }
-keepnames class com.microsoft.cognitiveservices.speech.* { *; }
-keepnames interface com.microsoft.cognitiveservices.speech.* { *; }
-dontwarn com.microsoft.cognitiveservices.speech.**
-keep class software.amazon.awssdk.** { *; }
-keepnames class software.amazon.awssdk.* { *; }
-keepnames interface software.amazon.awssdk.* { *; }
-dontwarn software.amazon.awssdk.**
-keep class io.netty.** { *; }
-keepnames class io.netty.* { *; }
-keepnames interface io.netty.* { *; }
-dontwarn io.netty.**
实现 AI 语音助手
下文展示如何在项目中集成 AIGCService 的核心 API,并实现发送音频数据、接收语言模型处理后的音频数据的逻辑。完整的 API 时序图可以参考 API 时序图。
1. 创建 AIGCService 实例
调用 create
和 initialize
方法创建并初始化 AIGC 服务示例。你需要在 initialize
方法中通过 AIGCServiceConfig
对服务实例进行配置。
if (null == mAIGCService) {
mAIGCService = AIGCService.create();
}
mAIGCService.initialize(new AIGCServiceConfig() {{
this.context = onContext;
this.callback = serviceCallback;
this.enableLog = true;
this.enableSaveLogToFile = true;
this.userName = "AI";
this.appId = KeyCenter.APP_ID;
this.rtmToken = KeyCenter.getRtmToken(KeyCenter.getUserUid());
this.userId = String.valueOf(KeyCenter.getUserUid());
this.enableMultiTurnShortTermMemory = false;
this.speechRecognitionFiltersLength = 0;
this.input = new SceneMode() {{
this.language = Language.ZH_CN;
this.speechFrameSampleRates = 16000;
this.speechFrameChannels = 1;
this.speechFrameBits = 16;
}};
this.output = new SceneMode() {{
this.language = Language.ZH_CN;
this.speechFrameSampleRates = 16000;
this.speechFrameChannels = 1;
this.speechFrameBits = 16;
}};
}});
初始化为耗时操作。完成初始化后,SDK 会触发 onEventResult(INITIALIZE, SUCCESS)
回调。
2. 监听回调事件
除了 onEventResult
回调外,你还需要注册如下回调,用来接收各语言模型返回的数据结果:
onSpeech2TextResult
:回调文字转语音 (TTS) 处理后的结果。onLLMResult
:回调大语言模型 (LLM) 处理后的结果。onText2SpeechResult
:回调语音识别出来的文字。
@Override
public void onEventResult(@NonNull ServiceEvent event, @NonNull ServiceCode code, @Nullable String msg) {
Log.i(TAG, "onEventResult event:" + event + " code:" + code + " msg:" + msg);
if (event == ServiceEvent.INITIALIZE && code == ServiceCode.SUCCESS) {
} else if (event == ServiceEvent.START && code == ServiceCode.SUCCESS) {
} else if (event == ServiceEvent.STOP && code == ServiceCode.SUCCESS) {
} else if (event == ServiceEvent.DESTROY && code == ServiceCode.SUCCESS) {
}
}
@Override
public HandleResult onSpeech2TextResult(String roundId, Data<String> result, boolean isRecognizedSpeech) {
Log.i(TAG, "onSpeech2TextResult roundId:" + roundId + " result:" + result + " isRecognizedSpeech:" + isRecognizedSpeech);
return HandleResult.CONTINUE;
}
@Override
public HandleResult onLLMResult(String roundId, Data<String> answer, boolean isRoundEnd) {
Log.i(TAG, "onLLMResult roundId:" + roundId + " answer:" + answer);
return HandleResult.CONTINUE;
}
@Override
public HandleResult onText2SpeechResult(String roundId, Data<byte[]> voice, int sampleRates, int channels, int bits) {
Log.i(TAG, "onText2SpeechResult roundId:" + roundId + " voice:" + voice + " sampleRates:" + sampleRates + " channels:" + channels + " bits:" + bits);
return HandleResult.CONTINUE;
}
3. 获取和设置参数
该步骤允许用户根据场景需要,自定义 AIGC 服务的属性。具体步骤如下:
- 调用
getRoles
获取 SDK 支持的所有AIRole
,包括 AI 服务的名称、职业、性别等。 - 调用
setRole
设置 SDK 使用的AIRole
。
可以设置的参数包括:
AIRole
:AI 服务的名称、职业、性别等ServiceVendor
:选用的 AI 服务模型厂商,包含三种类型:LLMVendor
:大语言模型STTVendor
:语音转文字模型TTSVendor
:文字转语音模型
@Override
public void onEventResult(@NonNull ServiceEvent event, @NonNull ServiceCode code, @Nullable String msg) {
Log.i(TAG, "onEventResult event:" + event + " code:" + code + " msg:" + msg);
if (event == ServiceEvent.INITIALIZE && code == ServiceCode.SUCCESS) {
runOnUiThread(new Runnable() {
@Override
public void run() {
enableUi(true);
}
});
Log.i(TAG, "getRoles:" + Arrays.toString(AIGCServiceManager.getInstance().getAIGCService().getRoles()));
Log.i(TAG, "getCurrentRole:" + AIGCServiceManager.getInstance().getAIGCService().getCurrentRole());
AIGCServiceManager.getInstance().getAIGCService().setRole(AIGCServiceManager.getInstance().getAIGCService().getRoles()[1].getRoleId());
Log.i(TAG, "getServiceVendors:" + AIGCServiceManager.getInstance().getAIGCService().getServiceVendors());
ServiceVendor serviceVendor = new ServiceVendor();
serviceVendor.setTtsVendor(AIGCServiceManager.getInstance().getAIGCService().getServiceVendors().getTtsList().get(1));
AIGCServiceManager.getInstance().getAIGCService().setServiceVendor(serviceVendor);
}
}
4. 开启 AIGCService
完成初始化相关设置后,调用 start
方法开启服务。
AIGCServiceManager.getInstance().getAIGCService().start()
该步骤也是耗时操作。完成开启服务后,会收到 onEventResult(START, SUCCESS)
回调,告知 AIGCService 开启成功。
5. 发送接收到的数据
获取到用户发送的消息数据后,你需要将该数据发送给 AIGCService
。AIGCService SDK 接收到数据后,会将数据发送给接入的各语言模型。
该步骤需要在调用 start
开启服务后实现。
- 语音消息
- 文字消息
如果用户发送的是语音消息,进行如下步骤:
-
采集本地用户发送的音频数据。如果你选用的是声网 RTC SDK,可以参考音频原始数据,从
onRecordAudioFrame
回调中获取。 -
获取到音频数据后,调用
pushSpeechDialogue
方法将数据发送给AIGCService
。JavaAIGCServiceManager.getInstance().getAIGCService().pushSpeechDialogue(data, Vad.UNKNOWN);
如果用户发送的是文字消息,你可以直接调用 pushTxtDialogue
将数据发送给 AIGCService
。
AIGCServiceManager.getInstance().getAIGCService().pushTxtDialogue("你叫什么名字?");
6. 接收处理结果
各语言模型处理结束后,会将处理结果返回给 SDK。SDK 会触发如下回调,将处理结果发送给 App。
- 语音消息
- 文字消息
如果用户发送的是语音消息,需要监听如下回调:
// 接收语音转文字的处理结果
@Override
public HandleResult onSpeech2TextResult(String roundId, Data<String> result, boolean isRecognizedSpeech) {
Log.i(TAG, "onSpeech2TextResult roundId:" + roundId + " result:" + result + " isRecognizedSpeech:" + isRecognizedSpeech);
return HandleResult.CONTINUE;
}
// 接收大语言模型的处理结果
@Override
public HandleResult onLLMResult(String roundId, Data<String> answer, boolean isRoundEnd) {
Log.i(TAG, "onLLMResult roundId:" + roundId + " answer:" + answer);
return HandleResult.CONTINUE;
}
// 接收语音识别的处理结果
@Override
public HandleResult onText2SpeechResult(String roundId, Data<byte[]> voice, int sampleRates, int channels, int bits) {
Log.i(TAG, "onText2SpeechResult roundId:" + roundId + " voice:" + voice + " sampleRates:" + sampleRates + " channels:" + channels + " bits:" + bits);
return HandleResult.CONTINUE;
}
如果用户发送的是文字消息,需要监听如下回调:
// 接收大语言模型的处理结果
@Override
public HandleResult onLLMResult(String roundId, Data<String> answer, boolean isRoundEnd) {
Log.i(TAG, "onLLMResult roundId:" + roundId + " answer:" + answer);
return HandleResult.CONTINUE;
}
// 接收语音识别的处理结果
@Override
public HandleResult onText2SpeechResult(String roundId, Data<byte[]> voice, int sampleRates, int channels, int bits) {
Log.i(TAG, "onText2SpeechResult roundId:" + roundId + " voice:" + voice + " sampleRates:" + sampleRates + " channels:" + channels + " bits:" + bits);
return HandleResult.CONTINUE;
}
接收到处理后的数据后,如果是文字消息,你可以直接返回;如果是语音消息,还需要播放获取到的音频数据。
如果你选用的是声网 RTC SDK,可以参考音频原始数据,使用 onPlaybackAudioFrame
播放。
7. 停止 AIGCService
无需使用 AI 语音助手后,你可以调用 stop
停止 AIGCService
实例。
该步骤需要在调用 start
开启服务后实现。
停止服务为耗时操作。成功停止后,会收到 onEventResult(STOP, SUCCESS)
回调。
AIGCServiceManager.getInstance().getAIGCService().stop();
8. 销毁 AIGCService
成功停止服务后调用 destroy
销毁服务实例。
AIGCService.destroy();
成功销毁后,会收到 onEventResult(DESTROY, SUCEESS)
回调。
API 时序图
实现 AI 语音助手的 API 调用时序图如下。其中,实现步骤需要用到如下关键类:
AIGCService
类:提供 AIGC 服务的核心类。AIGCServiceCallback
类:AIGCService
异步方法的事件回调类。
相关文档
开发过程中,你还可以参考如下文档:
如果你选择使用声网的 RTC SDK 来实现原始音频数据采集和播放功能,也可以参考: