Function Calling: Khi mô hình nhỏ 3.4GB đánh bại "gã khổng lồ" 25GB

07 tháng 4, 2026·13 phút đọc

Kết quả benchmark năm 2026 cho thấy Qwen3.5 4B đạt độ chính xác 97.5% trong function calling, vượt xa các mô hình lớn hơn nhiều. Điều này chứng minh rằng đối với các tác vụ cấu trúc, kích thước không phải là tất cả.

Function Calling: Khi mô hình nhỏ 3.4GB đánh bại "gã khổng lồ" 25GB

Function Calling là khả năng trang bị công cụ cho LLM (Large Language Model). Đây là công nghệ nền tảng của các AI Agent, là bước tiến hóa tiếp theo của RAG và là tính năng quyết định tính thực tiễn của các mô hình LLM cục bộ (local LLM).

Vậy thì, mô hình nào có độ chính xác cao nhất trong function calling? Kết quả benchmark năm 2026 của JD Hodges, trong đó 13 mô hình được kiểm thử với lượng tử hóa Q4_K_M, đã mang đến một kết quả đầy bất ngờ.

Mô hình đạt độ chính xác 97.5% lại chỉ có dung lượng 3.4GB. Trong khi đó, một mô hình dung lượng tới 25GB lại thua với chỉ 85% độ chính xác.

Ít nhất trong môi trường kiểm thử này, định kiến "mô hình lớn luôn mạnh hơn" đã không còn đúng.

Kết quả Benchmark: So sánh toàn bộ 13 mô hình

Tất cả các mô hình đều được lượng tử hóa Q4_K_M (nén 4-bit giúp giảm khoảng 75% dung lượng VRAM sử dụng mà vẫn giữ được độ chính xác của mô hình), kiểm thử trong môi trường thống nhất qua LM Studio. Độ chính xác function calling trên 40 trường hợp. Kích thước listed là dung lượng VRAM khi tải trên LM Studio (khác với kích thước file).

Xếp hạngMô hìnhKích thướcĐộ chính xác
1Qwen3.5 4B3.4GB97.5%
2GLM-4.7-Flash18GB95.0%
2Nemotron 3 Nano 4B4.2GB95.0%
4Mistral Nemo 12B7.5GB92.5%
5Qwen3 8B5GB85.0%
5GPT-OSS 20B12GB85.0%
5Nemotron 3 Nano 30B-A3B25GB85.0%
8DeepSeek-R1-Distill 14B9GB57.5%
8Phi-4 Mini2.5GB57.5%
10Gemma 3 4B QAT3.2GB55.0%
11Mistral Small 3.2 24B15GB42.5%
12Hammer 2.1 7B4.2GB20.0%
13xLAM-2 8B FC-R4.9GB15.0%

Ba điểm gây sốc

Sốc 1: 3.4GB đạt 97.5%

Qwen3.5 4B thành công trong 39/40 trường hợp. Chỉ thất bại đúng 1 trường hợp duy nhất. 3.4GB nhỏ hơn một nửa VRAM của chiếc RTX 4060 8GB. Bạn hoàn toàn có đủ dư địa để chạy đồng thời cả mô hình suy luận (inference model) và mô hình Embedding.

Sốc 2: Kích thước không dự đoán được độ chính xác

Kích thướcMô hìnhĐộ chính xác
3.4GBQwen3.5 4B97.5% (Hạng 1)
4.2GBNemotron 3 Nano 4B95.0% (Hạng 2)
7.5GBMistral Nemo 12B92.5% (Hạng 4)
18GBGLM-4.7-Flash95.0% (Hạng 2)
25GBNemotron 3 Nano 30B-A3B85.0% (Hạng 5)

Trong môi trường kiểm thử này (LM Studio + Q4_K_M), kích thước mô hình đơn thuần không thể dự đoán độ chính xác function calling. Mistral Small 15GB chỉ đạt 42.5%, thua kém Qwen3.5 3.4GB tới 55 điểm. Tuy nhiên, cần lưu ý rằng mẫu số n=13 và kết quả phụ thuộc vào đường dẫn suy luận của LM Studio, nên dữ liệu chưa đủ để đưa ra kết luận chung về khả năng vốn có của từng mô hình.

Lưu ý: Các mô hình hạng dưới gặp vấn đề tương thích LM Studio

Điểm số thấp của xLAM-2 8B FC-R (15%) và Hammer 2.1 (20%) không phải do chất lượng mô hình, mà do sự không tương thích của chat template trong LM Studio. Thực tế, xLAM-2 đứng hạng 1 trên bảng xếp hạng Berkeley Function Calling Leaderboard (BFCL), nhưng định dạng gọi công cụ riêng biệt của nó không được chuyển đổi chính xác qua API tương thích OpenAI của LM Studio. Benchmark này đo "độ chính xác thực tế khi chạy qua LM Studio", không đo lường khả năng vốn có của mô hình.

Tại sao mô hình nhỏ lại thắng trong Function Calling?

Tính chất đặc thù của đầu ra cấu trúc

Function calling là một nhiệm vụ đặc biệt trong các tác vụ của LLM. Nó không yêu cầu tạo văn bản tự do, mà đòi hỏi đầu tu phải tuân thủ cấu trúc nghiêm ngặt (JSON, tên hàm, kiểu tham số).

Yêu cầuFunction CallingTạo văn bản tự do
Định dạng đầu raJSON (tuân thủ schema nghiêm ngặt)Ngôn ngữ tự nhiên (linh hoạt)
An toàn kiểu (Type safety)Kiểu tham số phải chính xác tuyệt đốiKhông cần thiết
Chấp nhận ảo giác (Hallucination)Bằng 0 (không thể gọi hàm không tồn tại)Chấp nhận ở mức độ nhất định
Mức độ phụ thuộc kiến thứcThấp (việc tuân thủ định dạng là chủ đạo)Cao (kiến thức thế giới quyết định chất lượng)

Điểm mấu chốt: Function calling phụ thuộc vào khả năng tuân thủ định dạng (format adherence) hơn là lượng kiến thức. Điểm mạnh của các mô hình lớn là lượng kiến thức khổng lồ (kiến thức thế giới, khả năng suy luận), nhưng trong function calling, điều này không phát huy tác dụng. Thay vào đó, chất lượng dữ liệu huấn luyện và độ chính xác trong việc làm theo hướng dẫn (instruction following) mới là yếu tố quyết định.

Lý do Qwen3.5 4B mạnh hơn có thể là do:

  1. Instruction tuning thế hệ Qwen3.5 được tối ưu hóa cho định dạng function calling.
  2. 4 tỷ tham số vẫn đủ sức biểu diễn cho đầu ra cấu trúc.
  3. Việc giảm số lượng tham số giúp tín hiệu từ dữ liệu huấn luyện không bị chìm trong tiếng ồn.

Sự sụp đổ của huyền thoại số tham số (tiếp theo)

Đây là sự mở rộng của quy luật đã đề cập trong bài viết về quy tắc chọn mô hình cho VRAM 8GB.

Quy luật (xác nhận lại):
  Số tham số ≠ Hiệu suất tác vụ

Quy luật bổ sung (function calling):
  Trong các tác vụ đầu ra cấu trúc, mô hình nhỏ có thể thắng mô hình lớn
  Lý do: Khả năng tuân thủ định dạng chi phối hơn là lượng kiến thức

Triển khai Function Calling trên RTX 4060 8GB

Áp dụng kết quả benchmark vào thực tế triển khai.

Cấu hình đề xuất

Mục đíchMô hìnhVRAMĐộ chính xácVRAM dư thừa
Độ chính xác cao nhấtQwen3.5 4B Q4_K_M~3.4GB97.5%~4.6GB (Chạy được Embedding song song)
Cân bằngNemotron 3 Nano 4B Q4_K_M~4.2GB95.0%~3.8GB
Multi-turn (Đa vòng)Mistral Nemo 12B Q4_K_M~7.5GB92.5%~0.5GB (Khuyên dùng đơn lẻ)

Code cấu hình tối thiểu

# llama.cpp + Qwen3.5-4B để thực hiện function calling
import subprocess
import json

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_documents",
            "description": "Search internal documents by query",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "max_results": {"type": "integer", "default": 5}
                },
                "required": ["query"]
            }
        }
    }
]

def call_with_tools(user_message: str, tools: list) -> dict:
    """Thực thi function calling bằng Local LLM"""
    # Xây dựng prompt dạng ChatML
    tools_json = json.dumps(tools, ensure_ascii=False)
    prompt = f"""<|im_start|>system
You are a helpful assistant with access to tools.
Available tools: {tools_json}
When you need to use a tool, respond with JSON:
{{"name": "function_name", "arguments": {{"key": "value"}}}}
Only call a tool if the user's request requires it.
<|im_end|>
<|im_start|>user
{user_message}<|im_end|>
<|im_start|>assistant
"""

    result = subprocess.run(
        ["llama-cli", "-m", "qwen3.5-4b-q4_k_m.gguf",
         "-p", prompt, "-n", "200", "--temp", "0.1",
         "--grammar-file", "json.gbnf"],  # Ràng buộc ngữ pháp JSON
        capture_output=True, text=True
    )

    try:
        return json.loads(result.stdout.strip())
    except json.JSONDecodeError:
        return {"error": "Failed to parse", "raw": result.stdout}

# Ví dụ thực thi
result = call_with_tools(
    "Thời tiết ở Tokyo thế nào?",
    TOOLS
)
# Kết quả mong đợi: {"name": "get_weather", "arguments": {"location": "Tokyo"}}

Đảm bảo JSON bằng ngữ pháp GBNF

Sử dụng ràng buộc ngữ pháp GBNF (Generalized Backus-Naur Form) của llama.cpp giúp ép buộc đầu ra của LLM tuân thủ định dạng JSON. Điều này tăng độ tin cậy của đầu ra cấu trúc.

# json.gbnf (ngữ pháp JSON cơ bản)
root   ::= object
value  ::= object | array | string | number | "true" | "false" | "null"
object ::= "{" ws (string ":" ws value ("," ws string ":" ws value)*)? ws "}"
array  ::= "[" ws (value ("," ws value)*)? ws "]"
string ::= "\"" [^"\\]* "\""
number ::= "-"? [0-9]+ ("." [0-9]+)?
ws     ::= [ \t\n]*

Nếu không có ràng buộc GBNF, mô hình có thể chèn thêm văn bản tự do gây lỗi phân tích (parse error) thường xuyên:

# Không có GBNF — Mô hình chèn thêm văn bản thừa
{"name": "get_weather", "arguments": {"location": "Tokyo"}} Let me check the weather for you...

Khi có ràng buộc GBNF, mỗi token bị giới hạn bởi quy tắc ngữ pháp, giúp giảm thiểu việc tạo ra JSON sai cú pháp. Tuy nhiên, nếu đạt giới hạn token, JSON có thể bị incomplete. Độ chính xác về mặt ngữ nghĩa (tên hàm, giá trị tham số) không được đảm bảo tuyệt đối. Có một chút chi phí overhead cho tốc độ suy luận.

Tích hợp Function Calling với Agentic RAG

Khi có khả năng function calling, Agentic RAG (RAG dạng tác nhân) đã đề cập trong bài viết trước sẽ trở nên ổn định hơn.

# Triển khai tích hợp function calling + Agentic RAG
ARAG_TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "keyword_search",
            "description": "Tìm kiếm tài liệu dựa trên từ khóa",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "k": {"type": "integer", "default": 5}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "semantic_search",
            "description": "Tìm kiếm tài liệu dựa trên vector ngữ nghĩa",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "k": {"type": "integer", "default": 5}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "answer",
            "description": "Cung cấp câu trả lời cuối cùng cho người dùng",
            "parameters": {
                "type": "object",
                "properties": {
                    "text": {"type": "string"}
                },
                "required": ["text"]
            }
        }
    }
]

def agentic_rag_loop(question: str, max_steps: int = 3) -> str:
    """Agentic RAG dựa trên function calling"""
    context = []

    for step in range(max_steps):
        # Yêu cầu function call dựa trên ngữ cảnh hiện tại
        tool_call = call_with_tools(
            f"Question: {question}\n"
            f"Retrieved so far: {json.dumps(context[:3])}\n"
            f"Call a search tool or answer directly.",
            ARAG_TOOLS
        )

        if tool_call.get("name") == "answer":
            return tool_call["arguments"]["text"]

        elif tool_call.get("name") == "keyword_search":
            results = keyword_search(tool_call["arguments"]["query"])
            context.extend(results)

        elif tool_call.get("name") == "semantic_search":
            results = semantic_search(tool_call["arguments"]["query"])
            context.extend(results)

    # Khi đạt max_steps thì ép buộc trả lời
    return call_with_tools(
        f"Based on: {context}\nAnswer: {question}",
        [ARAG_TOOLS[2]]  # Chỉ dùng công cụ answer
    )["arguments"]["text"]

Chạy vòng lặp này với Qwen3.5 4B (độ chính xác 97.5%) giúp giảm thiểu rủi ro tác nhân chạy失控 (go haywire) do lỗi function calling. Hơn nữa, với dung lượng chỉ 3.4GB, việc chạy đồng thời với BGE-M3 Embedding (1.5GB) vẫn chỉ tốn khoảng 5GB. Vừa vặn với RTX 4060 8GB.

Các mô hình nên tránh và nên chọn

Các mô hình nên tránh

Các mô hình có độ chính xác thấp trong môi trường LM Studio:

Mô hìnhKích thướcĐộ chính xácLý do
Mistral Small 3.2 24B15GB42.5%Độ chính xác không tương xứng với VRAM
DeepSeek-R1-Distill 14B9GB57.5%Chuyên về suy luận, yếu ở đầu ra cấu trúc

Lưu ý: xLAM-2 8B FC-R (15%) và Hammer 2.1 (20%) có điểm số thấp chủ yếu do không tương thích chat template của LM Studio, nên không nên dùng làm thước đo chất lượng mô hình.

Đề xuất theo mục đích sử dụng

Mục đíchMô hình đề xuấtKích thướcĐộ chính xácLý do
Agentic RAGQwen3.5 4B3.4GB97.5%Độ chính xác cao nhất + Chạy Embedding song song
Multi-turn AgentMistral Nemo 12B7.5GB92.5%Mạnh trong xử lý tuần tự
Chi phí tối thiểuNemotron Nano 4B4.2GB95.0%Độ chính xác cao + Tiết kiệm VRAM
Tốc độ ưu tiênGLM-4.7-Flash18GB95.0%52 t/s (nhưng tốn VRAM lớn)
Kiến thức + Công cụ trên 8GBQwen3.5 4B + 32B chuyển đổiBiến thiênBiến thiênGọi công cụ bằng 4B, trả lời kiến thức bằng 32B

Mô hình cuối cùng rất thú vị. Một cấu trúc hai tầng: dùng mô hình nhỏ 3.4GB để xác định gọi công cụ, và mô hình lớn 32B để đưa ra câu trả lời kiến thức thực tế. Điều này giúp vừa đảm bảo độ chính xác khi chọn công cụ, vừa có chiều sâu về kiến thức.

Function Calling là biên giới tiếp theo của Local LLM

Các điểm chính của bài viết này:

  1. Độ chính xác function calling không thể dự đoán chỉ qua kích thước mô hình: Qwen3.5 4B (3.4GB) đã vượt qua mô hình 25GB (trong môi trường LM Studio).
  2. Đầu ra cấu trúc có xu hướng phụ thuộc vào khả năng tuân thủ định dạng hơn là lượng kiến thức: Đây là nhiệm vụ mà điểm mạnh của mô hình lớn (lượng kiến thức) khó phát huy.
  3. RTX 4060 8GB là môi trường lý tưởng cho function calling: Top 3 mô hình đều nằm gọn trong 8GB VRAM.
  4. Ràng buộc ngữ pháp GBNF giúp giảm thiểu lớn lỗi cú pháp JSON: Đây là tính năng mạnh mẽ của llama.cpp.
  5. Kết quả benchmark phụ thuộc vào máy chủ suy luận: Tính tương thích của LM Studio ảnh hưởng lớn đến điểm số của các mô hình hạng dưới.

Giá trị của Local LLM đang chuyển dịch từ "chạy được mô hình lớn" sang "sử dụng mô hình phù hợp cho tác vụ phù hợp". Function calling là một ví dụ điển hình cho xu hướng này. Tuy nhiên, do tính tương thích với máy chủ suy luận ảnh hưởng lớn đến kết quả, việc tự kiểm chứng trên môi trường của mình là điều kiện tiên quyết.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗