Bỏ qua điều hướng

Function calling là gì và hoạt động như thế nào?

Function calling giúp mô hình ngôn ngữ lớn (LLM) kết nối API, thực hiện hành động thực tế và trích xuất tham số JSON chuẩn xác thay vì chỉ sinh văn bản.

Tuan Tran Van
12 phút đọc
Mục lục (10 phần)
  1. Function calling là gì?
  2. Vòng lặp function calling hoạt động như thế nào?
  3. Điều gì thực sự xảy ra bên trong mô hình?
  4. Định nghĩa một function: JSON Schema và phần mô tả
  5. Gọi nhiều function: song song, tuần tự và đa công cụ
  6. Function calling khác structured outputs và JSON mode ra sao?
  7. Mô hình gọi function chính xác đến mức nào?
  8. Function calling hỏng ở đâu khi lên production
  9. Khi nào nên dùng function calling và khi nào không
  10. Tài liệu tham khảo

Function calling là cơ chế kỹ thuật cho phép mô hình ngôn ngữ lớn (LLM) kết nối trực tiếp với các công cụ, API và hệ thống phần mềm bên ngoài thay vì chỉ sinh văn bản đơn thuần.

Về bản chất, LLM không tự chạy code, cũng không tự vào internet; nó chỉ làm "đầu não" nhận diện yêu cầu, chọn đúng hàm cần dùng và trích xuất tham số dưới dạng JSON.

Việc thực thi logic thực tế hoàn toàn nằm ở phía ứng dụng (client), giúp đảm bảo an toàn hệ thống và tính kiểm soát cho lập trình viên.

Mô hình ngôn ngữ nằm giữa cuộc hội thoại và các công cụ bên ngoài, phát ra một lệnh gọi hàm có cấu trúc thay vì tự chạy code

Function calling là gì?

Function calling là "cầu nối" chuyển ngôn ngữ tự nhiên thành hành động thực thi có cấu trúc. Thay vì cố trả lời dựa trên kiến thức tĩnh đã cũ, mô hình trả về một cấu trúc JSON chứa tên hàm và các đối số (arguments) để ứng dụng của bạn xử lý tiếp.

Ba nhóm công dụng của function calling: thực hiện hành động, bổ sung kiến thức và mở rộng khả năng tính toán

Cơ chế này phục vụ ba nhóm công dụng rất thực dụng. Nó cho mô hình thực hiện hành động, tức là tương tác trực tiếp với thế giới thực qua API: đặt lịch họp, gửi mail, hoặc điều khiển thiết bị IoT như bật đèn và khóa cửa. Nó bổ sung kiến thức mô hình không có sẵn, bằng cách truy vấn dữ liệu từ các nguồn bên ngoài như SQL database doanh nghiệp hoặc API thời tiết để trả lời các câu hỏi về thông tin thời gian thực. Và nó mở rộng khả năng của mô hình: vì mô hình ngôn ngữ lớn vốn có giới hạn về toán học, bạn đẩy phần tính toán sang máy tính hoặc các thư viện vẽ biểu đồ chuyên dụng thay vì bắt nó tự nhẩm.

Điểm mấu chốt cần nhớ: khi gọi hàm, mô hình không trả lời trực tiếp câu hỏi của người dùng, nó trả về JSON. Nếu bạn thấy nó trả về văn bản thường trong tình huống lẽ ra phải gọi API, nghĩa là prompt hoặc schema của bạn đang có vấn đề chứ không phải mô hình dở.

Vòng lặp function calling hoạt động như thế nào?

Vòng lặp này gồm 4 bước tách biệt rõ rệt. Đừng bao giờ để mô hình nắm quyền thực thi trực tiếp, vì đó là lỗ hổng bảo mật chết người. Trách nhiệm thực thi luôn nằm ở phía client của bạn:

  1. Định nghĩa: Bạn khai báo tool schema (thường là JSON Schema) mô tả tên hàm và các tham số cho mô hình.
  2. Yêu cầu: Người dùng gửi prompt, ứng dụng gửi kèm schema đến mô hình. Mô hình phân tích và trả về một function_call gồm tên hàm và các giá trị tham số đã trích xuất.
  3. Thực thi: Ứng dụng phía client nhận JSON, kiểm tra tính hợp lệ và chạy mã nguồn thực tế (gọi API hoặc truy vấn database). Mô hình hoàn toàn đứng ngoài bước này.
  4. Phản hồi: Bạn gửi kết quả thực thi ngược lại cho mô hình. Lúc này, mô hình mới tổng hợp toàn bộ ngữ cảnh để đưa ra câu trả lời cuối cùng cho người dùng bằng ngôn ngữ tự nhiên.

Để ý kỹ, bước 2 và bước 4 là hai lần gọi mô hình khác nhau, và đó là chỗ nhiều người mới bắt đầu hay hiểu nhầm thành một. Vòng lặp chỉ kết thúc khi mô hình ngừng yêu cầu gọi hàm. Trong thực tế, bạn viết nó dưới dạng một vòng while bám vào tín hiệu dừng: chừng nào phản hồi còn báo cần dùng công cụ, bạn còn thực thi và gửi kết quả ngược lại; khi tín hiệu đổi sang kết thúc lượt, mô hình đã có câu trả lời cuối cùng.

Điều gì thực sự xảy ra bên trong mô hình?

Với các mô hình thế hệ mới thuộc dòng Gemini 3, quá trình này không còn là dự đoán token mù quáng. Mô hình sử dụng một "quy trình tư duy" (thinking process) nội bộ để lý giải schema phức tạp trước khi xuất dữ liệu, giúp giảm đáng kể lỗi cú pháp.

Bốn chế độ của tham số tool_choice đặt cạnh nhau: auto, any, none và validated

Hành vi này được kiểm soát chặt chẽ qua tham số tool_choice:

  • auto: Chế độ mặc định, mô hình tự quyết định có cần dùng tool hay không.
  • any: Ép mô hình phải gọi ít nhất một hàm (thường dùng cho các agent chuyên biệt).
  • none: Cấm gọi tool, chỉ sinh văn bản thuần.
  • validated: Mô hình đảm bảo đầu ra phải tuân thủ nghiêm ngặt schema đã định nghĩa.

Khi nhận diện nhu cầu gọi hàm, mô hình dừng sinh văn bản và trả về một tín hiệu dừng riêng cho việc dùng công cụ. Quy trình "tư duy" nói trên giúp mô hình kiểm tra logic các tham số trước khi "chốt" JSON, nhờ đó hạn chế được tình trạng ảo giác tham số. Trong bốn giá trị trên, auto xử lý gần hết các tình huống đời thường; tôi chỉ đụng tới any khi con agent có đúng một việc phải làm và không được phép trả lời suông.

Định nghĩa một function: JSON Schema và phần mô tả

Schema là ngôn ngữ duy nhất mô hình hiểu về công cụ của bạn. Một định nghĩa chuẩn bắt buộc phải có: name, description, parameters (loại object) và mảng required. Lưu ý rằng các nhà cung cấp chỉ hỗ trợ một tập con của chuẩn OpenAPI, nên schema quá lớn hoặc lồng ghép quá sâu có thể bị từ chối.

Kinh nghiệm xương máu: phần description chính là "prompt ẩn". Đừng viết chung chung kiểu "Hàm chỉnh màu", hãy viết cụ thể để mô hình biết khi nào nên dùng. Trường enum cũng cực kỳ quan trọng: nó khóa tham số vào một tập giá trị hợp lệ và loại bỏ hẳn một nhóm lỗi ảo giác tham số.

Mẫu JSON Schema chuẩn cho thiết bị thông minh:

json
{
  "name": "set_light_values",
  "description": "Điều chỉnh độ sáng và trạng thái màu sắc của đèn thông minh trong phòng.",
  "parameters": {
    "type": "object",
    "properties": {
      "brightness": {
        "type": "integer",
        "description": "Mức độ sáng từ 0 đến 100."
      },
      "color_temp": {
        "type": "string",
        "enum": ["daylight", "cool", "warm"],
        "description": "Nhiệt độ màu của ánh sáng, chỉ chấp nhận các giá trị: daylight, cool, hoặc warm."
      }
    },
    "required": ["brightness", "color_temp"]
  }
}

Mảng required phải khớp chính xác tên các thuộc tính bạn đã khai báo. Nghe hiển nhiên, nhưng sai lệch ở đúng chỗ này lại là một trong những nguyên nhân gây lỗi phổ biến nhất khi lên production, và nó chỉ lộ ra khi mô hình bắt đầu trả về tham số thiếu.

Gọi nhiều function: song song, tuần tự và đa công cụ

Các hệ thống agentic phức tạp cần gọi được nhiều công cụ trong cùng một lượt, và có ba kiểu gọi khác nhau nằm sau cái nhu cầu đó.

Ba kiểu gọi nhiều hàm: song song với hai hàm chạy cùng lúc, tuần tự khi đầu ra hàm này là đầu vào hàm kia, và đa công cụ kết hợp công cụ sẵn có với hàm API riêng

Parallel function calling là gọi nhiều hàm độc lập cùng lúc. Bạn bảo "Tắt đèn và hạ rèm", mô hình trả về cả hai lệnh gọi hàm trong một phản hồi (response) duy nhất, cho phép bạn thực thi song song để giảm độ trễ.

Compositional (Sequential) calling là gọi hàm theo chuỗi, khi đầu ra của hàm này chính là đầu vào của hàm kia: kết quả của get_location được dùng làm đầu vào cho get_weather. Mỗi mắt xích ở đây là một lượt gửi–nhận (round-trip) đầy đủ tới mô hình, nên chuỗi càng dài thì cái giá phải trả về độ trễ càng đắt.

Multi-tool use là khả năng kết hợp các công cụ "chính chủ" như Google Search với các hàm API riêng của bạn trong cùng một lượt yêu cầu (request), để giải quyết những tác vụ đa bước mà không công cụ đơn lẻ nào tự làm nổi.

Function calling khác structured outputs và JSON mode ra sao?

Rất nhiều người nhầm lẫn chỗ này, và cái giá phải trả là chọn sai hướng triển khai ngay từ đầu.

Ba mức tăng dần: JSON mode đảm bảo đúng cú pháp, structured outputs đảm bảo đúng schema, còn function calling là một vòng lặp hành động có thực thi bên ngoài

JSON Mode chỉ đảm bảo đầu ra là JSON hợp lệ về cú pháp, còn mô hình trả về những key gì, bạn vẫn phải tự đoán. Structured Outputs đi xa hơn một bậc, ép mô hình tuân thủ 100% schema, nên nó cực kỳ phù hợp để trích xuất dữ liệu. Function calling khác hẳn cả hai: nó là một vòng lặp hành động (action loop), không dừng ở chuyện định dạng mà gắn liền với logic thực thi của hệ thống bên ngoài, và nó còn chờ phản hồi từ API để tiếp tục suy luận.

Ba cơ chế này không loại trừ nhau. Bạn hoàn toàn có thể áp ràng buộc kiểu structured outputs lên chính các tham số của một lệnh gọi hàm, để đảm bảo đối số sinh ra luôn khớp schema trước khi chạm vào API thật.

Mô hình gọi function chính xác đến mức nào?

Đừng nghe quảng cáo, hãy nhìn vào con số thực tế từ Berkeley Function Calling Leaderboard (BFCL) V4. Khả năng xử lý các tham số lồng ghép và đa bước là thước đo thực sự:

  • Claude-Opus-4-5-20251101 (FC): Dẫn đầu bảng với độ chính xác tổng thể 77.47%.
  • Claude-Sonnet-4-5-20250929 (FC): Đạt 73.24%.
  • Gemini-3-Pro-Preview (Prompt): Đạt 72.51%, xếp hạng 3.
  • Qwen3-32B (FC): Đạt 48.71%, cho thấy khoảng cách còn khá lớn ở nhóm mô hình mở.

Lưu ý quan trọng cho dân triển khai: bản Gemini-3-Pro-Preview chạy theo lối "Prompt" (72.51%) lại đứng trên chính bản hỗ trợ gốc Native FC của nó (68.14%, hạng 7). Điều này cho thấy việc tối ưu prompt cho tool vẫn quan trọng, chứ không nên chỉ dựa dẫm vào hỗ trợ mặc định của model.

Function calling hỏng ở đâu khi lên production

Trên máy của bạn mọi thứ chạy ngọt, còn lên production lại là câu chuyện hoàn toàn khác, với 3 lỗi kinh điển bạn sẽ gặp đi gặp lại:

Ba lỗi thường gặp khi lên production: JSON hỏng định dạng, quá nhiều tool khiến mô hình chọn sai hàm, và mô tả mơ hồ khiến mô hình điền sai tham số

  1. Lỗi Malformed_Function_Call: Xảy ra khi bạn bắt mô hình vừa phải suy nghĩ (văn bản thuần) vừa phải xuất JSON gọi hàm ngay lập tức, gây nhiễu định dạng.
  2. Schema quá tải: Cung cấp quá nhiều tool (vượt quá khoảng 20 tool) hoặc schema lồng ghép 4-5 tầng khiến mô hình bị "ngợp" và chọn sai hàm hoặc trích xuất sai tham số.
  3. Độ nhạy format: Mô hình gọi sai enum hoặc thiếu tham số bắt buộc nếu phần mô tả không rõ ràng.

Giải pháp "Double Call": dùng một hàm update để bao bọc các ghi chú tư duy (pre-tool notes). Mô hình sẽ thực hiện hai cuộc gọi đồng thời trong cùng một lượt: một cuộc gọi hàm update để lưu lại kế hoạch tư duy và một cuộc gọi hàm thực thi chính. Cách này giúp tách biệt phần "nghĩ" ra khỏi phần "làm", nhờ vậy cú pháp JSON luôn sạch. Đây là kiểu giải pháp không đẹp về mặt thiết kế, nhưng nó chạy được, và trong sản phẩm thật, tôi chọn cái chạy được.

Khi nào nên dùng function calling và khi nào không

Hãy dùng function calling khi bạn cần tính chính xác tuyệt đối và kết nối dữ liệu thời gian thực. Đây là "xương sống" cho mọi AI agent hiện nay.

Tuy nhiên, đừng lạm dụng nó: nếu bạn chỉ cần định dạng đầu ra để hiển thị, hãy dùng Structured Outputs. Một lời khuyên thực tế: hãy giới hạn ở mức 10-20 tool tối đa cho mỗi request. Nếu hệ thống có hàng trăm API, hãy dùng thêm một bước RAG để lọc ra những tool cần thiết trước khi gửi schema cho mô hình.

Tài liệu tham khảo

Chia sẻ bài viết

X / TwitterFacebookLinkedIn