语音与对话运行时
连接时 Orbz 保持静音。在 @neongate-ai/orbz@1.0.3 中,包不提供问候语、角色或对话:talk 是冻结的空对象,DEFAULT_TALK_FLOW 是冻结的空数组。宿主通过 speech 提供单句或通过有类型的 talkFlow 提供对话,配置语音引擎,并且仅在用户明确操作后启动。
speech 优先于 talkFlow。两者都未提供时,startTalking() 不执行操作。启动非空对话流程会重置运行时上下文;宿主调用 receive() 时,ask 步骤将输入保存到其 capture 键,后续步骤可插值使用。此上下文不会持久保存到 cookie、本地存储、IndexedDB 或后端。
内置对话数据
import { DEFAULT_TALK_FLOW, talk } from "@neongate-ai/orbz";
console.log(talk); // {}
console.log(DEFAULT_TALK_FLOW); // []继续流程
下方自定义流程启动并到达 ask 步骤后,将用户提交的输入传给 receive()。fullName 键来自示例声明的 capture: "fullName",并非内置对话数据。
import "@neongate-ai/orbz/browser";
import type { OrbzElement } from "@neongate-ai/orbz";
const orb = document.querySelector<OrbzElement>("orb-z");
await orb?.receive("Jonatas");
console.log(orb?.talkContext.fullName);浏览器英语语音
已安装包中的 WebSpeechAdapter 默认使用 pt-BR。示例句子是英语,因此显式选择 en-US。其他语言应同时设置宿主提供的文本与适配器语言。适配器等待异步语音列表,并对符合请求语言的声音排序;可用声音仍取决于访客环境。
import '@neongate-ai/orbz/browser'
import {
WebSpeechAdapter,
type OrbzElement
} from "@neongate-ai/orbz";
const orb = document.createElement("orb-z") as OrbzElement;
orb.speech = "Hello. This is an explicit speech example.";
orb.voiceEngine = new WebSpeechAdapter({
language: "en-US",
preferredVoices: ["Google US English", "Microsoft Aria Online"]
});
document.body.append(orb);
const startVoiceButton = document.querySelector<HTMLButtonElement>("[data-start-voice]");
startVoiceButton?.addEventListener("click", async () => {
await orb.startTalking();
});实际安装的语音仍取决于访客的浏览器和操作系统。WebSpeechAdapter 能改善选择过程,但无法将系统语音变为 OpenAI 语音。
OpenAI 品质的语音
在 1.0.3 中,OpenAISpeechAdapter 默认使用 gpt-4o-mini-tts、marin、MP3 和巴西葡萄牙语朗读指令。本英语示例覆盖 instructions,使其与宿主文本一致。应用必须实现并保护示例端点。
import '@neongate-ai/orbz/browser'
import {
OpenAISpeechAdapter,
type OrbzElement
} from "@neongate-ai/orbz";
const orb = document.createElement("orb-z") as OrbzElement;
orb.speech = "Hello. This is an explicit speech example.";
orb.voiceEngine = new OpenAISpeechAdapter({
endpoint: "/api/orbz/speech",
instructions: "Speak in natural American English. Do not change the supplied text."
});
document.body.append(orb);
const startVoiceButton = document.querySelector<HTMLButtonElement>("[data-start-voice]");
startVoiceButton?.addEventListener("click", async () => {
await orb.startTalking();
});端点由实现集成的应用负责。它接收兼容 OpenAI 的 JSON 请求体,其中包含 input、instructions、model、response_format 和 voice,并返回生成的音频响应。将 OpenAI API 密钥保留在该服务端端点中;切勿将其放入浏览器代码或 npm 软件包。
使用生成语音的应用应明确告知用户,该语音由 AI 生成。
显式激活与浏览器策略
渲染一个具有明确标签(例如启动语音)的原生 <button>,然后在其点击处理函数中调用 startTalking()。连接球体、设置语音引擎或进入页面都不会开始播放音频。
如果浏览器仍以 NotAllowedError 拒绝所请求的音频,Orbz 会携带原始错误分发 orbz-talk-error,并在下一次指针、键盘或触摸交互后重试请求的流程。重置控件不应仅为了允许语音播放而重新挂载元素。
提供自定义流程
import "@neongate-ai/orbz/browser";
import { WebSpeechAdapter, type OrbzElement, type OrbzTalkStep } from "@neongate-ai/orbz";
const flow = [
{ id: "welcome", kind: "say", needsAuth: false, text: "Hello." },
{ id: "name", kind: "ask", needsAuth: false, text: "What is your name?", capture: "fullName" },
{ id: "help", kind: "say", needsAuth: false, text: "How can I help, {{fullName}}?" },
{ id: "answer", kind: "respond", needsAuth: false, strategy: "openai", fallback: "I cannot answer that right now." }
] as const satisfies readonly OrbzTalkStep[];
const orb = document.createElement("orb-z") as OrbzElement;
orb.talkFlow = flow;
orb.voiceEngine = new WebSpeechAdapter({ language: "en-US" });
document.body.append(orb);
const startVoiceButton = document.querySelector<HTMLButtonElement>("[data-start-voice]");
startVoiceButton?.addEventListener("click", async () => {
await orb.startTalking();
});在调用 startTalking() 之前设置 voiceEngine、talkFlow 和 intelligence,以便显式启动的这次运行使用它们。
可选的智能能力
import type {
OrbzElement,
OrbzIntelligencePort
} from "@neongate-ai/orbz";
const intelligence: OrbzIntelligencePort = {
async respond(input, context) {
return productAgent.respond({ context, input });
}
};
orb.intelligence = intelligence;事件与视觉状态
播放音频期间,Orbz 临时使用 speaking 视觉状态,随后恢复此前的状态。
| 事件 | 详情 |
|---|---|
orbz-speaking-change | { speaking: boolean } |
orbz-talk-error | { error: unknown } |
以上文本转语音流程不采集麦克风。宿主负责输入控件、权限、转录、产品逻辑和 receive() 调用。Orbz 另有独立的 Realtime 对话 API,这些 startTalking() 示例不会启动它。