PydanticAI: Bí kíp xây dựng AI Agent chuẩn Type-safe và ổn định

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

Khi LLM “nổi hứng” trả về dữ liệu không như mơ

Anh em làm AI chắc chẳng lạ gì cảnh ngồi còng lưng parse kết quả từ GPT hay Claude. Thực tế cho thấy, vấn đề đau đầu nhất không phải là AI kém thông minh. Ngược lại, nó quá “sáng tạo” trong việc trả về dữ liệu. Lúc thì nó thêm vài câu dẫn chuyện thừa thãi, lúc thì thiếu dấu ngoặc, hoặc tệ hơn là tự ý đổi user_id thành customer_id khiến code phía sau gãy vụn.

Mình từng tham gia một dự án xử lý ticket hỗ trợ khách hàng tự động. Hệ thống thỉnh thoảng lại báo lỗi 500 chỉ vì con bot trả về định dạng ngày tháng không thống nhất. Có những lúc tỷ lệ lỗi parse dữ liệu lên tới 15-20%, khiến mình phải viết hàng tá hàm regex và logic try-except rối rắm chỉ để dọn dẹp đống hỗn độn đó trước khi đưa vào database.

Nguyên nhân sâu xa là sự lệch pha giữa hai thế giới. LLM vận hành dựa trên xác suất, còn phần mềm lại chạy trên logic cứng nhắc. Nếu không có một lớp bảo vệ ở giữa, hệ thống AI Agent của bạn sẽ mãi ở trạng thái “hên xui”.

Lối mòn cũ: Parse thủ công hay dùng Framework “khủng”?

Trước khi biết đến PydanticAI, mình và nhiều anh em thường loay hoay giữa hai lựa chọn:

  • Parse thủ công: Dùng json.loads() rồi ngồi kiểm tra từng key. Cách này cực thân xác, code vừa dài vừa bẩn, lại cực kỳ khó bảo trì khi schema phình to.
  • Dùng Framework nặng: LangChain hay LlamaIndex có sẵn OutputParser, nhưng thú thật là chúng đôi khi quá cồng kềnh. Quá nhiều lớp trừu tượng (abstraction) khiến việc debug trở thành một cực hình, nhất là khi cần can thiệp sâu vào luồng dữ liệu.

Kinh nghiệm xương máu của mình là: Đừng cố “dạy” AI trả về đúng định dạng bằng prompt đơn thuần. Hãy ép nó phải tuân thủ nghiêm ngặt cấu trúc dữ liệu ngay từ tầng framework.

PydanticAI – Lời giải cho bài toán Type-safe

PydanticAI ra đời từ chính đội ngũ phát triển Pydantic – thư viện validate dữ liệu “quốc dân” của dân Python. Triết lý của nó rất thực dụng: Đưa sự chặt chẽ của Type-hinting vào thế giới đầy biến động của LLM.

Điểm ăn tiền nhất là PydanticAI coi việc validate kết quả là trung tâm của Agent. Nếu AI trả về sai schema, framework sẽ tự động chỉ ra lỗi và yêu cầu AI sửa lại (retry). Điều này giúp hệ thống của bạn luôn nhận được dữ liệu sạch 100%, giảm thiểu tối đa rủi ro đổ bug trên production.

Triển khai thực tế: Agent phân tích Ticket trong 5 phút

Đầu tiên, anh em cài đặt thư viện nhanh gọn qua pip:

pip install pydantic-ai

Dưới đây là cách mình cấu trúc một Agent để nó vừa hiểu ngữ cảnh, vừa trả về dữ liệu chuẩn chỉ không sai một ly:

from pydantic import BaseModel, Field
from pydantic_ai import Agent
from typing import List

# 1. Định nghĩa cấu trúc dữ liệu mong muốn
class TicketAnalysis(BaseModel):
    priority: str = Field(description="Mức độ ưu tiên: Low, Medium, High")
    category: str = Field(description="Phân loại: Kỹ thuật, Thanh toán, Tài khoản")
    tags: List[str] = Field(default_factory=list, description="Các từ khóa liên quan")
    summary_vi: str = Field(description="Tóm tắt nội dung bằng tiếng Việt")

# 2. Khởi tạo Agent với Model và Schema kết quả
agent = Agent(
    'openai:gpt-4o', 
    result_type=TicketAnalysis,
    system_prompt='Bạn là chuyên gia phân tích ticket. Hãy phân tích và trả về dữ liệu chuẩn.',
)

# 3. Chạy Agent
def analyze_customer_issue():
    user_input = "Tôi không thể đăng nhập dù đã đổi mật khẩu. App báo lỗi 500."
    result = agent.run_sync(user_input)
    
    # Dữ liệu trả về đã là Object, có sẵn gợi ý code (IntelliSense)
    data = result.data
    print(f"[{data.priority.upper()}] Phân loại: {data.category}")
    print(f"Tóm tắt: {data.summary_vi}")

analyze_customer_issue()

Dependency Injection: Tính năng cực “đáng đồng tiền bát gạo”

Một điểm mình cực kỳ ưng ý ở PydanticAI là cách nó xử lý Dependencies. Khi Agent cần truy cập Database hay gọi API bên ngoài, bạn không còn phải truyền biến toàn cục lắt léo như trước.

Bạn có thể định nghĩa một class Deps và inject nó trực tiếp vào Agent. Cách làm này cực kỳ hữu ích khi viết Unit Test hoặc khi cần thay đổi cấu hình database linh hoạt giữa các môi trường Dev và Prod.

from dataclasses import dataclass

@dataclass
class MyDeps:
    db_session: str  # Session DB thực tế
    api_key: str

agent_with_deps = Agent('openai:gpt-4o', deps_type=MyDeps)

@agent_with_deps.tool
def get_user_info(ctx, user_id: int) -> str:
    # Truy cập deps gọn gàng qua ctx.deps
    return f"User {user_id} từ database {ctx.deps.db_session} là VIP"

Tại sao đây là lựa chọn hàng đầu hiện nay?

Sau nhiều tháng chinh chiến với đủ loại framework, mình rút ra 3 lý do tại sao PydanticAI lại đáng dùng đến vậy:

  1. Độ tin cậy cao: Cơ chế tự động retry khi validate fail giúp dẹp bỏ 90% lỗi runtime liên quan đến định dạng dữ liệu.
  2. Code sướng hơn: Nhờ dùng Pydantic, VS Code hay PyCharm sẽ gợi ý code cực chuẩn. Bạn không còn phải nhớ xem field đó là int hay str, hay tên chính xác là gì.
  3. Dễ kiểm soát: Framework không giấu giếm logic sau những lớp trừu tượng bí ẩn. Nó vẫn là Python thuần túy, giúp anh em dễ dàng tích hợp vào luồng CI/CD hiện có.

Nếu bạn đang bắt đầu một dự án AI Agent nghiêm túc, hãy bỏ qua những chuỗi string vô định và thử ngay cách tiếp cận Type-safe này. Tin mình đi, nó sẽ giúp bạn ngủ ngon hơn mỗi khi nhấn nút deploy lên Production đấy.

Share: