快速发起语音通话
本文档用于说明如何快速集成客户端 SDK (ZEGO Express SDK)并实现与智能体进行语音互动。
前提条件
- 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目信息。
- 已从下载页面下载针对 AI Agent 优化的 ZEGO Express SDK,并集成到您的项目中。
- 已按 业务后台快速开始指引 集成了 AI Agent 相关服务端 API。
示例代码
以下是接入实时互动 AI Agent API 的业务后台示例代码,您可以参考示例代码来实现自己的业务逻辑。
包含最基本的获取 ZEGO Token、注册智能体、创建智能体实例、删除智能体实例等能力。
以下是客户端示例代码,,您可以参考示例代码来实现自己的业务逻辑。
包含最基本的登录、推流、拉流、退出房间等能力。
以下视频演示了如何跑通服务端和客户端(iOS)示例代码并跟智能体进行语音互动。
整体业务流程
- 服务端,参考业务后台快速开始文档跑通业务后台示例代码,部署好业务后台
- 接入实时互动 AI Agent API 管理智能体。
- 客户端,跑通示例代码
- 通过业务后台创建和管理智能体。
- 集成 ZEGO Express SDK 完成实时通信。
完成以上两个步骤后即可实现将智能体加入房间并与真实用户进行实时互动。
核心能力实现
集成 ZEGO Express SDK
请参考 集成 SDK > 2.2 > 方式三 手动集成 SDK。集成 SDK 后按以下步骤初始化 ZegoExpressEngine。
在 Info.plist 文件声明必要的权限
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以进行语音聊天</string>
</dict>
</plist>
运行时申请录音权限
- (void)requestAudioPermission:(void(^)(BOOL granted))completion {
/// 需要在项目的 Info.plist 文件中添加麦克风权限的使用说明
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession requestRecordPermission:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(granted);
});
}];
}
创建并初始化 ZegoExpressEngine
-(void)initZegoExpressEngine{
ZegoEngineProfile* profile = [[ZegoEngineProfile alloc]init];
profile.appID = kZegoPassAppId;
// !mark
profile.scenario = ZegoScenarioHighQualityChatroom; //设置该场景可以避免申请相机权限,接入方应按自己的业务场景设置具体值
[ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];
}
通知业务后台开始通话
可在客户端真实用户进入房间后立即通知业务后台开始通话,异步调用可加降低通话接通时间。业务后台收到开始通话通知后,使用与客户端相同的 roomID 及关联的 userID 和 streamID 创建智能体实例,这样智能体就能与真实用户在同一个房间内进行相互推拉流实现语音互动。
// 通知业务后台开始通话
/**
* 开始与AI智能体进行通话
*
* @param completion 完成回调,返回操作结果
* @discussion 该方法会向服务器发送开始通话的请求,用于初始化AI智能体实例
*/
- (void)doStartCallWithCompletion:(void (^)(NSInteger code, NSString *message, NSDictionary *data))completion {
// 构建请求URL
NSString *url = [NSString stringWithFormat:@"%@/api/start", self.currentBaseURL];
NSURL *requestURL = [NSURL URLWithString:url];
// 创建请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:requestURL];
request.HTTPMethod = @"POST";
// 设置请求头
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 创建请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
request.HTTPBody = jsonData;
// 创建会话
NSURLSession *session = [NSURLSession sharedSession];
// 发送请求
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
if (completion) {
completion(-1, @"网络请求失败", nil);
}
return;
}
NSHTTPURLResponse *httpUrlResponse = (NSHTTPURLResponse *)response;
if (httpUrlResponse.statusCode != 200) {
if (completion) {
completion(httpUrlResponse.statusCode,
[NSString stringWithFormat:@"服务器错误: %ld", (long)httpUrlResponse.statusCode],
nil);
}
return;
}
NSError *jsonError;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
if (completion) {
completion(-2, @"解析响应数据失败", nil);
}
return;
}
// 解析响应数据
NSInteger code = [dict[@"code"] integerValue];
NSString *message = dict[@"message"];
NSDictionary *responseData = dict[@"data"];
if (completion) {
completion(code, message, responseData);
}
});
}];
[task resume];
}
用户进入房间并推流
真实用户登录房间后推流。
在此场景下需要开启 AI 回声消除以获得更好的效果。
登录用的 token 需要从您的业务后台获取,请参考完整示例代码。
请确保 roomID、userID、streamID 在一个 ZEGO APPID 下是唯一的。
- roomID: 由用户自己定义生成规则,会用来登录 Express SDK 的房间。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
- userID: 长度不超过32字节。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
- streamID: 长度不超过256字节。仅支持数字,英文字符 和 '-', '_'。
// 记录智能体
self.streamToPlay = [self getAgentStreamID];
ZegoEngineConfig* engineConfig = [[ZegoEngineConfig alloc] init];
engineConfig.advancedConfig = @{
// !mark(1:2)
@"set_audio_volume_ducking_mode":@1,/**该配置是用来做音量闪避的**/
@"enable_rnd_volume_adaptive":@"true",/**该配置是用来做播放音量自适用**/
};
// !mark
[ZegoExpressEngine setEngineConfig:engineConfig];
//这个设置只影响AEC(回声消除),我们这里设置为ModeGeneral,是会走我们自研的回声消除,这比较可控,
//如果其他选项,可能会走系统的回声消除,这在iphone手机上效果可能会更好,但如果在一些android机上效果可能不好
[[ZegoExpressEngine sharedEngine] setAudioDeviceMode:ZegoAudioDeviceModeGeneral];
// 开启 AI 回声消除
// !mark(1:5)
[[ZegoExpressEngine sharedEngine] enableAGC:TRUE];
[[ZegoExpressEngine sharedEngine] enableAEC:TRUE];
[[ZegoExpressEngine sharedEngine] setAECMode:ZegoAECModeAIBalanced];
[[ZegoExpressEngine sharedEngine] enableANS:TRUE];
[[ZegoExpressEngine sharedEngine] setANSMode:ZegoANSModeMedium];
// 登录房间
// !mark
[self loginRoom:^(int errorCode, NSDictionary *extendedData) {
if (errorCode!=0) {
NSString* errorMsg =[NSString stringWithFormat:@"进入语音房间失败:%d", errorCode];
completion(NO, errorMsg);
return;
}
//进房后开始推流
[self startPushlishStream];
}];
拉智能体流
默认只有一个真实用户及智能体在同一个房间内,所以拉流时默认新增的就是智能体流。
//监听房间流信息更新状态,拉取智能体流播放
- (void)onRoomStreamUpdate:(ZegoUpdateType)updateType
streamList:(NSArray<ZegoStream *> *)streamList
extendedData:(nullable NSDictionary *)extendedData
roomID:(NSString *)roomID{
if (updateType == ZegoUpdateTypeAdd) {
for (int i=0; i<streamList.count; i++) {
ZegoStream* item = [streamList objectAtIndex:i];
// !mark
[self startPlayStream:item.streamID];
}
} else if(updateType == ZegoUpdateTypeDelete) {
for (int i=0; i<streamList.count; i++) {
ZegoStream* item = [streamList objectAtIndex:i];
[[ZegoExpressEngine sharedEngine] stopPlayingStream:item.streamID];
}
}
}
恭喜你🎉!完成这一步骤后,您已经可以用语音问智能体任何问题,智能体都会用语音回答您的问题!
退出房间结束通话
客户端调用退出登录接口退出房间,并停止推拉流。同时通知业务后台本次通话结束。业务后台收到结束通话通知后会删除智能体实例,智能体实例会自动退出房间并停止推拉流。这样一次完整的互动就结束了。
/**
* 通知业务后台结束通话
*
* @param completion 完成回调,返回操作结果
* @discussion 该方法会向服务器发送结束通话的请求,用于释放AI智能体实例
*/
- (void)doStopCallWithCompletion:(void (^)(NSInteger code, NSString *message, NSDictionary *data))completion {
// 构建请求URL
// !mark
NSString *url = [NSString stringWithFormat:@"%@/api/stop", self.currentBaseURL];
NSURL *requestURL = [NSURL URLWithString:url];
// 创建请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:requestURL];
request.HTTPMethod = @"POST";
// 设置请求头
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 创建请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
request.HTTPBody = jsonData;
// 创建会话
NSURLSession *session = [NSURLSession sharedSession];
// 发送请求
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
if (completion) {
completion(-1, @"网络请求失败", nil);
}
return;
}
NSHTTPURLResponse *httpUrlResponse = (NSHTTPURLResponse *)response;
if (httpUrlResponse.statusCode != 200) {
if (completion) {
completion(httpUrlResponse.statusCode,
[NSString stringWithFormat:@"服务器错误: %ld", (long)httpUrlResponse.statusCode],
nil);
}
return;
}
NSError *jsonError;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
if (completion) {
completion(-2, @"解析响应数据失败", nil);
}
return;
}
// 解析响应数据
NSInteger code = [dict[@"code"] integerValue];
NSString *message = dict[@"message"];
NSDictionary *responseData = dict[@"data"];
if (completion) {
completion(code, message, responseData);
}
// 退出房间
// !mark
[[ZegoExpressEngine sharedEngine] logoutRoom];
});
}];
[task resume];
}
以上就是您实现与智能体进行实时语音互动的完整核心流程。
ZEGO Express SDK 最佳配置实践
为了获得最佳的音频通话体验,建议按照以下最佳实践配置 ZEGO Express SDK。这些配置可以显著提升智能体语音交互的质量。
进房间前设置:
- 开启传统音频 3A 处理(回声消除AEC、自动增益控制AGC、噪声抑制ANS)
- 设置房间的使用场景为高品质语聊房场景,SDK 会针对不同的场景采取不同的优化策略
- 设置音频设备模式为默认模式
- 开启 AI 回声消除,提高回声消除效果(该功能需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK)
- 配置音量闪避,避免声音冲突
- 启用播放音量自适应,提升用户体验
- 启用 AI 降噪,设置适当的噪声抑制级别
ZegoEngineProfile* profile = [[ZegoEngineProfile alloc]init];
profile.appID = kZegoAppId;
profile.scenario = ZegoScenarioHighQualityChatroom; //高品质语聊房场景,设置该场景可以避免申请相机权限,接入方应按自己的业务场景设置具体值
ZegoEngineConfig* engineConfig = [[ZegoEngineConfig alloc] init];
engineConfig.advancedConfig = @{
@"set_audio_volume_ducking_mode":@1,/**配置音量闪避,避免声音冲突**/
@"enable_rnd_volume_adaptive":@"true",/**启用播放音量自适应**/
};
[ZegoExpressEngine setEngineConfig:engineConfig];
[ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];
// 开启传统音频 3A 处理
[[ZegoExpressEngine sharedEngine] enableAGC:TRUE];
[[ZegoExpressEngine sharedEngine] enableAEC:TRUE];
[[ZegoExpressEngine sharedEngine] enableANS:TRUE];
// 开启 AI 回声消除
[[ZegoExpressEngine sharedEngine] setAECMode:ZegoAECModeAIBalanced];
// 开启 AI 降噪,适度的噪声抑制
[[ZegoExpressEngine sharedEngine] setANSMode:ZegoANSModeMedium];
其他优化建议
监听异常回调
点击查看监听异常回调指引。监听回调中 Event 为 Exception 的事件。通过 Data.Code 和 Data.Message 可以快速定位问题。