Mô hình ngôn ngữ không biết giá hàng trong kho của bạn hay thời tiết hôm nay. Tool use (còn gọi là function calling) giải quyết chuyện đó: bạn mô tả các hàm Python, mô hình quyết định khi nào cần gọi hàm nào, còn code của bạn chạy hàm thật rồi gửi kết quả lại.
Bài này nối tiếp Gọi API mô hình ngôn ngữ bằng Python, vẫn dùng SDK anthropic.
Vòng lặp tool use diễn ra thế nào
- Bạn gửi câu hỏi kèm danh sách công cụ (tên, mô tả, tham số).
- Mô hình trả về yêu cầu gọi công cụ, kèm tham số, thay vì câu trả lời.
- Code của bạn chạy hàm, gửi kết quả lại, và mô hình viết câu trả lời cuối.
Mô hình không bao giờ tự chạy code của bạn. Nó chỉ yêu cầu, quyền quyết định vẫn nằm ở phía bạn.
Cách nhanh: decorator @beta_tool
import anthropic
from anthropic import beta_tool
from dotenv import load_dotenv
load_dotenv()
client = anthropic.Anthropic()
BANG_GIA = {"bàn phím": 450_000, "chuột": 220_000, "tai nghe": 890_000}
@beta_tool
def tra_gia(ten_san_pham: str) -> str:
"""Tra giá bán hiện tại của một sản phẩm trong cửa hàng.
Args:
ten_san_pham: Tên sản phẩm, ví dụ "chuột".
"""
gia = BANG_GIA.get(ten_san_pham.lower())
if gia is None:
return f"Không có sản phẩm '{ten_san_pham}'"
return f"{gia:,} đồng"
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=4096,
tools=[tra_gia],
messages=[{"role": "user", "content": "Mua 2 con chuột và 1 bàn phím hết bao nhiêu?"}],
)
tin_cuoi = runner.until_done()
print("".join(k.text for k in tin_cuoi.content if k.type == "text"))
SDK đọc chữ ký hàm và docstring để tạo mô tả công cụ, rồi tự chạy vòng lặp cho đến khi mô hình trả lời xong. Docstring vì thế không phải thứ trang trí: mô hình dựa vào nó để biết khi nào nên gọi hàm.
Tự viết vòng lặp để hiểu bản chất
cong_cu = [{
"name": "tra_gia",
"description": "Tra giá bán hiện tại của một sản phẩm trong cửa hàng.",
"input_schema": {
"type": "object",
"properties": {"ten_san_pham": {"type": "string"}},
"required": ["ten_san_pham"],
},
}]
def tim_gia(ten_san_pham):
gia = BANG_GIA.get(ten_san_pham.lower())
return f"{gia:,} đồng" if gia else "Không có sản phẩm này"
lich_su = [{"role": "user", "content": "Mua 2 con chuột và 1 bàn phím hết bao nhiêu?"}]
while True:
ph = client.messages.create(
model="claude-opus-5-5", max_tokens=4096, tools=cong_cu, messages=lich_su
)
lich_su.append({"role": "assistant", "content": ph.content})
if ph.stop_reason != "tool_use":
break
ket_qua = [
{"type": "tool_result", "tool_use_id": k.id, "content": tim_gia(**k.input)}
for k in ph.content if k.type == "tool_use"
]
lich_su.append({"role": "user", "content": ket_qua})
print("".join(k.text for k in ph.content if k.type == "text"))
Ở ví dụ này, mô hình thường xin tra giá chuột và bàn phím cùng lúc. Vì vậy hãy gom tất cả tool_result vào chung một tin nhắn như trên, đừng gửi từng cái một.
Những điều cần nhớ
- Mô hình tự quyết có gọi công cụ hay không. Muốn nó chắc chắn dùng, hãy nói rõ trong prompt. Các mô hình mới không còn cho ép gọi bằng
tool_choice. - Khi hàm lỗi, trả về
tool_resultcó"is_error": Truevà lời giải thích. Mô hình sẽ tự sửa tham số hoặc báo lại cho người dùng. - Coi tham số mô hình gửi như dữ liệu người dùng nhập: kiểm tra trước khi dùng. Đừng tạo công cụ xoá dữ liệu hay chuyển tiền mà không có bước xác nhận của con người.
