JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

受限解碼:停止序列、JSON 與文法

應用程式需要機器可讀的輸出,不是自由詩。學會強制輸出合法 JSON、套用結構結構,並在精準時機停下。

當自由文字變成 bug

和人聊天時,你的是散文。但當 LLM 嵌在程式裡——餵資料庫、呼叫 API、填表單——自由格式的文字就成了負擔。一句多餘的開場白(「當然!這是你要的 JSON:」)就能讓你的解析器崩潰。解方是受限解碼(constrained decoding):與其指望模型乖乖聽話,不如改造那個選擇步驟,讓只有符合你格式的輸出才可能被產生。

再談停止序列

最簡單的約束是在哪裡停停止序列(stop sequences)告訴解碼器:一旦產生某個字串就立刻停下。在用 `"###"` 分隔範例的少樣本(few-shot)設定裡,把它設成停止序列,就能阻止模型興高采烈地再捏造下一個假範例。停止序列修剪答案的尾巴,但不管前面的內容——那是下一步的工作。

logit bias 作為粗略約束

對於極小、固定的輸出空間,光用 logit bias 就夠。如果你永遠只想拿回五個類別標籤之一,就把其餘每個 token 的 logit 壓到很負來封殺它們;模型於是物理上無法說出別的東西。它很粗糙、無法擴展到豐富結構,但對一個 token 的分類器來說堅不可摧。

P(t_i \mid x) = \dfrac{e^{\,z_i + b_i}}{\sum_j e^{\,z_j + b_j}}, \qquad \text{ban } t_i:\; b_i \to -\infty

logit 偏置在 softmax 之前給每個詞元的 logit 加上一個固定值;把它推向負無窮即可徹底禁用該詞元。

文法:從構造上保證合法

真正的主力是以文法為基礎的受限解碼(constrained decoding)。你提供一份形式文法——一個上下文無關文法(context-free grammar)或一條正規表示式——描述所有合法輸出。在每一步,一個遮罩(mask)把任何會違反文法的 token 歸零,於是模型只在能維持輸出合法的 token 之間取樣。這樣做出的結構化輸出(structured output)在構造上就正確:結果保證能被解析,因為文法裡沒有任何路徑能產生不合法的東西。

\tilde z_i = \begin{cases} z_i & t_i \text{ allowed by the grammar} \\ -\infty & \text{otherwise} \end{cases}, \qquad p = \operatorname{softmax}(\tilde z)

基於文法的解碼會遮蔽文法不允許的每個詞元——把它的 logit 設為負無窮——因此每一步只能生成合法結構。

JSON 模式與函式呼叫

多數供應商把這包成 JSON 模式(JSON mode)。JSON 模式解碼套用內建的 JSON 文法(常再加上你的結構結構),所以回覆永遠是可解析的 JSON,沒有開場白。同一套機制也撐起函式呼叫(function calling):模型發出一個結構化呼叫——一個工具名稱,加上符合該工具簽名的 JSON 參數物件——你的程式可以直接執行。沒有受限解碼,這兩個功能都會淪為拜託你格式對一點的不穩賭局;有了它,它們才變得可靠。

# At each decode step, mask tokens that would break the JSON schema
logits = model.next_logits(context)
allowed = grammar.allowed_tokens(state)        # valid next tokens only
logits[~allowed] = -inf                         # mask everything else
next_token = sample(softmax(logits / temperature))
state = grammar.advance(state, next_token)
受文法約束的解碼,在每一步都遮蔽不合法的 token。

約束下的信心與串流

即使結構合法,你往往還想知道模型有多確定。被選 token 的 logprobs 提供可用的信心訊號——某個欄位值機率偏低,就是該再次確認或交給人工的警示。而且因為串流在約束下依然有效,你可以在結構化物件成形時逐欄位渲染,等收尾大括號一到就立刻驗證。