Open-LLM-VTuber:把"能被打断"做成一等公民的开源桌面 AI 伴侣
posts posts 2026-06-03T09:05:00+08:00Open-LLM-VTuber 是支持免提语音打断、本地 LLM、可换 Live2D 形象的开源 AI 伴侣。本文拆解 VAD 状态机、asyncio.Task 取消链与 Agent 装饰器链设计。技术笔记VTuber, Live2D, 语音交互, 本地LLM, Python阅读指引
目标读者:对本地语音 Agent、多模态实时交互、Live2D 接入 LLM 感兴趣的 Python 后端 / 全栈工程师,以及想评估"桌面 AI 伴侣"作为可运行参考实现的从业者。
本文拆解的内容:
- 桌面 AI 伴侣与传统桌面宠物(电子小鸡、桌面精灵)在"打断通道"上的本质差异
- Open-LLM-VTuber 仓库里
websocket_handler → service_context → vad/silero → agent/transformers → conversations这条主链路的职责切分 - Silero VAD 状态机(IDLE / ACTIVE / INACTIVE)+
asyncio.Task.cancel()如何实现"AI 正在说话也能被插嘴" - LLM 流式输出如何被
sentence_divider → actions_extractor → tts_filter → display_processor一串装饰器逐步改写成"句子 + 表情 + 嘴型 + 字幕"四种信号 - 本地部署时,Ollama / sherpa-onnx / CosyVoice / Edge TTS 各自落到管线的哪一节
前置知识:Python 异步编程基础(asyncio.Task / await),WebSocket 基本概念,对 LLM 流式接口有大致印象。不需要事先了解 Live2D SDK。
不覆盖:Live2D Cubism Web SDK 内部实现、Live2D 模型商用授权细节、v2.0 重写方向、具体第三方模型的训练数据。
一句话核心判断
Open-LLM-VTuber(仓库地址,约 1.4 万 stars / 1.7 千 forks,Python,2026-09-30 核实;后端 MIT,前端自 v1.2.0 起为 Open-LLM-VTuber License 1.0,Live2D 示例模型单独授权)在 Python 单进程里把"麦克风采样 → VAD 端点检测 → ASR 文字 → 流式 LLM 推理 → 句级切片 → TTS 合成 → Live2D 表情 + 嘴型"做成一条支持任意环节被打断的实时管线。当 AI 正在念一长段回答时,你随时可以开口,它能在亚秒级(受 LLM 推理与 TTS 合成链路共同影响)停下、收听你的新输入、然后接话。这种"被打断"的体验,是它和"按一下按钮问一句"的桌面助手在工程复杂度上最关键的鸿沟。
注:项目最新 release 为 v1.2.1(2025-08-26),v2.0 正在做完整重写;本文所有源码引用基于
main分支当前快照(最近推送 2026-05-15),关键读数以 2026-09-30 核实为准。
一张图:五段式架构 + 打断通道
这张图值得盯住的是中间那条竖向红线(VAD → WS → CONV)。打断链路成立,换 Ollama 还是 Claude 都不影响体验;链路一旦断了,再多花哨的 Live2D 模型也只是个"按按钮念台词"的木偶。
与传统桌面宠物的差异
很多人第一次看到"Live2D + LLM"会自然联想到桌面上"小鸡、小恐龙"那种电子宠物。它们的差异不在形象,在主循环(main loop)的设计:
| 维度 | 传统桌面宠物 | 语音助手式桌面宠物 | Open-LLM-VTuber |
|---|---|---|---|
| 输入通道 | 鼠标点击、托盘菜单 | 按住说话键 | 免提常驻麦克风 |
| 打断能力 | 不存在 | 需要手动松开按键 | VAD 自动检测,AI 说话中可被打断 |
| 状态机 | 单向 | 半双工 | 全双工 + 取消旧 asyncio.Task |
| 输出形式 | 动画 + 文字气泡 | TTS 完整句 | 按句流式:首句合成完即发声,后续句子边合成边排队 |
| 模型可换 | 形象换皮 | 通常锁定云 API | LLM/ASR/TTS 三类后端都做工厂解耦 |
| Live2D 表情 | 内置 idle 动画 | 一般不支持 | 后端 prompt 注入表情标签,agent 装饰器链解析 |
所谓"全双工"在代码里就一句:current_conversation_tasks[client_uid].cancel()。这句话要安全、可重复、不打架,前提是先有完整的"任务登记表 + 状态机 + 装饰器链"。
关键源码拆解
下面这些路径全部来自仓库 main 分支当前快照(路径相对于 src/open_llm_vtuber/)。我只挑出和"打断 + 表情 + 多后端解耦"这三条叙事相关的文件。
1. websocket_handler.py —— 入口与消息路由
整个服务端只有一个 WebSocket endpoint(server.py 注册),所有交互都通过 WS 消息的 type 字段分发。MessageType 枚举把消息归成六个家族——和打断直接相关的是两个:CONVERSATION 里的 mic-audio-end / text-input / ai-speak-signal 是会话触发入口,CONTROL 里的 interrupt-signal / audio-play-start 是打断与播放控制信号:
# src/open_llm_vtuber/websocket_handler.py
class MessageType(Enum):
GROUP = ["add-client-to-group", "remove-client-from-group"]
HISTORY = ["fetch-history-list", "fetch-and-set-history", "create-new-history", "delete-history"]
CONVERSATION = ["mic-audio-end", "text-input", "ai-speak-signal"]
CONFIG = ["fetch-configs", "switch-config"]
CONTROL = ["interrupt-signal", "audio-play-start"]
DATA = ["mic-audio-data"]WebSocketHandler.handle_new_connection() 为每个新客户端初始化一份独立的 ServiceContext,其中 config / system_config / character_config 三个配置字段各自 model_copy(deep=True),这是"多客户端互不污染"的工程基线。每个客户端持有自己的 asr_engine / tts_engine / vad_engine / agent_engine / live2d_model 引用。
WS 任务表 current_conversation_tasks: Dict[str, Optional[asyncio.Task]] 正是打断机制的核心寄存器:
self.current_conversation_tasks: Dict[str, Optional[asyncio.Task]] = {}注意:单聊用
client_uid当 key,群聊用group_id当 key,这一步的解耦让"打断某个人"和"打断一个群"在调用栈上是对称的。
2. vad/silero.py —— 语音活动检测的状态机
VAD 是"打断"这件事的触发器。VAD 反应慢或反应过激,AI 就会要么话没说完就闭嘴,要么听不见你说话——这个问题直接决定打断体验成不成立。
仓库里 VAD 抽象是 VADInterface,当前内置实现是 Silero VAD,关键参数写在 SileroVADConfig:
class SileroVADConfig(BaseModel):
orig_sr: int = 16000
target_sr: int = 16000
prob_threshold: float = 0.4 # 帧级语音概率阈值
db_threshold: int = 60 # 帧级音量阈值(dB)
required_hits: int = 3 # 连续 3 帧 = 0.096s 才确认开始
required_misses: int = 24 # 连续 24 帧 ≈ 0.768s 才确认结束
smoothing_window: int = 5 # 滑动窗口平滑window_size_samples = 512,即每 32ms 一帧(512/16000)。Silero VAD 的设计是"小窗口连续判定":
class State(Enum):
IDLE = 1 # 等待说话
ACTIVE = 2 # 正在说话
INACTIVE = 3 # 已停口StateMachine 在 IDLE 状态下累计 hit_count,达到 required_hits 才切到 ACTIVE;进 ACTIVE 后累计 miss_count,达到 required_misses 才切到 INACTIVE。这套"双阈值 + 平滑窗口"是它能"不戴耳机也听得清你"的关键——README 把它列为"voice interruption without headphones (AI won’t hear its own voice)“的能力,本质就是这两个阈值的取舍。
AI 自己 TTS 出来的声音会通过扬声器回灌麦克风。db_threshold=60 的音量门限把"AI 在远处说话"这种低分贝声音直接过滤掉,而 required_misses=24(约 0.8s)又让 AI 的句子之间不会因为短暂停顿被误判成"用户开口了”。
3. conversations/conversation_handler.py —— 打断的实际执行
打断的代码比想象中短,但要读懂它得先把角色分清楚:
- 前端通过 WS 发
mic-audio-end(VAD 检测到用户说完一段)或interrupt-signal(用户主动中断) - 后端走
handle_individual_interrupt:
# src/open_llm_vtuber/conversations/conversation_handler.py
async def handle_individual_interrupt(
client_uid: str,
current_conversation_tasks: Dict[str, Optional[asyncio.Task]],
context: ServiceContext,
heard_response: str,
):
if client_uid in current_conversation_tasks:
task = current_conversation_tasks[client_uid]
if task and not task.done():
task.cancel() # 1) 取消正在跑的协程
logger.info("🛑 Conversation task was successfully interrupted")
try:
context.agent_engine.handle_interrupt(heard_response) # 2) 通知 LLM agent
except Exception as e:
logger.error(f"Error handling interrupt: {e}")
if context.history_uid:
store_message(...) # 3) 把 AI 已说出的部分存进历史
store_message(role="system", content="[Interrupted by user]")三步缺一不可:
task.cancel()—— 取消会顺着await边界冒泡到所有正在awaitLLM token / TTS 音频的子协程,正在排队播放的 TTS 音频立刻停止派发。agent_engine.handle_interrupt(heard_response)—— 让 LLM agent 知道"用户刚才那段话我没听完,但会被记录";具体行为由 agent 实现决定(interrupt_method="user"或"system",见BasicMemoryAgent)。- 写历史 —— 把
heard_response(AI 已经说出的部分)写进chat_history_manager,并插入一条[Interrupted by user]系统消息,下次会话载入历史时不会丢失上下文。
群聊的打断几乎是同一套模板,task key 从 client_uid 换成 group_id,打断时还会广播给群里的所有成员。
4. agent/transformers.py —— 把 LLM token 流改写成"能驱动 Live2D"的多路信号
这是整个项目里最 Pythonic 的一段:4 个装饰器把一个 AsyncIterator[str] 的流式 LLM 输出,逐步转成 4 类信号。
# src/open_llm_vtuber/agent/transformers.py
@actions_extractor(live2d_model) # (1) 抽出情绪标签 → Live2D 表情
@display_processor() # (2) 解析 display_text → 内心独白
@tts_filter(tts_preprocessor_config) # (3) 过滤掉 TTS 不能念的字符
@sentence_divider(faster_first_response=True, segment_method="pysbd") # (4) 切成完整句
async def chat(memory, user_input): ... # 你的纯流式 LLM 调用实际顺序(装饰器从下往上应用)是 chat → sentence_divider → tts_filter → display_processor → actions_extractor,最后 yield 出 (SentenceWithTags, Actions) 元组或 dict。
最关键的是 (1) 表情抽取。Live2dModel 在初始化时把 model_dict.json 里模型的 emotionMap 读成 emo_map:
# src/open_llm_vtuber/live2d_model.py
self.emo_map: dict = {k.lower(): v for k, v in self.model_info["emotionMap"].items()}
self.emo_str: str = " ".join([f"[{key}]," for key in self.emo_map.keys()])
# 例如 "[fear], [anger], [disgust], [sadness], [joy], [neutral], [surprise]"emo_str 会被拼进 system prompt,告诉 LLM:“你想表达情绪时,请在句子里直接写 [joy] / [sadness] 这种标签”。后端装饰器 actions_extractor 解析这些标签后,调用 live2d_model.extract_emotion(sentence.text),把情绪事件通过 WS 推给前端,前端 Live2D SDK 切到对应表情。
表情驱动不是写死在 prompt 模板里"if 包含’开心’→joy"的字符串匹配,而是 LLM 自己在流式输出中插入结构化标签。换模型、换人设都不影响表情驱动——只要模型支持中文/英文标签输出。
5. conversations/single_conversation.py —— 把句子送进 TTS 的实际编排
每条 WS 触发的会话会创建一个 TTSTaskManager:
tts_manager = TTSTaskManager() # 每条会话一个它的职责是接收"句子级 chunk"并调度 TTS 合成 / 取消。流水线大致是:
LLM token stream
→ sentence_divider 切成完整句
→ actions_extractor 抽出表情
→ tts_filter 清洗 TTS 不可读字符
→ TTSTaskManager.speak 逐句登记 TTS 任务
→ tts_engine.async_generate_audio 整句合成
→ 音频 payload 按序号经 WS 推给前端
→ 前端 Live2D SDK 播放声音 + 切换表情合成与打断的衔接:TTSTaskManager 为每个句子起独立的 asyncio.Task(多句并行合成),但用序列号缓冲保证按句序推送;会话协程末尾 asyncio.gather(*tts_manager.task_list) 等待全部句子完成。打断时 task.cancel() 会顺着这条 gather 把未完成的合成任务一并取消,收尾时 cleanup_conversation 调 tts_manager.clear() 兜底清空任务表;已经推送到前端的音频由前端在收到打断信号后停止播放。
6. agent/agents/ —— Agent 的可插拔性
仓库的 Agent 设计遵循"接口 + 多种实现":
| Agent 实现 | 适用场景 | 关键依赖 |
|---|---|---|
BasicMemoryAgent | 默认,滚动窗口记忆 + 工具调用 | 自带 sentence_divider 装饰器链 |
LettaAgent | 长期记忆(v1.2.0 新增) | letta-client |
Mem0LLM | 第三方记忆系统 | mem0 |
HumeAI | Hume 的 EVI 情感对话 API | hume SDK |
它们都实现 AgentInterface:
class AgentInterface(ABC):
async def chat(...): ...
def handle_interrupt(self, heard_response: str): ...
async def close(self): ...用户继承 AgentInterface 就可以塞自己设计的 agent(Hume EVI、OpenAI Her、Mem0 等),仓库 README 也明确鼓励这种扩展。
顺带一提,仓库里目前实际可用的 Agent 抽象实现只有这 4 种(不含前端 MCP/HTTP 客户端)。它不像 LLM/ASR/TTS 有 20+ 后端,agent 维度是"少而精"——这是有意的取舍,避免过早陷入 agent 框架之争。
Inherit and implement the Agent interface to integrate any Agent architecture, such as HumeAI EVI, OpenAI Her, Mem0, etc.
Open-LLM-VTuber 不打算和你抢 agent 设计的活——它只做"把 agent 输出接上 Live2D 嘴型 + TTS 音频 + 打断通道"这一段,也就是前端、外观、流式管线三件套。
7. v1.2.0 关键里程碑(来自 Release Notes)
值得在拆解时提一下的几项 v1.2.0 变更(release 页面):
- MCP(Model Context Protocol)支持 —— agent 可以调外部工具,内置
time/ddg-searchMCP server,浏览器侧还能用 BrowserBase 的 Live View。 - Letta 长期记忆 —— v1.0 重构时被移除的长期记忆,v1.2.0 以
LettaAgent(对接外部 Letta 服务)的形式给出路径;main 分支 README 至今仍标注内置长期记忆 “temporarily removed (coming back soon)"。 - Live2D 升级到 Cubism 5 —— 前端从
pixi-live2d-display-lipsync切到官方 Live2D Web SDK;副作用是 Cubism 2.1 模型不再支持,release notes 明确提示这是 breaking change。 - B 站直播弹幕接入 ——
live/bilibili_live.py把 B 站弹幕转成user_input,理论上可以让 AI 在直播间里"看到"弹幕互动。 - LM Studio(LLM)/ OpenAI-Compatible / SparkTTS / SiliconFlow TTS(TTS) —— 后端矩阵再扩。
这一波变更说明项目在"对话质量 + 长期记忆 + 工具调用 + 跨平台直播"四个方向同时推进,而打断机制本身没改动——它已经是 v1.0.0 时就稳定下来的核心架构。
8. LLM / ASR / TTS 后端矩阵的工程含义
把"插拔"做到这个粒度不是炫技:因为"延迟 × 隐私 × 离线"在每一层的取舍完全不同,分开才能单独调。
- LLM 层:云 API(Claude / GPT-4o / Gemini)质量上限高、隐私差;Ollama / vLLM / LM Studio / llama.cpp 是本地化主力,官方 quick-start 的默认路径就是 Ollama(配置项预设
ollama_llm)。 - ASR 层:本地离线阵营(sherpa-onnx + SenseVoiceSmall、faster-whisper、whisper.cpp、FunASR)和云 API 阵营(OpenAI Whisper、Groq Whisper、Azure ASR)并行。v1.2.1 专门给内地用户准备了内置 SenseVoice 模型的中文加速下载包(release notes 写明"就不用再从 github 上拉取了”),离线部署省一步。
- TTS 层:19 种实现里既有 Edge TTS(免 API key 的微软云服务)、ElevenLabs / Cartesia / Azure(云)、也有 CosyVoice(v1+v2)/ GPTSoVITS / MeloTTS / pyttsx3 / Fish Audio(本地+可声音克隆)。按句合成是当前的首句延迟瓶颈——
TTSTaskManager句间并行、按序推送,但单句内部要等async_generate_audio把整句合成完才开始推流,首句延迟 = LLM 首 token + 第一句完整合成时间。
这种"每层都有 7-19 种可换实现"的工程代价是
*_factory.py抽象层会比较啰嗦,但好处是:你可以只换一层做实验,比如把 ASR 从云 Whisper 换到本地 sherpa-onnx,对比首句延迟和准确率,而不必动 LLM 和 TTS。
一段"用户插嘴"的完整时序
下面是我读代码后还原的、一次典型的"AI 正在回答,被用户打断"的事件流:
注意几个工程细节:
<|PAUSE|>标签是 Silero VAD 的内部协议 —— 在 IDLE→ACTIVE 转换瞬间 yield 这个特殊字节,告诉上层"这里是一段连续语音的开始",让前端/服务端能正确切分 buffer。- 打断的"已说出部分"是
heard_response—— 它由前端随interrupt-signal消息的text字段回传(后端data.get("text", "")),内容是 AI 已经播出的文本。把它写进历史,而不是写[用户在 X 时刻说话],能保住"上下文连续"。 - 新会话的 trigger 可以是
mic-audio-end / text-input / ai-speak-signal三种 ——ai-speak-signal用于"AI 主动说话"功能(proactive speaking),由前端定时器触发。
装上跑一遍的最小路径
以下步骤按官方 quick-start 文档还原(不要照搬 v1.0.0 之前的老文档,README 明确说 v1.0.0 是不兼容更新):
# 1. 前置依赖:git、FFmpeg、uv
# FFmpeg 是必需项,缺了会报"找不到音频文件";浏览器只用 Chrome(Edge/Safari 有已知问题)
# macOS: brew install git ffmpeg uv
# 其他: https://docs.astral.sh/uv/
# 2. 克隆(--recursive 拉 frontend 子模块,漏了会白屏;
# 也别用 GitHub "Code" 按钮的 Zip——不含子模块和 Git 信息)
git clone --recursive https://github.com/Open-LLM-VTuber/Open-LLM-VTuber.git
cd Open-LLM-VTuber
# 3. 同步依赖(v1.0.0 起推荐 uv;不想用 uv 也有 requirements.txt)
uv sync
# 4. 启动(浏览器访问 http://localhost:12393,端口在 conf.yaml 的 server 段改)
uv run run_server.py配置文件 conf.yaml 自 v1.1.0 起不再自动生成:从 config_templates/ 复制 conf.default.yaml(或中文版 conf.ZH.default.yaml)到项目根目录并改名为 conf.yaml,然后填好 LLM / ASR / TTS 后端信息。快速开始文档的默认组合是 Ollama + sherpa-onnx(SenseVoiceSmall)+ Edge TTS。
推荐的后端组合(按"零云、纯本地"严格排序)
| 层级 | 推荐 | 备注 |
|---|---|---|
| LLM | Ollama + Qwen2.5 / Llama3.1 | 本地推理,断网可跑;GGUF 也可走 llama.cpp |
| ASR | sherpa-onnx + SenseVoiceSmall | 完全离线,中文/英文/日文/韩文都覆盖;v1.2.1 中文加速包内置该模型 |
| TTS | Edge TTS(免 API key,但需联网) / CosyVoice / GPTSoVITS | 想纯离线就选 CosyVoice 或 pyttsx3 |
| VAD | Silero VAD(默认) | 已经内置 |
⚠️ 跨机访问:麦克风 API 只在
https/localhost下能拿到getUserMedia,所以手机远程访问桌面端需要反代 + HTTPS,README 也专门提醒了这一点。
适用边界与决策建议
| 场景 | 建议 |
|---|---|
| 想做"按一下按钮问一句"的桌面助手 | 不必上 Open-LLM-VTuber,直接 LangChain / LlamaIndex 桌面化更轻 |
| 想要"全双工 + Live2D"的伴侣 / 助手 / 直播间数字人 | ✅ 这是它的强项 |
| 想研究"AI 说话中插嘴"的工程范式 | ✅ 仓库代码就是一份开源参考实现 |
| 想要长期记忆 + 工具调用 | ✅ v1.2.0 起有 Letta agent + MCP;内置长期记忆仍在开发,Letta 路线需跑外部服务 |
| 想做企业内私有部署 | ⚠️ 注意 Live2D 模型商用授权,中大规模企业单独谈;后端 MIT 计划在 v1.3/v1.4 前后收紧 |
| 想要低延迟直播口播 | ⚠️ TTS 按句合成(句间并行、单句等整句合成完),首句延迟主要由 LLM 首 token + 首句 TTS 合成决定 |
| 想接 GPT-4o realtime 那种原生多模态语音 | ❌ 当前仓库主要做"模块化拼接",原生多模态是另一条路线 |
采用顺序建议(假设你从零开始做这件事):
- 先跑通最小链路:Ollama + sherpa-onnx + Edge TTS + 默认 Live2D 模型(
mao_pro),确认能正常对话。 - 再上打断:默认配置里打断就是开的,验证你能正常插嘴。
- 再换形象:用你自己的 Live2D Cubism 5 模型,编辑
model_dict.json配上emotionMap。 - 再换声线:TTS 切到 CosyVoice 或 GPTSoVITS 做声音克隆。
- 最后接工具/记忆:上 MCP + Letta agent。
不要做反了 —— 很多人在第一步就试图塞自定义 agent,结果排查问题时不知道是 LLM 端、网络端、TTS 端还是 WS 端出问题。
FAQ(来自常见问题,但答案以仓库证据为准)
Q: 它和 neuro-sama 是什么关系?
README 直接说了:项目没有叫 Open-LLM-Companion 或 Open-LLM-Waifu,而是叫 Open-LLM-Vtuber,是因为最初目标是用开源方案在 Windows 之外的平台上离线复现 neuro-sama(闭源 AI VTuber)。它不接 neuro-sama 的模型,但设计目标对标它的体验。
Q: 真的能"免提 + 不戴耳机"被插嘴吗?
可以,因为 Silero VAD 的状态机 + db_threshold 把"AI 自己 TTS 出来的较远声音"挡掉了。代价是如果你离麦克风很近且开大声,AI 偶尔会听见自己尾音、误以为你要插嘴——这时调高 db_threshold 即可。
Q: MCP 是什么?
Model Context Protocol 是 Anthropic 推动的"让 LLM 调用外部工具"的标准。仓库 v1.2.0 起内置了 time 和 ddg-search 两个 MCP server,你也可以自己接 BrowserBase 等服务。需要在前端 status bar 看工具调用状态。
Q: v1.x 和 v2.0 怎么选?
README 顶部明确写:v2.0 是一次完整重写,目前还在早期规划阶段,开发者会修 v1 的 bug,但不建议在 v1 上提新功能 PR。新项目可以从 v1.x 起步学思路,但生产部署要评估迁移成本。
Q: 能在树莓派上跑吗?
官方 quick-start 明确写了最低要求是"电脑,树莓派也行"——因为各组件(ASR / LLM / TTS / 翻译)都可以选 API,官方推荐的思路就是本地跑得动的放本地、跑不动的用 API。实际瓶颈在本地 LLM:SBC 上跑 Ollama 大模型不现实,树莓派上可行的组合是 LLM 走云 API,ASR / TTS / VAD 留在本地。
Q: 前端必须用它的 web/desktop 客户端吗?
是的,至少 v1.x 的 Live2D 表情驱动逻辑在 frontend/ 子模块里和后端 WS 协议绑定。如果你要换前端,需要复刻 transformers.emo_str 那一套 prompt 约定和 WS 消息格式(仓库 frontend/ 是 Open-LLM-VTuber-Web 的 git submodule)。
Q: 商用 Live2D 模型会被怎么影响?
仓库的 sample 模型(shizuku、mao_pro 等)由 Live2D Inc. 单独授权,MIT 不覆盖它们。README 提醒中大型企业商用前要确认 Live2D Free Material License Agreement 下的额外授权要求,或使用不含这些模型的版本。v1.2.0 之后默认模型换成了 mao_pro,因为 shizuku 在 Live2D 5 版本里被官方去掉了表情。另外留意:前端自 v1.2.0 起已是 Open-LLM-VTuber License 1.0,官方也宣布后端 MIT 计划在 v1.3 或 v1.4 前后统一为同一许可证,商用决策前盯一下 release 公告。
结语:参考实现,不是产品
Open-LLM-VTuber 的价值在于它完整暴露了一套"语音 + LLM + Live2D + 打断"的开源参考架构,而不是给你一个开箱即用的完美 AI 女友。想验证某个跨模块的机制,直接去对应文件看实现:
- 想弄清怎么用 Silero VAD 区分"自己说话 vs 用户说话"?看
vad/silero.py。 - 想弄清怎么把 LLM 流式输出切到 Live2D 表情?看
agent/transformers.py。 - 想弄清"AI 正在念台词时被插嘴"怎么工程化?看
conversations/conversation_handler.handle_individual_interrupt。 - 想弄清怎么给 LLM 装上可插拔 ASR/TTS/MCP?看
service_context.py+ 各*_factory.py。
每个模块都是"实现 + 工厂 + 接口“三层,留好扩展点。
如果你的目标是在生产里跑一个"AI 数字员工"或"AI 直播间主播”,可以把它当作起点;如果只是好奇"桌面 AI 伴侣怎么搭",clone 下来、按推荐配置跑一遍、再顺着本文这几条路径去翻源码,1~2 小时就能理解这条管线的全貌。
引用清单
- 仓库主页与 README:https://github.com/Open-LLM-VTuber/Open-LLM-VTuber
- Release Notes v1.2.0(含许可证变更与 breaking changes):https://github.com/Open-LLM-VTuber/Open-LLM-VTuber/releases/tag/1.2.0
- Release Notes v1.2.1(中文加速包):https://github.com/Open-LLM-VTuber/Open-LLM-VTuber/releases/tag/v1.2.1
- 全部 release 页:https://github.com/Open-LLM-VTuber/Open-LLM-VTuber/releases
- 项目文档(快速开始):https://open-llm-vtuber.github.io/docs/quick-start
- 关键源码(路径相对于仓库根):
src/open_llm_vtuber/websocket_handler.pysrc/open_llm_vtuber/service_context.pysrc/open_llm_vtuber/vad/silero.pysrc/open_llm_vtuber/conversations/conversation_handler.pysrc/open_llm_vtuber/conversations/tts_manager.pysrc/open_llm_vtuber/conversations/conversation_utils.pysrc/open_llm_vtuber/agent/transformers.pysrc/open_llm_vtuber/agent/agents/basic_memory_agent.pysrc/open_llm_vtuber/live2d_model.pyconfig_templates/conf.default.yamlrun_server.py/pyproject.toml
- Silero VAD 文档:https://github.com/snakers4/silero-vad
- MCP 协议:https://modelcontextprotocol.io/
参与讨论
使用 GitHub 登录。欢迎补充事实、异议与实践。
讨论暂时无法加载。