手册手册
  • 实时数字人交互SDK 集成开发

实时数字人交互SDK 集成开发

1. 简介

实时数字人交互SDK 提供了一套简单易用的接口,让开发者可以在各种平台上快速集成数字人交互功能。通过该 SDK,您可以在 Web、iOS、Android 和小程序中实现与数字人的实时交互。

1.1 功能特点

  • 多平台支持:同时支持 Web、iOS、Android 和小程序
  • 悬浮按钮入口:提供悬浮按钮,点击即可打开数字人对话界面
  • 自定义加载界面:支持自定义加载界面,提升用户体验
  • 热词过滤:支持热词过滤,保障交互内容的安全性
  • 简单易用:提供简洁的 API,快速集成到现有项目

1.2 支持的平台

  • Web 端(浏览器)
  • iOS 端(Swift)
  • Android 端(Kotlin/Java)
  • 小程序端(微信小程序)

2. 快速开始

2.1 获取 API Key

在开始之前,您需要在 NuwaAI平台完成以下步骤:

  1. 注册一个 NuwaAI账号,并登录
  2. 登录后,记录下用户ID
  3. 创建一个数字人,并记录下数字人 ID
  4. 创建一个API Key,并记录下 API Key

获取 API Key, 数字人ID, 用户ID

2.2 安装 SDK

根据您的平台选择相应的安装方式:

示例
# 使用 npm 安装
npm install nuwaai-human-sdk

# 或直接下载 SDK 包
# 下载地址:https://nuwaai.com/demo/npcTalk/npcTalk.tgz

# 直接下载 SDK 包
# 下载地址 https://docs.nuwaai.com/sdk/NuwaAI-IOSSDK.zip
# 在 Xcode 中添依赖 libNuwaSDK.a, WebRTC.framework

# 下载android SDK 包
# 下载地址:https://docs.nuwaai.com/sdk/nuwaai-human-sdk.aar

# 解压后将 SDK 文件复制到android项目目录
# 微信小程序page页面web-view 组件的src属性传入web 地址,用微信内置浏览器实现

2.3 第一个数字人交互

现在,让我们创建第一个数字人交互应用:

示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SDK 初始化页面</title>
     <link rel="stylesheet" href="/node_modules/nuwaai-human-sdk/css/demo.css">
    <script type="module" src="/node_modules/nuwaai-human-sdk/js/npcTalkFloatingButton.js"></script>
</head>
<body>
    <div class="container">
        <h1>🔧 SDK 初始化配置</h1>
        <form id="sdkForm">
            <div class="form-group">
                <label for="apiKey">API Key</label>
                <input type="text" id="apiKey" name="apiKey" placeholder="请输入您的 API Key" value="sk-jqZ-gBPcRCG1nLpaenmS1gew7rcCkIs-NUWSPzpd3DeWYxobM_CHm-uPTCa8gDPv">
            </div>
            <div class="form-group">
                <label for="avatarId">数字人 ID</label>
                <input type="text" id="avatarId" name="avatarId" placeholder="请输入数字人 ID" value="2027224426241638401">
            </div>
            <div class="form-group">
                <label for="userId">用户 ID</label>
                <input type="text" id="userId" name="userId" placeholder="请输入用户 ID" value="54">
            </div>
            <button type="button" class="btn-init" onclick="initializeSDK()">初始化 SDK</button>
        </form>
        <div id="result" class="result"></div>
    </div>

    <script>
        // 初始化 SDK 函数
        function initializeSDK() {
            let floatingButtonInstance = null;
            const apiKey = document.getElementById('apiKey').value.trim();
            const avatarId = document.getElementById('avatarId').value.trim();
            const userId = document.getElementById('userId').value.trim();
            const resultDiv = document.getElementById('result');
            // 验证输入
            if (!apiKey || !avatarId || !userId) {
                resultDiv.className = 'result error';
                resultDiv.textContent = '请填写所有必填字段';
                return;
            }

            fetch('https://api.nuwaai.com/web/apiKey/auth', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    secretKey: apiKey,
                }),
            })
            .then(res => res.json())
            .then(data => {
                if (data.code === 0) {
                    console.log('token:', data.data);
                    localStorage.setItem('token', data.data);
                    initWithCustomLoading(avatarId, userId);
                } else {
                    console.error('请求失败:', data.msg);
                }
            })
            .catch(error => {
                console.error('请求出错:', error);
            });
            
            function initWithCustomLoading(avatorId, userId) {
                try {
                    // 销毁之前的实例
                    if (floatingButtonInstance) {
                        floatingButtonInstance.destroy();
                    }

                    // 创建新实例
                    // loadingConfig 可以直接传递对象,npcTalkFloatingButton.js 会自动处理编码
                    floatingButtonInstance = new window.NpcTalkFloatingButton({
                        avatorid: avatorId,
                        userid: userId,
                        npcPage1: '/node_modules/nuwaai-human-sdk/index-sdk.html',
                        loadingConfig: null,
                        disabledButton: false, //按钮隐藏  调用floatingButtonInstance.navigateToNpcTalk()
                        title: '数字人聊天', //页面标题(可选,会自动更新 index-sdk.html 的 <title>)
                        href: '', //页面logo地址(可选,会自动更新 index-sdk.html 的 <link rel="icon">)
                        sliceTime:10000,    //毫秒
                        backAddress:'', // 返回地址
                        hotWords:['咱们','休息一下','你好呀']   //热词,用来过滤敏感词
                    });
                } catch (error) {
                    console.error('初始化失败:', error);
                }
            }
        }
        
        // 监听回车键提交
        document.getElementById('sdkForm').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                e.preventDefault();
                initializeSDK();
            }
        });
    </script>
</body>
</html>

import UIKit
import NuwaSDK

class ViewController: UIViewController {
    private var apiKeyTextField: UITextField!
    private var avatarIdTextField: UITextField!
    private var userIdTextField: UITextField!
    private var initializeButton: UIButton!
    private var floatingButton: NuwaFloatingButton?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        title = "Nuwa SDK 初始化"
        
        setupUI()
    }
    
    func setupUI() {
        let screenWidth = view.frame.size.width
        let padding: CGFloat = 40
        let fieldWidth = screenWidth - padding * 2
        let fieldHeight: CGFloat = 50
        let verticalSpacing: CGFloat = 24
        let topOffset: CGFloat = 120
        
        // API Key 输入框
        let apiKeyLabel = createLabel(withText: "API Key", y: topOffset)
        view.addSubview(apiKeyLabel)
        
        apiKeyTextField = createTextField(withY: apiKeyLabel.frame.origin.y + apiKeyLabel.frame.size.height + 8)
        apiKeyTextField.placeholder = "请输入 API Key"
        apiKeyTextField.text = "sk-u96kvX4vRJ9CtX7Zj4KSK-CnHXpihy4B6ehyyw8v8SwfDIUbEwA_HmFTG3E_jGMy"
        view.addSubview(apiKeyTextField)
        
        // 数字人 ID 输入框
        let avatarIdLabel = createLabel(withText: "数字人 ID", y: apiKeyTextField.frame.origin.y + apiKeyTextField.frame.size.height + verticalSpacing)
        view.addSubview(avatarIdLabel)
        
        avatarIdTextField = createTextField(withY: avatarIdLabel.frame.origin.y + avatarIdLabel.frame.size.height + 8)
        avatarIdTextField.placeholder = "请输入数字人 ID"
        avatarIdTextField.text = "2031288580266336258"
        view.addSubview(avatarIdTextField)
        
        // 用户 ID 输入框
        let userIdLabel = createLabel(withText: "用户 ID", y: avatarIdTextField.frame.origin.y + avatarIdTextField.frame.size.height + verticalSpacing)
        view.addSubview(userIdLabel)
        
        userIdTextField = createTextField(withY: userIdLabel.frame.origin.y + userIdLabel.frame.size.height + 8)
        userIdTextField.placeholder = "请输入用户 ID"
        userIdTextField.text = "54"
        view.addSubview(userIdTextField)
        
        // 初始化按钮
        initializeButton = UIButton(type: .system)
        initializeButton.frame = CGRect(x: padding, y: userIdTextField.frame.origin.y + userIdTextField.frame.size.height + verticalSpacing * 2, width: fieldWidth, height: fieldHeight)
        initializeButton.setTitle("初始化 SDK", for: .normal)
        initializeButton.backgroundColor = UIColor(red: 0.122, green: 0.467, blue: 0.918, alpha: 1.0)
        initializeButton.setTitleColor(.white, for: .normal)
        initializeButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        initializeButton.layer.cornerRadius = 8
        initializeButton.layer.shadowColor = UIColor.black.cgColor
        initializeButton.layer.shadowOffset = CGSize(width: 0, height: 2)
        initializeButton.layer.shadowOpacity = 0.1
        initializeButton.layer.shadowRadius = 4
        initializeButton.addTarget(self, action: #selector(initSDK), for: .touchUpInside)
        view.addSubview(initializeButton)
    }
    
    func createLabel(withText text: String, y: CGFloat) -> UILabel {
        let padding: CGFloat = 40
        let screenWidth = view.frame.size.width
        
        let label = UILabel(frame: CGRect(x: padding, y: y, width: screenWidth - padding * 2, height: 24))
        label.text = text
        label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
        label.textColor = UIColor(red: 0.333, green: 0.333, blue: 0.333, alpha: 1.0)
        return label
    }
    
    func createTextField(withY y: CGFloat) -> UITextField {
        let padding: CGFloat = 40
        let screenWidth = view.frame.size.width
        
        let textField = UITextField(frame: CGRect(x: padding, y: y, width: screenWidth - padding * 2, height: 50))
        textField.borderStyle = .roundedRect
        textField.font = UIFont.systemFont(ofSize: 16)
        textField.backgroundColor = UIColor(red: 0.957, green: 0.957, blue: 0.957, alpha: 1.0)
        textField.layer.cornerRadius = 8
        textField.layer.borderWidth = 1
        textField.layer.borderColor = UIColor(red: 0.878, green: 0.878, blue: 0.878, alpha: 1.0).cgColor
        return textField
    }
    
    @objc func initSDK() {
        guard let apiKey = apiKeyTextField.text, !apiKey.isEmpty else {
            showAlert(withTitle: "提示", message: "请输入 API Key")
            return
        }
        
        guard let avatarId = avatarIdTextField.text, !avatarId.isEmpty else {
            showAlert(withTitle: "提示", message: "请输入数字人 ID")
            return
        }
        
        let userId = userIdTextField.text ?? ""
        
        // 创建配置
        let config = NuwaFloatingButtonConfig()
        config.apiKey = apiKey
        config.avatorId = avatarId
        config.userId = userId
        
        // 创建并显示悬浮按钮
        floatingButton = NuwaFloatingButton(config: config)
        floatingButton?.show(in: view)
        
        // 显示成功提示
        showAlert(withTitle: "成功", message: "SDK 初始化成功,悬浮按钮已显示")
    }
    
    func showAlert(withTitle title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
        alert.addAction(okAction)
        present(alert, animated: true, completion: nil)
    }
}
//  objc 示例
// ViewController.h
#import <UIKit/UIKit.h>
#import "NuwaSDK.h"

@interface ViewController : UIViewController

@property (nonatomic, strong) UITextField *apiKeyTextField;
@property (nonatomic, strong) UITextField *avatarIdTextField;
@property (nonatomic, strong) UITextField *userIdTextField;
@property (nonatomic, strong) UIButton *initializeButton;
@property (nonatomic, strong) NuwaFloatingButton *floatingButton;

@end

// ViewController.m
#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"Nuwa SDK 初始化";
    
    [self setupUI];
}

- (void)setupUI {
    CGFloat screenWidth = self.view.frame.size.width;
    CGFloat padding = 40;
    CGFloat fieldWidth = screenWidth - padding * 2;
    CGFloat fieldHeight = 50;
    CGFloat verticalSpacing = 24;
    CGFloat topOffset = 120;
    
    // API Key 输入框
    UILabel *apiKeyLabel = [self createLabelWithText:@"API Key" y:topOffset];
    [self.view addSubview:apiKeyLabel];
    
    self.apiKeyTextField = [self createTextFieldWithY:apiKeyLabel.frame.origin.y + apiKeyLabel.frame.size.height + 8];
    self.apiKeyTextField.placeholder = @"请输入 API Key";
    self.apiKeyTextField.text = @"sk-u96kvX4vRJ9CtX7Zj4KSK-CnHXpihy4B6ehyyw8v8SwfDIUbEwA_HmFTG3E_jGMy";
    [self.view addSubview:self.apiKeyTextField];
    
    // 数字人 ID 输入框
    UILabel *avatarIdLabel = [self createLabelWithText:@"数字人 ID" y:self.apiKeyTextField.frame.origin.y + self.apiKeyTextField.frame.size.height + verticalSpacing];
    [self.view addSubview:avatarIdLabel];
    
    self.avatarIdTextField = [self createTextFieldWithY:avatarIdLabel.frame.origin.y + avatarIdLabel.frame.size.height + 8];
    self.avatarIdTextField.placeholder = @"请输入数字人 ID";
    self.avatarIdTextField.text = @"2031288580266336258";
    [self.view addSubview:self.avatarIdTextField];
    
    // 用户 ID 输入框
    UILabel *userIdLabel = [self createLabelWithText:@"用户 ID" y:self.avatarIdTextField.frame.origin.y + self.avatarIdTextField.frame.size.height + verticalSpacing];
    [self.view addSubview:userIdLabel];
    
    self.userIdTextField = [self createTextFieldWithY:userIdLabel.frame.origin.y + userIdLabel.frame.size.height + 8];
    self.userIdTextField.placeholder = @"请输入用户 ID";
    self.userIdTextField.text = @"54";
    [self.view addSubview:self.userIdTextField];
    
    // 初始化按钮
    self.initializeButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.initializeButton.frame = CGRectMake(padding, self.userIdTextField.frame.origin.y + self.userIdTextField.frame.size.height + verticalSpacing * 2, fieldWidth, fieldHeight);
    [self.initializeButton setTitle:@"初始化 SDK" forState:UIControlStateNormal];
    self.initializeButton.backgroundColor = [UIColor colorWithRed:0.122 green:0.467 blue:0.918 alpha:1.0];
    [self.initializeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    self.initializeButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
    self.initializeButton.layer.cornerRadius = 8;
    self.initializeButton.layer.shadowColor = [UIColor blackColor].CGColor;
    self.initializeButton.layer.shadowOffset = CGSizeMake(0, 2);
    self.initializeButton.layer.shadowOpacity = 0.1;
    self.initializeButton.layer.shadowRadius = 4;
    [self.initializeButton addTarget:self action:@selector(initSDK) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.initializeButton];
}

- (UILabel *)createLabelWithText:(NSString *)text y:(CGFloat)y {
    CGFloat padding = 40;
    CGFloat screenWidth = self.view.frame.size.width;
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(padding, y, screenWidth - padding * 2, 24)];
    label.text = text;
    label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
    label.textColor = [UIColor colorWithRed:0.333 green:0.333 blue:0.333 alpha:1.0];
    return label;
}

- (UITextField *)createTextFieldWithY:(CGFloat)y {
    CGFloat padding = 40;
    CGFloat screenWidth = self.view.frame.size.width;
    
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(padding, y, screenWidth - padding * 2, 50)];
    textField.borderStyle = UITextBorderStyleRoundedRect;
    textField.font = [UIFont systemFontOfSize:16];
    textField.backgroundColor = [UIColor colorWithRed:0.957 green:0.957 blue:0.957 alpha:1.0];
    textField.layer.cornerRadius = 8;
    textField.layer.borderWidth = 1;
    textField.layer.borderColor = [UIColor colorWithRed:0.878 green:0.878 blue:0.878 alpha:1.0].CGColor;
    return textField;
}

- (void)initSDK {
    NSString *apiKey = self.apiKeyTextField.text;
    NSString *avatarId = self.avatarIdTextField.text;
    NSString *userId = self.userIdTextField.text;
    
    if (!apiKey || apiKey.length == 0) {
        [self showAlertWithTitle:@"提示" message:@"请输入 API Key"];
        return;
    }
    
    if (!avatarId || avatarId.length == 0) {
        [self showAlertWithTitle:@"提示" message:@"请输入数字人 ID"];
        return;
    }
    
    // 创建配置
    NuwaFloatingButtonConfig *config = [[NuwaFloatingButtonConfig alloc] init];
    config.apiKey = apiKey;
    config.avatorId = avatarId;
    config.userId = userId;
    
    // 创建并显示悬浮按钮
    self.floatingButton = [[NuwaFloatingButton alloc] initWithConfig:config];
    [self.floatingButton showInView:self.view];
    
    // 显示成功提示
    [self showAlertWithTitle:@"成功" message:@"SDK 初始化成功,悬浮按钮已显示"];
}

- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:okAction];
    [self presentViewController:alert animated:YES completion:nil];
}

@end
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.nuwaai.human.NuwaHumanLoadingConfig;
import com.nuwaai.human.NuwaHumanSDK;
import com.nuwaai.human.NuwaHumanSDKOptions;
import com.nuwaai.sdk.NuwaAIClient;
import com.nuwaai.sdk.NuwaAIConfig;
import com.nuwaai.sdk.NuwaCallback;

import java.util.ArrayList;
import java.util.List;

public class HumanDemoActivity extends AppCompatActivity {

    private EditText etApiKey;
    private EditText etAvatarId;
    private EditText etUserId;
    private EditText etHotWords;
    private CheckBox cbHideFloat;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_human_demo);

        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setTitle("数字人交互");
        }

        initViews();
        setupClickListeners();
    }

    @Override
    public boolean onSupportNavigateUp() {
        finish();
        return true;
    }

    private void initViews() {
        etApiKey = findViewById(R.id.et_api_key);
        etAvatarId = findViewById(R.id.et_avatar_id);
        etUserId = findViewById(R.id.et_user_id);
        etHotWords = findViewById(R.id.et_hot_words);
        cbHideFloat = findViewById(R.id.cb_hide_float);

        
        etAvatarId.setText("2027224426241638401");
        etUserId.setText("54");
        etHotWords.setText("咱们,休息一下,你好呀");
    }

    private void setupClickListeners() {
        findViewById(R.id.btn_init_sdk).setOnClickListener(v -> initializeSDK());
        findViewById(R.id.btn_navigate_talk).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "请先初始化 SDK", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.navigateToNpcTalk();
        });
        findViewById(R.id.btn_show_float).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "请先初始化 SDK", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.showFloatingButton();
        });
        findViewById(R.id.btn_hide_float).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "请先初始化 SDK", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.hideFloatingButton();
        });
        findViewById(R.id.btn_destroy_sdk).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk != null) {
                sdk.destroy();
                Toast.makeText(this, "已销毁 SDK", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "当前无 SDK 实例", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void initializeSDK() {
        String apiKey = etApiKey.getText().toString().trim();
        String avatarId = etAvatarId.getText().toString().trim();
        String userId = etUserId.getText().toString().trim();
        boolean hideFloat = cbHideFloat.isChecked();

        if (avatarId.isEmpty() || userId.isEmpty()) {
            Toast.makeText(this, "请填写数字人 ID 与用户 ID", Toast.LENGTH_SHORT).show();
            return;
        }
        if (apiKey.isEmpty()) {
            Toast.makeText(this, "请填写 API Key(仅用于 nuwaai-sdk 鉴权,不会传入数字人 SDK)", Toast.LENGTH_SHORT).show();
            return;
        }

        NuwaAIConfig config = NuwaAIConfig.builder().apiKey(apiKey).build();
        NuwaAIClient.init(config);
        NuwaAIClient.getInstance().authenticate(new NuwaCallback<String>() {
            @Override
            public void onSuccess(String token) {
                runOnUiThread(() -> {
                    NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
                            .html("<div class=\"loading\">加载中...</div>")
                            .style("display", "flex")
                            .style("flex-direction", "column")
                            .style("align-items", "center")
                            .style("justify-content", "center")
                            .style("background", "#111111")
                            .style("color", "#eeeeee")
                            .additionalCss(
                                    ".custom-loading-container .loading{text-align:center;width:100%;}")
                            .build();

                    NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
                            .avatarId(avatarId)
                            .userId(userId)
                            .token(token)
                            .loadingConfig(loadingConfig)
                            .disabledButton(hideFloat)
                            .hotWords(parseHotWords(etHotWords.getText().toString()))
                            .build();

                    NuwaHumanSDK.initialize(HumanDemoActivity.this, options);
                    Toast.makeText(HumanDemoActivity.this,
                            hideFloat ? "已初始化(未显示悬浮按钮)" : "已初始化并显示悬浮按钮",
                            Toast.LENGTH_SHORT).show();
                });
            }

            @Override
            public void onFailure(int code, String message) {
                runOnUiThread(() -> Toast.makeText(HumanDemoActivity.this,
                        "鉴权失败: " + message, Toast.LENGTH_SHORT).show());
            }
        });
    }

    private static List<String> parseHotWords(String raw) {
        if (raw == null || raw.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String[] parts = raw.split(",");
        List<String> list = new ArrayList<>();
        for (String p : parts) {
            String t = p.trim();
            if (!t.isEmpty()) {
                list.add(t);
            }
        }
        return list;
    }
}

<!--pages/webview/webview.wxml   微信小程序page页面web-view 组件的src属性传入web 地址,用微信内置浏览器实现--> 
<web-view  src="{{webUrl}}" ></web-view>

至此,您已经成功地将 Nuwa 数字人实时交互 SDK 集成到了您的应用中。

web端集成后演示:点击这里

3. SDK开发详解

3.1 获取 Access Token

在使用 SDK 前,需要先获取 Access Token: 获取 Access Token

3.2 初始化 SDK

示例
let floatingButtonInstance = new window.NpcTalkFloatingButton({
  avatorid: '2027224426241638401',
  userid: '54',
  npcPage1: 'index-sdk.html',
  loadingConfig: {
    html: '<div class="loading">加载中...</div>',
    style: {
      background: '#fff',
      color: '#333'
    }
  },
  disabledButton: false,
  title: '数字人聊天',
  href: '',
  sliceTime: 10000,
  backAddress: '',
  hotWord: ['咱们', '休息一下', '你好呀']
});
// 创建配置
let config = NuwaFloatingButtonConfig()
config.apiKey = "your_api_key"
config.avatorId = "your_avatar_id"
config.userId = "your_user_id"

// 创建并显示悬浮按钮
let floatingButton = NuwaFloatingButton(config: config)
floatingButton.show(in: view)
NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
        .html("<div class=\"loading\">加载中...</div>")
        .additionalCss(
                ".custom-loading-container .loading{text-align:center;width:100%;}")
        .build();

NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
        .avatarId(avatarId)
        .userId(userId)
        .token(token)
        .loadingConfig(loadingConfig)
        .disabledButton(false)
        .build();

NuwaHumanSDK.initialize(HumanDemoActivity.this, options);
//小程序通过web-view组件传入 web 数字人url地址观看即可
参数类型必填说明平台
avatoridString是数字人 ID全部
useridString是用户 ID全部
tokenString是Access TokeniOS/Android/小程序
npcPage1String是数字人聊天页面路径Web/小程序
loadingConfigObject否自定义加载界面配置全部
disabledButtonBoolean否是否隐藏悬浮按钮全部
titleString否页面标题Web/iOS/Android
hrefString否页面 logo 地址Web
sliceTimeNumber否时间切片,单位毫秒Web
backAddressString否返回地址Web
hotWordArray否热词列表,用于过滤敏感词全部

3.3 自定义加载界面

您可以自定义加载界面,提升用户体验:

示例
const loadingConfig = {
  html: `
  <div class="custom-loading-wrapper">
    <div class="custom-loading-spinner"></div>
    <div class="custom-loading-text">正在连接数字人...</div>
    <div class="custom-loading-subtext">请稍候</div>
  </div>
`,
  style: {
    background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    color: 'white',
  },
  additionalCSS: `
    .custom-loading-container .custom-loading-wrapper {
      width: 100%;
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    .custom-loading-container .custom-loading-spinner {
      width: 60px;
      height: 60px;
      border: 4px solid rgba(255, 255, 255, 0.3);
      border-top-color: white;
      border-radius: 50%;
      animation: custom-loading-spin 1s linear infinite;
      margin-bottom: 20px;
    }
    @keyframes custom-loading-spin {
      to { transform: rotate(360deg); }
    }
  `,
};

floatingButtonInstance = new window.NpcTalkFloatingButton({
  // 其他参数...
  loadingConfig: loadingConfig
});
// NuwaFloatingButton 目前不支持自定义加载界面
// 您可以在初始化过程中通过 UI 显示自定义加载状态

// 示例:在初始化前显示加载指示器
func initSDK() {
    // 显示加载指示器
    let activityIndicator = UIActivityIndicatorView(style: .large)
    activityIndicator.center = view.center
    activityIndicator.color = .blue
    view.addSubview(activityIndicator)
    activityIndicator.startAnimating()
    
    // 模拟网络请求延迟
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
        // 隐藏加载指示器
        activityIndicator.stopAnimating()
        activityIndicator.removeFromSuperview()
        
        // 创建配置并初始化 SDK
        let config = NuwaFloatingButtonConfig()
        config.apiKey = "your_api_key"
        config.avatorId = "your_avatar_id"
        config.userId = "your_user_id"
        
        // 创建并显示悬浮按钮
        let floatingButton = NuwaFloatingButton(config: config)
        floatingButton.show(in: self.view)
        
        // 显示成功提示
        self.showAlert(withTitle: "成功", message: "SDK 初始化成功,悬浮按钮已显示")
    }
}
NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
    .html("<div class=\"loading\">加载中...</div>")
    .style("display", "flex")
    .style("flex-direction", "column")
    .style("align-items", "center")
    .style("justify-content", "center")
    .style("background", "#111111")
    .style("color", "#eeeeee")
    .additionalCss(
            ".custom-loading-container .loading{text-align:center;width:100%;}")
    .build();

NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
    // 其他参数...
    .loadingConfig(loadingConfig)
    .build();    
NuwaHumanSDK.initialize(HumanDemoActivity.this, options);    
//小程序加载页面由web 控制

3.4 控制悬浮按钮

示例
// 隐藏悬浮按钮
floatingButtonInstance = new window.NpcTalkFloatingButton({
  // 其他参数...
  disabledButton: true
});

// 手动打开数字人聊天界面
floatingButtonInstance.navigateToNpcTalk();

// 销毁 SDK 实例
floatingButtonInstance.destroy();
// NuwaFloatingButton 提供了简单的数字人集成方式

// 显示悬浮按钮
floatingButton?.show(in: view)

// 隐藏悬浮按钮
floatingButton?.hide()

// 注意:NuwaFloatingButton 不需要手动销毁,它会在视图控制器销毁时自动清理资源
// 隐藏悬浮按钮
NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
  // 其他参数...
  .disabledButton(true)
  .build();

NuwaHumanSDK.initialize(HumanDemoActivity.this, options);

//打开数字人界面
NuwaHumanSDK.getInstance().navigateToNpcTalk()

// 显示悬浮按钮
NuwaHumanSDK.getInstance().showFloatingButton()

// 隐藏悬浮按钮
NuwaHumanSDK.getInstance().hideFloatingButton()

// 销毁 SDK 实例
NuwaHumanSDK.getInstance().destroy()

//page页面返回即退出数字人
方法说明平台
showFloatingButton()显示悬浮按钮全部
hideFloatingButton()隐藏悬浮按钮全部
navigateToNpcTalk()打开数字人聊天界面全部

4. 注意事项

4.1 Web 平台

要求

  • 设备需要具备麦克风
  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

权限

  • 具有麦克风及风权限

4.2 iOS 平台

要求

  • iOS 12.0+
  • Xcode 12.0+
  • Swift 5.0+

权限

  1. 在 Info.plist 中添加麦克风权限:
<key>NSMicrophoneUsageDescription</key>
<string>需要使用麦克风进行语音通话</string>

集成步骤

  1. 安装 NuwaSDK:

    • 使用 CocoaPods:在 Podfile 中添加 pod 'NuwaSDK'
    • 或直接将 NuwaSDK.framework 添加到项目中
  2. 导入头文件:

    import NuwaSDK
    
  3. 创建配置:

    let config = NuwaFloatingButtonConfig()
    config.apiKey = "your_api_key"
    config.avatorId = "your_avatar_id"
    config.userId = "your_user_id"
    
  4. 创建并显示悬浮按钮:

    let floatingButton = NuwaFloatingButton(config: config)
    floatingButton.show(in: view)
    
  5. 隐藏悬浮按钮(可选):

    floatingButton.hide()
    

4.3 Android 平台

要求

  • Android 5.0+
  • minSdkVersion 21+
  • targetSdkVersion 33+

权限

在 AndroidManifest.xml 中添加权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

4.4 小程序平台

要求

  • 微信小程序基础库 2.10.0+
  • 已开通实时音视频功能

配置

在 app.json 中添加:

{
  "requiredBackgroundModes": ["audio", "video"],
  "permission": {
    "scope.camera": {
      "desc": "用于视频通话"
    },
    "scope.record": {
      "desc": "用于语音通话"
    }
  }
}

5. 常见问题

5.1 数字人聊天界面不显示

问题:点击悬浮按钮后,数字人聊天界面不显示。

解决方案:

  • 检查 npcPage1 路径是否正确
  • 确保相关页面存在且可访问
  • 查看浏览器的控制台或日志中的错误信息

5.2 热词过滤不生效

问题:设置的热词过滤没有生效。

解决方案:

  • 确保 hotWord 参数是一个数组
  • 检查热词是否正确设置
  • 确保热词格式符合要求

5.3 平台特定问题

iOS

  • 问题:悬浮按钮不显示 解决方案:检查 API Key、数字人 ID 是否正确,确保网络连接正常
  • 问题:视频不显示 解决方案:确保已添加摄像头权限,检查设备摄像头是否可用
  • 问题:音频不工作 解决方案:确保已添加麦克风权限,检查设备音量是否开启

Android

  • 问题:权限被拒绝 解决方案:在运行时请求麦克风权限

小程序

  • 问题:无法打开麦克风 解决方案:确保用户已授权麦克风权限
最近更新:: 2026/3/28 11:34
Contributors: 邹丹晔, 李伟尧, 005609