Thiết kế Tool Use cho AI Agent — Nguyên tắc và best practices
Điểm nổi bật
Nhấn để đến mục tương ứng
- 1 Một tool được thiết kế tốt giúp Agent hoạt động chính xác và đáng tin cậy.
- 2 Nhất quán trong cùng bộ tool: Nếu bạn có "get_user", hãy dùng "get_order" thay vì "fetch_order" hoặc "retrieve_order".
- 3 Agent đọc tên tool nên sự rõ ràng quan trọng hơn sự ngắn gọn.
- 4 Dùng enum khi có giá trị cố định: Thay vì cho phép string tự do, liệt kê các giá trị hợp lệ giúp Agent chọn đúng.
- 5 Nguyên tắc 4: Xử lý lỗi rõ ràng Agent cần hiểu tại sao tool thất bại để quyết định hành động tiếp theo: thử lại, hỏi thêm thông tin, hoặc báo lỗi cho người dùng.
Tool use (hay function calling) là khả năng cho phép AI Agent tương tác với thế giới bên ngoài — gọi API, truy vấn database, xử lý file, gửi email. Chất lượng của tool quyết định trực tiếp khả năng của Agent. Một tool được thiết kế tốt giúp Agent hoạt động chính xác và đáng tin cậy. Một tool thiết kế kém dẫn đến lỗi, hiểu sai và trải nghiệm người dùng tệ. Bài viết này trình bày các nguyên tắc thiết kế tool hiệu quả, kèm ví dụ thực tế và anti-patterns cần tránh.
Nguyên tắc 1: Tên tool rõ ràng và nhất quán
Tên tool là tín hiệu đầu tiên giúp Agent quyết định nên dùng tool nào. Tên tốt giúp Agent chọn đúng tool mà không cần đọc description chi tiết.
Quy tắc đặt tên
- Dùng verb_noun format: get_weather, search_products, create_order, send_email. Verb cho biết hành động, noun cho biết đối tượng.
- Tránh tên mơ hồ: "process_data" (xử lý gì? dữ liệu gì?) hoặc "handle" (handle cái gì?). Thay vào đó dùng "validate_email", "calculate_shipping_cost".
- Nhất quán trong cùng bộ tool: Nếu bạn có "get_user", hãy dùng "get_order" thay vì "fetch_order" hoặc "retrieve_order". Chọn một convention và giữ nhất quán.
- Tránh viết tắt: "calc_ship" khó hiểu hơn "calculate_shipping". Agent đọc tên tool nên sự rõ ràng quan trọng hơn sự ngắn gọn.
// Tên tốt
"get_customer_by_id"
"search_products"
"create_invoice"
"cancel_subscription"
"list_recent_transactions"
// Tên kém
"data" // Quá mơ hồ
"doStuff" // Không rõ làm gì
"helper" // Không mô tả chức năng
"process" // Quá chung chung
"customerAPI" // Không rõ hành động
Nguyên tắc 2: Mô tả (Description) chính xác
Description là thông tin quan trọng nhất giúp Agent hiểu khi nào nên dùng tool và kỳ vọng kết quả gì. Một description tốt cần ba phần:
- Chức năng: Tool làm gì, bằng một câu ngắn gọn
- Khi nào dùng: Tình huống nào nên dùng tool này (và không nên dùng)
- Kết quả trả về: Format và nội dung kết quả
// Description tốt
{
"name": "search_products",
"description": "Tìm kiếm sản phẩm trong catalog theo từ khóa, danh mục hoặc khoảng giá. Trả về danh sách tối đa 20 sản phẩm phù hợp nhất, sắp xếp theo độ liên quan. Dùng tool này khi người dùng hỏi về sản phẩm, muốn so sánh hoặc tìm mua hàng. KHÔNG dùng cho tra cứu đơn hàng — dùng get_order cho việc đó."
}
// Description kém
{
"name": "search_products",
"description": "Tìm sản phẩm"
// Quá ngắn: không biết tìm theo tiêu chí gì, trả về gì
}
Nguyên tắc 3: Schema chặt chẽ với validation
Input schema định nghĩa chính xác những gì tool chấp nhận. Schema tốt giúp Agent truyền đúng tham số và giảm lỗi runtime.
// Schema tốt — chi tiết, có constraint và description cho từng field
{
"name": "search_products",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Từ khóa tìm kiếm. Có thể là tên sản phẩm, thương hiệu hoặc mô tả.",
"minLength": 1,
"maxLength": 200
},
"category": {
"type": "string",
"description": "Danh mục sản phẩm. Để trống nếu muốn tìm tất cả danh mục.",
"enum": ["electronics", "clothing", "food", "home", "beauty"]
},
"min_price": {
"type": "number",
"description": "Giá tối thiểu (VND). Mặc định 0.",
"minimum": 0
},
"max_price": {
"type": "number",
"description": "Giá tối đa (VND). Mặc định không giới hạn.",
"minimum": 0
},
"sort_by": {
"type": "string",
"description": "Tiêu chí sắp xếp kết quả",
"enum": ["relevance", "price_asc", "price_desc", "rating", "newest"],
"default": "relevance"
},
"limit": {
"type": "integer",
"description": "Số kết quả tối đa trả về",
"minimum": 1,
"maximum": 50,
"default": 20
}
},
"required": ["query"]
}
}
Lưu ý quan trọng về schema
- Mô tả rõ từng field: Agent đọc description để biết nên truyền giá trị gì. Field không có description dễ bị truyền sai.
- Dùng enum khi có giá trị cố định: Thay vì cho phép string tự do, liệt kê các giá trị hợp lệ giúp Agent chọn đúng.
- Đặt default cho tham số tùy chọn: Agent không cần truyền giá trị cho mọi field.
- Phân biệt required vs optional: Chỉ required những field thật sự cần thiết. Càng ít required, Agent càng dễ sử dụng tool.
Nguyên tắc 4: Xử lý lỗi rõ ràng
Agent cần hiểu tại sao tool thất bại để quyết định hành động tiếp theo: thử lại, hỏi thêm thông tin, hoặc báo lỗi cho người dùng.
// Xử lý lỗi tốt — trả về thông tin đủ để Agent xử lý
function handleToolError(error) {
// Phân loại lỗi
if (error.code === "NOT_FOUND") {
return {
is_error: true,
content: [{
type: "text",
text: JSON.stringify({
error_type: "not_found",
message: "Không tìm thấy sản phẩm với ID này.",
suggestion: "Kiểm tra lại ID hoặc dùng search_products để tìm kiếm."
})
}]
};
}
if (error.code === "VALIDATION_ERROR") {
return {
is_error: true,
content: [{
type: "text",
text: JSON.stringify({
error_type: "validation",
message: "Tham số không hợp lệ: " + error.details,
invalid_fields: error.fields,
suggestion: "Kiểm tra lại giá trị các tham số."
})
}]
};
}
if (error.code === "RATE_LIMIT") {
return {
is_error: true,
content: [{
type: "text",
text: JSON.stringify({
error_type: "rate_limit",
message: "Đã vượt giới hạn số request.",
retry_after_seconds: error.retryAfter,
suggestion: "Đợi " + error.retryAfter + " giây rồi thử lại."
})
}]
};
}
// Lỗi không xác định
return {
is_error: true,
content: [{
type: "text",
text: JSON.stringify({
error_type: "internal",
message: "Lỗi hệ thống. Vui lòng thử lại sau.",
suggestion: "Nếu lỗi tiếp tục, hãy báo cho quản trị viên."
})
}]
};
}
Các nguyên tắc xử lý lỗi
- Phân loại lỗi: Agent cần biết đây là lỗi input (có thể sửa) hay lỗi hệ thống (cần thử lại hoặc bỏ qua)
- Đề xuất hành động: Kèm gợi ý để Agent biết làm gì tiếp theo
- Không expose thông tin nhạy cảm: Stack trace, connection string, internal path không nên xuất hiện trong error message trả về Agent
- Retry guidance: Với lỗi tạm thời (rate limit, timeout), cho biết khi nào nên thử lại
Nguyên tắc 5: Khi nào expose tool vs ẩn complexity
Không phải mọi thao tác đều nên là một tool riêng. Cần cân bằng giữa tính linh hoạt (nhiều tool nhỏ) và tính đơn giản (ít tool, mỗi tool làm nhiều việc).
Nên expose thành tool riêng khi:
- Agent cần quyết định có dùng hay không tùy ngữ cảnh
- Thao tác có input/output rõ ràng và độc lập
- Người dùng có thể yêu cầu trực tiếp (ví dụ: "gửi email", "tìm sản phẩm")
Nên ẩn (abstract) bên trong tool khi:
- Các bước luôn thực hiện cùng nhau theo thứ tự cố định
- Agent không cần biết chi tiết implementation
- Expose quá nhiều bước nhỏ khiến Agent khó điều phối
// Ví dụ: Tạo hóa đơn
// Cách 1: Quá nhiều tool nhỏ (KHÔnG NÊN)
// Agent phải gọi tuần tự: validate_customer -> calculate_tax
// -> generate_invoice_number -> create_invoice_record
// -> send_invoice_email
// => Quá phức tạp, dễ sai thứ tự, dễ bỏ sót bước
// Cách 2: Một tool gộp mọi thứ (cũng KHÔNG NÊN)
// create_and_send_invoice: tạo + gửi luôn, không cho phép
// tạo mà không gửi
// => Thiếu linh hoạt
// Cách 3: Cân bằng (NÊN)
// create_invoice: validate, calculate, generate, save (gộp logic nội bộ)
// send_invoice: gửi hóa đơn đã tạo qua email
// => Agent quyết định tạo xong có gửi ngay hay không
Tool Composition Patterns
Khi Agent cần kết hợp nhiều tool, có các pattern phổ biến:
Pattern 1: Sequential (Tuần tự)
Output của tool A là input của tool B.
// Ví dụ: Tìm khách hàng rồi lấy đơn hàng
// Bước 1: search_customers("Nguyen Van A") -> customer_id: 123
// Bước 2: get_orders(customer_id: 123) -> danh sách đơn hàng
// Agent tự quyết định truyền customer_id từ bước 1 sang bước 2
Pattern 2: Conditional (Có điều kiện)
Dựa trên kết quả tool A, Agent quyết định gọi tool B hay tool C.
// Ví dụ: Kiểm tra tồn kho rồi xử lý đơn
// Bước 1: check_inventory(product_id: "SKU001") -> in_stock: true/false
// Nếu true -> create_order(...)
// Nếu false -> find_alternative_products(...) hoặc notify_restock(...)
Pattern 3: Parallel (Song song)
Claude hỗ trợ gọi nhiều tool cùng lúc khi các tool không phụ thuộc nhau.
// Ví dụ: Lấy thông tin tổng hợp cho dashboard
// Gọi đồng thời:
// - get_sales_summary(period: "today")
// - get_active_orders_count()
// - get_low_stock_alerts()
// Agent tổng hợp kết quả từ cả 3 tool để trả lời
Pattern 4: Iterative (Lặp)
Agent lặp lại tool call cho đến khi đạt kết quả mong muốn hoặc hết dữ liệu.
// Ví dụ: Phân trang kết quả tìm kiếm
// Lần 1: search_products(query: "laptop", page: 1) -> 20 results, has_more: true
// Lần 2: search_products(query: "laptop", page: 2) -> 15 results, has_more: false
// Agent dừng khi has_more = false hoặc đã đủ thông tin
5 ví dụ tool được thiết kế tốt
Tool 1: Tra cứu thời tiết
{
"name": "get_weather",
"description": "Lấy thông tin thời tiết hiện tại và dự báo 5 ngày cho một địa điểm. Trả về nhiệt độ, độ ẩm, mô tả thời tiết và dự báo. Dùng khi người dùng hỏi về thời tiết tại một nơi cụ thể.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "Tên thành phố hoặc địa điểm (ví dụ: 'Ha Noi', 'Ho Chi Minh City')"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius",
"description": "Đơn vị nhiệt độ"
}
},
"required": ["location"]
}
}
Tool 2: Gửi email
{
"name": "send_email",
"description": "Gửi email đến một hoặc nhiều người nhận. Hỗ trợ plain text và HTML. Trả về message_id nếu gửi thành công. CHÚ Ý: Luôn xác nhận với người dùng trước khi gửi — không gửi tự động.",
"input_schema": {
"type": "object",
"properties": {
"to": {
"type": "array",
"items": { "type": "string", "format": "email" },
"description": "Danh sách email người nhận",
"minItems": 1,
"maxItems": 50
},
"subject": {
"type": "string",
"description": "Tiêu đề email",
"maxLength": 200
},
"body": {
"type": "string",
"description": "Nội dung email (plain text)"
},
"html_body": {
"type": "string",
"description": "Nội dung email HTML (tùy chọn, ưu tiên hơn body nếu có)"
}
},
"required": ["to", "subject", "body"]
}
}
Tool 3: Tìm kiếm tài liệu nội bộ
{
"name": "search_knowledge_base",
"description": "Tìm kiếm trong kho tài liệu nội bộ bằng semantic search. Trả về top 5 tài liệu liên quan nhất kèm đoạn trích phù hợp. Dùng khi người dùng hỏi về quy trình, chính sách, hoặc thông tin nội bộ công ty.",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Câu hỏi hoặc từ khóa tìm kiếm",
"minLength": 3
},
"department": {
"type": "string",
"enum": ["hr", "engineering", "sales", "finance", "all"],
"default": "all",
"description": "Lọc theo phòng ban"
},
"doc_type": {
"type": "string",
"enum": ["policy", "procedure", "guide", "faq", "all"],
"default": "all",
"description": "Lọc theo loại tài liệu"
}
},
"required": ["query"]
}
}
Tool 4: Tạo task trong project management
{
"name": "create_task",
"description": "Tạo task mới trong hệ thống quản lý dự án. Trả về task_id và URL. Dùng khi người dùng muốn tạo việc cần làm, bug report, hoặc feature request. Yêu cầu xác nhận title và assignee trước khi tạo.",
"input_schema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Tiêu đề task, ngắn gọn và rõ ràng",
"maxLength": 200
},
"description": {
"type": "string",
"description": "Mô tả chi tiết task"
},
"assignee": {
"type": "string",
"description": "Username người được giao task"
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"],
"default": "medium"
},
"due_date": {
"type": "string",
"format": "date",
"description": "Ngày deadline (YYYY-MM-DD)"
},
"labels": {
"type": "array",
"items": { "type": "string" },
"description": "Danh sách nhãn (bug, feature, improvement...)"
}
},
"required": ["title"]
}
}
Tool 5: Chuyển đổi tiền tệ
{
"name": "convert_currency",
"description": "Chuyển đổi giữa các loại tiền tệ theo tỷ giá hiện tại. Trả về số tiền đã chuyển đổi và tỷ giá áp dụng. Tỷ giá được cập nhật mỗi giờ, không phải real-time. Dùng khi người dùng hỏi về quy đổi tiền hoặc so sánh giá quốc tế.",
"input_schema": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"description": "Số tiền cần chuyển đổi",
"minimum": 0,
"exclusiveMinimum": true
},
"from_currency": {
"type": "string",
"description": "Mã tiền tệ nguồn (ISO 4217)",
"pattern": "^[A-Z]{3}$"
},
"to_currency": {
"type": "string",
"description": "Mã tiền tệ đích (ISO 4217)",
"pattern": "^[A-Z]{3}$"
}
},
"required": ["amount", "from_currency", "to_currency"]
}
}
Anti-patterns cần tránh
Anti-pattern 1: Tool "thùng rác" (Kitchen Sink Tool)
Một tool làm quá nhiều việc không liên quan. Agent khó biết khi nào nên dùng và truyền tham số gì.
// KHÔNG NÊN: Tool làm mọi thứ
{
"name": "manage_customer",
"description": "Tạo, cập nhật, xóa, tìm kiếm khách hàng, xem đơn hàng, gửi email..."
// Agent sẽ confused: truyền tham số cho create hay search hay delete?
}
// NÊN: Tách thành các tool riêng biệt
"create_customer", "search_customers", "get_customer",
"update_customer", "delete_customer"
Anti-pattern 2: Thiếu description cho tham số
// KHÔNG NÊN
{
"properties": {
"type": { "type": "string" },
"status": { "type": "integer" },
"flag": { "type": "boolean" }
}
}
// Agent: "type" là type gì? "status" 0 hay 1 nghĩa là gì? "flag" bật nghĩa là sao?
// NÊN
{
"properties": {
"order_type": {
"type": "string",
"enum": ["standard", "express", "same_day"],
"description": "Loại giao hàng"
},
"payment_status": {
"type": "string",
"enum": ["pending", "paid", "refunded"],
"description": "Trạng thái thanh toán đơn hàng"
},
"include_cancelled": {
"type": "boolean",
"default": false,
"description": "Có bao gồm đơn đã hủy trong kết quả không"
}
}
}
Anti-pattern 3: Không xử lý lỗi
// KHÔNG NÊN: throw hoặc trả về lỗi không rõ ràng
// Tool crash -> Agent không biết chuyện gì xảy ra
// NÊN: Luôn trả về response có cấu trúc, kể cả khi lỗi
// { is_error: true, content: [{ type: "text", text: "..." }] }
Anti-pattern 4: Side effects không thông báo
// KHÔNG NÊN: Tool "get" nhưng lại modify dữ liệu
{
"name": "get_report",
"description": "Lấy báo cáo"
// Nhưng thực tế tool này tạo mới report mỗi lần gọi,
// tốn tài nguyên và tạo dữ liệu trùng lặp
}
// NÊN: Tên và description phản ánh đúng hành vi
{
"name": "generate_report",
"description": "Tạo báo cáo mới. Mỗi lần gọi tạo một bản mới. Dùng get_report nếu muốn lấy báo cáo đã tạo."
}
Anti-pattern 5: Quá nhiều tool tương tự
// KHÔNG NÊN: 10 tool search khác nhau
"search_by_name", "search_by_email", "search_by_phone",
"search_by_id", "search_by_date", ...
// Agent sẽ confused dùng tool nào
// NÊN: 1 tool search linh hoạt
{
"name": "search_customers",
"input_schema": {
"properties": {
"query": { "type": "string", "description": "Tìm theo tên, email hoặc SĐT" },
"customer_id": { "type": "string", "description": "Tìm chính xác theo ID" },
"date_range": { "type": "object", "description": "Lọc theo ngày tạo" }
}
}
}
Testing tool
Tool cần được test kỹ trước khi đưa vào Agent. Các loại test cần có:
// Test framework cho tool
describe("search_products", () => {
// Test input validation
test("reject empty query", async () => {
const result = await searchProducts({ query: "" });
expect(result.is_error).toBe(true);
expect(result.content[0].text).toContain("query must not be empty");
});
// Test kết quả bình thường
test("return matching products", async () => {
const result = await searchProducts({ query: "laptop" });
const data = JSON.parse(result.content[0].text);
expect(data.products.length).toBeGreaterThan(0);
expect(data.products.length).toBeLessThanOrEqual(20);
});
// Test không tìm thấy
test("return empty for no matches", async () => {
const result = await searchProducts({ query: "xyznonexistent123" });
const data = JSON.parse(result.content[0].text);
expect(data.products.length).toBe(0);
expect(data.message).toContain("Không tìm thấy");
});
// Test edge cases
test("handle special characters", async () => {
const result = await searchProducts({ query: "laptop
Bai viet co huu ich khong?
Bản quyền thuộc về tác giả. Vui lòng dẫn nguồn khi chia sẻ.




