跳到主要内容
返回开发者精选

NIGHTWAVE:由 1B 模型主持的 1970s 深夜电台

作者:ratandeep模型:MiniCPM5-1B

由 1B 模型主持、可现场来电的复古电台

NIGHTWAVE 是一台 1970 年代的深夜电台,暖乎乎的,手工搭的,旋钮真能拧,台也真能收到。主持节目的 DJ 是一个 10 亿参数的模型。

把旋钮转到 98.6,一位深夜主持人就开始陪你熬过后半夜。他的声音来自 Kokoro 的 am_michael 音色。他会放一些根本不存在的歌手的唱片,念听众的点歌留言,播报地图上找不到的小镇天气,对着黑夜自言自语,还会当场接你的电话。每张唱片都是浏览器里实时生成的,没有采样,也没有版权问题。拿起听筒,或者直接按空格键,就能跟他说话。

作者自己的说法是:这是唯一一个可以闭着眼睛评审的参赛作品。NIGHTWAVE 参加了 Build Small Hackathon(Thousand-Token Wood 赛道)。

核心特性​

  • 现场 AI DJ:MiniCPM5-1B(约 1.08B 参数,可微调)负责写串词,电台呼号、深夜随想、虚构天气、点歌留言、歌曲介绍,每段插播都是现写的。有听众来电时,他会围着对方说的真实内容即兴回应。这一点装不出来,也说明模型确实在线、确实在干活。
  • 浏览器里生成音乐:一个小的 Web Audio 引擎为每首曲目渲染一段约 60 秒的 lo-fi / 氛围器乐,调式、音阶、速度、音色各不相同。曲目来自一个精选的虚构唱片库,每条包含标题、艺人和氛围。全程没有受版权保护的音频;也留了一个有文档说明的位置,可以放你自己的 CC0 mp3。
  • 像真的广播信号:Kokoro 的声音要过一遍 AM 带通滤波(300 Hz–3.4 kHz)、轻度压缩、磁带饱和、板式混响,底下一直铺着黑胶噼啪声,听起来就像从电波里传出来的。DJ 还会压着每首歌的前奏说话。
  • 不冷场:当前唱片还在放,下一段串词就已经在生成了;来电有延迟,先用预先缓存的“拖延”台词顶上;万一某次模型调用失败,还有预制片段让节目继续。
  • 一台能上手操作的收音机:拟物化的木纹琥珀色机箱,有可拖动的调谐旋钮、随音频跳动的 VU 表、ON-AIR 电子管、转动的磁带盘、正在播放铭牌,还有一键通话的听筒(空格 = 说话,Esc = 挂断)。支持减少动态效果设置,也带字幕。

架构​

Plain Text
Browser (this HF Space, free CPU) Modal (one GPU app, scale-to-zero)
---------------------------------- ----------------------------------
Show loop (state machine): @modal.asgi_app (gpu=T4)
break (DJ segments) -> song -> repeat /asr faster-whisper small -> text
Generative music engine (Web Audio) /brain MiniCPM5-1B GGUF -> {text,mood,arc_cue}
Broadcast audio chain + crackle bed /speak Kokoro am_michael -> {audio,words,times}
Now-playing plate · sonic logo · VU
| fetch (same-origin /api/* ONLY)
| GET /api/songs server-side proxy (FastAPI):
| POST /api/segment {kind, ctx} builds host-persona prompts (arc.py)
| POST /api/call {audio_b64} calls /brain then /speak; sanitizes text
v holds MODAL_URL + proxy token in Secrets
Space server (FastAPI + Gradio) browser NEVER sees Modal creds
  • 收音机界面是一个自包含的 HTML/CSS/JS 文档,挂在 / 根路径下。放在顶层,麦克风和自动播放才能正常工作。另外在 /gradio 挂了一个极简的 Gradio Blocks,这样它才算一个真正的 Gradio 应用。
  • DJ 始终是同一个人设:温暖、风趣(arc.py: build_host_prompt)。节目结构由应用掌控,模型只负责把当前这一段说出来。
  • 片段类型有 station_id、thought、weather、dedication、song_intro。随想和歌曲介绍由大模型写,其余来自 content.py 的模板,最后都通过 /speak 朗读。

技术栈​

层选型
大脑openbmb/MiniCPM5-1B GGUF(Q4_K_M),通过 llama.cpp 运行
语音识别faster-whisper small(CPU,int8)
语音合成Kokoro-82M,音色 am_michael,带词级时间戳
音乐浏览器内 Web Audio 生成式引擎(无模型、无采样)
算力一台 Modal T4,min_containers=0,可缩容到零
前端自包含的拟物化收音机(HTML/CSS/JS),真实的 Web Audio 处理链
编排免费 CPU 的 Hugging Face Gradio Space(Docker SDK,FastAPI + gr.Blocks)

配置(Space Secrets)​

Secret含义
MODAL_URL已部署的 Modal asgi_app 基础 URL。
MODAL_KEYModal 代理认证 key,放在 Modal-Key 请求头里发送。
MODAL_SECRETModal 代理认证 secret,放在 Modal-Secret 请求头里发送。
NIGHTWAVE_MOCK设为 1,或者不设置 MODAL_URL,界面会用预制数据和静音 WAV 跑起来,不需要 Modal 后端。
NIGHTWAVE_TRACE设为 1 时,把只有内容的节目 trace(片段 + 来电,不含音频和个人信息)写进 traces/session.jsonl,用于公开 trace 数据集。

API(同源,浏览器只调这些)​

  • GET /api/songs → 精选歌曲库(驱动音乐引擎和正在播放铭牌)。
  • POST /api/segment {kind, ctx} → {text, mood, arc_cue, audio_b64, words, wtimes},一段 DJ 片段。
  • POST /api/call {audio_b64} → DJ 的现场回答(以及来电者的转写文本)。
  • POST /api/seek / GET /api/stalls → 拍摄用的跳转,以及预缓存的延迟填充片段。

本地开发​

Bash
pip install -r requirements.txt
NIGHTWAVE_MOCK=1 python devserver.py # full radio + /api/* in mock mode, no Modal
# open http://localhost:7860
pytest tests/ # arc / content / brain-contract unit tests

在 HF Spaces 上,容器跑 uvicorn app:app,在 / 挂着收音机界面和同源的 /api/* 代理,并在 /gradio 挂载 Gradio。

位置与隐私​

如果你允许浏览器获取位置,NIGHTWAVE 只会把坐标发给 Space,用来查你所在地的天气、时间和城市(走 Open-Meteo 和 BigDataCloud),好让 DJ 聊聊你头顶真实的夜空。坐标不会被存储,也不会写进日志,可选的 trace 里最多出现解析出的城市名。要是拒绝授权,电台就给你播报那些不存在的小镇的天气。

相关链接​

项目信息

基于模型
MiniCPM5-1B
作者
ratandeep