跳至主要內容
所有文章

把限制寫成程式,而不是寫進提示詞

要模型「每句七個字、句尾押韻」,它會回你一段看起來很像的東西。我們改成在程式裡數:字數逐句比對,韻腳按十三轍歸類,沒過的那幾句退回去重寫。這篇講這個迴圈怎麼做,以及路上撞到的一個 pinyin 錯誤。

閱讀約 3 分鐘分類工程產品

寫歌詞的人不會說「請盡量押韻」。他手上有一段旋律,旋律把每一句的長度直接交給他——這句六個字,下句九個字,副歌那句只有四個字。這不是風格偏好,是硬限制。

語言模型很擅長寫出看起來符合限制的東西。你要它每句七個字,它給你六個字、七個字、八個字混在一起的一段,讀起來還挺順的。你要它押韻,它給你「光」和「亮」——這兩個字在國語裡不押韻,江陽轍和遙條轍差得遠。

問題不在模型不夠強。問題在於,沒有人去數

先數,再談

我們在 詞光 LyricLab 裡把這件事拆成三步,中間那步是重點:

  1. 模型照著主題和句長寫一次。
  2. 程式逐句量:字數對不對、韻腳落在哪個韻轍。
  3. 沒過的句子——只有沒過的那幾句——連同「你差在哪」退回模型重寫。

第二步完全沒有模型參與。字數是 length,韻腳是查表。這代表出來的結果不是「模型宣稱它遵守了」,而是「我們數過了」。首頁那個 demo 顯示的狀態列也不是動畫效果,它報的是這個迴圈真的走到哪一步。

模型負責想句子,程式負責說它不算數。

這是整個功能唯一的設計原則

押韻不是比對尾字

最容易做錯的是韻腳。直覺做法是比對最後一個字的拼音字尾,但那會出錯得很難看。

國語的傳統做法是十三轍——十三個韻族,一個寫詞的人腦子裡本來就有這張表。同一轍就押,不同轍就不押,跟尾字長得像不像無關:

情況判定為什麼
昏 (hun) / 群 (qun)都在人辰轍
昏 (hun) / 孤 (gu)不押人辰 對 姑蘇
光 (guang) / 亮 (liang)不押江陽 對 遙條

所以 lib/rhyme.ts 做的是:取句尾那個漢字,用 pinyin-pro 拿到它的韻母,再把韻母映到十三轍其中一轍。聲調直接忽略,因為唱起來本來就不分。

撞到的那個 bug

pinyin-pro 在 n、l、j、q、x 後面會正確回報 ü。在 y 後面不會。

「魚」回來是 u,「月」回來是 ue。照單全收的話,「魚」會被歸進姑蘇轍,然後跟「孤」判定為押韻——這是錯的,而且錯得很安靜:沒有例外、沒有警告,只有一首歌裡某幾句莫名其妙地被放行。

這類錯誤是「在程式裡驗證」這個做法本身的成本。你把判斷從模型手上拿回來,就得為判斷的正確性負責。差別在於:程式錯了,你可以寫一個測試把它釘住;模型錯了,你只能再拜託它一次。

鄰韻是設定,不是含糊

實務上完全嚴格的同轍限制太緊,寫起來會卡。所以我們加了鄰韻:允許幾組聽感相近的韻轍互押。

重點是它是一個明確的開關,不是「差不多就好」。使用者選了鄰韻,系統就按鄰韻的規則檢查;沒選,就按同轍檢查。兩種情況下都是數出來的,只是尺不同。寬鬆可以是一個設定,但不能是一句謊。

字數容差也一樣:容差 ±1 是使用者選的,選了之後八個字的句子在七字限制下會過,九個字不會。

這樣做值得嗎

多一次往返、多一次計算、多一份要維護的韻表。換來的是:當畫面上寫著「每句七個字」的時候,那七個字是被數過的。

我們對其他產品也是同一條規矩。能量的就量,量不了的就不要寫在頁面上。

接著讀

心裡已經有想法了嗎?

說說你想做什麼,我們會老實告訴你,這件事適不適合交給我們。