Structured Outputs
Đầu ra có cấu trúc
Bạn yêu cầu LLM trả về danh sách sản phẩm JSON. 95% lần được JSON đúng, 5% lần LLM thêm 'Đây là danh sách...' trước JSON khiến code parse lỗi. Giải pháp?
Hình minh họa
Bộ thực thi JSON schema
Dán output thô của LLM và xem cách schema ràng buộc từng token.
- name — Tên sản phẩm, giữ nguyên chữ hoa/thường của input
- price — Giá bán bằng VND, chỉ chấp nhận số nguyên
- category — Danh mục (food | drink | other)
- in_stock — Còn hàng hay hết, bắt buộc là true hoặc false
Bạn cần LLM extract thông tin từ CV: tên, email, kinh nghiệm (list), kỹ năng (list). Schema có 4 trường required. Không có structured outputs, 1000 CVs có bao nhiêu sẽ parse lỗi?
Team bạn đang dùng 'parse → nếu lỗi thì retry' cho 1M requests/ngày, retry rate 7%. Chi phí thêm lớn nhất là gì?
Giải thích
Structured Outputs đảm bảo LLM sinh output theo schema cố định (JSON, XML) thay vì văn bản tự do — thiết yếu cho production systems.
3 cấp độ đảm bảo:
- Prompt-based:"Trả về JSON" — khoảng 90-95% compliance. Không đủ cho production
- JSON mode: Đảm bảo valid JSON nhưng không đảm bảo schema. Khoảng 98%
- Schema-strict: Constrained decoding theo schema. 100% compliance
Ý nghĩa: với constrained decoding, xác suất token ngoài tập hợp lệ được đặt về 0 trước softmax. Những token hợp lệ được chuẩn hoá lại — phân phối vẫn "mượt" nhưng không bao giờ rời khỏi grammar.
import anthropic
from pydantic import BaseModel, Field
client = anthropic.Anthropic()
class Product(BaseModel):
name: str = Field(description="Tên sản phẩm")
price: int = Field(description="Giá VND (integer)")
category: str = Field(description="food | drink | other")
in_stock: bool
class ProductList(BaseModel):
products: list[Product]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{
"role": "user",
"content": (
"Extract sản phẩm từ menu: "
"Phở bò 65.000đ, Cơm tấm 55.000đ (hết hàng), Bún chả 50.000đ"
),
}],
tools=[{
"name": "output_products",
"description": "Output danh sách sản phẩm",
"input_schema": ProductList.model_json_schema(),
}],
tool_choice={"type": "tool", "name": "output_products"},
)
# response.content[0].input là JSON đảm bảo match schemaKhi schema trở nên phức tạp:nested objects, arrays of objects, enum với hàng chục giá trị — tất cả đều hỗ trợ. Nhưng càng phức tạp, mask càng lớn, và model cũng khó "nhớ" thứ tự. Mẹo thực tế: flatten schema khi có thể, dùng enum thay vì free string cho category-like fields.
So sánh với function calling:cả hai cùng dùng một cơ chế schema-strict. Function calling chỉ là structured outputs với thêm ngữ cảnh "đây là một tool cần gọi". Nếu chỉ cần extract data, dùng structured outputs trực tiếp gọn hơn. Nếu cần LLM chọn giữa nhiều action, function calling với tool_choice = auto là lựa chọn đúng.
Đo lường compliance trong production:thêm metric "schema_parse_failures_total" vào observability stack. Với strict mode, con số này phải xấp xỉ 0 — nếu tăng đột biến là dấu hiệu API provider đang có vấn đề, không phải bug ở code bạn.
- Schema định nghĩa bằng Pydantic/Zod, commit vào repo với version.
- Bật strict mode / response_format schema ở layer client.
- additionalProperties: false để chặn hallucinated keys.
- Business validation layer sau LLM (price > 0, enum membership).
- Metric schema_parse_failures_total = 0 là điều kiện deploy.
- Sample 1% output lưu raw để debug khi regress.
- Structured outputs đảm bảo LLM sinh JSON/schema cố định — thiết yếu cho production (0% parse error)
- 3 cấp: Prompt (khoảng 95%), JSON mode (khoảng 98%), Schema-strict (100% constrained decoding)
- Constrained decoding: mask invalid tokens tại mỗi step trước softmax, dưới 5% overhead
- Dùng Pydantic BaseModel → model_json_schema() để tự generate schema từ class Python
- additionalProperties: false để chặn LLM hallucinated extra keys — strict hơn nữa
- Schema đảm bảo SHAPE, không đảm bảo SEMANTICS — vẫn cần business validation layer sau
Kiểm tra hiểu biết
Constrained decoding đảm bảo JSON hợp lệ bằng cách nào?