Khắc phục InstructorRetryException: Khi LLM không tuân thủ Schema của bạn

intermediate🧠 AI Tools2026-06-28| Python 3.9+, thư viện Instructor, Pydantic v2, OpenAI/Anthropic/Gemini API

Error Message

instructor.exceptions.InstructorRetryException: Failed to extract data after 3 retries
#instructor#openai#pydantic#llm#python#debugging

Hiểu về lỗiHãy coi lỗi này như một "lá cờ trắng" từ thư viện Instructor. Nó có nghĩa là LLM đã thử nhiều lần để tạo ra phản hồi khớp với mô hình Pydantic của bạn, nhưng đều thất bại. Theo mặc định, Instructor sẽ bỏ cuộc sau ba lần thử. Khi bạn thấy ngoại lệ này, hiếm khi đó là lỗi trong mã nguồn. Thay vào đó, đó là tín hiệu cho thấy schema của bạn và đầu ra của LLM về cơ bản không đồng bộ với nhau.

instructor.exceptions.InstructorRetryException: Failed to extract data after 3 retries

Tại sao việc trích xuất thất bạiĐừng cho rằng mô hình đang lười biếng. Hầu hết các lỗi xảy ra do hướng dẫn quá cứng nhắc hoặc dữ liệu quá lộn xộn. Các nguyên nhân phổ biến bao gồm:

  • Schema phân cấp quá sâu: Các mô hình nhỏ hơn như GPT-3.5-Turbo hoặc Claude Haiku thường mất dấu cấu trúc nếu bạn lồng các đối tượng sâu hơn ba cấp.- Nút thắt cổ chai Regex: Sử dụng các mẫu nghiêm ngặt như pattern=r"^\d{3}-\d{2}-\d{4}$" cho số An sinh Xã hội có thể gây ra lỗi nếu LLM thêm một khoảng trắng thừa hoặc một tiền tố.- Sự mơ hồ: Nếu một trường được đặt tên là type mà không có mô tả, mô hình có thể đoán là 'User' trong khi Enum của bạn mong đợi 'ADMIN_USER'.- Giới hạn Token bị cắt ngang: Đối với các phản hồi rất dài, mô hình có thể chạm giới hạn 4.096 token và dừng lại giữa chừng khi đang tạo JSON, khiến chuỗi không thể phân tách (parse).## Cách sửa 1: Cung cấp cho mô hình một bản đồ với mô tả trườngLLM không phải là người đọc được suy nghĩ. Chúng phụ thuộc rất nhiều vào siêu dữ liệu bên trong các mô hình Pydantic của bạn. Bằng cách thêm description vào các trường, bạn cung cấp ngữ cảnh cần thiết để mô hình ánh xạ văn bản thô vào cấu trúc của bạn.

Vấn đề: Schema mơ hồ```

from pydantic import BaseModel

class UserInfo(BaseModel): name: str age: int status: str # Mô hình không biết 'status' ở đây nghĩa là gì


### Giải pháp: Schema giàu ngữ cảnh```
from pydantic import BaseModel, Field

class UserInfo(BaseModel):
    name: str = Field(description="Họ và tên. Viết hoa chữ cái đầu của mỗi từ.")
    age: int = Field(description="Tuổi tính theo năm. Phải là số nguyên dương.")
    status: str = Field(description="Tình trạng việc làm hiện tại: chọn từ 'Employed', 'Unemployed', hoặc 'Student'.")

Cách sửa 2: Nới lỏng các ràng buộc xác thựcXác thực nghiêm ngặt rất tốt cho cơ sở dữ liệu nhưng lại khó khăn cho LLM. Nếu bạn yêu cầu một định dạng điện thoại cụ thể và LLM xuất ra (555) 123-4567 thay vì 5551234567, xác thực sẽ thất bại. Hãy thử trích xuất dữ liệu dưới dạng chuỗi thô trước, sau đó làm sạch nó bằng @field_validator của Pydantic hoặc một hàm Python riêng biệt.

# Tránh dùng cách này để trích xuất thô
# phone: str = Field(pattern=r"^\+\d{10,15}$") 

# Sử dụng cách này thay thế
phone: str = Field(description="Số điện thoại chính xác như nó xuất hiện trong văn bản.")

Cách sửa 3: Nâng cấp mô hình và tăng số lần thử lạiNếu bạn đang sử dụng một mô hình nhỏ cho các tác vụ phức tạp, nó có thể đơn giản là thiếu khả năng "lập luận" để tuân theo schema của bạn. Nâng cấp từ GPT-3.5 lên GPT-4o thường giải quyết các vấn đề thử lại ngay lập tức. Bạn cũng có thể cho mô hình thêm cơ hội để tự sửa lỗi bằng cách tăng max_retries.

import instructor
from openai import OpenAI

# Cú pháp Instructor hiện đại sử dụng from_openai
client = instructor.from_openai(OpenAI())

try:
    user = client.chat.completions.create(
        model="gpt-4o", # GPT-4o tốt hơn đáng kể trong việc xử lý JSON phức tạp so với GPT-3.5
        response_model=UserInfo,
        max_retries=5,  # Cho phép thử 5 lần để lấy đúng định dạng JSON
        messages=[{"role": "user", "content": "Trích xuất dữ liệu từ: John 30 tuổi và hiện đang đi học."}]
    )
except instructor.exceptions.InstructorRetryException as e:
    # Kiểm tra đầu ra thô để xem tại sao nó thất bại
    print(f"Đầu ra của lần thử cuối: {e.last_completion}")

Cách sửa 4: Triển khai Chain of Thought (CoT)Sự phức tạp là kẻ thù của độ chính xác. Bằng cách thêm một trường 'suy nghĩ', bạn buộc mô hình phải lập luận qua quá trình trích xuất trước khi viết JSON cuối cùng. Bước đơn giản này có thể giảm lỗi xác thực hơn 30% trong các tác vụ phức tạp.

class ComplexExtraction(BaseModel):
    explanation: str = Field(description="Logic từng bước về cách bạn tìm thấy dữ liệu.")
    data_points: list[str]
    confidence: float

Mẹo gỡ lỗiKhi bạn bị kẹt trong vòng lặp thử lại, hãy ngừng đoán mò. Hãy bắt ngoại lệ InstructorRetryException và ghi nhật ký thuộc tính e.last_completion. Điều này cho bạn thấy chính xác những gì LLM đã tạo ra. Thông thường, bạn sẽ tìm thấy một lỗi định dạng nhỏ, lặp đi lặp lại mà có thể được khắc phục bằng một tinh chỉnh prompt đơn giản. Ngoài ra, hãy cân nhắc sử dụng kiểu Optional cho bất kỳ trường nào không đảm bảo 100% sẽ có trong văn bản nguồn của bạn.

Related Error Notes