# Just for Fun:我們叫一個 Coding Agent 去做 VOCALOID,然後事情失控了 ## 一份包含 IDA、MCP、IA、Win32 彈窗、樂理、俠客、夏夜、雨、蟬與「還是不夠 Rock」的總回顧 **English edition:** [Just for Fun: We Told a Coding Agent to Make VOCALOID Songs, and Things Got Out of Hand](just-for-fun-we-taught-a-coding-agent-to-make-vocaloid-songs.en.md) > 一開始真的只是 just for fun。 我們有一套 VOCALOID3、一個 IA/IA_ROCKS 聲庫、一個跑在 WSL 裡的 coding agent,還有一個非常不負責任但很有誘惑力的問題: > 能不能讓 Codex 自己做一首 Vocaloid 歌? 如果把「做歌」偷偷定義成輸出 MIDI,這件事一個下午就能結束。 可惜我們沒有。 我們想要的是:原生 VSQX、日文歌詞、phoneme、IA 的聲音、伴奏、分軌、混音、mastering、真正的 VOCALOID3 Editor export、最後能打開的 project,以及「為什麼這一句要這樣唱」的完整記錄。 於是這個 fun project 很快變成: ```text TypeScript MCP server + 32-bit native Windows helper + Win32 UI automation + Yamaha schema / native loader + canonical song database + music-theory analyzer + Japanese mora allocator + audio production graph + creative-decision hash chain + several gigabytes of local experiments + many, many Save As dialogs ``` 最後我們確實做出了完整歌曲。 也確實學到了一個更好玩的答案:**讓 agent 把歌做完,可以工程化;讓它穩定寫出神曲,目前不行。** --- ## 第一幕:不要拿 MIDI 糊弄我 VOCALOID performance 不只是 note number 和 duration。 它還有: - lyric; - language-specific phoneme; - singer/voicebank component; - consonant timing; - accent、decay、opening; - bend、portamento; - vibrato type/length/depth/rate; - DYN、BRE、BRI、CLE、GEN、POR、PIT、PBS; - part、pre-measure、mixer、singer events。 所以第一條規矩是:不能從一個現成 VSQX 偷 template,再換幾顆音就說「AI 作曲」。 Agent 必須從空白建立 native project。 第一個小勝利長這樣: ![VOCALOID3 接受 MCP 從空白建立的 native score](../docs/assets/vocaloid-native-score-smoke.png) 畫面非常樸素:三顆音、合法 IA_ROCKS singer、Editor 願意打開。 但在那之前,我們已經: - 讀了 VOCALOID3 manual; - 讀了 VSQ3/VSQ4 XSD; - 用 IDA 看 32-bit DLL; - 寫了 native loader probe; - 搞清楚 MSVC 2008 ABI; - 走訪 Yamaha object tree; - 誤會過一次 phoneme `4 a`; - 最後才發現 `4` 其實是官方日文 flap/tap phoneme。 軟體工程有時候就是這樣:你花半天懷疑一個數字,manual 用一行告訴你「它本來就合法」。 ## 第二幕:我們差點直接調 VOCALOID 的 synthesis DLL IDA 裡可以看到一條很迷人的路: ```text VSQX → musical part → Daisy MIDI buffer → CRtSynthesis → DSE3 → 256-sample render frames → WAV ``` 我們甚至走到了: - load VSQX; - create time map; - encode Daisy events; - load DSCL3/DSE3; - resolve synthesis exports。 然後引擎在 voice database/registration boundary 回了 `-100`。 這時有兩種選擇: 1. 繼續往 activation/license 深處鑽; 2. 停下來,使用本來就合法擁有完整聲庫 context 的 original Editor。 我們選了第二種。 這不是「沒 reverse 成功」。這是 reverse engineering 最有價值的產物之一:知道哪裡應該停,而且能說明為什麼 production architecture 要往上移一層。 ## 第三幕:古老桌面軟體是 final boss VOCALOID3 Editor 不是 API server。 它會: - 開 modal dialog; - 等 Save As filename; - 記住或忘記上一個 path; - export 很久; - 在你以為它關完時還沒關完; - 偶爾在 cleanup 階段直接 access violation。 ![一次非常 VOCALOID3 的 cleanup crash](../docs/assets/vocaloid3-legacy-crash.png) 因此 UI automation 最後被當成 transaction protocol: ```text fingerprint editor → acquire host-wide lease → launch exact project → verify singer / dialog state → invoke Export Wave → discover fresh Save dialog → validate parent + filename → submit → wait with progress-aware timeout → verify WAV structure + non-silence + digest → close only the owned process → release lease ``` 「看到檔案出現」不算成功。 Partial export、zero-byte output、silent WAV、wrong singer fallback、stale dialog handle,都必須 fail closed。 某次 export 每次都卡在最後,原因真的只是 Save As 沒有 filename。 另一次彈窗說路徑不存在,因為 legacy app 看見了一個奇怪的 Windows path。 這些 bug 沒有一個跟作曲有關,但它們可以讓一首已經寫完的歌永遠出不來。 ## 第四幕:從 XML writer 長成一個小型 production system VSQX 很適合交給 Editor,卻不適合當 agent 的唯一 database。 它不自然保存: - arrangement renderer choices; - effect chains; - artifact digests; - review notes; - dependency graph; - mutation history; - creative intent; -「這個 A/B 到底在比什麼」。 所以 `.song.json` 變成 canonical source of truth。 每次 mutation 都有: - monotonic revision; - stable object IDs; - `composition_hash`; - `mix_hash`; - `content_hash`; - parent hash; - dry-run; - atomic write/backup; - artifact provenance。 VSQX、backing、stems、mix、analysis、mastered WAV 都是 derived artifacts。 這聽起來很不像「just for fun」,但它帶來一個非常 fun 的能力:agent 可以只改副歌的一句,不必重送三千顆 events,也不會默默讓舊 WAV 冒充現在的歌。 ## 第五幕:第一批真的能從頭到尾跑完的歌 在 composer-intent 階段之前,pipeline 已經完成三種 IA_ROCKS style closure: | 作品 | 方向 | 時長 | | --- | --- | ---: | | `花色日和` | 和風 Rock | 4:16 | | `火花の航路` | 疾走 J-Rock | 2:06 | | `夏の夜に浮かぶ声` | Summer-night Dream Pop | 3:18 | 它們都有 lead/harmony、多樂器、stems、mix、master、LUFS/LRA/true peak/clipping QC,以及 matching VSQX。 這個 checkpoint 證明「一首完整的歌可以被 agent-native pipeline 交付」。 然後我們第一次很認真地問: > 所以它到底想唱什麼? ## 第六幕:工程師退後一步,composer 才能出現 接下來的核心句子是: > 不再問「它能不能唱」,而開始問——「我希望 IA 在這一句裡,究竟唱出什麼?」 這句話導致 repository 多出: - composition intent; - section state transition; - anchor phrase direction; - listening question; - deterministic music theory; - bounded repair proposal; - phrase grammar; - 20–30 秒 audition ladder; - creative decision journal; - intent/composition/mix 三種 freshness; - declared/materialized/rendered/heard 四種 evidence。 一句話的創作流程變成: ```text 它為什麼存在? → 它在句子裡要改變什麼? → 哪個詞是 semantic focus? → melody / duration / harmony 怎麼服務它? → IA 要怎麼咬字、發亮、收回去? → 哪些伴奏要進,哪些必須讓位? → solo / harmony / minimal band 聽起來真的成立嗎? ``` Theory analyzer 可以告訴你 crossing、breath、register crowding、chord conflict。 它不能告訴你一句話是否令人心碎。 這個限制不是 TODO,而是 contract。 ## 第七幕:本地有很多 IA 歌,然後我們決定 production agent 一首都不能看 硬碟裡有數百首 IA 曲目。 一開始我們想做一個很完整的 corpus profile:tempo、form、harmony、F0、vocal performance、aggregate ranges、novelty audit。 再想一下就會發現危險:如果 agent 能查 nearest melody、下一個音或 per-track contour,這個「style system」遲早會滑向 imitation system。 最後我們做了一個看起來很反直覺、其實很自由的決定: ```text production agent 看不到 corpus。 ``` 曲庫只可以離線幫 maintainer 發現「我們的 phrase vocabulary 少了一種 broadly useful choice」。抽象後的 DSL 必須: - human reviewed; - source identity free; - non-reconstructive; - 沒有 melody/chord/lyric sequence; - 沒有 next-note hint。 最後 agent 看到的是 `delayed entry`、`late peak`、`semantic breath`、`modal pivot`,而不是「照某首歌的第 27 秒做」。 ## 第八幕:旋律真的會長成樓梯 Cold-start agent 寫完 `遠い灯、青い夜` 後,我們把 VSQX 打開。 ![Rise, fall, rise, fall:一眼就看懂的 phrase habit](../docs/assets/formulaic-rise-fall-contour.png) 它不是亂寫。 它甚至很工整。 問題正是太工整:上揚、下降、上揚、下降;所有 phrase 從同一位置開始;97.9% 都是小級進。 於是 phrase-grammar DSL 誕生了。 但我們特別禁止 DSL compile melody。因為如果修復「agent 太模板」的方法是「給它更多模板」,那只是把模板庫做大。 DSL 只能問: - 這句是 statement 還是 answer? - downbeat、pickup、delayed 還是 offbeat? - arch、terrace、plateau 還是 asymmetric wave? - semantic breath 在哪? - 是 varied repeat 還是 fragment? 最後的音仍然必須由 agent 自己寫。 ## 第九幕:原來「有 tuning intent」和「VSQX 有 tuning」是兩件事 一次 audit 發現 manifest 寫著: - dynamics arc; - opening arc; - focus vibrato; - pitch fall-away。 但 final VSQX 沒有相應 controls。 這就像 production note 寫「主唱最後一句要顫一下」,然後錄音時沒人做。 所以 evidence 被拆成: ```text declared → materialized → rendered → heard ``` `apply_tuning` compiler 必須真的把 gesture 變成 note styles 和 PIT/PBS/DYN/BRI 等 native data。沒有 materialization provenance,build 直接停。 更好笑的是:在那些 fancy controls 真的進去以前,那版歌已經比更早的版本好聽。 因為真正起作用的是歌詞、旋律輪廓、mora 時值、和聲、留白和克制配器。 原來 composition 不能被 automation 假裝。 ## 第十幕:新 Agent 真的能接手嗎? 我們開了全新的 `gpt-5.6-sol`/`xhigh` session,禁止它看曲庫、舊歌、舊 compose script。 第一個 intent 是兩個俠客在懸崖擦肩而過。 它寫出 `風は名を呼ばない`,讓 IA 成為沒有名字的山風: - original IA; - 3:10; - 68 bars; - 433 vocal events; - 23 decisions; - 真實 Editor render。 框架成功 transfer。 同時 agent 也成功撞出:timeout、dead lease、Save As、journal retry、false phrase-entry warning、endpoint trend limitation 等一整排 bug。 這很理想。好的 benchmark 不只是 pass/fail;它會把下一批 engineering work 指出來。 ## 第十一幕:固定 prompt + 單獨 intent file 為了讓下一個 session 不必靠聊天歷史,我們把創作入口固定成: ```text prompts/vocaloid-composition-agent.md + intents/.intent.json ``` Intent file 可以直接指定: ```json "tempo_bpm": { "min": 180, "max": 200, "preferred": 190 } ``` 也可以指定 exact voicebank component、duration range、genre、must-avoid、listener question 和 source boundary。 Agent 讀的不是上一個 agent 的心情,而是一份 strict、可驗證、可 hash 的 contract。 ## 第十二幕:夏夜、雨、蟬鳴、IA_ROCKS,190 BPM! 最後一個實驗故意把 BPM 拉到 180–200,voice 指定 IA_ROCKS,genre 寫 Rock。 新 session 交付 `雨と蝉のあいだ`: - 190 BPM; - 48 bars; - 64.4 s; - 287 vocal notes; - drums、bass、rhythm guitar、rain guitar、cicada pulse; - 24 hash-chained decisions; - valid VSQ3; - original Editor render; - -16.3 LUFS; - -1.2 dBTP; - no clipping。 ![雨と蝉のあいだ arrangement map](../docs/assets/ame-semi-no-aida-arrangement-map.svg) 它的 waveform 看起來非常像一首「完成了的歌」: ![Final stereo waveform](../docs/assets/ame-semi-no-aida-waveform.png) Spectrogram 也很熱鬧: ![Final spectrogram](../docs/assets/ame-semi-no-aida-spectrogram.jpg) 然後人耳說: > IA 被配樂蓋完了。 以及: > 還是比較平淡,沒有做出 Rock 的感覺。 完美。 不是歌曲完美,是實驗完美。 因為這兩句 feedback 把「工程完成」與「音樂完成」切得非常乾淨。 ## 第十三幕:100 passed tests 不能和耳朵吵架 Final repository test 結果是: ```text 102 tests 100 passed 2 skipped by environment 0 failed ``` 歌曲也確實: - valid; - current; - non-silent; - unclipped; - hash-verified; - reproducible; - documented。 但如果 listener 聽不到 IA,test suite 不會跳出來說「其實 vocal presence metric 是綠色的」。 如果聽起來不像 Rock,`primary_genre: rock` 也不會改變聲音。 這是整個專案最喜歡的一條規則: > Metrics 可以反駁錯誤的技術猜測,不能否決真實的 listening experience。 ## Boss fight ledger 我們遇過的 boss 包括: | Boss | 掉落物 | | --- | --- | | `4 a` 被誤判非法 | manual-backed phoneme regression | | Direct DSE `-100` | 清楚的 licensing boundary | | VOCALOID cleanup crash | owned-process teardown protocol | | Save As 沒 filename | fresh-dialog retry + path diagnostics | | Export timeout | progress-aware 900 s client | | Dead Editor lease | PID/liveness-aware reclaim | | IA/IA_ROCKS probe digest 相同 | singer identity collision guard | | Tuning intent 沒進 VSQX | materialization compiler + preflight blocker | | 旋律每句都 rise/fall | phrase grammar + concentration analyzer | | D Dorian C 被叫 leading tone | semitone-aware modal rule | | Sparse harmony 被判 46 crossings | overlap-weighted voice ordering | | F0 抓第二泛音 | honest open limitation | | Mastering 聽起來更尖 | equal-loudness A/B + transparency metrics | | IA 被伴奏蓋住 | 一個還沒有被自動化解決的真正 mix 問題 | | 190 BPM 仍不像 Rock | 一個還沒有被工程化解決的真正 music 問題 | ## 所以 Agent 現在到底算什麼? 它已經是一個很不錯的: - composer assistant; - arrangement hypothesis generator; - Japanese note-allocation worker; - production engineer; - test operator; - A/B machine; - obsessive archivist; - 半夜可以自己盯 VOCALOID export 的隊友。 它還不是: - taste oracle; - Rock band; - mixing engineer 的耳朵; - producer 的文化記憶; - hit-song compiler。 ## Repository 裡留下什麼 我們不把所有 WAV 塞進 Git。 ```text artifacts/songs/ local-only final listening WAVs artifacts/evals/ local-only stems / A-B / debug / traces build/ local-only working state artifacts/vsqxs/ tracked native projects artifacts/creative-records/ tracked manifests / journals / reports / digests notes/ tracked contracts and failure evidence blogs/ tracked long-form story ``` 這個選擇讓 public repo 既能證明做過什麼,又不會變成一個 3 GB 音訊垃圾車。 ## 如果你也想玩 最小入口: ```bash npm install npm test npm run build ``` 生成一份固定 workflow + intent 的 agent prompt: ```bash npm run --silent compose:prompt -- --intent=intents/template.intent.json ``` 你需要自己的合法 VOCALOID3 Editor 和 voicebank 才能做 original-Editor synthesis。Repository 不含聲庫、license data 或 final WAV。 然後,最重要的是準備一句你真的想聽 IA 唱出來的話。 不是「給我一首 Rock」。 而是: > 這一句出現以前,世界是什麼狀態?IA 唱完以後,什麼東西應該改變? 這個問題比 BPM 難多了。 也好玩多了。 --- ## Ending 整個專案從 IDA 裡的 32-bit vtable,一路走到 IA 在 190 BPM 夏夜裡唱雨和蟬。 中間有 native loader、UI crash、hard-left pan、silent fallback、LUFS、Dorian、俠客、山風、Save As、hash chain,以及很多「哭了,沒聽出來 XD」。 如果一開始問我們想得到什麼,答案大概是:「讓 agent 做一首 Vocaloid 歌,just for fun。」 現在答案變成: > 我們做出了一台很認真、很可審計、會自己撞 bug、會記錄每個決策、偶爾也真的能寫出有感覺片段的 Vocaloid production machine。 它還不會穩定寫神曲。 但它已經非常適合和人一起玩音樂。 而且說真的——這已經夠 fun 了。