mcpField
R
rw-integrate-characters

rw-integrate-characters

github.com/runwaymlAgent 工作流74222 次安装2 天前更新
代码仓库

关于 rw-integrate-characters

帮助开发者在代码库中集成 Runway Characters,构建由单张图像生成、支持实时对话的 AI 虚拟形象。提供服务端会话流程,并可选配知识文档和 React 通话界面。

主要功能

  1. 01从单张图像创建虚拟形象
  2. 02设置语音、个性与知识内容
  3. 03通过服务端 API 创建实时会话
  4. 04使用 WebRTC 建立对话连接
  5. 05可选集成文档与 React 界面

适用场景

  1. 01在应用中加入实时对话虚拟形象
  2. 02创建具有品牌个性和指定语音的角色
  3. 03为角色添加知识文档并嵌入 React 通话界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/runwayml/skills --skill rw-integrate-characters

安装后在 Claude Code 中输入/rw-integrate-characters确认已加载。

详细介绍

集成 Character(GWM-1 Avatar)

前置条件:

  • +rw-check-compatibility — 项目必须包含服务端组件(绝不能向客户端暴露 API 密钥)
  • +rw-fetch-api-reference — 集成前,从 https://docs.dev.runwayml.com/api/ 加载最新 API 参考
  • +rw-setup-api-key — 必须配置 API 凭据

可选依赖项:

  • +rw-integrate-documents — 为你的 Character 添加知识库
  • +rw-integrate-character-embed — 使用 React SDK 嵌入 Avatar 通话 UI

帮助用户创建 Runway Character — 由 GWM-1 驱动的实时对话式 AI Avatar。

仅在修改用户代码库时使用此功能。若要直接管理 Avatar,或由 Agent 执行其他一次性 Runway 账户操作,请改用 +use-runway-api。

Character 根据单张图片生成(支持任何视觉风格,包括照片级真实、动画、非人类),并可全面控制语音、个性、知识和操作。无需微调或训练。

核心概念

Avatar 与会话
概念说明
Avatar具有既定外观、语音和个性的持久 Persona。创建一次,可多次使用。
会话用于实时对话的 WebRTC 实时连接。将一位用户连接到一个 Avatar。最长时长:5 分钟。
会话生命周期
                    ┌───────────┐
         ┌──────────┤ NOT_READY ├──────────┐
         │          └─────┬─────┘          │
         │                │                │
         ▼                ▼                ▼
     CANCELLED          READY           FAILED
                       ┌──┴──┐
                       │     │
                       ▼     ▼
                    RUNNING FAILED
                    ┌──┴──┐
                    │     │
                    ▼     ▼
                COMPLETED CANCELLED
状态说明
NOT_READY正在配置会话。轮询直到就绪。
READY会话已就绪。sessionKey 已可用。
RUNNINGWebRTC 连接处于活动状态。对话正在进行。
COMPLETED会话正常结束。
FAILED发生错误。请检查 failure 字段。
CANCELLED会话在完成前被明确取消。

重要: 会话凭据只能使用一次。如果凭据使用后 WebRTC 连接失败,你必须创建一个新会话。

架构

API 密钥必须保留在服务端。流程如下:

Client (React)  →  Your Server  →  Runway API
                                      ↓
Client (React)  ←─── WebRTC ───← Runway (realtime)
  1. 客户端向你的服务器请求会话
  2. 你的服务器调用 Runway API 创建会话(POST /v1/realtime_sessions)
  3. 你的服务器轮询,直到会话状态变为 READY(GET /v1/realtime_sessions/:id)
  4. 你的服务器使用凭据(POST /v1/realtime_sessions/:id/consume)
  5. 你的服务器将凭据返回给客户端
  6. 客户端与 Runway 建立直接的 WebRTC 连接

步骤 1:安装依赖项

npm install @runwayml/sdk @runwayml/avatars-react
  • @runwayml/sdk — 服务端 SDK(创建会话、管理 Avatar)
  • @runwayml/avatars-react — 客户端 React 组件(WebRTC、UI)

步骤 2:创建 Avatar

可以通过开发者门户(UI)或 API(编程方式)创建 Avatar。

选项 A:开发者门户(首次使用推荐)
  1. 前往 https://dev.runwayml.com/ → Characters 标签页
  2. 点击 Create a Character
  3. 上传参考图片(提示见下文)
  4. 选择语音预设
  5. 编写个性指令(例如:“你是 Acme Corp 的一名乐于助人的客户支持 Agent……”)
  6. 可选:添加开场脚本(Character 首先说的话)
  7. 可选:上传知识文档(.txt 文件)
  8. 点击 Create Character
  9. 复制 Avatar ID(UUID,例如 8be4df61-93ca-11d2-aa0d-00e098032b8c)
选项 B:API(编程方式)
// Node.js
import RunwayML from '@runwayml/sdk';

const client = new RunwayML();

const avatar = await client.avatars.create({
  name: 'Support Agent',
  referenceImage: 'https://example.com/avatar.png',
  voice: {
    type: 'runway-live-preset',
    presetId: 'clara',
  },
  personality: 'You are a helpful customer support agent for Acme Corp. You help users with billing questions and technical issues.',
});

console.log('Avatar ID:', avatar.id);
# Python
from runwayml import RunwayML

client = RunwayML()

avatar = client.avatars.create(
    name='Support Agent',
    reference_image='https://example.com/avatar.png',
    voice={
        'type': 'runway-live-preset',
        'preset_id': 'clara',
    },
    personality='You are a helpful customer support agent for Acme Corp.',
)

print('Avatar ID:', avatar.id)

如果参考图片是本地文件,请先使用 +rw-integrate-uploads 上传:

import fs from 'fs';

const upload = await client.uploads.createEphemeral(
  fs.createReadStream('/path/to/avatar-image.png')
);

const avatar = await client.avatars.create({
  name: 'Support Agent',
  referenceImage: upload.runwayUri,
  voice: { type: 'runway-live-preset', presetId: 'clara' },
  personality: 'You are a helpful customer support agent...',
});
参考图像(必需)

创建头像时,必须提供 referenceImage。它支持以下三种格式:

格式限制使用场景
https://… URL2048 个字符图像已公开托管
data:image/…;base64,…5 MB(字符)中小型本地文件(原始文件最大约 3.5 MB)
runway://… URI5000 个字符先通过 /v1/uploads 上传的大型文件

<<<<<<< HEAD:skills/rw-integrate-characters/SKILL.md 对于超过约 3.5 MB 的本地文件,请使用上传流程(+rw-integrate-uploads)获取 runway:// URI,而不要使用 data URI。

对于超过约 3.5 MB 的本地文件,请使用上传流程(+integrate-uploads)获取 runway:// URI,而不要使用 data URI。

810dd3a (Improve CLI error details, auth fallback, and skill docs from testing):skills/integrate-characters/SKILL.md

参考图像指南
  • 任何视觉风格均可:写实人物、动画吉祥物、风格化品牌角色
  • 使用光线良好的高质量图像
  • 面部必须清晰可见且位于画面中央——无法识别出面部的图像将无法通过处理
  • 避免使用包含多人的图像或有遮挡的图像
  • 建议宽高比:1088×704
语音预设
预设 ID名称风格
claraClara柔和、亲切
victoriaVictoria坚定、专业
vincentVincent知识渊博、权威

在 Developer Portal 中预览所有语音。

步骤 3:创建会话(服务端)

这是客户端将调用的服务端 API 路由。它会创建会话、轮询直至会话就绪、使用凭据并返回这些凭据。

Next.js App Router
// app/api/avatar/session/route.ts
import RunwayML from '@runwayml/sdk';

const client = new RunwayML();

export async function POST(request: Request) {
  const { avatarId } = await request.json();

  // 1. Create session
  const { id: sessionId } = await client.realtimeSessions.create({
    model: 'gwm1_avatars',
    avatar: { type: 'custom', avatarId },
  });

  // 2. Poll until ready
  let sessionKey: string | undefined;
  for (let i = 0; i < 60; i++) {
    const session = await client.realtimeSessions.retrieve(sessionId);

    if (session.status === 'READY') {
      sessionKey = session.sessionKey;
      break;
    }
    if (session.status === 'FAILED') {
      return Response.json({ error: session.failure }, { status: 500 });
    }

    await new Promise(r => setTimeout(r, 1000));
  }

  if (!sessionKey) {
    return Response.json({ error: 'Session timed out' }, { status: 504 });
  }

  // 3. Consume session to get WebRTC credentials
  const consumeResponse = await fetch(
    `${client.baseURL}/v1/realtime_sessions/${sessionId}/consume`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${sessionKey}`,
        'X-Runway-Version': '2024-11-06',
      },
    }
  );
  const credentials = await consumeResponse.json();

  return Response.json({
    sessionId,
    serverUrl: credentials.url,
    token: credentials.token,
    roomName: credentials.roomName,
  });
}
Express.js
import RunwayML from '@runwayml/sdk';
import express from 'express';

const client = new RunwayML();
const app = express();
app.use(express.json());

app.post('/api/avatar/session', async (req, res) => {
  const { avatarId } = req.body;

  try {
    // 1. Create session
    const { id: sessionId } = await client.realtimeSessions.create({
      model: 'gwm1_avatars',
      avatar: { type: 'custom', avatarId },
    });

    // 2. Poll until ready
    let sessionKey: string | undefined;
    for (let i = 0; i < 60; i++) {
      const session = await client.realtimeSessions.retrieve(sessionId);

      if (session.status === 'READY') {
        sessionKey = session.sessionKey;
        break;
      }
      if (session.status === 'FAILED') {
        return res.status(500).json({ error: session.failure });
      }

      await new Promise(r => setTimeout(r, 1000));
    }

    if (!sessionKey) {
      return res.status(504).json({ error: 'Session timed out' });
    }

    // 3. Consume credentials
    const consumeResponse = await fetch(
      `${client.baseURL}/v1/realtime_sessions/${sessionId}/consume`,
      {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${sessionKey}`,
          'X-Runway-Version': '2024-11-06',
        },
      }
    );
    const credentials = await consumeResponse.json();

    res.json({
      sessionId,
      serverUrl: credentials.url,
      token: credentials.token,
      roomName: credentials.roomName,
    });
  } catch (error) {
    console.error('Session creation failed:', error);
    res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
  }
});
FastAPI (Python)
import time
import httpx
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from runwayml import RunwayML

app = FastAPI()
client = RunwayML()

class SessionRequest(BaseModel):
    avatar_id: str

@app.post("/api/avatar/session")
async def create_session(req: SessionRequest):
    # 1. Create session
    result = client.realtime_sessions.create(
        model='gwm1_avatars',
        avatar={'type': 'custom', 'avatar_id': req.avatar_id},
    )
    session_id = result.id

    # 2. Poll until ready
    session_key = None
    for _ in range(60):
        session = client.realtime_sessions.retrieve(session_id)

        if session.status == 'READY':
            session_key = session.session_key
            break
        if session.status == 'FAILED':
            raise HTTPException(status_code=500, detail=str(session.failure))

        time.sleep(1)

    if not session_key:
        raise HTTPException(status_code=504, detail='Session timed out')

    # 3. Consume credentials
    async with httpx.AsyncClient() as http:
        resp = await http.post(
            f"{client.base_url}/v1/realtime_sessions/{session_id}/consume",
            headers={
                "Authorization": f"Bearer {session_key}",
                "X-Runway-Version": "2024-11-06",
            },
        )
    credentials = resp.json()

    return {
        "session_id": session_id,
        "server_url": credentials["url"],
        "token": credentials["token"],
        "room_name": credentials["roomName"],
    }

步骤 4:从客户端连接

请参阅 +rw-integrate-character-embed,了解用于处理 WebRTC 连接和渲染的 React SDK 组件。最简单的方法如下:

'use client';
import { AvatarCall } from '@runwayml/avatars-react';
import '@runwayml/avatars-react/styles.css';

export default function CharacterPage() {
  return (
    <AvatarCall
      avatarId="your-avatar-id"
      connectUrl="/api/avatar/session"
      onEnd={() => console.log('Call ended')}
      onError={(error) => console.error('Error:', error)}
    />
  );
}

故障排查

  • **API 密钥错误:**密钥以 key_ 开头,后跟 128 个十六进制字符。请确保密钥处于有效状态。
  • **没有额度:**账户必须有预付额度才能开始通话。
  • **会话超时:**60 次迭代的轮询循环会等待约 60 秒。如果会话持续超时,请检查你所在套餐的并发限制。
  • **凭据已被使用:**会话凭据只能使用一次。如果使用凭据后 WebRTC 连接失败,请创建新会话。
调试日志
<AvatarCall
  avatarId="your-avatar-id"
  connectUrl="/api/avatar/session"
  onError={(error) => {
    console.error('Avatar error:', error);
    console.error('Error name:', error.name);
    console.error('Error message:', error.message);
    if (error.cause) console.error('Cause:', error.cause);
  }}
/>
监控会话状态
import { useAvatarSession } from '@runwayml/avatars-react';

function DebugInfo() {
  const { state, sessionId, error } = useAvatarSession();
  return (
    <pre>
      {JSON.stringify({ state, sessionId, error: error?.message }, null, 2)}
    </pre>
  );
}
使用最简配置测试
npx degit runwayml/avatars-sdk-react/examples/nextjs-simple test-app
cd test-app
npm install
# Add your API key to .env.local
npm run dev

浏览器支持

浏览器最低版本
Chrome74+
Firefox78+
Safari14.1+
Edge79+

用户必须授予麦克风权限。如果启用了用户视频,还需要摄像头权限。

获取帮助

资源说明
Developer Portal管理头像、查看日志、访问仪表板
SDK Repository报告 Bug、查看示例、检查版本发布

报告问题时,请提供:浏览器及版本、SDK 版本(npm list @runwayml/avatars-react)、错误消息、会话 ID 和重现步骤。

评论

支持 Markdown