如何通过服务端接口发起智能体通话

本文将向您详细介绍,如何借助服务端的OpenAPI发起智能体呼叫。

场景说明

当您的业务需要实时监控或记录每一次通话时,可以通过服务端OpenAPI:GenerateAIAgentCall - 生成AI智能体通话实例接口来发起通话,该接口需要的服务端来发起,并把发起后的结果下发给客户端,客户端再通过返回的信息进入通话。

业务流程

image

APP在启动AI智能体后,便可调用call()进入通话,在通话过程中,可以调用AICallKitAPI实现智能体的实时字幕、打断等交互功能。AICallKit依赖于实时音视频能力,因此在内部已实现AliVCSDK_ARTC SDK的相关功能。如果您的业务场景还需要用到直播与点播能力,可以使用音视频终端组合SDK,例如AliVCSDK_StandardAliVCSDK_InteractiveLive,具体组合方式,请参考SDK选择与下载

服务端流程

服务端提供启动通话的接口,例如generateAIAgentCall接口,核心实现是接收来自客户端的请求,调用OpenAPI:GenerateAIAgentCall - 生成AI智能体通话实例,再把结果透传给端侧。

// 演示如果调用GenerateAIAgentCall  
// This file is auto-generated, don't edit it. Thanks.
package com.aliyun.rtc;

import com.alibaba.fastjson.JSON;
import com.aliyun.ice20201109.models.GenerateAIAgentCallResponse;
import com.aliyun.tea.*;

public class SampleGenerateaiagentcall {

    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化账号Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.ice20201109.Client createClient() throws Exception {
        // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
        // 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId("YourAccessKeyId")
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret("YourAccessKeySecret");
        // Endpoint 请参考 https://api.aliyun.com/product/ICE
        config.endpoint = "ice.cn-shanghai.aliyuncs.com";
        return new com.aliyun.ice20201109.Client(config);
    }



    public static void main(String[] args) throws Exception {
        generateAIAgentCall();
    }


    public static void generateAIAgentCall() throws Exception {
        com.aliyun.ice20201109.Client client = createClient();
        com.aliyun.ice20201109.models.AIAgentTemplateConfig.AIAgentTemplateConfigVoiceChat AIAgentTemplateConfigVoiceChat = new com.aliyun.ice20201109.models.AIAgentTemplateConfig.AIAgentTemplateConfigVoiceChat()
                .setGreeting("你好!");

        com.aliyun.ice20201109.models.AIAgentTemplateConfig AIAgentTemplateConfig = new com.aliyun.ice20201109.models.AIAgentTemplateConfig()
                .setVoiceChat(AIAgentTemplateConfigVoiceChat);

        com.aliyun.ice20201109.models.GenerateAIAgentCallRequest generateAIAgentCallRequest = new com.aliyun.ice20201109.models.GenerateAIAgentCallRequest()
                .setAIAgentId("YOUR_AIAGENTID")
                .setTemplateConfig(AIAgentTemplateConfig);
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            // 复制代码运行请自行打印 API 的返回值
            GenerateAIAgentCallResponse resp = client.generateAIAgentCallWithOptions(generateAIAgentCallRequest, runtime);
            System.out.println(JSON.toJSONString(resp));
        } catch (TeaException error) {
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            // 错误 message
            System.out.println(error.getMessage());
            // 诊断地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            // 错误 message
            System.out.println(error.getMessage());
            // 诊断地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        }
    }
}

客户端流程

步骤一:客户端创建并初始化通话引擎

通过AICallKit SDK创建并初始化通话引擎的代码示例如下:

Android
ARTCAICallEngine mARTCAICallEngine = null;

// 创建engine实例
void initEngine(Context context, String userId) {
    // 初始化
    // context -> Android Context
    // userId -> 进入rtc频道的用户id
    mARTCAICallEngine = new ARTCAICallEngineImpl(context, userId);

    // 指定智能体的类型:纯语音、数字人、视觉理解
    ARTCAICallEngine.ARTCAICallAgentType aiAgentType = VoiceAgent;
    mARTCAICallEngine.setAICallAgentType(aiAgentType);

    // 如果是数字人类型,则需要配置数字人显示的视图容器
    if (aiAgentType == AvatarAgent) {
        ViewGroup avatarlayer;
        engine.setAgentView(
                avatarlayer,
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
    }
    // 如果是视觉理解类型,则需要配置本地视频预览显示的视图容器
    else if (aiAgentType == VisionAgent) {
        ViewGroup previewLayer;
        engine.setLocalView(previewLayer,
                new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
    } else if (aiAgentType == VideoAgent) {
        ARTCAICallEngine.ARTCAICallVideoCanvas remoteCanvas = new ARTCAICallEngine.ARTCAICallVideoCanvas();
        remoteCanvas.zOrderOnTop = false;
        remoteCanvas.zOrderMediaOverlay = false;
        ViewGroup avatarlayer;
        engine.setAgentView(
                avatarlayer,
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT),
                remoteCanvas);

        ViewGroup previewLayer;
        engine.setLocalView(previewLayer,
                new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
    }
}

// 设置回调
void initCallback() {
    mARTCAICallEngine.setEngineCallback(mCallEngineCallbackWrapper);
}

// 回调处理(仅示例部分核心的回调操作)
ARTCAICallEngine.IARTCAICallEngineCallback mCallEngineCallbackWrapper = new ARTCAICallEngine.IARTCAICallEngineCallback() {
    @Override
    public void onErrorOccurs(ARTCAICallEngine.AICallErrorCode errorCode) {
        // 发生了错误,结束通话
        mARTCAICallEngine.handup();
    }

    @Override
    public void onCallBegin() {
        // 通话开始(入会)
    }

    @Override
    public void onCallEnd() {
        // 通话结束(离会)
    }

    @Override
    public void onAICallEngineRobotStateChanged(ARTCAICallEngine.ARTCAICallRobotState oldRobotState,
            ARTCAICallEngine.ARTCAICallRobotState newRobotState) {
        // 机器人状态同步
    }

    @Override
    public void onUserSpeaking(boolean isSpeaking) {
        // 用户说话回调
    }

    @Override
    public void onUserAsrSubtitleNotify(String text, boolean isSentenceEnd, int sentenceId) {
        // 同步ASR识别用户的话
    }

    @Override
    public void onAIAgentSubtitleNotify(String text, boolean end, int userAsrSentenceId) {
        // 同步智能体回应的话
    }

    @Override
    public void onNetworkStatusChanged(String uid, ARTCAICallEngine.ARTCAICallNetworkQuality quality) {
        // 网络状态回调
    }

    @Override
    public void onVoiceVolumeChanged(String uid, int volume) {
        // 音量变化
    }

    @Override
    public void onVoiceIdChanged(String voiceId) {
        // 当前通话的音色发生了改变
    }

    @Override
    public void onVoiceInterrupted(boolean enable) {
        // 当前通话的语音打断设置改变
    }

    @Override
    public void onAgentVideoAvailable(boolean available) {
        // 智能体视频是否可用(推流)
    }

    @Override
    public void onAgentAudioAvailable(boolean available) {
        // 智能体音频是否可用(推流)
    }

    @Override
    public void onAgentAvatarFirstFrameDrawn() {
        // 数字人首视频帧渲染
    }

    @Override
    public void onUserOnLine(String uid) {
        // 用户上线回调
    }

};
iOS
// 创建engine实例
let engine = ARTCAICallEngineFactory.createEngine()
let agentType: ARTCAICallAgentType

// 初始化engine实例
public func setup() {
    // 设置回调
    self.engine.delegate = self

    // 如果是数字人类型,则需要配置数字人显示的视图配置
    if self.agentType == .AvatarAgent {
        let agentViewConfig = ARTCAICallViewConfig(view: self.avatarAgentView)
        self.engine.setAgentViewConfig(viewConfig: agentViewConfig)
    }
    // 如果是视觉理解类型,则需要配置本地视频预览配置
    else if self.agentType == .VisionAgent {
        let cameraViewConfig = ARTCAICallViewConfig(view: self.cameraView)
        self.engine.setLocalViewConfig(viewConfig: cameraViewConfig)
    }
    // 如果是视频通话类型,则需要配置数字人显示的视图配置+本地视频预览配置
    else if self.agentType == .VisionAgent {
        let agentViewConfig = ARTCAICallViewConfig(view: self.avatarAgentView)
        self.engine.setAgentViewConfig(viewConfig: agentViewConfig)

        let cameraViewConfig = ARTCAICallViewConfig(view: self.cameraView)
        self.engine.setLocalViewConfig(viewConfig: cameraViewConfig)
    }
}

// 回调处理(仅示例不分核心的回调操作)
public func onErrorOccurs(code: ARTCAICallErrorCode) {
    // 发生了错误
    self.engine.handup()
}

public func onCallBegin() {
    // 通话开始
}

public func onCallEnd() {
    // 通话结束
}

public func onAgentStateChanged(state: ARTCAICallAgentState) {
    // 智能体状态改变
}

public func onUserSubtitleNotify(text: String, isSentenceEnd: Bool, sentenceId: Int) {
    // 用户提问被智能体识别结果的通知
}

public func onVoiceAgentSubtitleNotify(text: String, isSentenceEnd: Bool, userAsrSentenceId: Int) {
    // 智能体回答结果通知
}

public func onVoiceIdChanged(voiceId: String) {
    // 当前通话的音色发生了改变
}

public func onVoiceInterrupted(enable: Bool) {
    // 当前通话的语音打断是否启用
}
Web
// 引入SDK
import AICallEngine, { AICallErrorCode, AICallAgentState, AICallAgentType } from 'aliyun-auikit-aicall';

// 创建engine实例
const engine = new AICallEngine();

// 其他功能调用示例,请参考API说明

// 回调处理(仅示例不分核心的回调操作)
engine.on('errorOccurred', (code) => {
  // 发生了错误
  engine.handup();
});

engine.on('callBegin', () => {
  // 通话开始
});

engine.on('callEnd', () => {
  // 通话结束
});

engine.on('agentStateChanged', (state) => {
  // 智能体状态改变
});

engine.on('userSubtitleNotify', (subtitle) => {
  // 用户提问被智能体识别结果的通知
});

engine.on('agentSubtitleNotify', (subtitle) => {
  // 智能体回答结果通知
});

engine.on('voiceIdChanged', (voiceId) => {
  // 当前通话的音色发生了改变
});

engine.on('voiceInterruptChanged', (enable) => {
  // 当前通话的语音打断是否启用
});

// 初始化
await engine.init(agentType);

步骤二:进入通话

通过AICallKit SDK进入通话的代码示例如下:

Android
// 启动智能体后,开始通话
void call() {
    // 设置engine的启动参数
    ARTCAICallEngine.ARTCAICallConfig artcaiCallConfig = new ARTCAICallEngine.ARTCAICallConfig();
    // 如果有临时的智能体ID,可以配置
    artcaiCallConfig.agentId = aiAgentId;
    artcaiCallConfig.region = "cn-shanghai";//智能体区域,必填
    artcaiCallConfig.agentType = VoiceAgent;//定智能体的类型:纯语音、数字人、视觉理解、视频通话
    mARTCAICallEngine.init(artcaiCallConfig);

    // 从服务端获取到aIAgentInstanceId、rtcAuthToken、aIAgentUserId、channelId
    String aIAgentInstanceId = “XXX”;
    String rtcAuthToken = "XXX";
    String aIAgentUserId = "XXX";
    String channelId = "XXX";
    mARTCAICallEngine.call(rtcAuthToken, aIAgentInstanceId, 
                           aIAgentUserId, channelId);
}
iOS
public func call() {
    // 从服务端获取到agent_instance_id、rtc_auth_token、ai_agent_user_id、channel_id
    let agentInfo = ARTCAICallAgentInfo(agentType: self.agentType, channelId: channel_id, uid: ai_agent_user_id, instanceId: agent_instance_id)
    self.engine.call(userId: self.userId, token: rtc_auth_token, agentInfo: agentInfo) { [weak self] error in
        if let error = error {
            // 处理错误
        }
        else {
            // 成功通话
        }
    }
}
Web
const userId = 'xxx';  // 当前发起通话的用户的uid

// 从服务端获取 agentInfo
// 您需要部署自己的 AppServer,访问 GenerateAIAgentCall 接口生成AI智能体通话实例
// 其中的返回值即为 agentInfo
// agentInfo.instanceId 智能体实例ID
// agentInfo.channelId: ARTC频道ID
// agentInfo.userId: ARTC用户ID
// agentInfo.rtcToken: ARTC入会Token
// agentInfo.reqId: 请求ID
const agentInfo = await fetchAgentInfo();
// 可以与 fetchAgentInfo 并行处理, agentType 为智能体类型,参考 AICallAgentType
await engine.init(agentType);

try {
  // 启动智能体后,开始通话
  engine.call(userId, agentInfo);
} catch (error) {}

步骤三:挂断通话

通过AICallKit SDK挂断通话的代码示例如下:

Android
// 结束通话
void handup() {
    mARTCAICallEngine.handup();
}
iOS
public func handup() {
    // 结束通话
    self.engine.handup()
}
Web
// 结束通话,在需要时调用
engine.handup();

相关文档

在开启通话后,挂断前,可以根据您的业务需求处理字幕、打断智能体讲话等,请参见基础功能