Pouchy 伴侣接入指南 — 网页游戏 · 实时语音陪玩
面向第三方网页游戏(如德州扑克)接入 Pouchy 伴侣,用实时语音陪玩:伴侣在通话里出声,
跟着你推送的牌局状态实时反应。本文是给对接方的接口说明,配合可运行示例
examples/poker-voice/。
一、对接方需要拿到的东西(交付清单)
| 文件 / 资源 | 作用 |
|---|---|
@pouchy_ai/companion-sdk(packages/companion-sdk/) |
接入用的 JS SDK。已发布到公共 npm:npm install @pouchy_ai/companion-sdk;也可经 esm.sh / jsDelivr / 自托管 CDN(pouchy.ai/sdk/companion-sdk.js)直接引入。仓库内开发仍可本地构建。 |
本文档 docs/companion-voice-integration.md |
接口说明(语音 + world-state 配方)。 |
examples/poker-voice/ |
可运行的德州扑克语音陪玩示例,复制即用。 |
OpenAPI GET /api/companion/openapi.json |
REST 面的机读契约(可生成 client / 导入 Postman)。 |
协议规范 docs/companion-sdk-protocol.md |
底层 wire 协议(只有要自己实现传输时才需要)。 |
纯 JS/TS SDK,浏览器即可跑(WebRTC + 麦克风)。语音音频走provider 直连(ElevenLabs Convai 主、 OpenAI Realtime 兜底),Pouchy 服务器不在音频链路里。
二、令牌与权限(token)
伴侣属于玩家本人的账号。游戏需要拿到该玩家的一个 Pouchy access token(pchy_…),两种方式都行:
- 个人令牌(最快): 目前应用内还没有"伴侣接入密钥"面板 —— 玩家登录 pouchy.ai 后调用
POST /api/companion/keys(Bearer 为 Firebase ID token)生成。不传scopes即得 "安全默认权限包",已经包含语音陪玩需要的call/voice/events.subscribe/worldstate.write(以及chat/files/memory.*:app)。 不要额外传wallet.spend/social.message/skills.execute这些敏感权限,陪玩用不到。 token 只在响应里出现一次,记得当场复制。 - Login with Pouchy(OAuth 2.1 + PKCE): 游戏注册一个 client(
/app/developer/oauth),走授权码流程拿access_token。
语音陪玩实际用到的 scope(默认 key 已包含,无需手动勾):call(必需)· voice · events.subscribe ·
worldstate.write(+ 可选 chat)。陪玩不需要敏感权限。
三、四步接通(核心代码)
import { createCompanion } from '@pouchy_ai/companion-sdk';
// 1) 建客户端(声明语音模态)
const companion = createCompanion({
baseUrl: 'https://pouchy.ai',
token: PLAYER_TOKEN, // 玩家的 pchy_… 令牌
surface: 'poker-table', // 标识这个接入点
modalities: ['text', 'voice']
});
// 2) 握手 + 打开出站事件流
await companion.connect();
companion.start();
// 3) 开实时语音通话(浏览器会请求麦克风)
const call = await companion.connectCall({
locale: 'zh',
onTranscript: ({ role, text }) => render(role, text), // 字幕(可选)
onSpeakingChange: (speaking) => toggleMicGlow(speaking), // 说话指示(可选)
onError: (err) => console.warn(err)
});
// 4) 把牌局状态推给伴侣;⭐ 的时刻它会在通话里出声反应
companion.sendWorldState({ type: 'poker.hand', data: { hole: 'A♠ K♠', blinds: '10/20' }, retained: true });
companion.sendWorldState({
type: 'poker.event.river',
data: 'The river is K♦ — top pair, top kicker.',
salience: 0.85,
voiceRelevant: true // ← 这一条会被推进通话,伴侣立刻出声评论
});
// 结束
call.close();
connectCall 一步到位:它申请通话凭据 → 打开到 provider 的 WebRTC(麦克风+扬声器)→ 并自动把服务器
推来的 voiceRelevant 时刻注入到通话里。你不用手动桥接,只管 sendWorldState。
陪玩会被记住(跨会话记忆):语音音频是 provider 直连的,Pouchy 听不到通话内容,所以记忆是在 通话结束时固化的。你调
call.close()时,SDK 会自动把这次通话的字幕 + 你推过的 world-state(发牌、 输赢、关键时刻)一起发回服务器,提炼成长期记忆。所以一定要在结束时调call.close()(SDK ≥ 0.3.0)。 这样玩家回到 Pouchy 主应用问"刚才德州打得怎样",伴侣就知道。非语音会话在结束时手动调companion.endSession()。 想让记忆进入跨应用共享的核心大脑,给 token 加memory.write:core权限即可。
四、world-state 配方(陪玩好不好,全看这里)
伴侣的"陪玩感"来自你推的牌局上下文。两类事件:
retained: true—— 当前状态(按type合并/覆盖):当前手牌、盲注、筹码、阶段。- transient(不带 retained) —— 一次性事件,带
salience(0–1,越戏剧越高); 关键时刻加voiceRelevant: true,伴侣会在通话里当场出声。
德州扑克建议推送:
| 时机 | 例子 | 建议 |
|---|---|---|
| 发底牌 | { type:'poker.hand', data:{hole:'A♠ K♠', blinds:'10/20'}, retained:true } |
retained |
| 轮到玩家 | { type:'poker.event.your_turn', data:'…pot 30', salience:0.7, voiceRelevant:true } |
⭐ 出声 |
| 翻牌/转牌/河牌 | { type:'poker.event.river', data:'river K♦ …', salience:0.85, voiceRelevant:true } |
⭐ 关键牌出声 |
| 对手动作 | { type:'poker.event.opponent_raise', data:'raised to 120', salience:0.5 } |
一般不出声,只做上下文 |
| 摊牌/输赢 | { type:'poker.event.win', data:'won 260 pot!', salience:0.95, voiceRelevant:true } |
⭐ 出声 |
经验法则:只给真正值得开口的时刻打 voiceRelevant(轮到你、关键牌、输赢)。把每个小动作都标出声,
伴侣会变成话痨。其余只做 retained 状态或低 salience 事件,作为它的背景认知。
五、provider 依赖说明
- ElevenLabs Convai(主): 需要可选 peer 依赖
@elevenlabs/client(npm i @elevenlabs/client)。 示例已包含。 - OpenAI Realtime(兜底): 自带 WebRTC,零额外依赖。
服务器按账号配置自动选 provider(EL 优先 → OpenAI 兜底),客户端无法强制;两者 connectCall 用法完全一致。
无打包器(no-bundler)项目:直接从 Pouchy 源加载 SDK(自带 CORS,无需 vendor),
@elevenlabs/client 走 esm.sh。在 module script 之前加 import map:
<script type="importmap">
{
"imports": {
"@pouchy_ai/companion-sdk": "https://pouchy.ai/sdk/companion-sdk.js",
"@elevenlabs/client": "https://esm.sh/@elevenlabs/client@1.9.0"
}
}
</script>
<script type="module">
import { createCompanion } from '@pouchy_ai/companion-sdk';
</script>
详见 companion-integration-faq.md 第 6 条。
六、本地跑通示例
cd packages/companion-sdk && npm i && npm run build # 先构建 SDK
cd ../../examples/poker-voice && npm i && npm run dev # 跑示例
打开 localhost 地址 → 粘贴带 voice+call 的 token → 连接 → 开始语音陪玩 → 点牌桌按钮,
听伴侣在通话里反应。
七、让伴侣用语音/文字操作你的游戏(暂停、开关功能…)
声明你的游戏能做的动作,伴侣在语音通话或文字里都能调用它们——同一套回调,你只写一份逻辑 (SDK ≥ 0.4.0)。玩家说"暂停一下 / 把提示关掉",伴侣就会触发对应工具:
const companion = createCompanion({
baseUrl: 'https://pouchy.ai', token: PLAYER_TOKEN, surface: 'poker-table',
modalities: ['text', 'voice'],
tools: [
{ name: 'pause_game', description: '暂停当前牌局', parameters: { type:'object', properties:{} } },
{ name: 'set_feature', description: '开关一个功能(如发牌提示)',
parameters: { type:'object', properties:{ feature:{type:'string'}, on:{type:'boolean'} }, required:['feature','on'] } }
]
});
await companion.connect();
companion.start(); // 文字/事件流
const call = await companion.connectCall({ locale: 'zh' }); // 语音
companion.onToolCall(async ({ id, name, args }) => { // 语音 + 文字共用
const a = JSON.parse(args);
if (name === 'pause_game') game.pause();
if (name === 'set_feature') ui.toggle(a.feature, a.on);
await companion.sendToolResult(id, { ok: true }); // 回报结果,语音里它会据此应一句
});
- 良性的应用内操作(暂停、开关功能、高亮某处)立即执行,不弹确认。
- provider 差异(重要):语音工具调用在 OpenAI Realtime 上是全动态的(你声明什么就能调什么); ElevenLabs Convai 只会调它预先配置过的工具,共享 Convai agent 没法在运行时接收任意第三方工具。 provider 是服务端按账号自动选的,所以要让语音可靠地操作 app,把账号固定到 OpenAI Realtime (或让我们把你的几个工具登记到 Convai agent 上)。文字路径不受此限,任何 provider 都行。
八、(进阶)敏感操作:发消息 / 转账 / 跑技能
如果想让伴侣发起敏感操作(给对手发消息、转筹码/打赏、跑一个技能),那不会被静默执行:服务器会拦截并弹出
Pouchy 托管确认页让玩家本人批准(见 companion.confirm_request 事件 + /companion/confirm 页)。
普通陪玩 + 上面的 app 控制类工具都不需要这些权限。