構(gòu)化生成 30 分鐘上手)
別再手動校驗 LLM 輸出Outlines 結(jié)構(gòu)化生成 30 分鐘上手【免費下載鏈接】outlinesStructured Outputs項目地址: https://gitcode.com/GitHub_Trending/ou/outlinesOutlines 結(jié)構(gòu)化生成只做一件事LLM 每吐一個 token 之前先和你定義的結(jié)構(gòu)對一遍不符合的 token 根本不會被生成出來。你不需要再為模型返回的 JSON 手寫校驗代碼、寫格式不對請重試的兜底提示——輸出天然落在你的 Pydantic 模型、正則或上下文無關(guān)文法里。翻車現(xiàn)場把 JSON 吐給你的 API 那一瞬間所有讓模型重試一下的兜底代碼都可以刪了。先說最常見的線上事故上游服務(wù)約定返回 JSON你在 prompt 里寫了請只輸出 JSON結(jié)果模型前面吐了半截{name:后面開始解釋以下是您要的角色設(shè)定。于是你寫后處理正則去摳摳不出來就調(diào)模型重生成重試三次還失敗就吞掉異常。prompt 約束本質(zhì)是求模型配合后處理校驗本質(zhì)是生成完了再擦屁股兩條路都在浪費 token。Outlines 的解法換個思路不讓模型盡量生成合法 JSON而是讓它只能生成合法 JSON。下面這套流程在本地就能跑不依賴任何 API key。先裝好它三步加載模型# pip install outlines[transformers] import transformers import outlines name HuggingFaceTB/SmolLM2-135M-Instruct model outlines.from_transformers( transformers.AutoModelForCausalLM.from_pretrained(name), transformers.AutoTokenizer.from_pretrained(name), ) # 不傳約束模型自由發(fā)揮 print(model(22? 用一個單詞回答))裝完加載model就是一個可調(diào)用對象。同一個model第二個參數(shù)傳什么類型輸出就長什么結(jié)構(gòu)——這是后文所有玩法的開關(guān)。注意它只封裝了本地推理Transformers、llama.cpp、MLXLM 這三類模型能直接操控生成過程OpenAI、Anthropic、Gemini、vLLM 等 API 模型也接入了 Outlines約束會通過服務(wù)端的 guided decoding 完成調(diào)用方式不變。? 用 Pydantic 模型鎖定輸出字段把一個 Pydantic 類作為第二個參數(shù)傳進(jìn)去模型輸出的每個 token 都在你的 schema 約束下拿到手就能model_validate_json一步都不用修。from datetime import date from pydantic import BaseModel class Character(BaseModel): name: str birth_date: date skills: list[str] result model(創(chuàng)建一個游戲角色, Character) print(result) # {name: Aurora, birth_date: 1990-06-15, ...} print(Character.model_validate_json(result)) # 直接拿到 Pydantic 對象字段怎么定的輸出就怎么長birth_date: date保證日期格式永遠(yuǎn)合法skills: list[str]保證是個數(shù)組缺字段、多字段、類型錯位這類事在生成階段就物理上不可能發(fā)生。把這套能力用在數(shù)據(jù)抽取上就很直觀——比如倉庫里有個超市小票數(shù)字化的例子把小票圖片丟給多模態(tài)模型讓它把商品、數(shù)量、金額抽成結(jié)構(gòu)化記錄每條記錄都是一個 Pydantic 實例直接入庫。不止 JSONint、枚舉、正則都是合法的輸出模具約束類型不只有 JSON Schema基礎(chǔ)類型、枚舉、正則甚至一整篇 Lark 文法都能當(dāng)模具直接傳給model。from enum import Enum from outlines.types import Regex class Food(Enum): pizza pizza burger burger print(model(世界上大約有多少個國家, int)) # 200只會生成整數(shù)不會生成大約 200 print(model(披薩還是漢堡, Food)) # pizza只會生成枚舉里定義的取值 print(model(寫一個三位數(shù), Regex(r[0-9]{3}))) # 236恰好三位數(shù)字多一位少一位都不行正則還能用組合子拼either、at_least、optional這些運算符在 src/outlines/types/ 里都有另外內(nèi)置了uuid4、ipv4、semver、email這類現(xiàn)成的正則類型拿來即用。更重的玩法是上下文無關(guān)文法把一段 Lark 語法規(guī)則比如四則運算表達(dá)式作為CFG對象傳進(jìn)去模型就只能生成該文法接受的字符串——官方文檔里的算術(shù)文法示例可以直接抄。高頻調(diào)用時把模型和約束打包成Generator約束只編譯一次from outlines import Generator # 模型 約束打包成一個對象約束的編譯只做一次 gen Generator(model, Character) print(gen(創(chuàng)建一個角色)) print(gen(再創(chuàng)建一個角色換個職業(yè)))對本地模型來說這一步很關(guān)鍵把 schema 編譯成約束處理器可能要幾百毫秒Generator讓這活兒只在初始化時付一次后續(xù)調(diào)用直接復(fù)用。核心實現(xiàn)在 src/outlines/generator.py。它是怎么做到的logits 處理器給模型戴了個嘴套原理一句話模型每生成一個 token 前先看當(dāng)前詞表里哪些 token 會把輸出帶出合法軌道把這些 token 的概率直接砍成 -inf剩下的才參與采樣。就像給模型戴了個嘴套每次張嘴前先過一遍濾網(wǎng)——模型想說錯話但嘴上只可能說出對的話。具體分工是這樣的約束Pydantic 類、正則、CFG先被編譯成有限狀態(tài)機(jī)生成過程中狀態(tài)機(jī)跟著 token 走每步給出當(dāng)前合法 token 集合。Outlines 內(nèi)置三個編譯后端——xgrammar、outlines-core、llguidance源碼在 src/outlines/backends/可以按模型和約束類型選logits 處理器文檔里還有一個只允許輸出 0 和 1的自定義處理器示例能直觀看到掩碼是怎么加的。API 類模型OpenAI 等沒有本地詞表可攔Outlines 會把 schema 轉(zhuǎn)成服務(wù)端的結(jié)構(gòu)化輸出請求效果等價寫法不變。下一步跑完這三件事用上面第一個代碼塊在本地跑通自由文本生成確認(rèn) transformers 環(huán)境沒問題換 Pydantic 約束重跑用Character.model_validate_json(result)驗證輸出再試試故意把字段改成conint(ge18, le99)看年齡被鎖在區(qū)間里把Regex(r[0-9]{3})換成CFG文法約束抄文檔里的四則運算語法感受生成即合法的邊界在哪【免費下載鏈接】outlinesStructured Outputs項目地址: https://gitcode.com/GitHub_Trending/ou/outlines創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考