如果你在 Gemma 4 之上建構應用,你需要結構化輸出——不是自由格式的文字。你需要可以解析、驗證並導入你資料庫或 API 的 JSON。每一次,無一例外。
這是與本機 LLM 工作最棘手的部分之一,但使用正確的技巧,Gemma 4 可以出乎意料地可靠。讓我們走過每種方法。
為什麼結構化輸出很重要
當你將 Gemma 4 作為更大系統中的一個元件使用時——不只是和它聊天——你需要可預測的輸出:
# 這是你想要的:
{"sentiment": "positive", "confidence": 0.92, "topics": ["pricing", "support"]}
# 這是你不想要的:
"The sentiment of this text is positive, with a confidence of about 92%..."第一個可以被解析並以程式方式使用。第二個需要另一輪解析,增加延遲、成本和失敗點。
方法 1:System Prompt 技巧
最簡單的方法——在 system prompt 中告訴模型你想要什麼:
import requests
import json
response = requests.post("http://localhost:11434/api/chat", json={
"model": "gemma4:26b",
"messages": [
{
"role": "system",
"content": """You are a JSON-only response API.
You MUST respond with valid JSON and nothing else.
No markdown, no explanation, no code blocks — just raw JSON.
Schema:
{
"sentiment": "positive" | "negative" | "neutral",
"confidence": number between 0 and 1,
"topics": string[],
"summary": string (one sentence)
}"""
},
{
"role": "user",
"content": "Analyze: 'The new update is amazing! The UI is so much cleaner and everything loads faster. Only complaint is the price went up.'"
}
],
"stream": False,
})
result = json.loads(response.json()["message"]["content"])
print(result)這大多數時候有效。但「大多數時候」對正式環境來說不夠好。模型可能偶爾加上「Here's the JSON:」之類的前言,或用 markdown 程式碼區塊包裝輸出。
方法 2:Ollama Format 參數
Ollama 有內建的 format 參數,可將輸出限制為有效的 JSON:
response = requests.post("http://localhost:11434/api/chat", json={
"model": "gemma4:26b",
"messages": [
{
"role": "system",
"content": "Analyze the sentiment of the given text. Return: sentiment (positive/negative/neutral), confidence (0-1), topics (list), summary (one sentence)."
},
{
"role": "user",
"content": "The customer service was terrible but the product itself is excellent."
}
],
"format": "json",
"stream": False,
})
# 這保證是有效的 JSON
result = response.json()["message"]["content"]
parsed = json.loads(result)format: "json" 旗標告訴 Ollama 限制 token 生成只產生有效的 JSON。這比單純的提示詞工程可靠得多。
限制: 它保證有效的 JSON 語法,但不保證符合 schema。模型可能回傳 {"answer": "positive"} 而不是你預期的格式。你仍然需要驗證。
方法 3:用 Pydantic 定義 Schema
對於正式環境程式碼,用 Pydantic 定義預期的 schema 並驗證:
from pydantic import BaseModel, Field
from typing import Literal
import json
import requests
class SentimentResult(BaseModel):
sentiment: Literal["positive", "negative", "neutral"]
confidence: float = Field(ge=0, le=1)
topics: list[str]
summary: str
def analyze_sentiment(text: str) -> SentimentResult:
schema_str = json.dumps(SentimentResult.model_json_schema(), indent=2)
response = requests.post("http://localhost:11434/api/chat", json={
"model": "gemma4:26b",
"messages": [
{
"role": "system",
"content": f"""Respond with JSON matching this exact schema:
{schema_str}
No other text. Just valid JSON."""
},
{
"role": "user",
"content": f"Analyze this text: {text}"
}
],
"format": "json",
"stream": False,
})
raw = json.loads(response.json()["message"]["content"])
return SentimentResult.model_validate(raw)
# 使用
result = analyze_sentiment("Great product, terrible shipping time.")
print(f"Sentiment: {result.sentiment} ({result.confidence:.0%})")
print(f"Topics: {', '.join(result.topics)}")這給你型別安全和驗證。如果模型回傳非預期的內容,Pydantic 會拋出清晰的錯誤而不是默默地破壞你的資料。
方法 4:驗證和重試模式
為了最大可靠性,加入重試迴圈:
from pydantic import BaseModel, ValidationError
import json
import requests
import time
def get_structured_output(
prompt: str,
schema_class: type[BaseModel],
model: str = "gemma4:26b",
max_retries: int = 3,
) -> BaseModel:
schema_str = json.dumps(schema_class.model_json_schema(), indent=2)
for attempt in range(max_retries):
try:
response = requests.post("http://localhost:11434/api/chat", json={
"model": model,
"messages": [
{
"role": "system",
"content": f"Respond ONLY with JSON matching this schema:\n{schema_str}"
},
{"role": "user", "content": prompt}
],
"format": "json",
"stream": False,
"options": {
"temperature": 0.1 if attempt == 0 else 0.3,
},
})
raw = json.loads(response.json()["message"]["content"])
return schema_class.model_validate(raw)
except (json.JSONDecodeError, ValidationError) as e:
if attempt == max_retries - 1:
raise ValueError(
f"Failed to get valid output after {max_retries} attempts: {e}"
)
time.sleep(0.5)
raise ValueError("Unreachable")
# 使用
class ProductReview(BaseModel):
rating: int = Field(ge=1, le=5)
pros: list[str]
cons: list[str]
recommendation: bool
review = get_structured_output(
"Review: 'Solid laptop, great keyboard, battery could be better. 4/5 would buy again.'",
ProductReview,
)關鍵設計選擇:
- 從低溫度(0.1)開始以取得一致性,重試時提高以取得多樣性
- 使用
format: "json"保證有效的 JSON 語法 - 用 Pydantic 驗證 schema 正確性
- 重試上限 3 次——如果失敗 3 次,提示詞可能需要改進
常見失敗和修復
模型用 markdown 包裝 JSON:
```json
{"key": "value"}
修復:在 Ollama 中使用 `format: "json"`。如果不可用,剝離 markdown:
```python
def clean_json(text: str) -> str:
text = text.strip()
if text.startswith("```"):
text = text.split("\n", 1)[1] # 移除第一行
text = text.rsplit("```", 1)[0] # 移除最後的 ```
return text.strip()模型加入額外欄位:
模型可能回傳你沒要求的欄位。Pydantic 預設會忽略額外欄位。或設定 model_config = ConfigDict(extra="forbid") 來拒絕它們。
模型使用錯誤的型別:
有時模型回傳 "0.92"(字串)而不是 0.92(數字)。Pydantic 的 model_validate 會自動處理大多數型別轉換。
空的或 null 欄位:
當欄位可能為空時設為可選:
class Result(BaseModel):
name: str
email: str | None = None # 模型可能找不到 email
topics: list[str] = [] # 預設為空列表巢狀物件:
Gemma 4 能很好地處理巢狀 JSON,但將巢狀控制在 2-3 層以內:
class Address(BaseModel):
city: str
country: str
class Person(BaseModel):
name: str
age: int
address: Address # 一層巢狀——可以效能技巧
- 降低溫度(0.1-0.3)產生更一致的 JSON
- 較短的 schema 獲得更好的遵循——不要一次要求 20 個欄位
- 在 system prompt 中用 few-shot 範例 大幅提升可靠性
- 26B 模型 在 JSON 上明顯優於 E4B——查看模型比較
- 思考模式 對複雜 schema 有幫助——查看思考模式指南
下一步
- 在你的應用中搭配 Ollama API 使用 JSON 輸出
- 用 vLLM + Docker 部署 JSON API 伺服器
- 為你特定的 JSON 格式 微調 Gemma 4
- 了解用於複雜結構化任務的思考模式
Stop reading. Start building.
~/gemma4 $ Get hands-on with the models discussed in this guide. No deployment, no friction, 100% free playground.
Launch Playground />


