Dùng AI

Tool use: cho mô hình AI gọi hàm Python của bạn

Tool use, hay function calling, là cách cho mô hình ngôn ngữ dùng hàm do bạn viết: tra cơ sở dữ liệu, gọi một API, tính toán chính xác. Mô hình không tự chạy hàm; nó trả về tên hàm và tham số, chương trình của bạn chạy hàm rồi gửi kết quả lại để mô hình trả lời. Bài này giải thích vòng gọi hàm theo tài liệu của Anthropic, kèm chương trình Python tra điểm và quy đổi thang 4 có bắt lỗi và giới hạn số vòng.

Ý chính
  • Tool use (OpenAI gọi là function calling) cho mô hình dùng hàm do bạn viết. Mô hình chỉ chọn hàm và tham số; chương trình của bạn chạy hàm.
  • Một vòng gồm bốn bước: gửi câu hỏi kèm danh sách công cụ, nhận khối tool_use, chạy hàm, gửi khối tool_result. Lặp tới khi mô hình trả lời xong.
  • Mô tả công cụ là phần quan trọng nhất: mô hình quyết định gọi hàm nào dựa vào tên, mô tả và schema tham số.
  • Luôn bắt lỗi khi chạy hàm, trả lỗi lại cho mô hình, và đặt giới hạn số vòng để chương trình không lặp mãi.

Bài này dựa trên các trang tài liệu tool use của Anthropic (đọc ngày 11/11/2026). Cơ chế ở các hãng khác tương tự, chỉ khác tên trường trong API. Nếu chưa từng gọi API mô hình, đọc trước bài gọi API AI bằng Python.

Vì sao cần tool use

Mô hình ngôn ngữ giỏi hiểu câu hỏi và viết câu trả lời, nhưng có ba giới hạn. Nó không biết dữ liệu riêng của bạn, như điểm của học sinh trong trường. Nó không biết thông tin mới hơn dữ liệu huấn luyện. Và nó có thể tính sai, nhất là với phép tính dài. Tool use giải quyết cả ba bằng cách để mô hình nhờ chương trình của bạn làm những việc đó.

Tài liệu của Anthropic chia công cụ làm hai loại theo nơi code chạy. Công cụ phía khách (client tools) chạy trong chương trình của bạn: mô hình trả về yêu cầu gọi, bạn chạy và gửi kết quả lại. Công cụ phía máy chủ (server tools), như tìm kiếm web hay chạy code, chạy trên hạ tầng của Anthropic và bạn nhận kết quả luôn. Bài này nói về loại thứ nhất, vì đó là cách nối mô hình với dữ liệu và hệ thống của riêng bạn.

Tool use: một vòng gọi hàm Mô hình chọn hàm, chương trình của bạn chạy hàm 1. Gửi câu hỏiKèm các hàmtên, mô tả, schema 2. tool_useTên hàm, tham sốmô hình chưa chạy 3. Bạn chạy hàmKiểm tham sốbắt lỗi 4. tool_resultGửi kết quả lạimô hình trả lời Lặp lại tới khi mô hình trả lời xong, có giới hạn số vòng
Tool use: một vòng gọi hàm 1. Gửi câu hỏiKèm các hàmtên, mô tả, schema 2. tool_useTên hàm, tham sốmô hình chưa chạy 3. Bạn chạy hàmKiểm tham sốbắt lỗi 4. tool_resultGửi kết quả lạimô hình trả lời Lặp lại tới khi mô hình trả lời xong, có giới hạn số vòng
Hình 1. Một vòng tool use: (1) gửi câu hỏi kèm danh sách hàm (tên, mô tả, schema); (2) mô hình trả về khối tool_use gồm tên hàm và tham số, chưa chạy gì; (3) chương trình của bạn chạy hàm, xét tham số, bắt lỗi; (4) gửi tool_result, mô hình dùng kết quả để trả lời. Lặp tới khi xong, có giới hạn số vòng.

Bốn bước của một vòng

Bước 1: gửi câu hỏi kèm danh sách công cụ. Mỗi công cụ có ba phần: tên, mô tả bằng lời, và schema tham số theo chuẩn JSON Schema (kiểu dữ liệu, tham số nào bắt buộc, giá trị cho phép). Danh sách này đi trong tham số tools của yêu cầu.

Bước 2: mô hình trả về yêu cầu gọi. Nếu mô hình thấy cần dùng công cụ, câu trả lời có lý do dừng là tool_use và một hoặc nhiều khối tool_use, mỗi khối gồm mã định danh, tên hàm và tham số. Đến đây chưa có hàm nào được chạy.

Bước 3: chương trình của bạn chạy hàm. Bạn đọc tên hàm, tìm hàm tương ứng, chạy với tham số mô hình đưa. Đây là chỗ bạn quyết định: hàm nào được phép, tham số có hợp lệ không, có cần hỏi người dùng trước khi làm việc quan trọng không.

Bước 4: gửi kết quả lại. Kết quả đi trong khối tool_result, kèm đúng mã định danh của khối tool_use tương ứng. Bạn gửi lại cả lịch sử hội thoại: câu hỏi, câu trả lời có khối tool_use của mô hình, và các kết quả. Mô hình đọc kết quả rồi hoặc trả lời, hoặc yêu cầu gọi thêm hàm khác. Vì vậy chương trình cần một vòng lặp.

Ví dụ: trợ lý tra điểm

Chương trình dưới đây có hai công cụ: tra_diem tra điểm hệ 10 của một học sinh ở một môn (từ dữ liệu giả định trong code), và quy_doi_thang_4 đổi điểm hệ 10 sang thang 4 theo một bảng ngưỡng. Hàm hoi chạy vòng lặp tối đa năm lượt; hàm chay_cong_cu chạy hàm được yêu cầu và bắt lỗi.

"""Tool use: cho Claude gọi hàm Python của bạn để tra điểm và quy đổi thang 4.

Mô hình KHÔNG tự chạy hàm. Nó trả về khối tool_use (tên hàm + tham số), chương trình của bạn chạy hàm,
rồi gửi kết quả lại trong khối tool_result. Vòng lặp dừng khi mô hình trả lời xong (stop_reason khác "tool_use"),
khi câu trả lời bị cắt hay bị từ chối (báo rõ, không trả chữ dở dang), hoặc khi đã hết số vòng cho phép.

Cài: python -m pip install anthropic ; đặt biến môi trường ANTHROPIC_API_KEY.
Dữ liệu điểm bên dưới là dữ liệu giả định.
"""
import json
import os
import sys

DIEM = {"an": {"toan": 8.5, "tin": 9.0}, "binh": {"toan": 6.0, "tin": 7.5}}

CONG_CU = [
    {
        "name": "tra_diem",
        "description": "Tra điểm hệ 10 của một học sinh ở một môn. Dùng khi người dùng hỏi điểm cụ thể.",
        "input_schema": {
            "type": "object",
            "properties": {
                "hoc_sinh": {"type": "string", "description": "Tên học sinh, chữ thường không dấu, ví dụ: an"},
                "mon": {"type": "string", "enum": ["toan", "tin"]},
            },
            "required": ["hoc_sinh", "mon"],
        },
    },
    {
        "name": "quy_doi_thang_4",
        "description": "Quy đổi một điểm hệ 10 sang thang 4 theo bảng: >=8.5 là 4.0, >=7.0 là 3.0, >=5.5 là 2.0, >=4.0 là 1.0, còn lại 0.",
        "input_schema": {
            "type": "object",
            "properties": {"diem": {"type": "number", "minimum": 0, "maximum": 10}},
            "required": ["diem"],
        },
    },
]


def tra_diem(hoc_sinh, mon):
    if hoc_sinh not in DIEM:
        raise KeyError(f"không có học sinh '{hoc_sinh}'")
    return DIEM[hoc_sinh][mon]


def quy_doi_thang_4(diem):
    for nguong, thang4 in [(8.5, 4.0), (7.0, 3.0), (5.5, 2.0), (4.0, 1.0)]:
        if diem >= nguong:
            return thang4
    return 0.0


HAM = {"tra_diem": tra_diem, "quy_doi_thang_4": quy_doi_thang_4}


def chay_cong_cu(ten, dau_vao):
    """Chạy một hàm theo yêu cầu của mô hình. Lỗi được trả lại cho mô hình (is_error) thay vì làm sập chương trình."""
    if ten not in HAM:
        return f"Không có công cụ tên {ten}", True
    try:
        return json.dumps(HAM[ten](**dau_vao), ensure_ascii=False), False
    except (KeyError, TypeError, ValueError) as e:
        return f"Lỗi: {e}", True


def hoi(client, cau_hoi, mo_hinh="claude-opus-5-5", toi_da_vong=5, nhat_ky=None):
    tin_nhan = [{"role": "user", "content": cau_hoi}]
    for _ in range(toi_da_vong):
        # Opus 5.5 luôn suy nghĩ trước khi trả lời, và token suy nghĩ tính vào max_tokens dù không hiện ra,
        # nên để rộng chỗ. max_tokens chỉ là mức trần: bạn trả tiền theo số token thật sự sinh ra.
        tl = client.messages.create(model=mo_hinh, max_tokens=16000, tools=CONG_CU, messages=tin_nhan)
        if tl.stop_reason in ("max_tokens", "model_context_window_exceeded"):
            return "Dừng: câu trả lời bị cắt giữa chừng (hết chỗ), chưa xong."
        if tl.stop_reason == "refusal":
            return "Dừng: mô hình từ chối trả lời câu này."
        if tl.stop_reason != "tool_use":
            return "".join(k.text for k in tl.content if k.type == "text")
        tin_nhan.append({"role": "assistant", "content": tl.content})
        ket_qua = []
        for k in tl.content:
            if k.type == "tool_use":
                noi_dung, loi = chay_cong_cu(k.name, k.input)
                if nhat_ky is not None:
                    nhat_ky.append((k.name, k.input, noi_dung))
                ket_qua.append({"type": "tool_result", "tool_use_id": k.id, "content": noi_dung, "is_error": loi})
        tin_nhan.append({"role": "user", "content": ket_qua})
    return "Dừng: đã hết số vòng gọi công cụ cho phép."


def main(cau_hoi):
    if not os.environ.get("ANTHROPIC_API_KEY"):
        sys.exit("Chưa có ANTHROPIC_API_KEY. Đặt biến môi trường trước khi chạy.")
    import anthropic
    nhat_ky = []
    try:
        tra_loi = hoi(anthropic.Anthropic(), cau_hoi, nhat_ky=nhat_ky)
    except anthropic.APIStatusError as e:
        sys.exit(f"API báo lỗi {e.status_code}: {e.message}")
    except anthropic.APIConnectionError:
        sys.exit("Không kết nối được tới API. Kiểm tra mạng rồi thử lại.")
    for ten, vao, ra in nhat_ky:
        print(f"[gọi {ten}({json.dumps(vao, ensure_ascii=False)}) -> {ra}]")
    print(tra_loi)


if __name__ == "__main__":
    main(" ".join(sys.argv[1:]) or "Điểm Tin của An là bao nhiêu, đổi sang thang 4 được mấy?")

Với câu hỏi "Điểm Tin của An là bao nhiêu, đổi sang thang 4 được mấy?", vòng lặp diễn ra như sau. Lượt một, mô hình yêu cầu gọi tra_diem với học sinh "an", môn "tin"; chương trình trả 9.0. Lượt hai, mô hình yêu cầu quy_doi_thang_4 với điểm 9.0; chương trình trả 4.0. Lượt ba, mô hình có đủ dữ liệu và trả lời bằng lời. Mô hình có thể gọi cả hai hàm trong một lượt hoặc theo thứ tự khác; chương trình xử lý được cả hai trường hợp vì nó duyệt mọi khối tool_use trong mỗi câu trả lời.

Phần quy đổi được giao cho hàm chứ không để mô hình tự tính. Đây là nguyên tắc chung: việc gì cần chính xác tuyệt đối và có quy tắc rõ ràng thì viết thành hàm.

Viết mô tả công cụ cho tốt

Mô hình quyết định gọi hàm nào và điền tham số gì dựa hoàn toàn vào tên, mô tả và schema. Vài điều giúp mô hình dùng đúng:

  • Mô tả nói hàm làm gì, khi nào nên dùng, và trả về gì. Câu "Tra điểm hệ 10 của một học sinh ở một môn. Dùng khi người dùng hỏi điểm cụ thể" rõ hơn nhiều so với "lấy điểm".
  • Mô tả từng tham số kèm ví dụ định dạng, như "tên học sinh, chữ thường không dấu, ví dụ: an".
  • Dùng enum khi tham số chỉ có vài giá trị hợp lệ, và minimum, maximum cho số. Schema chặt thì mô hình ít điền sai.
  • Mỗi hàm làm một việc. Một hàm "làm mọi thứ" với nhiều tham số tùy chọn khiến mô hình dễ chọn sai.

Tài liệu của Anthropic còn lưu ý: nếu câu hỏi thiếu thông tin để điền tham số bắt buộc, mô hình có thể hỏi lại, nhưng cũng có thể tự đoán một giá trị. Vì vậy hàm của bạn không được tin tham số một cách mù quáng.

Bắt lỗi và giới hạn

Trong ví dụ, hỏi điểm của một học sinh không có trong dữ liệu sẽ làm hàm tra_diem báo lỗi. Thay vì để chương trình dừng, chay_cong_cu bắt lỗi và gửi lại cho mô hình một tool_result có is_error là đúng, kèm lời nhắn. Mô hình đọc lỗi và thường sẽ nói với người dùng là không tìm thấy, hoặc hỏi lại tên. Một yêu cầu gọi hàm không có trong danh sách cũng được trả lỗi theo cách đó.

Giới hạn số vòng là lớp bảo vệ thứ hai. Nếu vì lý do nào đó mô hình cứ gọi công cụ mãi, vòng lặp dừng sau năm lượt và báo rõ. Không có giới hạn này, một lỗi nhỏ có thể khiến chương trình gọi API hàng trăm lần và tốn tiền. Mỗi lượt gọi cũng để max_tokens rộng (16000), vì Claude Opus 5.5 luôn suy nghĩ trước khi trả lời và phần suy nghĩ tính vào giới hạn này; nếu câu trả lời vẫn bị cắt (stop_reason là max_tokens) hay bị từ chối (refusal), hàm hoi báo rõ thay vì trả về chữ dở dang.

Lớp thứ ba là quyền. Chỉ đưa vào danh sách những hàm an toàn để mô hình tự gọi. Hàm có tác dụng lớn, như xóa dữ liệu hay gửi email, nên dừng lại hỏi người dùng xác nhận trước khi chạy. Bài giới hạn quyền khi cho AI tự chạy lệnh nói kỹ hơn về điểm này.

Chi phí của tool use

Theo trang tài liệu, yêu cầu có công cụ được tính tiền như yêu cầu thường, theo số token vào và ra. Phần token thêm đến từ danh sách công cụ (tên, mô tả, schema), các khối tool_use và tool_result, cộng một đoạn chỉ dẫn hệ thống mà API tự thêm khi có công cụ; với Claude Opus 5.5 đoạn này là 286 token theo bảng trong tài liệu. Mỗi vòng gửi lại cả lịch sử, nên hội thoại nhiều vòng tốn hơn tổng các câu hỏi riêng lẻ. Công cụ phía máy chủ như tìm kiếm web có thể tính thêm phí theo lượt dùng.

Từ tool use đến agent và MCP

Vòng lặp trong bài chính là lõi của một agent: mô hình tự quyết định bước tiếp theo, gọi công cụ, đọc kết quả, lặp lại. Bài tự xây agent bằng Python mở rộng vòng lặp này với nhiều công cụ hơn. Khi muốn dùng lại cùng một bộ công cụ cho nhiều ứng dụng, giao thức MCP giúp đóng gói công cụ thành máy chủ riêng; xem bài MCP là gì. Thư viện anthropic cũng có sẵn công cụ chạy vòng lặp tự động (tool runner); viết tay một lần như trong bài giúp bạn hiểu nó làm gì.

Cẩn thận
  • Để mô hình tự tính những việc cần chính xác thay vì viết thành hàm.
  • Mô tả công cụ một hai chữ, không nói khi nào dùng, không có ví dụ tham số.
  • Tin tham số mô hình đưa mà không kiểm tra, nhất là với hàm ghi hoặc xóa dữ liệu.
  • Để lỗi trong hàm làm sập chương trình thay vì trả lỗi lại cho mô hình.
  • Vòng lặp không có giới hạn số lượt.

Câu hỏi thường gặp

Function calling và tool use khác nhau không?

Cùng một cơ chế. OpenAI gọi là function calling, Anthropic gọi là tool use; khác nhau ở tên trường trong API.

Mô hình có chạy code của tôi không?

Không, với công cụ phía khách. Mô hình chỉ trả về tên hàm và tham số; chương trình của bạn chạy hàm trên máy của bạn.

Có thể gửi bao nhiêu công cụ?

Không nên quá nhiều trong một yêu cầu, vì mỗi công cụ tốn token và làm mô hình khó chọn. Khi có rất nhiều công cụ, tài liệu có công cụ tìm kiếm công cụ để nạp dần.

Làm sao bắt mô hình phải gọi công cụ?

Cách đơn giản là nói rõ trong chỉ dẫn hệ thống, ví dụ yêu cầu dùng công cụ để tra trước khi trả lời. Tài liệu có tham số tool_choice cho việc này; mức hỗ trợ khác nhau giữa các mô hình, nên xem trang tài liệu của mô hình bạn dùng.

Học tiếp

Nếu bạn muốn học Python và AI bài bản cùng giảng viên, tham khảo khóa học AI của Big-O Coding.

Muốn học bài bản cùng giảng viên?

Khóa AI – Introduction to Machine Learning: nền tảng Máy học từ thuật toán cơ bản đến Neural Networks và LLM, học bằng Python. Học thử buổi 1 miễn phí.

Xem Khóa AI

Nguồn tham khảo

Chia sẻ: Facebook LinkedIn
Nhắn Zalo tư vấnZalo Gọi 0937 401 483