
Agogic 这个方向最值得关注的不是“让 LLM 输出一段 MIDI”而是它把 Performance-Timed Music Tokens 引入 LLM-Native Text-to-Symbolic-Music Generation 的流程让“文本生成符号音乐”从过去只关心音高和时值推进到还能表达真实演奏中的时间偏移。简单说以前模型能告诉你“该弹哪个音、弹多长”Agogic 这类思路还想告诉模型“这个音在真实演奏里会稍微提前或延后乐句之间怎么呼吸”。适合正在做 AI 音乐工具、作曲辅助、游戏音频、音乐教育产品或者刚接触 LLM 应用开发的人看。原始标题没有附带可运行仓库和具体版本所以下面按设计思路拆解再补一套落地时要复核的环境、验证和排错路径。1. 先搞清楚它做的是“文本到符号音乐”不是“文本到音频”1.1 符号音乐到底是什么符号音乐可以理解为用结构化文本或标准数据格式描述的音乐。它不是直接记录声音波形而是记录“哪个音、在哪个节拍、持续多久、力度多少、属于哪个声部、用什么调号和拍号”。常见格式包括 ABC Notation、MusicXML、MIDI 文件以及各种 MIDI 事件序列。文本生成符号音乐就是让模型根据自然语言描述或乐谱提示直接产出这类结构化内容。例如输入“一段 8 小节、4/4 拍、C 大调、钢琴抒情旋律”模型输出一段 MusicXML 或 MIDI 事件流。它和文本生成音频是两种任务。音频生成要处理采样率、波形、音色、混响、人声口型输出通常不容易逐音编辑符号音乐生成的输出是乐谱层面的信息拿到后可以在打谱软件、DAW、游戏引擎里继续调整。很多做 AI 音乐产品的人会把这两件事混在一起判断。如果产品需要的是最终听感、人声、音色质感那就不能只看符号音乐生成如果产品核心是“用户输入一句描述快速得到可编辑的旋律和伴奏”符号音乐生成更实际。Agogic 标题里明确写了 Text-to-Symbolic-Music Generation定位就在符号音乐这一层。1.2 为什么 LLM 适合做符号音乐生成LLM 本质上是在学习 token 序列的联合概率分布。音乐符号天然可以 token 化。一个 MIDI 事件可以拆成音高、时值、力度、起始时间等字段每个字段对应一个或几个 token一串事件组合起来就形成类似自然语言句子结构的序列。对比波形和频谱符号音乐的离散化和压缩要容易得多所以 LLM 原生处理符号音乐有天然优势。“LLM-Native”这个说法意思是不要把 LLM 只当辅助工具而是直接用 LLM 生成完整音乐序列。这种方案的现实基础是任务长度可控。一段两分钟钢琴曲的 MIDI 事件即使按较细的 tick 量化通常也只有几百到几千个事件。这个规模在当前 LLM 上下文窗口内是可以接受的比直接处理几秒钟音频的 token 少得多。工程落地时也更容易设计缓存、批量和接口协议。1.3 传统序列模型和 LLM-Native 的差异传统方案里生成符号音乐通常用专门的 LSTM 或 Transformer decoder 输出 MIDI 事件流。它们也能做到音高基本正确、节奏基本稳定但很少能表达人类演奏中的细微时机偏移。普通 MIDI token 会把音符开始时间、时长、力度分开编码时间信息一般只精确到固定的八分或十六分音符。结果是生成内容听起来所有音都很整齐像节拍器带出来的缺少乐句感。Performance-Timed Music Tokens 试图把时间信息做成更细粒度、与演奏上下文有关的 token。它不只要描述“这个音持续多久”还要描述“这个音实际什么时候触发、比标准网格早多少或晚多少”。这个差异对听感影响非常大同样的音符序列早几毫秒、晚几毫秒再叠加力度起伏听感就会从“机械演奏”变成“像人弹的”。Agogic 在音乐术语里本来指速度处理的细微变化所以整套设计方向很明确把表演层面的时序信息注入 token而不是生成之后再统一调速。2. Performance-Timed Music Tokens 的核心设计思路2.1 一个可承载表演计时的 token 结构标题没有给出具体 token 规范但从命名可以推断一个音符 token 至少要把“音符是什么”和“这个音符在真实计时里怎么触发”放在一起。我一般会从三层信息去理解这种设计。信息层包含内容作用音高层pitch、note number决定旋律与和声基础时值层nominal duration、division决定乐谱级节奏表演计时层onset offset、agogic shift、micro timing决定真实演奏听感一个示意性的 token 序列可以长这样{n:60, d:240, s:1920, v:80, a:8} {n:62, d:360, s:2168, v:72, a:-6} {n:64, d:240, s:2530, v:85, a:12}这里 n 是音高d 是标准时值s 是绝对起始 tickv 是力度a 是相对标准网格的演奏偏移。a 为正表示稍微延后a 为负表示稍微提前。这只是一个说明概念用的伪 token不是真实实现。重点不是字段名而是 token 要能同时表达乐谱时值和真实时间。如果方案真的采用类似结构模型需要学习的就不只是旋律衔接还包括“在哪些位置人类会让音符稍微迟到或提前”。这类模式通常和乐句起伏、和声张力、力度变化有关只有从精细对齐的演奏数据里才能学出来。2.2 为什么不能靠后处理统一调速一个很自然的想法是先让 LLM 生成规整 MIDI再用后处理给整首歌加一个速度包络模拟渐快渐慢。但这个方案从根本上解决不了局部 timing 问题。真人演奏中的时间偏移是局部的不是全曲统一变化。举几个常见现象乐句末尾的长音通常会稍微延长前一个音可能略微提前一点给呼吸留空间和弦转换时不同声部之间可能出现极小的错开快速跑动段落里演奏者可能略微放宽节奏而不是机械匀速。这些偏移都是按乐句、按声部、按和声张力变化的。后处理统一缩放只能改变整体速度无法单独控制每个音符的偏移。我实测时还会遇到另一种误用有人给每个音符随机加偏移结果听起来像所有音都走不稳而不是像人弹的。原因就是偏移没有上下文逻辑缺少乐句方向的渐进变化。Performance-Timed token 的价值是让 timing 成为模型预测目标的一部分让模型学会上下文相关的 timing 模式而不是生成后做随机抖动。2.3 tick 分辨率和序列长度的取舍如果想把开始时间精确到毫秒级马上会遇到一个问题token 序列变长训练数据里也会出现大量低频边界值。所以这类系统一般不会直接输出“精确到 1ms 的绝对时间”而是用相对标准网格的偏移表示偏移量并做离散化。几种常见的起点先按八分或十六分音符建立标准网格所有 timing 偏移都相对于网格。偏移量用 0、±1、±2 等少量离散档位表示而不是连续数值。保留绝对 tick但降低全局分辨率比如每个 tick 代表 5ms 或 10ms。分辨率越细表达力越强但学习难度和序列长度也会增加。复现类似方案时我会建议先统计训练数据里的真实演奏偏移分布看偏移集中在什么范围、有没有明显的双峰结构再决定离散档位。不要凭空拍一个偏移上限也不要一开始就追求最细精度。很多情况下8 到 16 个偏移档位已经能带来明显的听感差异。3. 复现或落地这类系统环境至少要准备到什么程度3.1 从 Demo 到训练的硬件差异原始标题没有附带可运行仓库所以要区分“跑 Demo”和“训练或微调模型”。如果只是复现一个已训练好的生成模型通常需要一块能跑 LLM 推理的 GPU显存 16GB 以上比较稳妥。显存不够时可以试试小模型或量化版本但要注意音乐 token 的输出是否会变乱。我遇到过低显存环境跑量化模型生成普通文本问题不大但生成音乐时开始时间错位、时值非法的比例增加。这通常不是量化本身的问题而是 tokenizer 映射、采样参数和输出长度控制没有对齐。如果要微调或从头训练门槛会明显更高。训练数据是大量带节拍对齐和 timing 标注的 MIDI 演奏文件数据清洗、对齐、验证需要单独做即便只做 LoRA 微调也需要稳定可用的 GPU 资源。对刚开始接触的人不建议直接全参数训练。先找现成模型跑推理用小规模数据集验证 token 设计和输出效果再决定要不要进入训练阶段。3.2 音乐数据怎么准备这类模型依赖的数据不是普通 MIDI 文件而是带有演奏时间和人类表演信息的 MIDI 数据。原始 MIDI 里能读出每个 note on 和 note off 的绝对 tick但读不出“这个音相对标准节拍偏移了多少”。所以数据准备的第一步是节拍对齐根据 BPM 和拍号把 MIDI 事件映射到标准网格上再计算每个事件的偏差。这个环节有几个常见的坑。MIDI 的 tick 精度在不同软件里不一样有的用 480 PPQ有的用 960 PPQ处理前要统一。部分 MIDI 文件实际上是被量化过的没有任何 timing 偏移对训练帮助不大。多轨 MIDI 要区分哪种轨道是真实演奏录制哪种是人工输入的打击乐或贝斯线它们的 timing 特征不一样。数据里不能只留钢琴独奏否则模型只能学会一种声部的 timing 习惯换到弦乐或吉他时表现会很不稳定。我一般会先写脚本统计每轨的平均偏移、偏移标准差、音符密度把偏移过大或过于机械的文件过滤掉再做 token 化。这一步如果省掉模型学到的 timing 特征会很混乱。输出质量不好时不要先怀疑模型结构先检查数据过滤逻辑。3.3 LLM 推理框架和输入输出格式符号音乐 token 化之后本质上是一段文本序列所以可以复用常见 LLM 推理框架做部署。但实际工程中要关心的不是框架本身多快而是几个和音乐任务强相关的点模型输入是完整 prompt 加音乐 token 前缀输出需要解析成可播放的 MIDI 或 MusicXML。输出序列可能很长要设置合理的 max_new_tokens避免中途截断。批量推理时不同样本输出长度差异很大要注意 padding 和截断策略。如果通过 API 提供服务要定义好输入输出格式比如 JSON 里包含风格、BPM、调号、小节数、输出格式。这些点听起来很工程但实际决定系统能不能在真实产品里用起来。单独一个模型能生成好听的片段和整个服务能稳定返回合法乐谱是两件事。项目早期就要把输入输出格式定清楚否则后面每个环节都要返工。4. 生成结果怎么验证不能只看“有没有旋律”4.1 第一层格式合法性拿到模型输出不要先试听先解析。最基础的问题是输出能不能被 MIDI 解析器或 MusicXML 解析器成功读取。我经常遇到的情况是旋律听着还行但输出里包含音高越界、时值负数、起始时间早于上一事件结束等非法字段。这类问题在训练和推理时不明显只有到了文件转换阶段才会爆发。建议做一个自动校验脚本至少检查这些项目检查项判断标准音高范围是否在 MIDI 0-127 或程序限定范围内时值是否合法是否大于 0且不超过设定上限起始时间是否单调同一轨道的声部事件是否随顺序递增小节边界音符是否跨过预期小节边界声部数量是否超出预期轨道或声部上限只要有一项不通过就不要直接进入试听。试听可能掩盖格式问题后续编辑会很痛苦。4.2 第二层音乐结构合理性格式合法只是开始。接下来要看结构是不是只有那么几个音在来回转节奏型是否单一有没有形成乐句。可以用程序统计几个指标。音高种类和分布范围能看出旋律有没有在合理音域内流动时值分布能看出节奏是否僵硬。如果所有音都是同一时值大概率模型退化成固定节奏发生器。相邻音高间隔也要留意连续大跳比例过高听感会非常不自然。乐句长度和段落边界也要看有没有明确的断句还是整段内容像一条线拉到底。如果这些指标都不正常优先检查 Prompt 和训练数据而不是急着调温度。音乐模型退化时采样参数只能稍微缓解不能根治。4.3 第三层演奏计时是否真的生效这是 Agogic 类方案最核心的验证点。生成结果里如果所有起始 tick 都正好落在标准网格上偏移量全是 0说明模型没有真正学到 performance-timed 信息或者后处理阶段把偏移抹掉了。观察方法很简单用 MIDI 工具查看 note-on 事件的时间戳计算它们与标准网格的偏差。正常的人类演奏片段偏差不是均匀随机噪声而是有趋势的。乐句开始通常比较稳定句中有轻微加速长音结束前可能有延后声部之间会有微小错开。如果你看到偏移杂乱无章说明模型只是模仿了抖动没有学到上下文规律。更严格一点的验证是把生成结果分别导出为“保留 timing 偏移”和“抹掉 timing 偏移”两个版本做对比试听。如果两个版本听感差异很小说明 performance-timed 信息还没有真正进入模型输出如果差异明显但不自然则需要调整偏移离散化范围和训练数据质量。5. 调参时最容易出问题的几个点5.1 温度、Top-p 和重复惩罚文本生成里的常规采样参数用到符号音乐生成上要更谨慎。温度太高音高会乱跳时值会不稳定温度太低会不断重复同一个乐句。音乐序列本来就有天然的重复性模型很容易生成连续好几小节几乎一样的材料。我建议先固定一组起点temperature 在 0.6 到 0.9 之间Top-p 在 0.9 附近重复惩罚不要开太大。音乐需要一定重复来形成主题又需要变化来推进。关键是先定义清楚“成功长什么样”。如果目标是单段动机温度可以偏低如果目标是整首作品就要允许更大多样性。5.2 上下文长度、段落生成和截断一首完整钢琴曲的 token 数量往往超过普通推理服务的默认限制。常见处理方式是分段生成先定好总长度和段落结构每次只生成一个段落并把上一段结尾作为下一段的上下文。这里最容易踩的坑是截断。如果你直接把长序列从头硬截到模型窗口内后半段会丢失主题和风格。更好的做法是保留最近 N 个 token 作为条件上下文同时把表示调性、速度、风格的固定信息放在 prompt 最前面。也就是说上下文不是简单把历史序列往窗口里塞而是“固定控制信息 最近音乐事件”的组合。如果生成长片段时出现中途跑调或风格突变先看是不是上下文窗口里前面的控制信息被挤出窗口了。这个问题在音乐生成里很常见比模型能力不足更容易导致失败。5.3 推理精度、批量化与并发符号音乐生成对推理精度的敏感度比很多人想象的低。FP16 和 BF16 在大部分情况下都能稳定工作FP32 会明显增加显存和带宽压力但不一定带来更好的音乐结构。如果输出出现大量非法 token不要第一时间怀疑精度先看 tokenizer 映射、采样参数和输出限制。只有你确认 FP16/BF16 下同一 prompt 多次生成结果都异常时才值得切 FP32 做对比。批量生成时先做小批量压力测试。我一般会先开 2 到 4 个并发观察显存、延迟和失败率再逐步增加。不要一上来就开几十个并发。符号音乐输出长度差异大有的请求几十个 token有的几百个并发一高容易造成尾部延迟抖动和显存溢出。要按 token 数或预期输出时长做限流而不是只看请求数。5.4 把音乐生成接进 Agent 或 API 时要注意什么现在很多 LLM 应用会走 Agent 编排把工具调用、RAG、外部 API 串起来。音乐生成也可以作为一个子工具暴露出去但设计上要注意几件事。子工具输入要有明确字段比如风格、长度、调号、速度、乐器、期望输出格式。子工具输出必须是结构化数据不能返回一大段自然语言。调用方需要做格式校验失败时返回可读的错误信息。生成结果如果很长不要一次性塞回对话上下文建议保存到文件或对象存储只返回引用地址。这不是性能问题而是稳定性问题。把音乐生成塞进 Agent 流程之后日志、超时、重试都会变成关键点。没有这些单个 Demo 看着酷连续跑一天就会暴露问题。6. 从一句文字描述到可播放乐谱的最小闭环6.1 Prompt 怎么设计要让模型输出规范的音乐符号Prompt 里至少需要写下乐器、风格、速度、调号、拍号、结构意图、输出格式。比如一句很含糊的“来一段悲伤钢琴”模型会自由发挥输出结构通常不稳定。比这稍微结构化的 Prompt 可以这样写用钢琴生成一段 8 小节、4/4 拍、C 大调、速度 72 BPM 的旋律。 风格偏抒情第二句在第一句基础上变化最后回到主音。 输出格式为 MusicXML不要解释。注意不要指望一次 Prompt 就得到完美结果。实际流程里Prompt 包含的风格词要和训练数据分布一致。你可以准备几种典型风格模板再把用户输入映射到模板上。6.2 输出校验与格式转换模型输出的字符串需要先解析成内部事件结构再转换成 MusicXML 或 MIDI。转换环节不能直接把模型输出写入文件因为格式错误会导致打谱软件打不开。比较稳妥的闭环顺序是请求模型生成限定输出格式。用解析器把输出拆成事件列表。做格式合法性和结构合理性校验。校验通过后转成 MusicXML 或 MIDI 文件。校验失败时自动重试一次并修正 Prompt 或截断输出。如果自动重试两次仍然失败不要无限尝试。记录日志暴露给调用方处理。这样既方便定位问题也避免浪费算力。6.3 多段拼接与风格一致性多段生成时最关键的问题是段落衔接。你不能每段都用全新采样否则第一段 C 大调第二段可能滑到别的调上。常见做法是每个段落生成时把上一段最后 2 到 4 个小节作为前缀上下文。固定调号、拍号、速度参数不让模型自己改。段落之间保留少量重叠音符在后期拼接时做重叠抵消或交叉淡化。我建议先做 8 小节的单段生成跑通后再尝试 16 小节的多段拼接。多段拼接一旦出现问题优先检查段落边界的事件是重叠还是断档多数问题都出在边界。7. 真正落地时容易被忽略的边界7.1 Performance-Timed 不是随机加偏移最容易产生的误解是把 performance timing 等同于随机化。很多人在后处理里给每个音符加乱数偏移结果听起来像走不稳而不是像人弹的。真实演奏的 timing 偏移有方向、有上下文、有乐句逻辑。渐强处可能微加速长音前会有自然延后和弦底部与旋律音之间也存在微小错开。这些模式必须从数据里学不能用随机参数模拟。衡量方案好坏不是看有没有偏移字段而是看偏移字段有没有被模型真正预测出来以及预测结果是否和乐句、力度、和声变化相关。7.2 Demo 能跑不等于能批量单条生成跑通和连续生成 100 条不出错是完全不同的工作量。批量任务需要关注输出命名、失败重试、队列管理、磁盘清理和日志。如果你只是本地学习默认单条流程就够如果要部署成服务优先把任务队列和错误日志设计好。一个非常实际的经验批量跑之前先把输出目录和文件命名规则定好。很多模型生成的失败案例不是因为模型输出为空而是因为重试时把文件覆盖了或日志里没有记录是哪条输入对应的哪条输出。7.3 符号转换是有损的MIDI、MusicXML、ABC 之间的转换并不是无损的。MusicXML 能表达一些乐谱记号但复杂踏板、滑音、装饰音转成 MIDI 后会丢失MIDI 能记录绝对 timing但转成 MusicXML 时可能被重新量化。如果你的产品链路包含多种格式要明确一个事实无论在哪一步转换都可能产生信息损失。最稳妥的方式是以模型输出的原始事件结构为基准。所有格式转换都从事件结构出发而不是从 MIDI 转 MusicXML、再从 MusicXML 转回 MIDI。来回转换很容易把 performance timing 信息抹掉。7.4 数据来源要合规训练数据如果来自公开 MIDI 数据集需要核实授权范围。自行爬取或转换受版权保护的音乐去训练会带来不小的合规风险。这一点在项目起步时容易被忽略但一旦产品化就绕不开。如果做的是个人实验也尽量选择明确开放授权的数据集并在文档里记录来源和授权信息。这样做既可以保证后续可以继续使用也方便别人复核你的实验结果。真正的落地经验是先把单条生成、格式校验、试听对比这条主链路跑稳再扩展批量、接口和 Agent 编排。很多音乐生成效果不稳定的问题根源不在模型音乐感而在输入格式没有规范化、输出没有校验、timing 偏移没有真正进入 token 空间。Agogic 这个方向最有价值的提醒就是音乐生成不能只盯音高要把时间当作一等公民去建模。