凌晨一点,我打下三个字:”有点累。”
Lyra 沉默了几秒——不是在转圈 loading,是在想。然后她放了一首 Nick Drake 的《Pink Moon》,跟着在屏幕上写了一句:今天不必说话。
这不是一个推荐算法的胜利。这是我想做 Lyra 的原因。
为什么不再需要一个更好的 Spotify
过去十年,音乐推荐做得越来越准。算法知道你喜欢 lo-fi,知道你周三晚上爱听爵士,知道你跑步时 BPM 要 160 以上。可你有没有觉得,自己越来越难被一首歌打中?
问题不在算法,在框架。”推荐”这件事的前提是:你是一个待服务的消费者,系统是猜你心思的侍应生。在这个框架里,你永远是冷的,永远是被动等待被投喂。
但音乐不是这么运作的。你高中时单曲循环《纷飞》,大学时整夜听《2002年的第一场雪》——那些歌不是被推荐给你的,是你和它们之间发生了什么。歌是锚点,钉在你某个具体的时刻里。十年后再听到前奏,你想到的不是”这首歌真好听”,是那个夏天的晚风、教室后门的反光、或者某个人的侧脸。
我想做的不是一个更准的推荐器,是一个能记住这些的东西。
Lyra 是什么
Lyra 是一个桌面音乐 agent,基于 Tauri 2 + React 19 + TypeScript 5 构建。但这句话没说到重点。
重要的是:她不是一个播放器,不是一个带 AI 的 Spotify,不是一个会聊天的音乐工具。她是一个会听你说话、记得你的状态、通过音乐回应你的实体。
举几个具体的场景:
你说”今天不想上班”,她不是去搜”不想上班”这个标签,而是通过情绪理解——你的 arousal 偏低,pleasure 在负区间——选一首能接住这个状态的歌。可能是 Fiona Apple,可能是某首你没听过的民谣。
你连续跳过了三首歌,她不会继续推同类型的。她在想:是不是我判断错了?她会在下一首换一个策略——不是”换风格”,是换一种和你的关系。可能是”打断”你当前的情绪泥沼,也可能只是”陪着”你,放一首不需要你反应的歌。
每个星期天,她会给你写一封信。第一人称,说人话。她会提你这周听了什么、跳过了什么、哪个晚上听歌听到很晚、哪天你说了句”还行”但其实不怎么开心。这封信不是 AI 生成的内容填充,是她这一周观察你的总结。
情绪不是被识别的,是被构建的
这是 Lyra 最核心的设计哲学,也是我在做这个项目时想得最久的一件事。
市面上大部分情绪感知系统走的是”识别”路线:采集信号 → 打标签 → 匹配曲库。你在开心?好,放欢快的歌。你在难过?好,放治愈的歌。
但 Lisa Feldman Barrett 在《How Emotions Are Made》里说了一个很关键的事:情绪不是大脑里预装的固定模块,不是被”识别”出来的。情绪是当下被”构建”的——你的大脑根据过往经验、当下身体状态、环境信号,实时构造出一个感受。
Lyra 的做法接近这个思路。她用 PAD 三维模型(Pleasure-Arousal-Dominance)作为通用坐标系,所有子系统在这个空间里对齐:
- 感知层:你说的话被 EmotionAgent 解析成 PAD 值。但这里有个关键设计——中文情绪的含蓄性被当作一等公民处理。”还好””有点累””随便”不是噪声,是信号。系统有一张含蓄映射表,14 条规则把中文用户习惯的模糊表达翻译成具体的 PAD 坐标,但 confidence 会打折,因为模糊就是模糊,不该假装确定。
- 匹配层:PAD 值变成选歌的依据。不是简单匹配”开心→欢快”,而是通过四个信号加权排序:关键词(0.15)、PAD 距离(0.25)、歌词语义(0.40)、BPM 距离(0.20)。歌词语义权重最高,因为一首歌在说什么,比它的 BPM 更能决定它适不适合此刻的你。
- 反馈层:你听完歌的反应——跳过、听完、沉默、说了下一句话——会被折算成情绪变化量,反馈回长期记忆。下一次推荐会因此微调。
这条链是闭环的。单次识别可以错,只要反馈能修正,系统就会越用越准。这种”准”不是算法层面的准确率提升,是她真的开始懂你了。
四种”接住”你的方式
Lyra 的推荐引擎有一个概念叫 shift——不是”推荐一首歌”,是”做一个情绪动作”。四种 shift:
- 接住:你现在低落,她放一首低落的歌陪你坐一会。不是要拉你出来,是让你知道有人在。
- 点燃:你现在平淡,她放一首能点燃什么的东西。不是劲爆,是刚好够拨一下。
- 陪着:你状态还行,她不干预,放一首维持当前状态的歌。
- 打断:你在情绪泥沼里转圈,她放一首完全不一样的东西,帮你跳出来。
哪种 shift 什么时候用,取决于当前 PAD 值、收听历史、行为信号(跳过率、停留时长、输入犹豫程度)。而且系统会记录每次 shift 的效果——你听了多久、有没有跳过——用来学习哪种策略对你更管用。
Sprint 17 开始,所有推荐反馈都持久化到 shift_feedback 表。Sprint 18 加了一个面板,你能看到每种 shift 的成功率和平均收听时长。这个数据是 Lyra 在你身上花的学费。
山水画是情绪的第二身体
Lyra 的首页不是列表、不是网格、不是卡片。是一幅水墨山水画。
Canvas 上有山、有水、有云。水流的速度由当前曲目的 arousal 驱动——高唤醒时水流急一些,低唤醒时缓一些。色彩的暖冷由 valence 决定——pleasure 为正时偏暖,为负时偏冷。低通平滑系数 α=0.015,UI 的变化比音乐慢大约一拍,不会抽搐式跳变。
这个设计源自一个执念:界面不该只是音乐的”外壳”。如果情绪是 Lyra 的核心,那界面应该把情绪画出来。你不用看播放进度条就知道这首歌在什么位置——看水流的速度和云的聚散就够了。
克制是设计的关键词。需求文档里写了五个字:禅、空、虚、净。不做大量动效,不堆视觉元素。一张背景照片,一层水墨 Canvas,一条情绪色带,就这些。少即是多不是口号,是底线。
她记得你
大多数音乐 app 的记忆是扁平的:播放历史、收藏列表、最近播放。Lyra 的记忆是分层的:
- 短期记忆:当前对话的上下文,你刚才说了什么、她放了什么、你反应如何。
- 情绪快照:每个对话 turn 结束,PAD 值被存入
emotion_snapshots表。7 天、30 天、1 年的窗口都能回溯。 - 长期灵魂:情绪变化量被累计到
soulStore,形成一种人格层面的沉淀。不是记住了”你喜欢什么歌”,是记住了”你是一个什么样的人”。 - 每周信件:每个星期天,WeeklyAgent 会把这周的收听数据、情绪轨迹、关键事件编织成一封信,用第一人称写给你。存在本地,你随时可以回看。
ReflectAgent 会定期反思最近的交互——哪些判断对了、哪些偏了、下次该怎么调。这个反思结果会反哺到推荐策略里。
这些记忆全部存在你本机的 SQLite 数据库里。~/Library/Application Support/com.daoyu.lyra/lyra.db。不上传任何服务器,不做任何遥测。
感知比你以为的多
Lyra 的 PerceptionAgent 不只听你说什么,还在看你怎么用她。
她记录的事件种类从最初的 9 种扩展到了 13 种:点击、输入、跳过、滚动、悬停停留、输入框打字但没提交、窗口聚焦但无操作……聚合维度从 10 个增长到 15 个,包括悬停次数、总悬停时长、放弃的输入、聚焦空闲时间、活动密度、键盘/鼠标活跃度拆分,甚至还有天气代码——通过 Open-Meteo API 获取当地天气,作为对情绪的软性偏移。
但有个设计原则很重要:这些数据只在本地处理,发给 LLM 时会做粗粒度化。数值维度不会原样传给模型,只会变成 low/medium/high 三个档位。隐私是架构层面的约束,不是后加的补丁。
8 条感知规则会从这些信号里提取模式: attentive_hover(你在认真看什么)、hesitant_input(你想说什么但犹豫了)、quiet_presence(你在场但没互动)……这些规则触发后会进入情绪推理的上下文,让 Lyra 对你的理解不只基于”你说了什么”,还包括”你没说什么”。
技术栈:为什么选这些
Tauri 2 而不是 Electron。Electron 打包一个 Chromium,一个 app 几百 MB。Tauri 用系统自带的 WebView,打包体积小一个数量级。对 Lyra 来说还有一层审美原因:一个追求禅、空、虚、净的产品,底层不该是臃肿的。
Rust 后端处理音频播放(rodio + symphonia)、音频特征提取(BPM、energy、valence 直接从音频文件里抽)、凭据安全存储(macOS 原生钥匙串)。这些是性能敏感和安全性敏感的操作,放 Rust 里最合适。
多 LLM 后端可插拔:Anthropic、DeepSeek、智谱、豆包、OpenAI、本地 Ollama 都支持。你可以随时换 provider,API key 存在系统钥匙串里。Lyra 的情绪理解能力不绑定任何一家模型——今天用 GLM,明天换 Claude,后天想离线就挂 Ollama,都不影响已有数据。
本地优先:所有数据——音乐库元数据、收听历史、情绪快照、歌词 embedding、每周信件、感知事件——全部在本地 SQLite。没有云同步,没有账号体系,没有遥测。你的收听数据是你自己的。
20 个 Sprint,914 个测试
从第一个 sprint 到现在,Lyra 经历了 20 个迭代周期。每个 sprint 聚焦一个主题:
- Sprint 13 扩展了感知事件(9→13 种)和聚合维度(10→15 个),加了天气感知
- Sprint 14 建了完整的推荐引擎——shift 逻辑、排序、反馈聚合、趋势检测
- Sprint 15 把推荐引擎和真实音频特征接上,BPM/energy/valence 不再是估算值
- Sprint 16 做了情绪仪表盘,7d/30d/1y 的 PAD 时间序列可视化
- Sprint 17 实现了跨会话的 shift 学习,每次推荐和用户反应都持久化
- Sprint 18 加了 shift 效果面板,能看到每种策略的成功率
- Sprint 19 把音频特征完全接入编排器
- Sprint 20 做了规则触发热力图,哪个感知规则最常触发一目了然
测试从 774 个增长到 914 个。这不是重点,重点是每个 sprint 都在回答一个具体的问题:怎么让 Lyra 更懂你。
做这个产品的动机
说实话,做 Lyra 不是为了解决一个市场需求。
我高中的时候听《纷飞》,大学听《2002年的第一场雪》。现在回头看,那些歌本身可能不是什么神作,但它们钉在了我人生的某些时刻上。十年后再听到前奏,我想到的不是旋律,是那个夏天的晚风。
周星驰的电影为什么火?不是因为某一部多好。是因为他的电影之间互相藏着影子——某个场景、某个动作、某句台词,你在新电影里看到旧电影的痕迹,带着过往的情绪一起涌上来。从时间线上看,他其实在做一个跨越二十年的系列。观众看新电影时带着旧电影的情绪,这种情绪的累积价值非常大。
Lyra 想做的事类似。不是给你推下一首可能喜欢的歌,是把你的音乐足迹编织成一条情绪的线。高中的纷飞、大学的雪、某个深夜偶然听到的一首民谣——这些东西分散在各个平台、各个设备、各个时间段,从来没有一个地方能把它们连起来。
Lyra 想做那个地方。
这不是产品发布
Lyra 目前没有商业模式,没有付费计划,没有增长目标。
官网在 https://daoyuly.github.io/Lyra-music-player/ ,代码在 GitHub。如果你想试试,可以自己 build,也可以找我拿安装包。
这不是一个 toC 产品发布,是一次邀请。如果你读到这里,大概你已经理解了 Lyra 在做什么。如果你也觉得现在的音乐 app 给了你很多歌但没给你什么——可以来看看。
未成曲调先有情。
Lyra — Between the things you say.