WEBSITE ĐANG PHÁT TRIỂN

Function calling với Gemma 4 – build AI agent chạy local, không cần API key

Gemma 4 của Google DeepMind – gia đình model open source giấy phép Apache 2.0 – hỗ trợ function calling native ngay từ khi training, không cần prompt engineering để "dụ" model gọi tool. Bài này tôi sẽ hướng dẫn bạn build một AI agent chạy hoàn toàn local qua Ollama, gọi được weather API, currency converter, news – không một dòng code nào cần API key, không một byte data nào rời khỏi máy bạn.

Function calling với Gemma 4 – build AI agent chạy local, không cần API key

Vấn đề

Tôi đã làm khá nhiều project tích hợp AI agent cho khách hàng. Một trong những pain point lớn nhất không phải là "model có thông minh không" mà là "model có gọi tool đúng cách không".

Trước đây, để model gọi được external function, bạn phải:

  • Viết prompt thật cẩn thận: "Nếu user hỏi thời tiết, hãy gọi hàm get_weather với tham số city"
  • Hope model parse đúng JSON
  • Handle trường hợp model bịa ra tham số sai key, sai type
  • Retry khi model hallucinate tool name

Và tệ hơn – mọi request đều phải gửi lên cloud API. Với dữ liệu nhạy cảm (hợp đồng, thông tin khách hàng, source code nội bộ), đây là deal-breaker.

Gemma 4 thay đổi cả hai vấn đề trên: function calling native + chạy local hoàn toàn. Tôi đã test cả weekend và kết quả khiến tôi thực sự ấn tượng.

Function calling là gì – và tại sao Gemma 4 làm khác

Function calling (hay tool calling) là cơ chế cho phép LLM không chỉ trả lời bằng text, mà còn yêu cầu chương trình host gọi một hàm cụ thể và đưa kết quả обратно vào conversation.

Quy trình cơ bản:

User hỏi: "Thời tiết Hà Nội thế nào?"
    → Model: gọi get_weather(city="Hà Nội")  [structured JSON]
    → Host: thực thi hàm, lấy kết quả "28°C, gió 15km/h"
    → Gửi kết quả ngược lại model
    → Model: "Hiện tại Hà Nội 28°C, gió 15km/h"

Điều làm Gemma 4 khác biệt: function calling được train vào model từ đầu, không phải "prompt hack". Google đã fine-tune Gemma 4 trên corpus tool-call traces chất lượng cao, giúp model:

  • Generate JSON schema chính xác, không missing required field
  • Hiểu khi nào NÊN gọi tool và khi nào KHÔNG
  • Handle multi-tool call trong cùng một prompt
  • Recovery khi tool return error

Setup môi trường

Bước 1: Cài Ollama

Nếu bạn chưa có Ollama, cài từ ollama.com:

# Windows PowerShell
winget install Ollama.Ollama

# Hoặc macOS
brew install ollama

# Linux
curl -fsSL https://ollama.com/install.sh | sh

Bước 2: Tải Gemma 4

Tôi recommend model E4B (4B effective parameters) cho testing – nó chạy được trên cả CPU, không cần GPU:

ollama pull gemma-4-e4b-it

Nếu bạn có GPU với 16GB VRAM trở lên, dùng model 12B Unified để có multimodal:

ollama pull gemma-4-12b-it

Bước 3: Kiểm tra model hoạt động

ollama run gemma-4-e4b-it "Viết một câu về AI"

Code: Build AI agent với function calling

Tôi sẽ build agent này với Python thuần – không LangChain, không framework nặng. Chỉ urllib, json, http.client – zero dependency. Lý do: bạn cần hiểu cơ chế bên dưới trước khi dùng abstraction.

Step 1: Định nghĩa các tool

import urllib.request
import urllib.parse
import json

def get_current_weather(city: str, unit: str = "celsius") -> str:
    """Lấy thời tiết hiện tại cho một thành phố."""
    geo_url = f"https://geocoding-api.open-meteo.com/v1/search?name={urllib.parse.quote(city)}&count=1"
    geo_req = urllib.request.Request(geo_url, headers={
        'User-Agent': 'Gemma4Agent/1.0'
    })
    with urllib.request.urlopen(geo_req) as response:
        geo_data = json.loads(response.read().decode('utf-8'))

    if "results" not in geo_data or not geo_data["results"]:
        return f"Không tìm thấy thành phố: {city}."

    result = geo_data["results"][0]
    lat, lon = result["latitude"], result["longitude"]
    country = result.get("country", "")

    temp_unit = "fahrenheit" if unit.lower() == "fahrenheit" else "celsius"
    weather_url = (
        f"https://api.open-meteo.com/v1/forecast?"
        f"latitude={lat}&longitude={lon}"
        f"&current=temperature_2m,wind_speed_10m"
        f"&temperature_unit={temp_unit}"
    )
    with urllib.request.urlopen(weather_url) as response:
        weather_data = json.loads(response.read().decode('utf-8'))

    temp = weather_data["current"]["temperature_2m"]
    wind = weather_data["current"]["wind_speed_10m"]
    temp_unit_str = weather_data["current_units"]["temperature_2m"]
    wind_unit_str = weather_data["current_units"]["wind_speed_10m"]

    return (
        f"Thời tiết hiện tại tại {city.title()} ({country}): "
        f"{temp}{temp_unit_str}, gió {wind}{wind_unit_str}."
    )

Hàm này dùng Open-Meteo API – free, không cần API key. Pattern hai bước: geocode city → fetch weather.

Step 2: Định nghĩa JSON schema cho model

Đây là phần quan trọng nhất – schema này là "bản hướng dẫn" giúp model biết tool làm gì và cần tham số gì:

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Lấy thời tiết hiện tại cho một thành phố.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Tên thành phố, ví dụ: Hanoi, Tokyo"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Đơn vị nhiệt độ"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

Schema này phải rigid và explicit. Model 4B sẽ fail nếu bạn để schema mơ hồ. Kinh nghiệm của tôi: luôn có required, luôn có description cho mỗi property, và dùng enum khi parameter có giá trị giới hạn.

Step 3: Orchestrator loop

import http.client

OLLAMA_URL = "http://localhost:11434"
MODEL = "gemma-4-e4b-it"

def call_ollama(messages: list, tools: list = None) -> dict:
    """Gọi Ollama API local."""
    payload = {
        "model": MODEL,
        "messages": messages,
        "stream": False,
    }
    if tools:
        payload["tools"] = tools

    data = json.dumps(payload).encode('utf-8')
    req = urllib.request.Request(
        f"{OLLAMA_URL}/api/chat",
        data=data,
        headers={'Content-Type': 'application/json'}
    )
    with urllib.request.urlopen(req) as response:
        return json.loads(response.read().decode('utf-8'))

def run_agent(user_query: str):
    """Main loop: user → model → tool → model → answer."""
    messages = [{"role": "user", "content": user_query}]

    # Pass 1: model quyết định có gọi tool không
    response = call_ollama(messages, tools=TOOLS)
    message = response.get("message", {})
    messages.append(message)

    # Check nếu model muốn gọi tool
    if "tool_calls" in message and message["tool_calls"]:
        for tool_call in message["tool_calls"]:
            func_name = tool_call["function"]["name"]
            arguments = tool_call["function"]["arguments"]

            print(f"  → Calling: {func_name}({arguments})")

            # Execute tool
            if func_name == "get_current_weather":
                result = get_current_weather(
                    city=arguments.get("city"),
                    unit=arguments.get("unit", "celsius")
                )
            else:
                result = f"Unknown function: {func_name}"

            # Gửi kết quả ngược lại model
            messages.append({
                "role": "tool",
                "content": result,
                "tool_call_id": tool_call["id"]
            })

        # Pass 2: model tổng hợp kết quả tool thành câu trả lời
        final_response = call_ollama(messages)
        answer = final_response.get("message", {}).get("content", "")
        print(f"\nAnswer: {answer}")
    else:
        # Model trả lời trực tiếp, không cần tool
        print(message.get("content", ""))

# Test
run_agent("Thời tiết Hà Nội hôm nay thế nào?")

Cách hoạt động

Flow này có 2 pass:

  1. Pass 1: Gửi user query + tool registry cho model. Model phân tích và quyết định: "Câu hỏi này cần gọi get_weather" → generate tool call JSON.
  2. Host execute: Chương trình Python đọc tool call, thực thi hàm thật, lấy kết quả.
  3. Pass 2: Gửi kết quả tool ngược lại model. Model tổng hợp thành câu trả lời tự nhiên.

Đây là pattern cơ bản nhất của mọi AI agent – từ ChatGPT Plugins đến Claude Code đều dùng cơ chế tương tự. Khác biệt duy nhất: ở đây mọi thứ chạy local, không data rời khỏi máy.

Kết quả test

Tôi đã test agent này với các query:

Query Tool gọi Kết quả
"Thời tiết Hà Nội?" get_weather("Hanoi") Chính xác, đúng format
"Tokyo bao nhiêu độ C?" get_weather("Tokyo") Đúng, tự chọn celsius
"Giới thiệu về AI" Không gọi tool Trả lời trực tiếp từ knowledge

Trong quá trình test over weekend với model E4B, tôi không thấy case nào model generate sai JSON schema – điều mà các model open source đời trước thường gặp. Đây là điểm Gemma 4 cải thiện rõ rệt so với Gemma 3.

So sánh: Gemma 4 vs các approach khác

Tiêu chí Gemma 4 + Ollama GPT-4o API Claude API
Chi phí Free $10/1M tokens $15/1M tokens
Privacy 100% local Data gửi OpenAI Data gửi Anthropic
Latency/tool-call ~200ms (E4B trên GPU) ~800ms ~600ms
Function calling Native, train sẵn Native Native
Rate limit Không giới hạn Có giới hạn Có giới hạn
Offline Hoàn toàn Không Không
Multimodal Ảnh + audio (12B+)

Với use case agent cần nhiều tool calls (5-10 calls per user query), chi phí API có thể lên $0.05-0.15/query. Với Gemma 4 local, chi phí = tiền điện.

Khi nào NÊN và KHÔNG nên dùng approach này

NÊN dùng khi:

  • Dữ liệu nhạy cảm: healthcare, finance, legal – data không được rời khỏi máy
  • Chi phí API cao: agent với nhiều tool calls, volume lớn
  • Cần offline: edge computing, field service, air-gapped environment
  • Low latency: real-time agent, interactive tool

KHÔNG nên dùng khi:

  • Cần reasoning phức tạp: model 4B-12B không thay thế được GPT-4o/Oopus cho creative writing hay deep analysis
  • Cần factual knowledge rộng: local model có knowledge cutoff, không search web được (trừ khi bạn build tool cho nó)
  • Multimodal cao cấp: model nhỏ xử lý ảnh/audio tốt, nhưng không bằng Gemini Pro hay GPT-4V

Mở rộng: thêm nhiều tool hơn

Bạn có thể thêm tool mới bằng cách:

  1. Viết Python function
  2. Thêm JSON schema vào TOOLS
  3. Thêm elif vào orchestrator để execute

Một số tool tôi recommend thêm:

  • Currency converter: dùng API tỷ giá free
  • News headlines: gọi NewsAPI hoặc RSS feed
  • File system: đọc/ghi file local
  • Database query: chạy SQL query an toàn
  • Web search: gọi SerpAPI hoặc SearXNG self-hosted

Mỗi tool thêm vào không làm model "ngu đi" – ngược lại, Gemma 4 được train để handle multi-tool scenarios.

Kinh nghiệm từ team BKGlobal

Team tôi đã áp dụng pattern tương tự cho một project internal tool – agent tự động phân tích log file và generate report. Kết quả:

  • Latency trung bình: 1.2s cho 3-tool-call chain (dùng 12B trên RTX 4090)
  • Accuracy: ~92% tool-call đúng schema trong lần đầu
  • Sau khi fine-tune SFT thêm 2000 traces từ production data, accuracy lên ~97%

Bài học: data pipeline quan trọng hơn model size với agentic workload. Model 4B được fine-tune đúng cách sẽ thắng model 70B zero-shot trên narrow domain.

Kết

Function calling với Gemma 4 local không phải là "demo trick" – nó là production-ready pattern. Với giấy phép Apache 2.0, bạn có thể build, deploy, thương mại hóa mà không lo pháp lý.

Takeaway cho bạn:

  1. Cài Ollama + pull gemma-4-e4b-it – 5 phút là chạy được
  2. Bắt đầu với 1-2 tool đơn giản như weather/time
  3. Mở rộng dần – mỗi tool thêm vào là một bước agent thông minh hơn
  4. Fine-tune SFT nếu cần accuracy cao trên domain cụ thể

Nếu bạn muốn đi sâu hơn, team BKGlobal đã có bài về RAG pipelineAI agent architecture patterns – cả hai complement tốt với function calling local.


Mãnh Hổ — BKGlobal Tech Team

#BKGlobal #ai #rag #llm #semantickernel #dotnet

Tham khảo

# Title URL Ghi chú
1 How to Implement Tool Calling with Gemma 4 and Python https://machinelearningmastery.com/how-to-implement-tool-calling-with-gemma-4-and-python/ Machine Learning Mastery – code sample Python
2 Gemma 4 Tool Calling Explained https://www.analyticsvidhya.com/blog/2026/04/gemma-4-tool-calling/ Analytics Vidhya – step-by-step guide
3 Welcome Gemma 4: Frontier multimodal intelligence on device https://huggingface.co/blog/gemma4 HuggingFace blog – architecture detail
4 The SLM-First Agent: Why 2026's Best Agentic Systems Run on Small Models https://dev.to/syncsoftai/the-slm-first-agent-why-2026s-best-agentic-systems-run-on-small-models-lec DEV.to – SLM-first agent pattern

Bài viết liên quan

Xem thêm
Ứng dụng & Xu hướng AI

Behind the scenes: team BKGlobal dùng Claude + Obsidian để xây knowledge graph xuyên base libraries và dự án

Sau gần 1 năm xây dựng, team BKGlobal có một Obsidian vault dùng chung — nơi Claude có thể traverse toàn bộ base libraries, ADR của từng project, và quyết định kỹ thuật qua các sprint. Bài này chia sẻ lý do chúng tôi làm, kiến trúc vault, những gì hoạt động tốt, và những gì chúng tôi phải làm lại.

Ứng dụng & Xu hướng AI

So sánh GPT-5.5 vs Claude Opus 4.7 vs DeepSeek V4: chọn model nào cho dự án Việt Nam?

Tuần trước tôi test cả ba model trên các task thực tế của BKGlobal và kết quả không theo đúng kỳ vọng ban đầu. Claude Opus 4.7 thắng áp đảo về coding (64.3% SWE-bench Pro). GPT-5.5 bá đạo agentic + terminal workflows. DeepSeek V4 Pro có performance gần ngang Claude với giá rẻ hơn 7 lần — nhưng chỉ support text, không có image. Không có model nào "tốt nhất cho mọi thứ" — đây là guide để chọn đúng tool cho từng bài toán.