Agent Zero 内置 Kokoro TTS 插件详解:声音配置、合成 API 与浏览器降级机制
Agent Zero 内置 Kokoro TTS 插件详解声音配置、合成 API 与浏览器降级机制【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zeroAgent Zero 的内置语音合成插件plugins/_kokoro_tts负责把 WebUI 的朗读能力从浏览器原生speechSynthesis升级为服务端 Kokoro 模型合成。本篇基于 插件说明文档 展开覆盖其两个配置项voice、speed、两条 API 路由synthesize、status的完整行为并结合 运行时源码、迁移逻辑 与 前端状态仓库 讲解模型懒加载、WAV 编码与浏览器降级回退的实现细节帮助读者理解该插件从配置到音频返回的整条链路。插件定位与三大行为约定README 明确给出了插件的三条行为约定这也是理解整个模块设计的出发点注册为活动 TTS 提供方插件启用后Kokoro 被注册为 Agent Zero 的语音合成提供方TTS providerWebUI 的朗读请求会走服务端 Kokoro 模型而不是浏览器本地发音。浏览器原生 TTS 作为降级路径插件禁用时语音输出自动回落到浏览器原生speechSynthesis用户感知上只是音质不同功能不断裂。依赖走核心 Docker/bootstrap 路径Kokoro 相关的 Python 依赖kokoro包、soundfile等保留在核心 Docker/引导安装路径上插件本身不做按需安装任何包或二进制。这与 AGENTS.md 中Keep Kokoro dependencies on Docker/bootstrap paths, not opportunistic runtime installs的本地契约一致。从 plugin.yaml 的元数据可以看到该插件的关键声明name: _kokoro_tts title: Kokoro TTS version: 1.0.0 always_enabled: false settings_sections: - agent per_project_config: false per_agent_config: false几个值得注意的点always_enabled: false插件默认不强制启用由用户在插件管理中显式开启settings_sections: [agent]其配置入口挂在 Agent 设置分区下per_project_config: false与per_agent_config: false配置是全局单一份不存在按项目或按 Agent 的差异化配置这与 运行时中get_config()直接读取全局插件配置的实现相符。配置项voice与speedREADME 中列出的两个配置项其默认值定义在两处相互呼应default_config.yaml 和 runtime.py 的DEFAULT_CONFIG# default_config.yaml voice: am_puck,am_onyx speed: 1.1# helpers/runtime.py DEFAULT_CONFIG { voice: am_puck,am_onyx, speed: 1.1, }配置项类型默认值校验规则源码依据voice字符串am_puck,am_onyx去除首尾空白后必须非空否则回退默认值speed数值倍率1.1必须能转为float且大于 0否则回退默认值校验逻辑集中在normalize_config()runtime.pydef normalize_config(config: dict[str, Any] | None) - dict[str, Any]: normalized dict(DEFAULT_CONFIG) if not isinstance(config, dict): return normalized voice str(config.get(voice, normalized[voice]) or ).strip() if voice: normalized[voice] voice try: speed float(config.get(speed, normalized[speed])) if speed 0: normalized[speed] speed except (TypeError, ValueError): pass return normalized从源码结构看这一函数是防御式归一化非法输入不会抛错而是静默回退到默认值保证synthesize请求在任何脏配置下都能拿到可用的voice/speed。两个配置钩子hooks.py都经过它处理——读取配置时先跑migration.ensure_migrated()再做归一化保存配置时同样强制归一化后才落盘def get_plugin_config(defaultNone, **kwargs): migration.ensure_migrated() return runtime.normalize_config(default or {}) def save_plugin_config(defaultNone, settingsNone, **kwargs): return runtime.normalize_config(settings or default or {})WebUI 的设置页config.html与之一致voice为文本输入speed为min0.1 step0.1的数字输入页面文案也明确提示禁用时回落到浏览器 speech API。模型懒加载KPipeline 与 hexgrad/Kokoro-82M模型并非在插件启用时立即加载而是首次合成时按需预载。核心实现在runtime._preload()runtime.pyfrom kokoro import KPipeline _pipeline KPipeline(lang_codea, repo_idhexgrad/Kokoro-82M)要点解析模型来源hexgrad/Kokoro-82M即 Kokoro-82M 权重仓库lang_codea指定英文美式语音管道与默认am_puck,am_onyx两个am_前缀的美式声音标识相互对应全局单例 更新锁模块级变量_pipeline保存管道实例is_updating_model布尔量作为互斥标记。_preload()开头有while is_updating_model: await asyncio.sleep(0.1)的等待循环防止并发合成请求同时触发模型加载用户可见的加载状态加载开始/结束分别通过NotificationManager推送 Loading Kokoro TTS model...停留 99 秒与 Kokoro TTS model loaded. 两条通知前端据此展示状态状态查询接口is_downloaded()判断_pipeline is not Noneis_downloading()返回is_updating_model二者被status路由透出给前端。合成流程从句子列表到 Base64 WAVsynthesize_sentences()是插件的核心入口runtime.py其内部_synthesize_sentences()的完整链路如下await _preload() combined_audio: list[float] [] for sentence in sentences: if not sentence.strip(): continue segments _pipeline(sentence.strip(), voicevoice, speedspeed) for segment in list(segments): audio_tensor segment.audio audio_numpy audio_tensor.detach().cpu().numpy() combined_audio.extend(audio_numpy.tolist()) if not combined_audio: return buffer io.BytesIO() sf.write(buffer, combined_audio, 24000, formatWAV) return base64.b64encode(buffer.getvalue()).decode(utf-8)从源码可以确认以下实现事实逐句合成、逐段拼接Kokoro 管道对每个句子返回若干 segment每段携带audio张量代码将其detach().cpu().numpy()后展平合并为单一浮点波形列表空句子仅空白会被跳过采样率固定 24000 Hzsf.write(buffer, combined_audio, 24000, formatWAV)用 soundfile 写入内存中的 24 kHz WAV这也是 Kokoro-82M 的原生输出采样率Base64 字符串返回最终音频以 Base64 编码的字符串形式返回而非文件路径与 AGENTS.md 中Do not expose generated speech artifacts outside intended response paths的约定一致——生成音频不落盘只随 API 响应传递异常处理任何异常会先经PrintStyle.error(...)打印到控制台再向上抛出由 API 层捕获并转成 JSON 错误体。路由一POST /api/plugins/_kokoro_tts/synthesizeapi/synthesize.py 定义了合成端点行为契约清晰class Synthesize(ApiHandler): async def process(self, input: dict, request: Request) - dict | Response: if not runtime.is_globally_enabled(): return Response(status409, responseKokoro TTS plugin is disabled) text str(input.get(text) or ).strip() if not text: return Response(status400, responseMissing text) try: audio await runtime.synthesize_sentences([text]) return { success: True, audio: audio, mime_type: audio/wav, } except Exception as e: return {success: False, error: str(e)}场景响应插件全局禁用HTTP 409Kokoro TTS plugin is disabled请求体缺少text或仅空白HTTP 400Missing text合成成功{success: true, audio: base64 wav, mime_type: audio/wav}合成异常{success: false, error: 异常信息}请求体只需一个text字段后端将其包装为单元素句子列表调用synthesize_sentences因此该路由当前面向整段文本一次合成的调用方式。路由二POST /api/plugins/_kokoro_tts/statusapi/status.py 是前端与状态面板的信息源返回体结构如下字段逐一对应源码字段来源含义plugin常量_kokoro_tts插件名enabledruntime.is_globally_enabled()插件当前是否全局启用configruntime.get_config()归一化后的voice/speedmodel.readyruntime.is_downloaded()模型是否已加载model.loadingruntime.is_downloading()模型是否正在加载package.versionimportlib.metadata.version(kokoro)已安装kokoro包的版本package.error版本读取异常信息kokoro包缺失时的错误详情fallback常量文案说明禁用时浏览器speechSynthesis仍是降级路径值得注意的是enabled的计算路径is_globally_enabled()runtime.py会先调用migration.ensure_migrated()再按各插件根目录下的启用/禁用标记文件倒序裁决最终开关。前端集成Provider 注册与浏览器降级前端状态仓库 kokoro-tts-store.js 实现了 README 中注册/回退约定的浏览器侧拉取状态refreshStatus()调用/plugins/_kokoro_tts/status填充enabled、config、modelReady、modelLoading、packageVersion条件注册 Provider仅当enabled为真时通过ttsService.registerProvider(PLUGIN_NAME, {...})注册一个合成回调该回调 POST/plugins/_kokoro_tts/synthesize并把返回的audioBase64与mime_type交给全局 TTS 服务播放失败时抛出Kokoro TTS synthesis failed.并弹出前端错误 toast禁用即注销enabled为假或请求失败时调用unregisterProvider()注销后 TTS 服务自然走浏览器原生speechSynthesis路径——这就是降级在代码层面的具体形态状态文案statusText计算属性按enabled / modelLoading / modelReady输出Disabled / Loading / Ready / Idle四态用于 WebUI 面板展示。遗留配置迁移从 usr/settings.json 到插件开关migration.py 处理旧版本usr/settings.json中的tts_kokoro布尔键向新插件开关体系的过渡ensure_migrated()的决策逻辑是旧值为真或已存在显式开关文件任一插件根目录下已有启用/禁用标记不迁移尊重用户现状两者皆无全新安装场景为该插件写入一个DISABLED标记文件并清理插件缓存——即新安装默认关闭 Kokoro继续由浏览器 TTS 兜底直到用户在插件管理中显式开启。这一策略与plugin.yaml中always_enabled: false的设计呼应Kokoro 是默认关闭、显式启用的可选增强避免未安装模型环境下的无谓失败。依赖与部署前提结合 README 的 Behavior 一节与 AGENTS.md 的本地契约部署时需要满足Python 环境已安装kokoro包提供KPipeline与soundfile提供sf.write且这些依赖被刻意保留在核心 Docker/bootstrap 安装路径上插件自身不触发任何运行时安装模型权重hexgrad/Kokoro-82M在首次合成时按需拉取首次合成延迟会明显高于后续请求若kokoro包缺失status路由会在package.error中给出具体异常此时插件应被视为不可用前端会保持浏览器降级路径默认声音am_puck,am_onyx与管道的lang_codea美式英文绑定如需更换音色应使用与语言代码匹配的声音标识。小结_kokoro_tts插件用极小的代码面实现了完整的服务端 TTS能力voice/speed两个配置项经防御式归一化后驱动 Kokoro-82M 管道synthesize路由把 24 kHz WAV 以 Base64 随响应返回、不落盘status路由透出启用状态、模型就绪状态与kokoro包版本前端按状态条件注册/注销 provider实现禁用时无感回退到浏览器speechSynthesis迁移逻辑则保证新安装默认关闭、旧配置平滑过渡。整条链路的行为均可以在 plugins/_kokoro_tts 目录下的api/、helpers/、webui/源码中逐行验证相关前端拆分逻辑另有 test_speech_plugin_split.py 覆盖。【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zero创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考