@dtelecom/代理服务器
AI 语音代理框架 d 电信 房间。 建立实时语音代理,加入WebRTC房间,并使用语音对文本,LLM,和文本对语音与参与者互动.
建筑
Participant mic -> SFU -> server-sdk-node (Opus decode) -> PCM16 16kHz
-> STT plugin -> transcription
-> LLM plugin -> streaming text response
-> Sentence splitter -> TTS plugin -> PCM16 48kHz
-> AudioSource (48kHz Opus encode) -> SFU -> Participants
管道使用出厂/消费者模式:LLM符被分出句子并排队,而消费者则合成并同时播放音频. 这会将时间到第一时间最小化.
在完成语音后,管道保留了800ms的音频估计,以便在将UI状态更改为闲置之前到达听众. 此等可取消:新用户转会立即发布,中断的响应或问候不会在提供者清理后添加等. 没有语音的回复完全跳过 取消仍然冲出本地缓冲音频并等待活动制作人/消费者完成后再开始排队转弯; TTS 合成对于需要的提供者来说仍然是相继的.
取消覆盖了取暖,对话总结,内存取景和响应生成. 已取消的摘要不能取代对话历史 。 答复中到达的最后记录稿占用一个等待交接的档期(最新的最后记录取代任何较早的等待交接的最后记录)并中断目前的周期。 下一个循环在活动提供者任务完成取消清理后开始. 一个 APIUserAbortError 从中止的周期中取消; 真正的提供者失败仍然是错误.
间隔延迟日志 Queue (答复周期开始后收到最后记录), Prepare (温暖、摘要和内存检索), LLM (对第一句的要求,包括 TTFT和 TTS (第一句至第一声. STT 是服务器报告的处理时间. Overall 包括这些阶段,但不包括STT端点等;没有文本回复,无法进行转弯。 这些测量不是麦克风对讲机的空位.
对 dTelecom TTS , 取消发送 clear 等待着 cleared 在重用 WebSocket 之前。 取消请求的后期音频被丢弃 。 缺失的确认在5秒后关闭连接, 因此后续的请求会重新连接; 这是错误的超时, 而不是用于成功取消的延迟 .
安装
npm install @dtelecom/agents-js @dtelecom/server-sdk-js @dtelecom/server-sdk-node
快速启动( 其他提供者)
import { VoiceAgent, setLogLevel } from '@dtelecom/agents-js';
import { DeepgramSTT, OpenRouterLLM, CartesiaTTS } from '@dtelecom/agents-js/providers';
const agent = new VoiceAgent({
stt: new DeepgramSTT({ apiKey: process.env.DEEPGRAM_API_KEY! }),
llm: new OpenRouterLLM({
apiKey: process.env.OPENROUTER_API_KEY!,
model: 'openai/gpt-4o',
}),
tts: new CartesiaTTS({
apiKey: process.env.CARTESIA_API_KEY!,
voiceId: 'your-voice-id',
}),
instructions: 'You are a helpful voice assistant.',
});
await agent.start({
room: 'my-room',
apiKey: process.env.DTELECOM_API_KEY!,
apiSecret: process.env.DTELECOM_API_SECRET!,
});
apiKey 财务报告和审定财务报表 apiSecret 是一个 dTelecom Cloud 项目的 API 密钥; 代理的符号来自云 at DTELECOM_CLOUD_URL。通过 cloud 改为重新使用客户端(见下文).
d 腾讯云语音 (0.7.0+)
通过 dTelecom Cloud 计划,给代理商和 dTelecom 供应商一个 CloudClient 从 @dtelecom/server-sdk-js 4.1或更新鲜. 代理薄荷自有的WebRTC令牌;提供商打开演讲会话,将其与代理的房间会话链接,并在用完前延长.
import { CloudClient } from '@dtelecom/server-sdk-js';
import { VoiceAgent } from '@dtelecom/agents-js';
import { DtelecomSTT, DtelecomTTS } from '@dtelecom/agents-js/providers';
// DTELECOM_API_KEY, DTELECOM_API_SECRET and DTELECOM_CLOUD_URL from the environment
const cloud = new CloudClient();
const agent = new VoiceAgent({
stt: new DtelecomSTT({ cloud, language: 'en', partials: true, smartTurn: true }),
tts: new DtelecomTTS({ cloud, voices: { en: { voice: 'af_heart', langCode: 'a' } } }),
llm, instructions: 'Be helpful and concise.',
});
await agent.start({ room: 'voice-demo', identity: 'assistant', cloud, ttl: '2h' });
你的后端给每个人类参与者一个符号 cloud.createToken. 会议工作方式:
- STT 服务器:每个参与者流一个会话,每个连接分钟计费. 每人保持10分钟的平衡,2岁以下者则延长10分钟。 换一个
cloudSession: { minutes, extendWhenMinutesLeft }。 。 。 . - TTS 数字:一个会话用于代理语音,每个字符计费. 它持有2000个字符,在判决前被延长会留下不到500个,在30分钟的窗口关闭前被延长. 换一个
cloudSession: { characters, extendWhenCharactersLeft }。 。 。 . - 未使用的搁置在会话结束后释放 。 扩展将新密钥发送到打开的套接字; 重连接使用最新的密钥 。 云拒绝( 例如)
insufficient_balance表面为error活动. ttl设置代理机自己的房间会话(默认为6小时,最多24小时).
收费率在仪表板的计费目录中:STT每接通分钟0.004美元,TTS每1000个字符0.006美元,WebRTC音频每参与者分钟0.01美元. 文件复制、WAV综合、翻译、视觉和温和 CloudClient ; VoiceAgent 不给他们打电话.
dTelecom网关语音 (x402)
创建预付捆绑 DtelecomGateway 从 @dtelecom/x402-client,然后将其迹象传递给供货商。 此示例假设 gateway 配置有资助的钱包,并且 llm 执行 LLM 插件接口 :
import { VoiceAgent } from '@dtelecom/agents-js';
import { DtelecomSTT, DtelecomTTS } from '@dtelecom/agents-js/providers';
const session = await gateway.createAgentSession({
roomName: 'voice-demo', participantIdentity: 'assistant',
clientIdentity: 'listener', durationMinutes: 5, language: 'en',
});
const agent = new VoiceAgent({
stt: new DtelecomSTT({
serverUrl: session.stt.serverUrl, sessionKey: session.stt.token,
language: 'en', partials: true, smartTurn: true,
}),
tts: new DtelecomTTS({
serverUrl: session.tts.serverUrl, sessionKey: session.tts.token,
voices: { en: { voice: 'M5', langCode: 'en' }, ru: { voice: 'M5', langCode: 'ru' } },
speed: 1.1, // application preference; SDK default is 1.0
// watermark: true, // independent explicit opt-in
}),
llm, instructions: 'Be helpful and concise.',
});
await agent.start({ room: 'voice-demo', token: session.webrtc.agent.token, wsUrl: session.webrtc.agent.wsUrl });
客户端加入 session.webrtc.client。STT为 0.004/ 连接会话分钟; TTS 为 0.006/ 1 000 美元 Unicode代码点;WebRTC为$0.0001/autio参与者分钟. 两名与会者、一次STT会议和367 TTS字符/分钟的费用约为0.0064美元/分钟,不包括LLM费用. 现场定价和启用服务。 。 。 .
这个框架运行现场语音代理. 翻译、文件抄写/分写、WAV工作、远见和节制使用 x402 客户端 SDK;它们不会被VoiceAgent自动引用.
插件接口
STT 服务器
interface STTPlugin {
createStream(options?: STTStreamOptions): STTStream;
}
interface STTStream {
sendAudio(pcm16: Buffer): void;
on(event: 'transcription', cb: (result: TranscriptionResult) => void): this;
on(event: 'error', cb: (error: Error) => void): this;
close(): Promise<void>;
}
LLM 语言
interface LLMPlugin {
chat(messages: Message[], signal?: AbortSignal): AsyncGenerator<LLMChunk>;
warmup?(systemPrompt: string): Promise<void>;
}
TTS 数字
interface TTSPlugin {
synthesize(text: string, signal?: AbortSignal): AsyncGenerator<Buffer>;
warmup?(): Promise<void>;
}
dTelecom 部分笔录和智能转折
import { DtelecomSTT } from '@dtelecom/agents-js/providers';
const stt = new DtelecomSTT({
serverUrl: session.stt.serverUrl,
sessionKey: session.stt.token,
language: 'en',
partials: true,
smartTurn: true, // independent opt-in; omit for ordinary acoustic endpointing
});
这两个选项都默认被省略,保留了现有的最终唯一行为. partials 请求可替换的识别窗口。 提供者发送 smartTurn 作为 smart_turn;它需要配置的服务器模型,并支持ANE VAD语言路由. 这些选择在语言变化和重联后幸存.
与此提供者在 VoiceAgent,订阅 :
transcription_partial数字 :{ text, isFinal: false, utteranceId, revision, windowStartSeconds, provisional: true, replacePrevious: true, speaker }。替换显示的临时窗口;不要将其附加到承诺文本中.partial_end数字 :{ utteranceId, reason, speaker }明确临时案文finalizing, (中文(简体) ),discarded, (中文(简体) ),replaced或者说reconfigured。 。 。 .transcription: 部分也在此出现isFinal: false; 决赛带相同utteranceId并取代他们。 只有决赛开始LLM转会. 非空地部分可以在代理说话时触发出驳.stt_ready数字 :{ clientId?, language?, smartTurn?, speaker }。 。 。 .smartTurn: "active"确认语义钩;"acoustic_fallback"报告静音探测 缺失的字段不能确认激活 。 缺少服务器模型出错error。 。 。 .
原始内容 STTStream 揭露同样的事件,没有 speaker,与 ready 改为 stt_ready. dTelecom以外的供应商不得排放这些可选事件类型. 要显示记录,请订阅其中之一 transcription_partial 或非最终 transcription而不是两个.
智能转接默认关闭。 目前的服务器默认是完成阈值为0.5,最小暂停为500 ms,最多为2秒得到的沉默和分类器截止为250 ms. 这些不是末到末的延迟保障。 使用服务器-sdk-node0.2.6或后期在Opus DTX/输入档提供静态. 见 智能转动集成笔记 对于当前的行为 和单独日期的历史接受结果.
配置
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
stt | STTPlugin | 所需经费 | 语音对文本提供者 |
llm | LLMPlugin | 所需经费 | 语言模型提供者 |
tts | TTSPlugin | undefined | 文本对语音提供者( 如果省略, 仅文本) |
instructions | string | 所需经费 | LLM 系统提示 |
respondMode | 'always' | 'addressed' | 'always' | 何时回应发言 |
agentName | string | 'assistant' | 地址模式检测名称 |
nameVariants | string[] | [] | 需要回复的其他名称 |
onDataMessage | DataMessageHandler | undefined | 回调数据通道消息 |
活动
| 活动 | 有效载荷 | 说明 |
|---|---|---|
transcription | { text, isFinal, speaker, utteranceId? } | 最终或临时STT结果 |
transcription_partial | { text, utteranceId, revision, windowStartSeconds, speaker } | 替换临时显示文本 |
partial_end | { utteranceId, reason, speaker } | 清除临时显示文本 |
stt_ready | { clientId?, language?, smartTurn?, speaker } | 检查活动/ 后退终点 |
response | string | 完整的代理响应文本 |
speaking | boolean | 代理开始/ 停止发言 |
error | Error | 管道错误 |
connected | —— 说 | 代理连接到房间 |
disconnected | string? | 代理已断开 |
自定义提供者
执行插件接口并传递到 VoiceAgent数字 :
import { BaseSTTStream, type STTPlugin, type STTStreamOptions } from '@dtelecom/agents-js';
class MySTTStream extends BaseSTTStream {
sendAudio(pcm16: Buffer): void {
// Send audio to your STT service
}
async close(): Promise<void> {
// Clean up
}
}
class MySTT implements STTPlugin {
createStream(options?: STTStreamOptions) {
return new MySTTStream();
}
}
const agent = new VoiceAgent({
stt: new MySTT(),
// ...
});
数据通道
从参与者接收数据通道消息 :
const agent = new VoiceAgent({
// ...
onDataMessage: (payload, participantIdentity, topic) => {
const message = JSON.parse(new TextDecoder().decode(payload));
console.log(`${participantIdentity} sent:`, message);
},
});
完整请求 SSML(0.5.0+)
DtelecomTTS.synthesizeSSML(document, signal) 谈判 dtelecom-ssml-v1
与服务器并发送一个完整的文档。 它没有使用
遗留语言-tag分解器或插入语言- switch silence。 服务器必须
明确启用配置文件; 在标记前拒绝不支持的服务器
用于合成.
问候或公告请使用:
await agent.saySSML(
'<speak>Hello.<break time="400ms"/>Welcome back.</speak>',
'Hello. Welcome back.',
);
第二个参数提供平面显示和对话文本.
Barge-in 使用与 say()常规 say() 流线
对LLM的答复仍然是纯文本。 递增的 SSML 和字标不是
已执行。 服务器侧验证和扩充文本计量具有权威性;
见 公共SSML合同. 1个文档留在一个引擎内,有60秒的输出约束,并使用有效速度0.75–1.5. 没有适用额外的SSML价格乘数.
OpenRouter 模型控制器
对于需要明确推理设置或拒绝采样参数的提供者:
new OpenRouterLLM({
apiKey: process.env.OPENROUTER_API_KEY!,
model: 'openai/gpt-6-luna',
reasoningEffort: 'none',
temperature: null, // omit the temperature parameter
providerRouting: { sort: 'latency', requireParameters: true },
});
这些控制也适用于简单文字的归纳请求。 现有呼叫者 保留 0. 7 温度默认值并省去推理工作, 除非配置 .
可选 TTS 水印 (0. 6.0+)
音频Seal B只有在SDK用户明确要求时才启用:
const tts = new DtelecomTTS({
serverUrl,
sessionKey,
voices: { en: { voice: 'M5', langCode: 'en' } },
watermark: true,
});
选择 watermark 或设置为 false 保留已有的无标记语句.
该选项适用于纯文本和 synthesizeSSML() 独立于 STT
智能转接/部分. 服务器必须启用可选特性; 提供者
检查热身/首次使用时的可用性,每次连接时缓存,以及
需求 watermark: "active"
在生成PCM之前。 记录 TTS watermark=active (AudioSeal B)无支持
服务器或失败的激活产生错误,从不默不作标记的输出.
接受的 B 配置在 TTS 重印后运行于 48 kHz 浮放音频 和SSML增益,在PCM16/Opus之前. 它增加了80ms的输入缓冲器 大约 M6实验室的5ms推断;活时间取决于负荷和音频到达. 水上标记后不应用过滤或再取样。 发言短/中断 可能无法检测。 此选项默认在有能力的服务器上关闭 .
许可证
阿帕奇 - 2.0