Sherry's Blog


  • 首页

  • 关于我

  • 标签

  • 目录

  • 吐槽

  • 文章列表

当年的周杰伦们也在抄,但他们抄得用心,不是AI时代粗制滥造的音乐垃圾

发表于 2026-04-14 | 分类于 娱乐

初中的时候听 S.H.E,高中的时候听周杰伦。

那时候没有手机,电脑也不能随便用,信息来源就是电视、杂志、偶尔一张 CD。《晴天》第一次从收音机里传出来的时候,我觉得这个人一定是个天才——故事不多 我只知道那些,那种少年心事,一句话就全说完了。

后来我一直带着这个滤镜。觉得周杰伦、林俊杰、田馥甄、许嵩这些人,是真正意义上的创作者,是那个时代华语乐坛的神。

直到互联网告诉我:很多事情我以前根本不知道。


我们以为的”原创”

我们那代人对”写一首歌”的理解,大概是这样的:

坐在钢琴前,灵感来了,旋律从脑子里涌出来,填上词,一首歌就诞生了。这是”创作”,是天才才能做到的事。

但实际上,音乐工业里大量的”创作”是另一种方式:找一首日本歌、欧美老歌,买版权或者打擦边球,把旋律改一改,换上中文歌词,发行。

这不违法,很多时候是正规授权的。但它跟我们想象的”全部原创”差距很远。

周华健的《花心》,原曲是冲绳民谣歌手喜纳昌吉的《花》。任贤齐的《伤心太平洋》,旋律来自中岛美雪。张学友、王菲翻唱改编日语歌的作品更是数不清。

这些在行业里叫”翻唱”或”改编授权”,属于公开操作,没什么不可说的。但在那个年代,大陆的普通听众没有出过国,没有渠道接触日本流行乐,更不知道欧洲有哪些民谣——所以这些信息对我们来说是隐形的。

我们听的时候,真心以为这是原创。


信息差才是护城河

港台艺人比大陆听众早十年、二十年接触海外音乐。他们知道日本在流行什么,知道欧美有哪些好旋律可以”借鉴”。把这些东西搬进华语市场,配上本地化的中文词,就是一首”原创新歌”。

大陆观众不知道源头在哪,自然觉得他们天才。

这不是阴谋论,这是产业逻辑。信息差就是他们的护城河。只要大陆听众没有渠道听到日文原曲,这道护城河就能一直存在。

护城河消失的时间节点很清晰:宽带普及,YouTube 可以翻墙,Bilibili 上开始有人做”扒源”视频——然后一切都变了。

“哦,原来这首歌是翻唱。”
“哦,原来这段旋律来自这里。”
“哦,原来他没有我以为的那么天才。”

滤镜不是一下子碎的,是一点一点被互联网磨掉的。


作曲没有那么神秘

我们以前觉得写旋律是天才的事,因为我们以为它是从零开始的纯粹创造。

但作曲是有章法的。和弦进行、曲式结构、旋律走向——这些都是可以学的技术。流行音乐里反复出现的和弦套路(比如 1645、1564)你随便听几首歌就能感受到。

真正难的不是”写一段过得去的旋律”,而是”写出一段让人一辈子记住的旋律”。这个确实需要天赋,不是所有人都能做到。

但”写一首合格的流行歌”,其实没有我们想象的那么高不可攀。尤其是在可以合法借鉴、在前人旋律上做改编的情况下,门槛更低了一截。

我们高估了”作曲”的难度,是因为我们以为所有的旋律都是凭空想出来的。


那感动是假的吗

说到这里,可能有人会问:你高中被《晴天》感动,现在知道那段旋律有借鉴成分,那你当时的感动是假的吗?

不是假的。那是真实的情感。

但问题是,那种感动很大程度上来自两件事:

一是那个年代的青春本身——高压的学习环境,暗恋,说不清楚的情绪,需要一个出口。周杰伦的歌恰好在那个时候出现,它是那段岁月的容器,不管旋律从哪来。

二是稀缺性。那时候好听的歌不多,一首《晴天》能反复听一年。现在一天能刷到一百首不同的歌,再好听也停不下来。

我们怀念的,与其说是周杰伦,不如说是那时候容易被打动的自己,和那时候信息稀缺造就的珍贵感。


那个时代真正难以复制的是什么

说了这么多,我并不是要否定那一代人的价值。

他们真正难以复制的,不是”天才旋律”,而是另外几件事:

本地化的能力。方文山的词、林夕的词,是真正的文学。把一首日文原曲的情感用中文重新表达,甚至表达得更好——这是实实在在的创作,不是搬运。

华语流行的工业体系。港台当时的唱片工业、录音室、制作人积累,比大陆成熟很多年。这不是信息差,是产业差。

放大器。电视台、唱片公司、杂志,那个年代的造星机器效率极高。一首歌能在几亿人里同步传播,现在这件事再也做不到了——不是因为没有好歌,而是因为放大器坏了,流量分散了,没有人能成为所有人共同的背景音乐。


写完这篇之后:我去让 AI 给我推荐音乐

这篇写完后我一直在想一件事:周杰伦那一代至少是”抄得用心”——编曲、人声、呼吸、mix,都有人在认真做。那现在呢?AI 时代的音乐是什么样的?

今天早上刚醒,想听点轻松的,打开豆包,进了它的”汽水音乐”。prompt 就是默认的”随便来点音乐”——一个刚睡醒的人能给出的最中性、最无攻击性的输入。

结果推给我的第一首歌的主题离谱到我现在都没缓过来。不是”不合口味”的那种离谱,是”这首歌怎么能出现在一个面向大众的推荐系统里”的那种离谱。我有截图,这里不贴——具体内容不重要,重要的是:一个默认 prompt、一个刚睡醒的用户,被精准投喂了极其 toxic 的东西。

这不是正常的算法和正常的人类能想出来的主题和歌曲。

我的第一反应是卸载豆包。第二反应是想写点什么。

别误会,AI 生成音乐本身是能做好的

我自己用 Suno 生成过歌,不管是主题还是曲调都还不错。你可以输入一个心情、一个场景、一段歌词,它给你返回一首结构完整、旋律合理、人声自然的歌。不惊艳,但及格。

所以问题不是”AI 生成的音乐一定很烂”。Suno 证明了 AI 作曲的下限其实不低。

那豆包推给我的那首是从哪来的?为什么会差到那种程度,还能出现在”默认推荐”里?

我怀疑的两件事

豆包的”汽水音乐”推荐系统,很可能在做两件事:

第一,用户的听歌行为是在给 AI 生成的歌打反馈标签。 哪些歌被听完、被跳过、被循环、被收藏——这些数据是训练 AI 音乐模型最值钱的标注。人类听完一首歌的行为数据,比任何人工标注都准。大量用户被塞一堆低质量 AI 生成的歌,字节拿到的是一大批”人类偏好信号”。换句话说:你是数据,不是听众。

第二,注意力算法直接从抖音平移过来。字节过去十年把”怎么让人停留得更久”这件事打磨到了极致。这套算法从短视频迁移到音乐不需要额外创新:推荐逻辑不变,只是内容载体从视频变成了音频。你听到的不是”适合你的歌”,是”能让你继续停留在 app 里的音频”。

这两件事单独看都可以不是恶意的。但叠加——用你做训练数据 + 用算法把你留住——结果就是:推荐池里充斥着低质量的 AI 生成歌,算法还会从里面挑”看起来能勾住你”的那些推给你。我今天早上被精准投喂的那首,大概就是算法的某种”探索性投放”。

为什么豆包的语音做得那么精细

顺着这条线想下去还能解释另一件事:为什么豆包的语音部分做得那么讲究?音色、呼吸声、语气、方言切换,每一项都在花大钱打磨。但它的事实准确度、推理能力、音乐审美呢?都是勉强合格。

因为语音是沉浸的入口。抖音十年经验告诉字节一件事:只要能让用户的感官停留够久,商业化后面都有办法。文字不够、视频不够,语音还要调呼吸、模拟情绪起伏、加方言——目标从来不是”帮你完成任务”,是”让你离不开”。

它要的不是你的满意,是你的时间。

我已经卸载了豆包。


最后

现在回头看那些所谓的”天王时代”,我觉得像看一个黄毛——当年觉得他酷,后来发现其实也就那样,光环是信息不对称给的,不全是真功夫。

但从周杰伦写到豆包,我自己也意识到另一件事:哪怕是抄,那个年代的人也是认真在做作品。编曲层次、人声处理、反复调 mix——你能听出来背后有人在花心思。他们的动机是”让人记住一首歌”。

今天的 AI 音乐推荐呢?动机是”让人停留在 app 里”。这两件事的产物在形态上都叫”音乐”,但它们不是同一个东西。

我也不想全盘否定对那个年代的记忆。那是真实发生过的感动,只是感动的来源比我以为的更复杂一点。

我们崇拜的从来不全是天才,有一半是信息不对称,有一半是那时候容易被感动的自己。

这两件事,AI 时代都回不去了。只是回不去的原因,比我一开始想的还要更悲观一点——不是”没有好歌了”,是”做音乐的动机变了”。

claude-mem:用一个Claude监视另一个Claude,值得吗?

发表于 2026-04-14 | 分类于 技术 , agent , claude-code

claude-mem:用一个Claude监视另一个Claude,值得吗?

最近在调研AI编程助手的记忆持久化方案时,看到了claude-mem这个项目。它试图解决一个真实的痛点:Claude Code每次会话结束后丢失所有上下文。但仔细研究源码后,我发现它的架构设计存在一个根本性的矛盾——为了记住东西,它付出的代价可能比遗忘本身更贵。

claude-mem做了什么

一句话概括:它在你用Claude Code编码的同时,启动一个并行的Claude子进程作为”观察者”,实时监视你的每一步操作,将观察结果压缩成结构化记录,存入SQLite和ChromaDB,下次开会话时自动注入相关历史上下文。

架构长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
用户编码会话(主Claude进程)
│
├─ SessionStart Hook → 注入历史上下文
├─ UserPromptSubmit → 初始化会话,启动观察者
├─ PostToolUse Hook → 每次工具调用都捕获
├─ PreToolUse(Read) → 读文件前注入该文件的历史
├─ Stop Hook → 生成会话摘要
└─ SessionEnd Hook → 完成会话生命周期
│
▼
Worker服务(port 37777)
│
├─ SDK Agent(观察者Claude子进程)
│ → 接收工具调用数据
│ → 生成XML结构化观察
│ → 严格只读,禁用全部12个工具
│
├─ SQLite(原始存储 + FTS5全文搜索)
└─ ChromaDB(向量语义搜索)

亮点在哪

公平地说,这个项目有几个设计是不错的:

1. Observer-Only的安全边界

观察者Agent显式禁用了全部12个工具(Bash、Read、Write、Edit、Grep、Glob等),确保记忆Agent不会对你的项目产生任何副作用。这是防止”Agent套娃”失控的关键:

1
2
3
4
5
6
// SDKAgent.ts:55-68
const disallowedTools = [
'Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob',
'WebFetch', 'WebSearch', 'Task', 'NotebookEdit',
'AskUserQuestion', 'TodoWrite'
];

2. 渐进式信息披露

查询端的3层搜索设计确实精巧:

  • search → 紧凑索引(~50-100 tokens/条)
  • timeline → 时间线上下文
  • get_observations → 完整详情(~500-1000 tokens/条)

先过滤再获取细节,查询时能省约10倍token。

3. 优雅降级

所有Hook在Worker不可用时返回exit 0,永远不阻塞用户的编码流程。隐私标签(<private>、<system-reminder>等)在Hook层就被剥离,数据进入Worker之前已经过滤。

4. 多IDE适配器模式

通过PlatformAdapter抽象,用一个适配器接口支持了Claude Code、Cursor、Gemini CLI、Windsurf、Codex CLI等六个平台。新增IDE只需要写一个适配器。

核心问题:写入端是个token黑洞

claude-mem宣传的”10x token节省”只是查询端的故事。但写入端呢?

看SDKAgent.ts的核心流程:每次你在Claude Code里执行一个工具调用(Bash、Read、Write、Edit、Grep……),PostToolUse Hook就会把tool_name、tool_input、tool_response全部捕获,JSON序列化后发给观察者Claude子进程:

1
2
3
4
5
6
7
8
// prompts.ts:114-123
return `<observed_from_primary_session>
<what_happened>${obs.tool_name}</what_happened>
<parameters>${JSON.stringify(toolInput, null, 2)}</parameters>
<outcome>${JSON.stringify(toolOutput, null, 2)}</outcome>
</observed_from_primary_session>

Return either one or more <observation>...</observation> blocks, or an empty response...`;

每一次工具调用都是一次API请求。一个正常的编码会话可能有几十上百次工具调用。

更要命的是,SDK Agent保持会话上下文,这意味着:

  • 第1次观察:input tokens = 系统prompt + 观察1
  • 第2次观察:input tokens = 系统prompt + 观察1 + 响应1 + 观察2
  • 第N次观察:input tokens = 系统prompt + 全部历史 + 观察N

Input tokens线性增长。它自己也知道这是个问题,所以代码里做了token追踪:

1
2
3
4
5
6
// SDKAgent.ts:210-232
session.cumulativeInputTokens += usage.input_tokens || 0;
session.cumulativeOutputTokens += usage.output_tokens || 0;
if (usage.cache_creation_input_tokens) {
session.cumulativeInputTokens += usage.cache_creation_input_tokens;
}

还把discoveryTokens存进了每条observation——说明作者知道这是个ROI问题。

具体成本估算

假设一个中等活跃的编码会话:80次工具调用,每次观察prompt约2000 tokens,观察者响应约500 tokens。

阶段 Token消耗
Init prompt ~1500
80次观察(累积上下文) ~1500 + 2500×1 + 2500×2 + … + 2500×79 ≈ 7.9M input
80次响应 ~40K output
Summary ~3K
总计 ~8M tokens

这还没算ChromaDB那边的embedding成本(虽然chroma-mcp用本地模型,但也要CPU/内存)。

为了”记住”这个会话做了什么,你可能额外花了8M tokens。而下次查询时省的那点token,可能只有几千。

对比:graphify的做法

graphify试图解决类似的”理解代码库”问题,但写入端的设计思路完全不同:

维度 claude-mem graphify
代码提取 全靠Claude做语义压缩 tree-sitter AST解析23种语言(免费)
写入触发 每次工具调用实时触发 一次性扫描,增量更新
音视频 不支持 faster-whisper本地转录(免费)
向量存储 ChromaDB + 额外embedding 不用向量,Leiden拓扑聚类
写入频率 O(n),n=工具调用次数 O(1),首次构建+增量更新
缓存策略 无(每次都调API) SHA256缓存,只处理变更文件

graphify的核心洞察是:代码的结构信息可以完全在本地提取。函数名、类定义、import关系、调用图——这些都是确定性的,tree-sitter解析就够了,不需要LLM。只有文档、图片这些非结构化内容才需要调Claude。

本质区别:graphify是”花一次钱建索引”,claude-mem是”每秒都在烧钱做笔记”。

如果让我重新设计

claude-mem的核心问题不是功能设计,而是在错误的层面使用了LLM。工具调用的输入输出本身就是结构化数据,用Claude去”理解”JSON格式的tool_input和tool_output,这是最大的浪费。

第一原则:能本地做的绝不调API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
用户编码会话
│
├─ PostToolUse Hook
│ │
│ ▼
│ ┌─────────────────────────────┐
│ │ 本地结构化提取(0 token) │
│ │ │
│ │ Bash → 命令 + 退出码 │
│ │ Read → 文件路径 + 行范围 │
│ │ Write → 文件路径 + 大小 │
│ │ Edit → 文件路径 + diff指纹 │
│ │ Grep → 搜索模式 + 匹配数 │
│ │ │
│ │ 全部是确定性的字符串解析 │
│ └───────────┬─────────────────┘
│ │
│ ▼
│ SQLite 原始事件流
│
├─ SessionEnd(会话结束时,一次性)
│ │
│ ▼
│ ┌─────────────────────────────┐
│ │ 批量语义压缩(花一次钱) │
│ │ │
│ │ 本地预压缩200个事件 │
│ │ → 几百token的紧凑描述 │
│ │ → 一次Claude调用做摘要 │
│ └───────────┬─────────────────┘
│ │
│ ▼
│ 结构化摘要存入DB
│
└─ SessionStart(下次会话开始)
│
▼
SQLite查询 → FTS5全文搜索 → 注入上下文

本地提取:工具调用本来就是结构化的

这是关键洞察。Claude Code的hook给你的数据已经是JSON了——tool_name、tool_input、tool_response都有明确的schema。用Claude去”理解”这些数据,就像用ChatGPT去解析一个已经格式化好的CSV。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function extractEvent(hookData: PostToolUseData): RawEvent {
const { tool_name, tool_input, tool_response } = hookData;

switch (tool_name) {
case 'Bash':
return {
type: 'command',
command: tool_input.command?.slice(0, 500),
exit_code: tool_response.exitCode,
output_lines: countLines(tool_response.stdout),
has_error: tool_response.exitCode !== 0,
};

case 'Read':
return {
type: 'file_read',
file: tool_input.file_path,
lines: `${tool_input.offset || 0}-${(tool_input.offset || 0) + (tool_input.limit || 0)}`,
};

case 'Edit':
return {
type: 'file_edit',
file: tool_input.file_path,
old_length: tool_input.old_string?.length,
new_length: tool_input.new_string?.length,
// 可选:用tree-sitter提取修改了哪个函数(本地,免费)
scope: detectScope(tool_input.file_path, tool_input.old_string),
};

case 'Write':
return {
type: 'file_create',
file: tool_input.file_path,
size: tool_input.content?.length,
};

// Grep/Glob 类似...
}
}

80次工具调用?80次字符串解析,0 token,< 10ms。

会话结束时的批量摘要:把200个事件压成一次API调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function summarizeSession(sessionId: string): Promise<Summary> {
const events = db.getEventsBySession(sessionId);

// 本地预压缩:200个事件 → 几百token的紧凑描述
const condensed = condenseEvents(events);
// 输出类似:
// "读了 src/auth.ts 3次,改了2次(函数 validateToken, refreshSession)
// 运行 npm test 5次,3次失败后2次成功
// 创建了 src/middleware/rate-limit.ts(新文件,约200行)
// 搜索了 'CORS' 相关代码,在3个文件中找到匹配"

// 一次Claude调用,输入可控(几百~几千token)
const summary = await claude.complete({
prompt: `基于以下编码活动,生成结构化摘要:\n${condensed}`,
max_tokens: 1000,
});

return parseSummary(summary);
}

同样80次工具调用的会话:

  • claude-mem当前设计:~8M tokens(80次API调用 + 上下文累积)
  • 我的设计:~3K tokens(1次API调用,输入是本地预压缩的几百token)

成本差距:约2600倍。

存储层:砍掉ChromaDB,SQLite一把梭

1
2
3
4
5
SQLite(唯一存储)
├── raw_events -- 原始工具事件(结构化,确定性提取)
├── session_summaries -- 会话结束时的AI摘要
├── file_index -- 文件路径→最近操作的倒排索引
└── FTS5虚拟表 -- 全文搜索(SQLite内置)

为什么不需要ChromaDB:

  • “我上次改了哪个文件”→ 文件路径精确查询
  • “上次处理CORS问题时做了什么”→ FTS5全文搜索
  • “这个项目最近的工作进展”→ 按时间排序取最近N条

这三类查询覆盖了跨会话记忆90%的使用场景,全部是SQLite原生能力。

如果真遇到需要语义理解的模糊查询,在查询时用Claude做rerank就够了——按需付费,不是预付费。一次查询花几百token做rerank,远比每次写入都做embedding划算。

查询时的按需处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户开始新会话
│
▼
SessionStart Hook
│
├─ 1. SQLite精确查询(免费,<10ms)
│ - 同项目最近N个会话摘要
│ - 当前文件相关的历史操作
│ - FTS5全文搜索匹配关键词
│
├─ 2. 结果足够?→ 直接注入,结束
│
└─ 3. 结果不够或需要语义匹配?
→ 此时才调Claude做rerank(按需)
→ 只在大项目+模糊查询时触发

设计哲学的本质差异

1
2
3
4
5
claude-mem的哲学:  实时观察,持续理解,先花钱后省钱
我的设计哲学: 延迟处理,批量压缩,不花钱直到必须花

claude-mem: Write-Heavy → 每步都调API,写入贵,查询便宜
我的设计: Read-Heavy → 写入免费,查询时按需付费

对于编码助手的记忆场景,大部分事件根本不需要语义理解。”改了哪个文件第几行”这种信息,字符串解析就够了。”这次会话整体在做什么”这种高层语义,会话结束时总结一次就够了。

不是每一步都值得被”理解”,但每一步都值得被”记录”。记录是廉价的,理解是昂贵的。好的架构应该把廉价的事情做到极致,把昂贵的事情推迟到不得不做的时候。

1234…24

54 日志
17 分类
44 标签
© 2026 Sherry