1. 系统概述

1.1 背景

直播过程中需要持续与观众互动以活跃气氛,但主播无法兼顾所有评论。本系统作为直播间自动互动机器人,监听抖音直播间评论,自动生成语音回复并从服务端声卡播报(直播系统采集声卡输出),实现"评论区有人说话就回应、没人说话也不冷场"。

1.2 功能目标

# 功能 说明
1 LLM 自动回复评论 调用 LLM 生成口语化回复,通过 TTS 语音播报(不是文字回复)
2 欢迎词播报 检测"XXX来了 / XXX进入直播间"类消息,语音播报"欢迎 XXX"(优先级低,优先回复高价值互动)
3 随机话题播报 没有回复和欢迎词时,LLM 围绕直播主题生成话题播报,防止冷场
4 话题延续性 话题与最近播报内容/用户评论关联,且不重复
5 前端管理页面 #live-interact 独立页面:设置直播主题、开关各功能、查看播报日志

1.3 关键决策

决策项 选择 理由
TTS 播放方式 服务端播放(pygame / ffplay) 直播系统采集声卡输出,服务端播放最稳定,无需对接直播软件
直播主题 用户手动设置 在互动页面填写,LLM 参考主题生成话题
话题来源 LLM 动态生成 结合主题与最近播报内容,保证延续性与不重复
LLM 调用 复用 frameflow.llm.client JSON 配置 + 热加载,多 Provider 支持,互动场景优先免费 provider
前端结构 独立新页面 与直播管理页(#live)分离,入口挂在该页

2. 整体架构

2.1 架构图

┌─────────────────────────────────────────────────────────────────┐
│                        抖音直播间(浏览器页面)                     │
└──────────────────────────────┬──────────────────────────────────┘
                               │ Playwright 抓取
┌──────────────────────────────▼──────────────────────────────────┐
│  live_monitor(评论监控)  MonitorManager                        │
│  评论去重 → 入库 live_comment → 回调通知                           │
└──────────────────────────────┬──────────────────────────────────┘
                               │ on_comment 回调(非阻塞入队)
┌──────────────────────────────▼──────────────────────────────────┐
│  InteractionManager(每房间一个实例)                              │
│                                                                  │
│  ┌──────────────┐   ┌────────────────┐   ┌───────────────────┐  │
│  │ 评论队列      │──▶│ 决策线程 _run   │──▶│ 播报任务队列(有界10)│  │
│  │ queue.Queue  │   │ 0.3s 节流轮询   │   │ queue.Queue       │  │
│  └──────────────┘   └───────┬────────┘   └─────────┬─────────┘  │
│                             │ 提交 LLM 任务          │ 串行消费    │
│                     ┌───────▼────────┐      ┌───────▼─────────┐  │
│                     │ LLM 线程池      │      │ 播放线程         │  │
│                     │ (2 workers)    │      │ _play_worker    │  │
│                     │ 回复 / 话题     │      │ TTS生成→播放→日志│  │
│                     └───────┬────────┘      └───────┬─────────┘  │
└─────────────────────────────┼───────────────────────┼────────────┘
                              │                       │
              ┌───────────────▼─────────┐   ┌─────────▼──────────┐
              │ frameflow.llm.client     │   │ EdgeTTSService     │
              │ (config/llm_config.json) │   │ → output/live_tts  │
              │ agness 优先,mimo 兜底    │   │ AudioPlayer        │
              └─────────────────────────┘   │ pygame / ffplay    │
                                            └─────────┬──────────┘
                                                      │ 声卡输出
                                              直播系统采集 → 观众

2.2 处理优先级

1. 欢迎词(无需 LLM,即时入播报队列,成本低、延迟小)
2. LLM 评论回复(高价值互动;同一时刻仅允许一个 LLM 任务在跑)
3. 随机话题(兜底:决策线程空闲且距上次话题 ≥ topic_interval 时才触发)

2.3 代码结构

文件 职责
service/live_interaction_service.py 核心服务:互动管理器、决策循环、LLM 调用、欢迎词检测、违禁词过滤
service/audio_player.py 服务端音频播放器(pygame 优先,ffplay 兜底),全局单例 audio_player
api/live_interact.py 互动管理 API,前缀 /api/live-interact
db/models.py LiveInteraction(互动配置)、LiveBroadcastLog(播报日志)
web/views/live-interact.html/.js 前端互动管理页面(#live-interact 路由)
frameflow/llm/client.py 底层 LLM 客户端(多 Provider,JSON 配置热加载)
frameflow/service/tts/text2speech_edge.py Edge-TTS 语音合成服务

3. 数据库设计

3.1 live_interaction(互动配置,每房间一条)

字段 类型 说明
room_id Text, 索引 关联 live_room.id(抖音房间号)
topic Text 直播主题(用户手动设置,供 LLM 参考)
is_active Boolean 是否正在互动(预留)
reply_enabled Boolean, 默认 true 是否启用评论回复
welcome_enabled Boolean, 默认 true 是否启用欢迎词
topic_enabled Boolean, 默认 true 是否启用随机话题
topic_interval Integer, 默认 60 随机话题最小间隔(秒)
voice String(32), 默认 "xiaoxiao" TTS 语音角色

3.2 live_broadcast_log(播报日志)

字段 类型 说明
room_id Text, 索引 房间 ID
broadcast_type String(16) reply / welcome / topic
content Text 播报文本
triggered_by_comment Text 触发的原始评论(欢迎词/回复场景)
triggered_by_user Text 触发用户昵称
llm_raw_reply Text LLM 原始返回(调试用)
llm_provider String(32) 实际使用的 LLM provider(成本核算依据)
llm_model String(64) 实际使用的模型名(成本核算依据)
created_at DateTime 播报时间

播报日志同时承担话题去重(LLM 提示词携带最近播报)与成本审计(每条记录实际 provider/model)两个职责。

3.3 依赖表

  • live_room:直播间基础信息(URL、主播昵称、状态等)。
  • live_comment:评论流水(message_id 唯一索引去重;message_type 区分 chat/system/bottom)。

4. 核心模块设计

4.1 InteractionManager(房间互动管理器)

每个房间一个实例,包含 3 类线程 + 2 个队列:

组件 说明
评论队列 queue.Queue 监控线程回调 on_comment 投递,决策线程消费(非阻塞)
决策线程 _run 0.3 秒节流轮询:① 检查 LLM 后台任务完成→入播报队列;② 消费评论队列做决策;③ 空闲且话题到期→提交话题生成
LLM 线程池 _LLM_POOL ThreadPoolExecutor(max_workers=2);同一时刻每房间仅 1 个 LLM 任务(_llm_future 占位),生成期间不阻塞决策线程
播报任务队列 有界队列(容量 10),满时丢弃最旧任务(信息轰炸保护)
播放线程 _play_worker 串行消费播报队列:TTS 生成(网络)→ 声卡播放(等播完)→ 写播报日志,保证语音不重叠

启动流程: 1. 清空评论队列与播报队列(防止上次会话残留); 2. 初始化评论水位线:取 live_comment 当前最大 ID,历史评论不重播; 3. 向 MonitorManager 注册评论回调(注册/注销使用同一回调引用); 4. 启动播放线程与决策线程(均为 daemon 线程)。

停止流程对称:注销回调 → 置停止事件 → join 线程(5s 超时)→ 清空队列 → audio_player.stop() 停止正在播放的语音。

4.2 评论决策(_handle_comment)

评论到达
 ├─ 欢迎词检测(welcome_enabled 时)
 │   ├─ 形态1:抖音进入提示 bottom 消息
 │   │        (content ∈ {来了, 来了来了, 来啦},username 字段即进入者昵称)
 │   ├─ 形态2:弹幕自报家门 "XXX来了/进入直播间/来啦/加入直播间"
 │   │        (先 _strip_decorations 剥离尾部 [表情]/emoji/标点,再正则 fullmatch;
 │   │          系统消息"欢迎 XX 进入直播间"自动剥掉"欢迎"前缀)
 │   ├─ 过滤:违禁词昵称 / 含 ≥5 位连续数字的昵称 → 不播报
 │   ├─ 冷却:距上次欢迎 < 5 秒 → 跳过
 │   └─ 通过 → format_welcome_text 生成"欢迎 XX"
 │            (尾缀 1~4 位数字保留播报;纯数字昵称改为"用户NNNN")→ 入播报队列
 │
 └─ LLM 回复(reply_enabled 时),需同时满足:
     ├─ msg_type == "chat"(普通聊天,非系统/bottom 消息)
     ├─ 非低价值评论(filter_reason 为空)
     ├─ 评论不含违禁词
     ├─ 无进行中的 LLM 任务(_llm_future is None)
     ├─ 距上次回复 ≥ 10 秒(REPLY_COOLDOWN)
     └─ 长度 > 2 且不以 "/" 开头(排除指令类文本)

4.3 LLM 调用(_call_llm)

  • Provider 策略:优先 agness(免费),失败自动回退 mimo;两个都失败返回空文本(本轮放弃播报)。
  • 调用链:asyncio.run(chat_completion(...)) → frameflow.llm.client,Provider 定义见 config/llm_config.json(JSON 配置 + 文件 mtime 热加载,改配置无需重启)。
  • 返回:(文本, "provider/model"),后者随播报日志落库,用于成本核算。

两类提示词(均为 system + user 两段式):

场景 要点 temperature
回复 _llm_reply 携带直播主题 + 最近播报(最多 3 条);语气亲切自然、符合直播场景;只返回回复内容;字数 100 字以内(在提示词中控制) 0.7
话题 _llm_topic 携带直播主题 + 最近播报(最多 3 条);与最近话题有关联但不重复;尽量延续上一次播报自然延伸;以陈述句为主(疑问句 ≤ 1/5);避免"评论区"一词;100 字以内 0.9

话题延续性与不重复的实现:决策线程维护 deque(maxlen=5) 的最近播报列表,每次生成话题/回复时把最近内容注入提示词。

4.4 播报队列与 TTL

播报任务带 created_at 时间戳,播放线程取出时检查 TTL:

参数 值 说明
TTS_QUEUE_SIZE 10 队列上限,满时丢最旧
WELCOME_TTL 10 秒 进入提示过时即无意义(人进来 10 秒后才欢迎很尴尬)
REPLY_TTL 60 秒 回复/话题过期丢弃
WELCOME_COOLDOWN 5 秒 欢迎词最小间隔
REPLY_COOLDOWN 10 秒 回复最小间隔

另外:播报进行中(_task_busy)到达的"来了"类进入提示直接丢弃,避免欢迎词堆积轰炸。

4.5 TTS 与音频播放

  • 合成:EdgeTTSService(微软 Edge TTS),输出目录 output/live_tts/,文件名 live_{room_id}_{time_ns}.mp3(纳秒时间戳防冲突)。
  • 播放:AudioPlayer 单例,优先 pygame.mixer,不可用回退 ffplay -nodisp -autoexit;内部维护播放队列 + 锁,多线程调用安全、自动排队。
  • 串行保证:播放线程在 audio_player.play()(异步播放)后轮询 is_playing() 等待播完,确保多条语音不重叠。
  • 磁盘清理:决策线程每 ~30 秒清理一次超过 1 小时的历史音频,防止磁盘膨胀。

4.6 内容安全过滤

规则 作用范围
BANNED_WORDS 违禁词表(脏话/色情等中英文关键词,子串匹配,不区分大小写) 昵称命中 → 不播欢迎词;评论命中 → 不触发 LLM 回复
昵称含 ≥5 位连续数字 判定为机器/广告号,不播欢迎词
昵称尾缀 1~4 位数字 保留数字播报(如"欢迎 阿明1234");纯数字昵称播"用户NNNN"

5. API 接口

前缀:/api/live-interact(注册于 src/mooclawer/douyin/api/__init__.py)

接口 方法 说明
/{room_id} GET 获取互动配置(不存在时自动创建默认配置),返回体含 is_running
/{room_id} PUT 更新配置(topic / reply_enabled / welcome_enabled / topic_enabled / topic_interval / voice,可选字段部分更新)
/{room_id}/start POST 启动互动;若该房间评论监控未开启会自动启动监控;已运行返回 400
/{room_id}/stop POST 停止互动;未运行返回 400
/{room_id}/logs GET 播报日志分页查询(page / page_size / broadcast_type 过滤,按时间倒序)
/{room_id}/test POST TTS 试播(自定义文本 + 语音角色),用于播放链路验证

6. 前端页面

  • 路由:http://localhost:8002/#live-interact(SPA 按需加载 live-interact.html + live-interact.js)
  • 入口:#live 直播管理页增加【直播互动】入口
  • 页面布局:
┌─────────────────────────────────────────────┐
│ 直播互动管理                    [启动互动]    │
├─────────────────────────────────────────────┤
│ 直播间: [下拉选择]                            │
│ 直播主题: [输入框 - 供LLM参考]                │
│                                              │
│ ┌─ 互动设置 ─────────────────────────────┐  │
│ │ ☑ 评论回复    语音: [下拉]              │  │
│ │ ☑ 欢迎词      话题间隔: [60]秒          │  │
│ │ ☑ 随机话题                              │  │
│ └────────────────────────────────────────┘  │
│                                              │
│ ── 最近播报 ──────────────────────────────  │
│ [时间] [类型] [内容]                          │
│ 14:30  回复  "谢谢支持,欢迎常来"             │
│ 14:28  话题  "今天我们聊聊..."               │
│ 14:25  欢迎  "欢迎小明进入直播间"            │
└─────────────────────────────────────────────┘

7. 运行与验证

7.1 启动与依赖

  • 直播互动依赖 mooclawer 服务(端口 8002):评论监控(Playwright 驱动浏览器读取弹幕)+ 本互动服务。
  • LLM 依赖:config/llm_config.json 中对应 provider 的 api_key_env 环境变量(agness → AGNESS_KEY,mimo → MIMO_API_KEY)。

7.2 验证清单

  1. 后端语法检查:python -m py_compile 各新建/修改的 .py 文件。
  2. 数据库:live_interaction / live_broadcast_log 表自动创建。
  3. API 冒烟:GET /api/live-interact/{room_id} 能返回默认配置。
  4. TTS 播放:POST /api/live-interact/{room_id}/test 确认声卡出声。
  5. 前端:http://localhost:8002/#live-interact 页面加载正常。
  6. 端到端:选直播间 → 填主题 → 启动互动 → 直播间发评论 → 观察语音回复。

7.3 观测与成本核算

  • 每条播报在 live_broadcast_log 记录实际 llm_provider / llm_model,按模型单价聚合即可核算互动场景的 LLM 成本。
  • 运行日志(loguru)关键行:
  • [Interaction] 前缀:互动启动/停止、回复、随机话题、丢弃的过期播报等;
  • [LLM] 前缀:底层 client 每次调用输出 provider、model、prompt_tokens、completion_tokens、total_tokens(用量日志,用于成本评估)。

8. 已知限制与后续优化

项 现状 可能的优化
LLM 并发 每房间同一时刻仅 1 个 LLM 任务,回复有 10 秒冷却 高热度直播间可提升并发或缩短冷却
回复字数 需求为 ≤30 字,当前提示词约束为 100 字以内 如需严格 30 字,调整 _llm_reply 提示词并加输出长度校验重试
违禁词 静态关键词表 可接入 LLM 审核或平台敏感词库
TTS Edge-TTS(免费但依赖网络) 可切换小米/火山等付费 TTS 提升音质,配置化切换
播报日志 记录 provider/model,未记录 token 数 可将 LLM usage 落库,实现自动成本统计报表
话题去重 靠提示词注入最近 5 条播报 可加相似度校验(如编辑距离/向量)硬保证不重复

以上就是本文的全部内容

如果觉得有帮助,欢迎 点赞 · 在看 · 转发,让更多朋友看到 👇

— END —