Structured Outputs: Ép AI trả về JSON chuẩn chỉnh 100% bằng Pydantic

AI Hunter

Member
Hãy tưởng tượng bạn đang viết một tính năng: Tự động đọc CV ứng viên và lưu vào Database.
Bạn gửi CV cho GPT-4 và bảo nó trả về JSON.
9 lần đầu nó trả về đúng. Đến lần thứ 10, nó nổi hứng thêm một câu "Here is your JSON" ở đầu, hoặc quên đóng ngoặc `}` ở cuối.
Backend của bạn: Internal Server Error (500). Sếp mắng, khách hàng chửi.

Đừng dùng Regex để bắt lỗi AI nữa. Hãy dùng Structured Outputs.
Đây là kỹ thuật ép mô hình ngôn ngữ (LLM) phải tuân thủ tuyệt đối một cấu trúc dữ liệu (Schema) mà bạn định nghĩa trước. Nếu nó trả sai, thư viện sẽ tự động bắt nó làm lại đến khi đúng thì thôi.

1. Pydantic là cái gì?​

Nếu bạn code Python mà chưa biết Pydantic thì... nên học ngay đi. Nó là thư viện kiểm tra dữ liệu (Data Validation) phổ biến nhất hiện nay.
Thay vì check thủ công `if 'name' in data`, bạn định nghĩa một Class.

Python:
from pydantic import BaseModel, Field
from typing import List

# Định nghĩa cái "Khuôn"
class UserProfile(BaseModel):
    full_name: str = Field(..., description="Họ và tên đầy đủ của ứng viên")
    age: int = Field(..., description="Tuổi, nếu không có thì đoán dựa trên năm sinh")
    skills: List[str] = Field(..., description="Danh sách kỹ năng lập trình (ví dụ: Python, SQL)")
    is_hired: bool = Field(False, description="Đánh giá xem có nên tuyển không (True/False)")

2. Cách 1: Dùng OpenAI "JSON Mode" (Cách cũ)​

OpenAI có chế độ `response_format={"type": "json_object"}`.
Cách này ổn, nhưng bạn vẫn phải viết Prompt rất dài dòng: "Hãy trả về JSON theo mẫu sau...". Và đôi khi nó vẫn sai key (ví dụ `fullname` thay vì `full_name`).

3. Cách 2: Dùng thư viện `Instructor` (Cách Pro)​

Đây là thư viện "thần thánh" được cộng đồng AI Engineer tôn sùng. Nó vá (patch) lại SDK của OpenAI để hỗ trợ Pydantic trực tiếp.

Cài đặt:
Bash:
pip install instructor pydantic openai

Code thực chiến:
Python:
import instructor
from openai import OpenAI
from pydantic import BaseModel, Field
from typing import List

# 1. Định nghĩa cấu trúc dữ liệu mong muốn
class UserProfile(BaseModel):
    full_name: str
    age: int
    skills: List[str]
    summary: str = Field(..., description="Tóm tắt ngắn gọn về ứng viên")

# 2. Vá OpenAI client bằng Instructor
client = instructor.from_openai(OpenAI())

# 3. Nội dung CV lộn xộn
cv_text = """
Chào công ty, em là Nguyễn Văn A. Em sinh năm 1999.
Em code tốt Python và biết chút chút về Docker.
Em rất mong được làm việc lương 5000$.
"""

# 4. Gọi AI và ép kiểu (response_model)
# PHÉP MÀU NẰM Ở ĐÂY: response_model=UserProfile
user_data = client.chat.completions.create(
    model="gpt-4o",
    response_model=UserProfile,
    messages=[
        {"role": "user", "content": f"Trích xuất thông tin từ đoạn văn sau: {cv_text}"},
    ],
)

# 5. Sử dụng kết quả (Đã là object Python xịn, không phải dict, không phải string)
print(f"Tên: {user_data.full_name}")  # Output: Nguyễn Văn A
print(f"Tuổi: {user_data.age}")        # Output: 26 (Tự tính 2025 - 1999)
print(f"Kỹ năng: {user_data.skills}") # Output: ['Python', 'Docker']

# Lưu vào DB dễ dàng
# database.save(user_data.model_dump())

4. Tại sao nó lại hoạt động?​

Bên dưới tảng băng chìm, `Instructor` và OpenAI sử dụng cơ chế **Function Calling** (Tool Use) mà chúng ta đã học ở bài trước.
Nó lừa con AI rằng: *"Tao có một cái hàm tên là `UserProfile`, hãy điền tham số vào để gọi hàm này"*.
AI sẽ cố gắng điền tham số khớp với Pydantic Schema. Instructor sẽ hứng lấy tham số đó và convert ngược lại thành Object cho bạn dùng.

[Image of pydantic llm validation flow loop]

5. Xử lý lỗi (Retry & Validation)​

Điều gì xảy ra nếu AI trích xuất sai (ví dụ: Tên lại chứa số)?
Pydantic có thể validate điều đó. Nếu sai, Instructor sẽ tự động gửi lỗi lại cho AI: *"Ê, trường `full_name` không được chứa số, làm lại đi!"*.
AI sẽ sửa lại và gửi kết quả đúng. Tất cả diễn ra tự động trong vòng lặp kín.

Python:
from pydantic import validator

class UserProfile(BaseModel):
    full_name: str
   
    @validator("full_name")
    def name_must_be_title_case(cls, v):
        if not v.istitle():
            raise ValueError("Tên phải viết hoa chữ cái đầu!")
        return v
Nếu AI trả về "nguyễn văn a", nó sẽ bị ăn lỗi ngay và phải tự sửa thành "Nguyễn Văn A".

Kết luận​

Structured Outputs là cây cầu nối vững chắc nhất để đưa AI từ phòng thí nghiệm ra sản phẩm thực tế (Production).
Nếu không có nó, hệ thống của bạn chỉ là một món đồ chơi may rủi.
Nếu có nó, bạn có thể tự tin: "Input rác vào, Output chuẩn ra".
 
Back
Top