Bỏ qua để đến nội dung

Tool use & function calling

Tool use (còn gọi là function calling) là cơ chế biến một model chỉ biết sinh văn bản thành một agent làm được việc. Đây là khái niệm nền tảng: mọi thứ Claude Code làm được - đọc file, chạy lệnh, tìm kiếm web, gọi MCP server - đều đi qua cùng một cơ chế này.

Điểm quan trọng nhất: model không tự chạy tool

Phần tiêu đề “Điểm quan trọng nhất: model không tự chạy tool”

Đây là điều dễ hiểu sai nhất, và nó là nền tảng của toàn bộ mô hình bảo mật.

Model không có khả năng chạy lệnh, đọc file, hay gọi mạng. Nó chỉ phát ra một khối dữ liệu có cấu trúc nói rằng “tôi muốn gọi tool bash với command: "npm test". Phần code bên ngoài - gọi là harness - mới là thứ thực sự thực thi và đưa kết quả trở lại.

Model ──phát ra──▶ tool_use { name: "bash", input: { command: "npm test" } }
Harness quyết định: cho phép? chặn? xin phép người dùng?
Thực thi thật, thu output
Model ◀──nhận──── tool_result { content: "42 tests passed" }

Hệ quả trực tiếp: mọi giới hạn an toàn nằm ở harness, không nằm ở model. Đây chính là lý do chế độ quyền hạnhooks hoạt động được - chúng chèn vào đúng cái khoảng giữa đó. Một chỉ dẫn trong CLAUDE.md kiểu “đừng bao giờ xoá file” chỉ là khuyến nghị; một PreToolUse hook chặn rmbắt buộc.

Một tool định nghĩa bằng ba thứ: tên, mô tả, và JSON Schema cho input.

{
"name": "get_weather",
"description": "Lấy thời tiết hiện tại của một địa điểm. Gọi tool này khi người dùng hỏi về thời tiết, nhiệt độ, hoặc dự báo.",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "Thành phố, ví dụ: Hà Nội" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
}
}
1. Gửi request: messages + tools
2. Model trả về stop_reason: "tool_use" kèm một hoặc nhiều khối tool_use
3. Thực thi từng tool
4. Gửi lại TẤT CẢ tool_result trong MỘT message role: "user"
5. Lặp lại đến khi stop_reason: "end_turn"

Hai điểm dễ sai:

  • Tool chạy song song. Một lượt trả lời có thể chứa nhiều tool_use. Hãy chạy chúng đồng thời và gửi tất cả tool_result trong một message. Chia ra nhiều message sẽ dạy model thôi gọi song song ở các lượt sau.
  • Tool lỗi vẫn phải trả kết quả. Trả tool_result với is_error: true kèm thông báo lỗi hữu ích, đừng bỏ qua. Model sẽ đọc lỗi và thử cách khác.
Loại Ai thực thi Ví dụ
Client-side / custom tool Code của bạn Tool bạn tự định nghĩa; bash, text_editor (Anthropic định nghĩa schema, bạn chạy)
Server-side tool Hạ tầng của Anthropic web_search, web_fetch, code_execution - khai báo là xong, không cần code thực thi
MCP tool MCP server bên thứ ba Tool từ Sentry, Linear, GitHub… xem MCP

Trong Claude Code, tool có sẵn (Read, Write, Edit, Bash, Grep, Glob…) là client-side - harness của Claude Code thực thi chúng trên máy bạn.

Một câu hỏi thiết kế thực tế khi bạn tự xây agent.

Bash cho phạm vi rộng nhất - với một tool bash, agent làm được gần như mọi thứ. Nhưng harness chỉ nhận một chuỗi lệnh mờ đục, hình dạng giống nhau cho mọi hành động.

Tool riêng cho khả năng kiểm soát. Nâng một hành động lên thành tool riêng cho harness một điểm móc có kiểu (typed) để chặn, hiển thị, ghi log, hoặc song song hoá:

  • Cần cổng duyệt? send_email dễ chặn; bash -c "curl -X POST ..." thì không.
  • Cần kiểm tra tính mới? Một tool edit riêng có thể từ chối ghi nếu file đã đổi từ lần đọc cuối. Bash không làm được.
  • Cần hiển thị riêng? Claude Code nâng việc “hỏi người dùng” lên thành tool để render được thành hộp thoại có lựa chọn.
  • Cần chạy song song an toàn? grep/glob đánh dấu được là chỉ-đọc, an toàn để song song. Qua bash, harness không phân biệt được grep với git push.

Quy tắc thực dụng: bắt đầu bằng bash cho phạm vi, nâng lên tool riêng khi cần chặn / hiển thị / ghi log / song song hoá.

Mỗi tool definition chiếm ngữ cảnh, và bộ tool quá lớn làm model chọn kém đi. Hai giải pháp ở tầng API:

  • Tool search - model tự tìm trong bộ tool lớn và chỉ nạp schema liên quan. Quan trọng: schema được thêm vào, không thay thế - nên prompt cache không mất.
  • Deferred loading - khai báo tool trước với defer_loading: true, chỉ nạp vào ngữ cảnh khi cần.

Trong Claude Code, bạn thấy điều này qua việc một số tool được liệt kê chỉ bằng tên và phải “fetch schema” trước khi gọi.