@kmchai/cortico-world-voice
Cortico 的语音 World:把回复念出来。触发、概率、文本过滤、情绪语义、队列与播放留在这里;合成经 Kmch AI · 服务 的 HTTP 网关完成。
- 语音工具:
voice_speak、voice_hush、voice_style。 - 三条触发路径:工具显式调用、用户语义请求(「念给我听」)、按概率自动朗读。
- 情绪与标签:自然语言指令、分类情绪、语速、副语言标签(
[Laughter]、[Sigh]等)。 - 文本过滤:Markdown、代码、网址、动作标记、emoji、指定符号都不念。
- 出口可拆:本机播放、发
tts.voice事件交给播放插件、或只合成并交回音频附件。
它不发送聊天消息,也不碰渠道;可见的回复仍走原来的发送工具。Voice 只负责声音,不唱歌。
安装
装两个包:服务负责托管本机 TTS 并暴露网关,语音只连它。
# 在部署的 extensions/ 目录里
corepack pnpm add "D:/path/to/cortico-world-service" --ignore-workspace
corepack pnpm add "D:/path/to/cortico-world-voice" --ignore-workspace
或在控制台「扩展」页选「从本机目录安装」,装完重启进程。启用 worlds.service.enabled 与 worlds.voice.enabled。
最小配置
服务负责拉起本机 TTS 并暴露网关;语音只连它:
{
"worlds": {
"voice": {
"enabled": true,
"serviceUrl": "http://127.0.0.1:9883",
"serviceTokenSecret": "",
"model": "ming3-tts",
"voice": "4016",
"playback": "local",
"autoSpeak": true,
"autoProbability": 0.3
}
}
}
serviceUrl 默认为 http://127.0.0.1:9883。serviceTokenSecret 填部署 .env 里的变量名,值只在请求时读取;服务监听非回环地址时必须配令牌。后端条目、模型 profile 与上游密钥都配在服务 World 里,见服务包 README。
工具
| 工具 | 作用 |
|---|---|
voice_speak | 念一句;可带 emotion、instruct、voice、speed、silent。不受概率限制。 |
voice_style | 读或改长期风格(情绪、指令、音色、语速),写回 worlds.voice,影响之后的自动朗读。 |
voice_hush | 立刻停声,清空排队。 |
silent: true 只合成、不在本机播放,音频随工具回执交给渠道,用于语音条这类出口。
触发
| 路径 | 行为 |
|---|---|
voice_speak 工具 | 立即念;不受概率限制。可带 emotion、instruct、voice、speed、silent。 |
| 用户语义请求 | 最近 explicitWindowSec 秒内的外部消息含 explicitKeywords(默认「语音,声音,发语音,念给我听,读给我听,voice,tts」)时必定朗读。 |
| 自动朗读 | 助手正文、指定工具参数、指定事件三类来源,按 autoProbability 掷一次。 |
<tts>…</tts> | 只念标签内文,并强制朗读。标签会出现在可见文字里;渠道可见时优先用工具触发。 |
自动来源:
autoFromText:一轮里只有正文、没有函数调用时,念这一轮的正文。autoTools:这些工具的参数会被当作正文。默认pet_say, pet_ask, console_send,取script/question/text/content/message/body里第一个字符串。autoEvents:轮询事件库,念这些事件类型的meta.body或正文。
同一段文字 5 秒内只念一次。概率按「一段话」掷,不按句掷。
只想响应工具与显式请求、不要随机朗读:保留 autoSpeak: true,把 autoProbability 设为 0。把 autoSpeak 整个关掉会让 tap 不再挂载,显式关键词也就看不到正文了;这时只保留 voice_speak 工具触发。
自动朗读依赖 Persona 把已挂载 World 的 outputTap() 扇进主 session;仓内 cormini 与 Coopanion 都这样做。只想要工具触发时,把 autoSpeak 关掉即可。
情绪、语速与标签
语音只把语义字段发给服务,由服务的模型 profile 决定怎么落到具体模型:
emotionMode | 语音发送 | audiocpp-ming3 profile 的落点 |
|---|---|---|
instruct(默认) | instructions:默认情绪/指令与语速折成自然语言。 | options.template_name = "freeform",带上指令。 |
categorical | emotion:只发情绪标签;没配时用「平静」。 | emotion 字段。 |
basic | speed:只发语速倍率。 | basic 模板,按 speedLevels 取慢速/中速/快速。 |
tags | 不发情绪参数,只用正文标签。 | 正文标签。 |
none | 都不发。 | — |
OpenAI 兼容条目把 instructions / emotion / speed 直接放进请求。其他模型可在服务面板里覆盖 profile 的字段映射,语音不用改配置。
response_format 每次都带;model 与 voice 配了才带,工具参数里的音色优先于配置默认值。
- 情绪来源:工具参数 > 正文里的
[emotion:开心]标记 >defaultEmotion。 voice_style读写长期风格(情绪、指令、音色、语速),写回worlds.voice。- 副语言标签:
[Breathing] [Cough] [Sigh] [Laughter] [Surprise-ah] [Surprise-oh] [Dissatisfaction-hnn] [Uhm] [Confirmation-en] [Question-ei] [Question-ah] [Question-en] [Question-oh] [Question-huh]。paralinguistic: auto保留标签,由服务里的模型决定演不演;keep一律保留,strip一律删除。表外[...]会从朗读文本里剥掉,防止被念出来。
文本过滤
按顺序:取 <tts> 内文 → 去围栏代码/行内代码 → 去图片/链接/网址 → 去 Markdown 记号 → 去 【…】、<…>、〈…〉 → 去 emoji 与不可见字符 → 去 filterSymbols 里的字符 → 压缩空白。
过滤后短于 minChars 不念,长于 maxChars 在句末截断。切句优先中英文句末标点,过长在逗号处再切;chunkMax 是单块上限,gapMs 是块间停顿。
出口
playback | 行为 |
|---|---|
local(默认) | 本机播放。Windows 用一个常驻 PowerShell 调 winmm MCI;macOS 用 afplay;Linux 用 ffplay/paplay/aplay。不需要原生 npm 模块。 |
event | 每块推一条 tts.voice 事件(带音频附件,deliver: false)。cortico-world-audio-play 默认就监听这个类型。 |
none | 只合成,不播放;适合语音只交给别的渠道。 |
shareAudio: true 时工具回执带音频附件,渠道工具可以用 [blob …] 句柄发送同一段音频。silent: true 的 voice_speak 只合成、不播放,并默认带回附件。
与服务的关系
语音只经 HTTP 调服务,不解析后端方言、不监听端口、不做模型 profile:
| 路由 | 用途 |
|---|---|
POST /v1/audio/speech | 合成音频;体 {model?, input, voice?, response_format, speed?, instructions?, emotion?}。 |
GET /health | 探活;World 页的服务灯每 15 秒刷新。 |
GET /v1/models | 模型下拉。 |
GET /v1/audio/voices?model= | 音色下拉。 |
服务不可达时:模型与音色下拉保留上一次读到的表,服务灯变红,voice_speak 回失败。语音 0.2.0 不保留直连后端的回退路径。服务不重试上游,语音也不重试。
语音不唱歌:0.1.x 的 voice_sing、【唱】 前缀与 sing* 配置已删除。唱歌由服务的 /v1/audio/sing 和网关/唱歌插件接入。
从 0.1.x 迁移
| 0.1.x | 0.2.0 |
|---|---|
backend、baseUrl、apiKeySecret | 删除;在服务里添加对应服务条目,语音改填 serviceUrl 与 serviceTokenSecret。 |
customUrl / customMethod / customHeaders / customBody / customResponse / customAudioPath | 删除;在服务的 custom 条目里配置。 |
httpApi / httpPort / httpToken / httpPlay | 删除;服务的网关取代本地语音接口。 |
singUrl / singToken、voice_sing、【唱】 | 删除;唱歌由服务的 /v1/audio/sing 与网关接入。 |
model / voice / format / timeoutMs | 保留,含义不变。 |
emotionMode / defaultEmotion / defaultInstruct / speed / paralinguistic | 保留;语音只发语义字段,由服务的模型 profile 映射到具体模型。 |
| 触发、过滤、输出配置 | 保留。 |
验证
pnpm typecheck
pnpm test
pnpm smoke:player out.wav # 本机播放器,音量 0
pnpm smoke:chain http://127.0.0.1:9883 # 触发→过滤→经服务合成→tts.voice 全链路
装进 Cortico 后应看到:World 页多一张「Kmch AI · 语音」卡,服务灯、队列灯与 service / model / voice 徽标;服务在线时灯亮;让 bot 调 voice_speak 能听见;说「念给我听」会绕过概率;playback: event 时时间线出现 tts.voice。
许可
MIT。