第一个请求示例:与实时数字人对话

📖第一个请求示例:与实时数字人对话

NavTalk 提供基于 WebSocket + WebRTC 的实时数字人能力,支持语音识别Function Calling 和视频口型同步。下面是完整的接入流程与关键代码说明。

整体流程

  1. 建立 WebSocket 连接(带 license + characterName)

  2. 配置会话参数(voice、音频格式、上下文等)

  3. 获取浏览器麦克风音频并转为 PCM,发送音频流到服务端

  4. 接收服务端响应(文本、音频、Function Call)

  5. 通过 WebRTC 获取实时视频展示数字人形象

🔹 Step 1:建立 WebSocket 实时语音连接

你需要通过我们颁发的 license 创建 WebSocket 连接,并传入 characterName 以选择数字人形象。

const license = "YOUR_LICENSE_KEY";
const characterName = "girl2";

const socket = new WebSocket(`wss://api.navtalk.ai/api/realtime-api?license=${encodeURIComponent(license)}&characterName=${characterName}`);
socket.binaryType = 'arraybuffer';

socket.onopen = () => {
  console.log("WebSocket 连接成功");
};

socket.onmessage = (event) => {
  if (typeof event.data === 'string') {
    const data = JSON.parse(event.data);
    handleReceivedMessage(data); // 处理 JSON 消息
  } else if (event.data instanceof ArrayBuffer) {
    handleReceivedBinaryMessage(event.data); // 处理音频流
  }
};

🔹 Step 2:配置会话参数(初始化)

session.created 返回后,发送 session.update 配置 AI 的行为风格、语言模型、音频参数、转录方式等。

const sessionConfig = {
  type: "session.update",
  session: {
    instructions: "chat",
    voice: "alloy",
    temperature: 1,
    max_response_output_tokens: 4096,
    modalities: ["text", "audio"],
    input_audio_format: "pcm16",
    output_audio_format: "pcm16",
    input_audio_transcription: {
      model: "whisper-1"
    }
  }
};

socket.send(JSON.stringify(sessionConfig));

可扩展:tools 字段支持 Function Calling 自定义函数能力。

🔹 Step 3:获取用户语音并推送

通过浏览器访问麦克风,实时录制语音、转换为 PCM16 格式,并 base64 编码后发送至服务器。

navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
  const audioContext = new AudioContext({ sampleRate: 24000 });
  const source = audioContext.createMediaStreamSource(stream);
  const processor = audioContext.createScriptProcessor(8192, 1, 1);

  processor.onaudioprocess = (event) => {
    const input = event.inputBuffer.getChannelData(0);
    const buffer = floatTo16BitPCM(input);
    const base64Audio = base64EncodeAudio(new Uint8Array(buffer));

    // 分片发送音频块
    const chunkSize = 4096;
    for (let i = 0; i < base64Audio.length; i += chunkSize) {
      const chunk = base64Audio.slice(i, i + chunkSize);
      socket.send(JSON.stringify({ type: "input_audio_buffer.append", audio: chunk }));
    }
  };

  source.connect(processor);
  processor.connect(audioContext.destination);
});

🔹 Step 4:处理 AI 返回事件

平台会返回多个事件,主要包括:

事件类型
说明

session.created

会话创建成功,需立即发送配置

session.updated

可开始发送音频

response.audio_transcript.delta

实时返回语音识别文本

response.audio.delta

返回 AI 音频数据(播放)

response.function_call_arguments.done

触发函数调用

response.audio.done

一轮响应结束

示例:

function handleReceivedMessage(data) {
  switch (data.type) {
    case "session.created":
      sendSessionUpdate();
      break;
    case "session.updated":
      startRecording();
      break;
    case "response.audio_transcript.delta":
      console.log("AI 说:", data.delta.text);
      break;
    case "response.audio.delta":
      // 播放 data.delta 音频内容
      break;
    case "response.function_call_arguments.done":
      handleFunctionCall(data);
      break;
  }
}

🔹 Step 5:建立 WebRTC 视频流连接(展示数字人)

WebRTC 是数字人实时表现力(唇形、表情、视线等)的载体,务必确保在建立 WebSocket 实时语音连接的同时,创建 WebRTC 视频通道。

你将需要:

  • 一个绑定了视频的 HTML <video> 标签

  • 一个专属的 license(即 userId)

  • 一个目标 sessionId(需由实时 WebSocket 会话关联)

1️⃣ 绑定视频元素

在你的 HTML 中预留一个用于显示数字人形象的 <video> 标签:

<video id="character-avatar-video" autoplay muted playsinline style="width: 320px; height: 400px;"></video>

autoplayplaysinline 是移动端浏览器展示视频的关键属性

然后在 JavaScript 中绑定该元素:

const remoteVideo = document.getElementById('character-avatar-video');

2️⃣ 建立 WebRTC 信令连接

使用你的 license 创建 WebSocket 信令通道:

const remoteVideo = document.getElementById('character-avatar-video');
const resultSocket = new WebSocket(`wss://api.navtalk.ai/iwebrtc?userId=${license}`);

resultSocket.onopen = () => {
  resultSocket.send(JSON.stringify({ type: 'create', targetSessionId: "123" }));
};

3️⃣ 接收 offer / answer / ICE 候选

服务端会依次返回:

  • offer(SDP 请求)

  • answer(SDP 应答)

  • iceCandidate(网络打洞地址)

处理这些消息如下:

resultSocket.onmessage = (event) => {
  const message = JSON.parse(event.data);
  switch (message.type) {
    case "offer": handleOffer(message); break;
    case "answer": handleAnswer(message); break;
    case "iceCandidate": handleIceCandidate(message); break;
  }
};

4️⃣ 创建 RTCPeerConnection 并播放视频

const configuration = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
let peerConnectionA = null;

function handleOffer(message) {
  const offer = new RTCSessionDescription(message.sdp);
  peerConnectionA = new RTCPeerConnection(configuration);

  // 设置远端 SDP
  peerConnectionA.setRemoteDescription(offer)
    .then(() => peerConnectionA.createAnswer())
    .then(answer => peerConnectionA.setLocalDescription(answer))
    .then(() => {
      resultSocket.send(JSON.stringify({
        type: 'answer',
        targetSessionId: message.targetSessionId,
        sdp: peerConnectionA.localDescription
      }));
    });

  // 监听远端流
  peerConnectionA.ontrack = (event) => {
    console.log("接收到视频流");
    remoteVideo.srcObject = event.streams[0];
    remoteVideo.play();
  };

  // 收集 ICE 候选
  peerConnectionA.onicecandidate = (event) => {
    if (event.candidate) {
      resultSocket.send(JSON.stringify({
        type: 'iceCandidate',
        targetSessionId: message.targetSessionId,
        candidate: event.candidate
      }));
    }
  };
}

5️⃣ 接收 ICE 反向通道

function handleIceCandidate(message) {
  const candidate = new RTCIceCandidate(message.candidate);
  if (peerConnectionA) {
    peerConnectionA.addIceCandidate(candidate).catch(console.error);
  }
}

🛠 常见问题与调试建议

问题
建议

没有返回语音

检查是否发送了 session.update,音频格式是否正确

视频不显示

检查 WebRTC 连接是否成功,是否已绑定 video DOM

AI 不回应

是否配置了语音转文本模型、是否成功发送音频流

ICE failed

检查网络环境,尝试更换 STUN 服务器

📦 完整示例项目

我们建议使用官方 DEMO 项目快速验证连接是否成功:这是一个待补充的链接。示例支持录音、角色选择、视频渲染、Function Call 等全流程。

Last updated