Hàm Python Tự Triển Khai: Cuộc Cách Mạng Trong Lập Trình AI Với AI Functions

Thế giới phát triển phần mềm đang chứng kiến những bước tiến nhảy vọt nhờ trí tuệ nhân tạo. Trong bối cảnh đó, một mô hình lập trình mới mẻ và đầy hứa hẹn đang dần định hình, cho phép các nhà phát triển tạo ra các hàm Python mà “chính nó tự triển khai”. Đây không còn là khoa học viễn tưởng, mà là thực tế với thư viện AI Functions, một dự án thử nghiệm đột phá từ Strands Labs. Hãy cùng khám phá cách tiếp cận này đang thay đổi cách chúng ta nghĩ về việc xây dựng ứng dụng AI.

Khái Niệm Đột Phá: Khi Mã Nguồn Tự “Viết” Lấy Chính Nó

Hãy tưởng tượng bạn có thể định nghĩa một hàm Python mà phần docstring (chuỗi tài liệu) của nó chính là phần triển khai. Bạn chỉ cần xác định đầu vào, kiểu trả về và viết logic xác thực (validation logic) để định nghĩa “điều gì là đúng”. Phần còn lại – quá trình triển khai chi tiết – sẽ được AI đảm nhiệm hoàn toàn. Đây chính là mô hình lập trình cốt lõi đằng sau AI Functions.

AI Functions là một thư viện thử nghiệm mới được phát triển bởi Strands Labs, một tổ chức GitHub mới nơi các tính năng thử nghiệm của Strands Agents SDK đang được xây dựng một cách công khai. Với AI Functions, thay vì phải tự tay viết toàn bộ thân hàm, bạn tập trung vào việc định nghĩa các tiêu chí chấp nhận và cho phép AI tạo ra và tự điều chỉnh mã nguồn dựa trên các kiểm tra của bạn. Điều này mở ra một kỷ nguyên mới, nơi các nhà phát triển có thể tập trung vào “cái gì” thay vì “làm thế nào”.

Lập Trình AI Đã Thay Đổi Như Thế Nào Với AI Functions?

Phần lớn mã nguồn được hỗ trợ bởi AI ngày nay thường tuân theo một khuôn mẫu khá rập khuôn và tốn thời gian.

Phương Pháp Truyền Thống: Phức Tạp và Lặp Lại

Theo cách tiếp cận truyền thống, khi làm việc với các mô hình AI, bạn thường phải thực hiện các bước sau:

  1. Gọi mô hình AI để nhận kết quả.
  2. Phân tích (parse) phản hồi từ mô hình, thường là JSON hoặc văn bản.
  3. Viết các kiểm tra xác thực (validation checks) để đảm bảo dữ liệu đúng định dạng và logic.
  4. Xử lý các lỗi có thể xảy ra trong quá trình.
  5. Thử lại (retry) khi mọi thứ không như mong đợi.

Đây là một quy trình lặp đi lặp lại, hay còn gọi là “boilerplate code”, mà mỗi nhà phát triển lại viết hơi khác nhau, dẫn đến sự thiếu nhất quán và tốn kém thời gian.

AI Functions: Đảo Ngược Mô Hình

AI Functions hoàn toàn đảo ngược mô hình này. Thay vì thực hiện các bước trên một cách tuần tự và thủ công, bạn chỉ cần định nghĩa:

  • Một **chữ ký hàm** (function signature).
  • Một **docstring** đóng vai trò là prompt cho mô hình AI.
  • Một **kiểu trả về** (return type) định nghĩa “hợp đồng” dữ liệu.
  • Các **hậu điều kiện** (post-conditions) định nghĩa “điều gì là đúng” sau khi hàm thực thi.

Điều đáng chú ý là: **không có thân hàm (function body)**. Hàm của bạn sẽ được thực thi trên một Mô hình Ngôn ngữ Lớn (LLM) thay vì trên CPU của bạn.

Điểm mấu chốt ở đây là bạn vẫn viết mã xác thực thực sự. Các hậu điều kiện là các hàm Python tiêu chuẩn do bạn tự tay tạo ra. Bạn định nghĩa các tiêu chí chấp nhận, và hệ thống sẽ đảm bảo chúng được thi hành.

Ví Dụ Thực Tế: Trích Xuất Dữ Liệu Hóa Đơn Một Cách Thông Minh

Để minh họa rõ hơn, hãy xem xét một ví dụ thực tế: xây dựng một bộ phân tích hóa đơn.

Tại Sao Trích Xuất Hóa Đơn Là Trường Hợp Lý Tưởng?

Trích xuất hóa đơn là một ứng dụng hoàn hảo cho mô hình này vì bản chất của việc trích xuất thông tin thường khá “mờ”. Các nhà cung cấp định dạng hóa đơn khác nhau, các mục hàng (line items) có thể thay đổi, và các quy tắc thuế không phải lúc nào cũng nhất quán. Tuy nhiên, việc **xác thực** các con số trên hóa đơn lại mang tính **xác định** (deterministic). Bạn có thể dễ dàng viết một hậu điều kiện để kiểm tra xem các phép toán có khớp với nhau bằng số học đơn giản.

Trong thực tế, hầu hết các hóa đơn bắt đầu dưới dạng hình ảnh hoặc PDF. Ví dụ này giả định rằng bạn đã trích xuất văn bản từ chúng bằng công nghệ OCR (Nhận dạng ký tự quang học) hoặc một dịch vụ xử lý tài liệu. Bây giờ, bạn cần biến văn bản thô đó thành dữ liệu có cấu trúc và đã được xác thực. Chúng ta sẽ xây dựng một cái gì đó để xử lý bước thứ hai này: trích xuất dữ liệu có cấu trúc từ văn bản hóa đơn và xác thực rằng các phép toán thực sự khớp nhau.

Triển Khai Với AI Functions

Đầu tiên, chúng ta sẽ sử dụng các mô hình Pydantic để định nghĩa cấu trúc dữ liệu đầu ra mong muốn.


from pydantic import BaseModel, Field
from ai_functions import ai_function

class LineItem(BaseModel):
    description: str = Field(description="Mô tả mặt hàng hoặc dịch vụ")
    quantity: int = Field(description="Số lượng đơn vị")
    unit_price: float = Field(description="Giá mỗi đơn vị")
    amount: float = Field(description="Tổng số tiền cho mục này (quantity * unit_price)")

class ReceiptData(BaseModel):
    vendor: str = Field(description="Tên nhà cung cấp hoặc công ty")
    invoice_number: str = Field(description="Số hóa đơn hoặc biên lai")
    date: str = Field(description="Ngày hóa đơn (định dạng YYYY-MM-DD)")
    items: list[LineItem] = Field(description="Danh sách các mục hàng")
    subtotal: float = Field(description="Tổng phụ của tất cả các mục hàng trước thuế")
    tax: float = Field(description="Số tiền thuế")
    total: float = Field(description="Tổng cuối cùng (subtotal + tax)")

Tiếp theo, chúng ta định nghĩa hàm `validate_math`, đây là một **hậu điều kiện** để kiểm tra tính nhất quán toán học của dữ liệu trích xuất.


def validate_math(result: ReceiptData) -> None:
    """Xác thực rằng tất cả các phép toán nhất quán nội bộ."""
    errors = []

    # Kiểm tra các mục hàng: amount = quantity × unit_price
    for i, item in enumerate(result.items):
        expected = item.quantity * item.unit_price
        if abs(item.amount - expected) > 0.01:
            errors.append(
                f"Mục hàng {i} ({item.description}): số tiền {item.amount} != "
                f"số lượng {item.quantity} * đơn giá {item.unit_price} = {expected}"
            )

    # Xác minh tổng phụ = tổng của các mục hàng
    items_sum = sum(item.amount for item in result.items)
    if abs(result.subtotal - items_sum) > 0.01:
        errors.append(f"Tổng phụ {result.subtotal} != tổng các mục hàng {items_sum}")

    # Xác nhận tổng cuối cùng = tổng phụ + thuế
    expected_total = result.subtotal + result.tax
    if abs(result.total - expected_total) > 0.01:
        errors.append(f"Tổng {result.total} != tổng phụ {result.subtotal} + thuế {result.tax} = {expected_total}")

    if errors:
        raise ValueError("\n".join(errors))

Cuối cùng, chúng ta định nghĩa hàm `parse_receipt` bằng cách sử dụng decorator `@ai_function` và docstring làm prompt cho AI.


@ai_function(
    description="Phân tích văn bản hóa đơn hoặc biên lai và trích xuất dữ liệu chi tiêu có cấu trúc",
    post_conditions=[validate_math],
    max_attempts=3,
)
def parse_receipt(receipt_text: str) -> ReceiptData:
    """
    Trích xuất dữ liệu có cấu trúc từ hóa đơn/biên lai này.
    Văn bản hóa đơn: {receipt_text}

    Hướng dẫn:
    - Trích xuất tất cả các mục hàng với số lượng, đơn giá và tổng số tiền của chúng
    - Tính tổng phụ là tổng của tất cả các số tiền mục hàng
    - Trích xuất số tiền thuế (nếu không có thuế, sử dụng 0.0)
    - Tính tổng cuối cùng là tổng phụ + thuế
    - Sử dụng định dạng YYYY-MM-DD cho ngày
    - Đảm bảo tất cả các phép toán nhất quán
    """

Các mô hình Pydantic định nghĩa hình dạng của đầu ra. Decorator `@ai_function` đánh dấu đây là một hàm được hỗ trợ bởi AI. Docstring trở thành prompt, với `{receipt_text}` là biến mẫu cho đầu vào. Kiểu trả về cho hệ thống biết cấu trúc nào cần tạo.

Cơ Chế Hoạt Động Ngầm

Khi bạn gọi `parse_receipt` với một văn bản hóa đơn, điều gì sẽ xảy ra bên trong?
1. **Chuyển giao cho Strands Agent:** Thư viện sẽ chuyển giao yêu cầu cho một vòng lặp agent của Strands.
2. **Tạo Prompt và Gửi đến Mô Hình:** Agent lấy docstring của bạn (với văn bản hóa đơn đã được điền vào), gửi đến mô hình ngôn ngữ lớn (LLM) và yêu cầu nó trả về một đối tượng `ReceiptData`.
3. **Khả năng của Strands Agent:** Vì nó đang chạy thông qua một Strands agent, hàm sẽ có quyền truy cập vào các khả năng sử dụng công cụ (tool-use capabilities) tương tự mà các Strands agent có, và khi tích hợp trưởng thành, có thể cả các tính năng khác của Strands nữa. Tuy nhiên, từ góc độ của bạn, người gọi, đó chỉ là một lời gọi hàm trả về một mô hình Pydantic.
4. **Chạy Post-conditions:** Sau khi mô hình phản hồi, hàm `validate_math` sẽ chạy để kiểm tra kết quả. Nó kiểm tra xem số tiền của mỗi mục hàng có bằng số lượng nhân với đơn giá không, tổng phụ có bằng tổng của tất cả các mục hàng không, và tổng cuối cùng có bằng tổng phụ cộng thuế không.
5. **Cơ Chế Thử Lại:**
* Nếu mọi thứ đều đúng, bạn sẽ nhận được đối tượng `ReceiptData` của mình.
* Nếu `validate_math` đưa ra một `ValueError` (ví dụ: “Subtotal 1,492.30 != sum of line items 1,492.80”), thư viện sẽ lấy thông báo lỗi đó và gửi lại cho mô hình cùng với prompt gốc. Mô hình sẽ thấy chính xác những gì nó đã làm sai và cố gắng tạo ra một phản hồi khác.
* Vòng lặp này lặp lại tối đa `max_attempts` lần. Với `max_attempts=3`, mô hình sẽ có ba cơ hội để tạo ra đầu ra vượt qua các kiểm tra của bạn.

Tầm Quan Trọng Của Post-conditions

Các hậu điều kiện cho phép bạn định nghĩa “điều gì là đúng” trong lĩnh vực cụ thể của bạn. Chúng là các hàm Python tiêu chuẩn để thực thi logic nghiệp vụ. Ví dụ, các phép toán phải khớp, tên nhà cung cấp không được trống, ngày phải đúng định dạng. Đây là những điều bạn không thể đảm bảo chỉ bằng kỹ thuật prompt engineering đơn thuần.

Điều đáng chú ý là `validate_math` kiểm tra **tính nhất quán nội bộ**, chứ không phải **độ chính xác của việc trích xuất**. Nếu mô hình đọc nhầm “$8,400” thành “$840” từ đầu ra OCR lộn xộn, phép toán vẫn có thể khớp nhưng dữ liệu lại hoàn toàn sai. Đây là lý do cần có các hậu điều kiện bổ sung. Bạn có thể viết một hậu điều kiện để đối chiếu các giá trị trích xuất với văn bản đầu vào thô, kiểm tra xem tổng số tiền mà mô hình trả về có thực sự xuất hiện trong hóa đơn hay không. Nếu không, có điều gì đó đã sai trong quá trình trích xuất, không chỉ trong phép toán. Mô hình này mở rộng đến bất kỳ định nghĩa “đúng” nào cho trường hợp sử dụng của bạn.

Bạn cũng có thể thêm nhiều hậu điều kiện hơn:
* `validate_completeness` để kiểm tra các trường bắt buộc không bị trống.
* `validate_date_format` để đảm bảo định dạng ngày tháng được phân tích đúng cách.
Mỗi hậu điều kiện chỉ là một hàm Python đưa ra lỗi khi có điều gì đó không đúng.

Đánh Đổi: Hiệu Năng và Chi Phí

Mô hình lập trình này rất gọn gàng và mạnh mẽ, nhưng cũng có một số đánh đổi quan trọng cần xem xét.

Độ Trễ (Latency)

Đây là yếu tố đầu tiên. Mỗi lần thử lại là một cuộc gọi mô hình khác. Nếu bạn đặt `max_attempts=3`, bạn có thể phải chờ tới ba lượt đi và về đến mô hình. Điều này có thể chấp nhận được cho xử lý hàng loạt (batch processing) và các công việc chạy nền (background jobs). Tuy nhiên, nó không lý tưởng cho các API hướng người dùng, nơi bạn cần phản hồi trong vòng dưới một giây.

Chi Phí

Thứ hai là chi phí. Các lần thử lại sẽ nhân lên chi phí API của bạn, và mỗi lần gọi đều sử dụng một phiên bản mới của agent. Nếu các hậu điều kiện của bạn thường xuyên thất bại, bạn sẽ phải trả tiền cho nhiều lần thử mỗi lần trích xuất.

Tuy nhiên, vòng lặp thử lại này là một **tính năng, không phải là một lỗi**. Bạn cần theo dõi tỷ lệ thất bại của quá trình xác thực. Nếu các hậu điều kiện thất bại trong hầu hết các lần thử đầu tiên, thì vấn đề nằm ở prompt của bạn cần được cải thiện, chứ không phải cần nhiều lần thử lại hơn. Các hậu điều kiện có mặt để bắt các trường hợp ngoại lệ, không phải để sửa chữa các prompt bị lỗi cơ bản.

Sự Đánh Đổi Hợp Lý

Về cơ bản, bạn đang đánh đổi độ trễ và chi phí để đổi lấy **sự đảm bảo về tính đúng đắn** của logic mà bạn không cần phải tự mình triển khai. Bạn không cần phải dự đoán mọi định dạng hóa đơn, xử lý mọi trường hợp ngoại lệ về cách các nhà cung cấp liệt kê các mục hàng, hay viết một bộ phân tích có tính đến hàng chục cách mọi người định dạng tiền tệ. Mô hình sẽ xử lý sự mơ hồ đó, và các hậu điều kiện sẽ bắt các lỗi.

Đây là một sự đánh đổi đúng đắn cho các trường hợp như:
* Các pipeline xử lý tài liệu.
* Trích xuất dữ liệu tài chính.
* Bất kỳ tác vụ nào mà **câu trả lời sai tệ hơn câu trả lời chậm**.

Ngược lại, đây là một sự đánh đổi không phù hợp cho:
* Giao diện trò chuyện thời gian thực.
* Các hoạt động có khối lượng lớn và nhạy cảm về chi phí.

Cần lưu ý rằng thư viện này vẫn đang trong giai đoạn thử nghiệm và là một kho lưu trữ mới từ Strands Labs. Nó rất đáng để khám phá, và hãy mong đợi nó sẽ thay đổi khi trưởng thành.

Mô Hình Lập Trình Tối Thượng: Ý Định và Sự Chính Xác

Điều thực sự thú vị về AI Functions là mô hình lập trình mà nó mang lại. Bạn khai báo **ý định** thông qua chữ ký hàm và docstring. Bạn định nghĩa **sự đúng đắn** thông qua các hậu điều kiện, và AI xử lý **triển khai**.

Ưu Điểm Của Sự Phân Tách

Sự phân tách này giữ cho logic xác thực của bạn là mã Python thực mà bạn kiểm soát, kiểm thử và quản lý phiên bản. Nó không bị chôn vùi trong một prompt hay hy vọng mô hình “hiểu” ý bạn muốn nói về sự đúng đắn.

  • Khi các yêu cầu thay đổi, bạn chỉ cần cập nhật các hậu điều kiện.
  • Khi mô hình AI được cải thiện, bạn sẽ có tỷ lệ thành công trong lần thử đầu tiên tốt hơn mà không cần thay đổi mã của mình.

Các hậu điều kiện cung cấp cho bạn một cách để định nghĩa “đúng” một cách lập trình cho lĩnh vực của bạn, điều mà kỹ thuật prompt engineering đơn thuần không thể làm được. Một prompt có thể yêu cầu mô hình “đảm bảo các phép toán khớp nhau”, nhưng một hậu điều kiện thực sự kiểm tra điều đó và cung cấp phản hồi cụ thể khi nó không khớp.

Khám Phá Thư Viện AI Functions

Cá nhân tôi đã có rất nhiều trải nghiệm thú vị khi thử nghiệm với dự án mới này. Tôi khuyến khích bạn tự mình thử nghiệm mô hình này. Thư viện đã có sẵn trên GitHub để bạn khám phá và đóng góp.

Thư viện AI Functions: https://github.com/strands-labs/ai-functions

Chỉ mục