以 DSL 約束 LLM:從介面設計看結構化輸出的可靠性工程
當大型語言模型(LLM)生成不穩定的 JSON 時,問題根源或許並非模型不夠聰明,而是我們給予的介面過於寬鬆。本文探討如何透過設計一個中介的領域特定語言(DSL),主動約束模型的輸出自由度,將驗證的複雜性從不確定的模型轉移到確定的解析器上。這不僅是一種技巧,更是一種提升 AI 系統可預測性與穩定性的可靠性工程思維。
當大型語言模型(LLM)在生成複雜 JSON 或結構化資料時,輸出不穩定是常見挑戰。這問題常被歸咎於模型能力,但核心癥結在於我們提供了過於寬鬆的「介面」。本文將從可靠性工程角度出發,提出透過設計一個中介的領域特定語言(DSL),主動約束 LLM 的輸出自由度。此策略能將驗證複雜性從不確定的模型轉移至確定的解析器,是提升 AI 系統可預測性與穩定性的關鍵工程思維。
為什麼直接要求 JSON 輸出是個不穩定的策略?
在許多應用中,我們需要 LLM 的輸出能直接被下游系統使用,例如呼叫 API、填寫表單或驅動 UI。JSON 因其通用性,成為最常見的目標格式。然而,即使是像 GPT-4 這樣頂尖的模型,在面對稍有深度的巢狀結構、嚴格的型別要求或大量的鍵值對時,也可能產生語法錯誤——多一個逗號、少一個括號,或是欄位名稱拼錯,都會導致整個輸出解析失敗。
這個問題的核心在於「自由度」過高。一個複雜的 JSON 結構,其語法組合的可能性是爆炸性的。對 LLM 而言,它不僅要理解語意,還必須在生成過程中,步步為營地遵循嚴格的語法規則。這就像要求一位作家在寫作時,同時確保每個標點符號都符合一套外部機器才能讀懂的複雜文法。這在機率性的生成模型中,本質上就是一個容易出錯的環節。
許多研究與工具,如 Microsoft 的 Guidance 或 Jsonformer,都試圖透過各種方式來解決這個問題,這也側面印證了其普遍性與難度。
我們習慣將失敗歸咎於模型,但從系統設計的角度看,這更像是一個介面設計問題。我們給予模型一個極度寬鬆、高自由度的介面(「請給我一個符合這個 schema 的 JSON」),卻期望它能回傳一個極度精確、低容錯的結果。這在工程實踐上,本身就埋下了不穩定的種子。
引入 DSL:一種約束介面的設計模式
與其在提示詞工程上無止盡地投入,一個更結構化的解法是引入中介層:設計一個領域特定語言(Domain-Specific Language, DSL)。這個 DSL 的目標不是取代 JSON,而是作為 LLM 與最終 JSON 之間的一座橋樑。它的設計原則是「對 LLM 友好,對機器可解析」。
最近看到一位日本開發者 sika7 提出的 StepArgsDSL 專案,就是這個思路的一個極佳實踐。它將複雜的 JSON 結構,轉換成一種更像函式呼叫的簡潔語法。例如,一個原本需要這樣表達的 JSON:
{
"action": "search_flights",
"parameters": {
"from": "TPE",
"to": "SFO",
"date": "2026-08-20",
"class": "economy"
}
}
在 DSL 中,可以被簡化為一行更直觀的表達式:
search_flights(from: "TPE", to: "SFO", date: "2026-08-20", class: "economy")
這種轉換帶來的好處是顯而易見的:
- 語法更簡單: 大幅減少了括號、引號、逗號等容易出錯的符號,降低了 LLM 的生成負擔。
- 自由度更低: 模型的任務從「生成一個結構任意複雜的 JSON」縮減為「生成一個或多個符合特定模式的函式呼叫字串」。
- 語意更清晰: 這種表達方式更接近自然語言中的指令,模型更容易理解其意圖。
LLM 的任務不再是直接生成最終的 JSON,而是生成這個中介 DSL 字串。接著,我們再用一段確定性的、自己完全掌控的程式碼(解析器)將這個 DSL 字串轉換成目標 JSON。Microsoft 發布的 TypeChat 專案也體現了類似的哲學,它讓模型生成的是程式碼的「意圖」,而非最終的精確語法,再由客戶端來驗證和轉換。
這如何體現可靠性工程的思維?
將 LLM 的輸出從 JSON 轉向 DSL,不僅僅是個取巧的技巧,它反映了深刻的可靠性工程(SRE)原則。在複雜系統中,我們總希望能將不確定性隔離在最小的範圍內。
核心思想是:將複雜性與驗證責任,從不確定、難以測試的元件(LLM),轉移到確定、可完全測試的元件(DSL 解析器)上。
LLM 本質上是個非確定性(non-deterministic)系統,我們無法保證它在兩次相同的輸入下,會產生完全相同的輸出。而我們自己編寫的 DSL 解析器,則是一個完全確定性(deterministic)的系統。只要 DSL 的語法規則被遵循,它總能產生穩定、可預期的 JSON 輸出。這是一種責任分離(Separation of Concerns)。
這種設計選擇,讓我們能夠為系統的穩定性建立更強的保障。當輸出發生錯誤時,問題的定位也變得更加簡單:是 LLM 未能生成合法的 DSL,還是我們的解析器有漏洞?這遠比除錯一個巨大且結構錯誤的 JSON 字串要容易得多。根據 Google SRE 的理念,可監控性(monitoring)與可除錯性是建構可靠服務的基石。DSL 方法顯著提升了這兩點。
當我們在建構基於 LLM 的應用時,尤其是在需要與外部系統進行精確互動的 Agent 或自動化流程中,這種對介面約束的思考就變得至關重要。與其期待一個更全能、更不會犯錯的「超級智慧」,不如透過務實的工程方法,設計出一個容錯性更高、可驗證性更強的系統架構。這或許才是讓 AI 技術能夠在真實世界中穩定運行的務實之路。
延伸閱讀
- Large Language Models for Compiler Error Correction (arXiv):一篇探討使用 LLM 修正編譯錯誤的論文,其中也涉及結構化程式碼的生成與理解。
- OpenAI Function Calling:OpenAI 官方提供的工具使用(Function Calling)功能,是另一種處理結構化輸出的內建方案。
- LLMはJSONが苦手らしい。だから生成しやすい中間言語を自作した:本文參考的原始概念文章(日文)。
我是江中喬,專注於 AI Agent Architecture、Memory Governance 與 Cognitive Diversity,持續研究如何打造能夠長期協作、可信任且可治理的 AI 系統。