0% read

Gemma 4 結構化輸出:如何每次都取得可靠的 JSON

Apr 7, 2026

如果你在 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 有幫助——查看思考模式指南

下一步

gemma4 — interact

Stop reading. Start building.

~/gemma4 $ Get hands-on with the models discussed in this guide. No deployment, no friction, 100% free playground.

Launch Playground />
Gemma 4 AI

Gemma 4 AI

Related Guides

Gemma 4 結構化輸出:如何每次都取得可靠的 JSON | 部落格