Viết tool definition cho API của khách: sáu bước để model chọn đúng tool và điền đúng tham số
Model không đọc code hay Swagger của khách. Mọi thứ nó biết về API chỉ gói trong vài dòng mô tả bạn viết, nên tool gọi sai thường bắt đầu từ chính mấy dòng đó.
Model chỉ biết những gì bạn viết, nên bản sau nói rõ từng thứ mà bản trước bỏ ngỏ.
Đồ hoạ: FDE Times
Tóm tắt nhanh
- Model chọn tool chỉ dựa vào tên, description và input, vì thế description là thứ bạn kiểm soát được và quan trọng nhất.
- Gộp các endpoint liên quan thành ít tool hơn, đặt tên kiểu acme_orders, dùng order_id thay vì order.
- Lỗi và response cũng là prompt: viết lại lỗi để model tự sửa, chỉ trả về dữ liệu nó thật sự cần.
Tài liệu tool use của Anthropic viết rằng description chi tiết là yếu tố quan trọng nhất, bỏ xa mọi yếu tố khác, quyết định một tool chạy tốt hay không. Nói cách khác, trước khi nghĩ tới chuyện đổi model hay đổi framework, hãy xem lại mấy câu mô tả do chính bạn viết.
Lý do rất đơn giản. Theo khóa Agents của Hugging Face, phần mô tả tool được chèn vào system prompt, nên model chỉ biết về tool những gì được viết ở đó. Paragon cũng mô tả cùng cơ chế: model chọn tool dựa vào tên, description và các input đi kèm từng tool.
Khách có sẵn một API nội bộ, bạn phải bọc nó lại để agent dùng được. Bài này hướng dẫn từng bước trên một ví dụ giả định, kèm cách kiểm tra sau mỗi bước.
Bạn sẽ làm gì, và cần chuẩn bị những gì?
Thử hình dung khách là một công ty giao vận tên Acme. Họ có ba endpoint: tra đơn theo mã, tìm đơn theo số điện thoại, và hủy đơn. Mục tiêu của bạn là để agent chăm sóc khách hàng gọi đúng endpoint với đúng tham số khi người dùng hỏi bằng ngôn ngữ tự nhiên.
Cần nhớ cách function calling vận hành. Prompting Guide mô tả nó là cách biến ngôn ngữ tự nhiên thành lời gọi API hợp lệ, nhưng model chỉ sinh ra đối số dạng JSON, còn việc gọi API thật là code của bạn. Bạn cần quen JSON Schema, có tài liệu API của khách, và có một lớp wrapper nhỏ đứng giữa model và API.
Bước 1: Nhìn ra vì sao bản nháp đầu tiên hỏng
Đây là kiểu định nghĩa hay gặp khi người ta copy thẳng tên endpoint vào:
{
"name": "get",
"description": "Get order",
"input_schema": {
"type": "object",
"properties": { "order": { "type": "string" } },
"required": ["order"]
}
}
Tên “get” không cho biết lấy cái gì, từ hệ thống nào. Tham số “order” có thể là mã đơn, cả đối tượng đơn hàng hay số thứ tự. Description chỉ hai chữ thì không nói khi nào nên dùng.
Bài trên blog kỹ thuật của Anthropic khuyên đặt tên tham số không mơ hồ, ví dụ dùng user_id thay cho user; ở đây cũng vậy, order_id rõ nghĩa hơn hẳn order.
Kiểm tra: đọc to định nghĩa cho một đồng nghiệp chưa từng thấy API của Acme. Nếu họ hỏi lại “order là gì?”, model cũng sẽ hiểu lầm y như vậy.
Bước 2: Gộp endpoint, đặt tên có tiền tố dịch vụ
Phản xạ đầu tiên của nhiều người là mỗi endpoint một tool. Tài liệu Anthropic khuyên ngược lại: gộp các thao tác liên quan vào ít tool hơn, dùng một tham số action, vì ít tool mà mạnh hơn thì model ít phải phân vân khi chọn.
Cũng tài liệu đó khuyên thêm tiền tố tên dịch vụ, kiểu github_list_prs hay slack_send_message, để khi thư viện tool lớn dần thì việc chọn vẫn rõ ràng.
Áp vào Acme, ba endpoint gộp thành một tool tên acme_orders, với action nhận một trong ba giá trị get, search, cancel. Sau này nếu agent có thêm tool của hệ thống CRM, tiền tố acme_ giúp phân biệt ngay.
Kiểm tra: liệt kê toàn bộ tool agent đang có. Nếu có hai tool mà bạn phải đọc kỹ mới biết khác nhau ở đâu, hãy gộp hoặc đổi tên.
Bước 3: Viết description như đang hướng dẫn nhân viên mới
Anthropic gợi ý hãy viết description theo cách bạn giải thích tool cho một người mới vào team, tức là nói ra hết những ngữ cảnh mà người trong nhà coi là hiển nhiên. Tài liệu của họ liệt kê những gì cần có: tool làm gì, khi nào dùng và khi nào không, mỗi tham số nghĩa là gì, các lưu ý.
Độ dài nên từ 3-4 câu trở lên cho mỗi tool, nhiều hơn nếu tool phức tạp. Lưu ý mỗi chuỗi JSON phải nằm trên một dòng; nếu muốn xuống dòng trong description, dùng ký tự thoát \n.
{
"name": "acme_orders",
"description": "Tra cứu, tìm kiếm hoặc hủy đơn giao hàng trong hệ thống Acme. Dùng action='get' khi người dùng đã đưa mã đơn; dùng action='search' khi chỉ có số điện thoại người nhận. Chỉ dùng action='cancel' khi người dùng nói rõ muốn hủy, và chỉ với đơn chưa xuất kho.
Không dùng tool này cho câu hỏi về giá cước hay khiếu nại bồi thường. Mã đơn có dạng ORD- theo sau là 8 chữ số.",
"input_schema": {
"type": "object",
"properties": {
"action": { "type": "string", "enum": ["get", "search", "cancel"] },
"order_id": { "type": "string", "description": "Mã đơn dạng ORD-12345678. Bắt buộc với get và cancel." },
"recipient_phone": { "type": "string", "description": "Số điện thoại người nhận, chỉ gồm chữ số. Dùng với search." }
},
"required": ["action"]
}
}
Định dạng mã đơn, quy tắc “chưa xuất kho” hay chuyện không xử lý khiếu nại ở đây đều là chi tiết giả định. Ở khách thật, bạn phải hỏi ra những quy tắc này, và thường chúng nằm trong đầu đội vận hành chứ không có trong tài liệu API.
Kiểm tra: description có câu nào bắt đầu bằng “Không dùng…” chưa?
Bước 4: Thêm ví dụ cho input khó định dạng
Với tool có input phức tạp hoặc nhạy cảm về định dạng, API của Anthropic có trường tùy chọn input_examples. Mỗi ví dụ phải hợp lệ theo input_schema, ví dụ sai sẽ bị trả lỗi 400. Ví dụ cũng tốn thêm token prompt, nên chỉ dùng khi định dạng thật sự hay bị điền sai. Đoạn dưới đây là bản giản lược:
"input_examples": [
{ "action": "get", "order_id": "ORD-20481234" },
{ "action": "search", "recipient_phone": "0901234567" }
]
Kiểm tra: gửi request lên. Nếu nhận lỗi 400, nhiều khả năng một ví dụ lệch schema, chẳng hạn sai tên trường hoặc giá trị action không nằm trong enum.
Bước 5: Response và lỗi cũng là prompt
Thứ API trả về sẽ quay lại context của model. Blog kỹ thuật của Anthropic khuyên tool chỉ trả về thông tin có giá trị cao, và gợi ý phân trang, lọc, cắt bớt khi payload lớn. Bài đó cũng chỉ ra rằng có thể viết lại thông báo lỗi để nói rõ cần sửa gì, giúp model tự điều chỉnh ở lần gọi sau.
Đoạn wrapper dưới đây là bản giản lược. Nó tách hai kiểu thất bại khác nhau: mã sai định dạng (bắt ngay trong wrapper, chưa cần gọi API) và mã đúng định dạng nhưng không tồn tại (API trả 404).
import re
# Giản lược: wrapper giữa model và API của khách
ORDER_ID_PATTERN = re.compile(r"^ORD-\d{8}$")
def run_acme_orders(args):
if args["action"] in ("get", "cancel"):
order_id = args.get("order_id", "")
if not ORDER_ID_PATTERN.match(order_id):
return ("order_id sai định dạng: phải là ORD- + 8 chữ số, "
"ví dụ ORD-20481234. Hãy hỏi lại người dùng mã đơn.")
resp = call_customer_api(args) # hàm HTTP của bạn
if resp.status == 404:
return ("Không tìm thấy đơn này dù mã đúng định dạng.
"
"Nếu người dùng có số điện thoại người nhận, "
"hãy gọi lại với action='search'.")
order = resp.json()
return {"order_id": order["id"], "status": order["status"],
"eta": order["eta"]} # bỏ các trường nội bộ model không cần
So với một chuỗi “404 Not Found” trần trụi, mỗi thông báo ở đây chỉ ra một đường đi tiếp khác nhau cho model: sai định dạng thì hỏi lại, không tồn tại thì chuyển sang tìm bằng số điện thoại. Cắt response xuống ba trường thì model khỏi phải lội qua vài chục trường nội bộ.
Kiểm tra: gọi thử hai lần, một với mã sai định dạng, một với mã đúng định dạng nhưng không có thật. Lần gọi kế tiếp của model nên khác nhau ở hai trường hợp, thay vì thử lại đúng mã sai đó.
Bước 6: Đừng tin cảm giác, hãy chạy eval
Anthropic ghi nhận rằng chỉ những tinh chỉnh nhỏ trong description cũng có thể cải thiện kết quả rất mạnh. Điều này có hai chiều: sửa một câu có thể làm kết quả tốt lên, nhưng cũng có thể làm hỏng một trường hợp vốn đang chạy đúng.
Cách làm thực tế là soạn khoảng 20 câu hỏi lấy từ log chăm sóc khách hàng của khách. Với mỗi câu, ghi sẵn tool, action và đối số đúng. Mỗi lần sửa description thì chạy lại cả bộ và đếm số câu đúng.
Những cái bẫy nên tránh
Một lỗi hay gặp là sinh tool tự động từ OpenAPI rồi để nguyên. Làm vậy dễ dẫn tới đúng thứ tài liệu Anthropic khuyên tránh: hàng chục tool mỗi cái một endpoint, description chép từ comment của dev và quá ngắn để model phân biệt được. Một lỗi khác là để tham số tên id, user, data mà không ghi định dạng.
Cũng đừng trả nguyên JSON của API, kể cả lỗi, rồi thắc mắc vì sao model lặp đi lặp lại cùng một lời gọi sai.
Ở chỗ khách, kỹ năng này trông ra sao?
Ở site khách, phần khó hiếm khi là cú pháp JSON. Khó là ngồi với đội vận hành để hỏi ra những câu kiểu “đơn đã xuất kho thì có hủy được không”, vì đó chính là nội dung của câu “khi nào không dùng” trong description.
Nếu bạn đang nhắm vai trò FDE, hãy để ý các JD có nhắc tới tool calling, agent integration hay việc làm việc trực tiếp với API của khách.
Khi viết CV, đừng chỉ ghi “tích hợp LLM với API nội bộ”. Hãy ghi bạn đã gộp bao nhiêu endpoint thành bao nhiêu tool, và độ chính xác trên bộ eval thay đổi thế nào sau các lần sửa description. Một con số trước và sau như vậy cho nhà tuyển dụng thấy bạn làm được việc mà khách sẽ trả tiền cho.
Lần tới khi agent gọi nhầm API, đừng vội đổi model. Mở description ra và đọc nó như một nhân viên mới vào làm ngày đầu tiên.