Hướng dẫn chi tiết tự động hóa tác vụ qua Function Calling

Đỗ Minh Đức Đỗ Minh Đức
Chia sẻ bài viết

Function Calling là một trong những tính năng quan trọng giúp AI Agent không chỉ trả lời câu hỏi mà còn thực thi hành động thực tế như truy xuất dữ liệu, gọi API hay điều khiển hệ thống. Bài viết này Bizfly sẽ hướng dẫn bạn từng bước định nghĩa, gọi và xử lý function trong OpenAI và Ollama để tự động hóa tác vụ hiệu quả.

Function Calling là gì?

Function Calling cho phép mô hình ngôn ngữ (như GPT hoặc Ollama) hiểu và kích hoạt các hàm (function) mà bạn định nghĩa sẵn trong ứng dụng. Thay vì chỉ trả về văn bản, AI có thể:

  • Gọi API lấy dữ liệu thật.
  • Tạo, sửa hoặc xóa tệp.
  • Kích hoạt quy trình tự động như gửi email, cập nhật CRM, xử lý đơn hàng,…

Về bản chất, Function Calling là cầu nối giữa AI và logic lập trình, giúp biến mô hình AI thành Agent có khả năng hành động.

Function-calling là khả năng để LLM hiểu một câu hỏi/yêu cầu

Kiến trúc tổng quan AI → Chọn hàm → Thực thi → Hợp nhất kết quả

Luồng chuẩn (single-step):

  1. Người dùng nhập yêu cầu tự nhiên (prompt).
  2. Model phân tích → quyết định gọi 1 hàm (function_call).
  3. Ứng dụng của bạn thực thi hàm thật (gọi API/DB/CRM, …).
  4. Trả kết quả lại cho model (tool result) để model diễn giải, viết câu trả lời cuối cùng cho người dùng.

Luồng mở rộng (multi-step/Agent): Lặp lại bước 2–4 nhiều lần (AI chọn hàm kế tiếp dựa trên kết quả trước đó) cho đến khi đạt mục tiêu (goal) hoặc chạm giới hạn bước.
Ghi nhớ rằng Model không tự kết nối Internet/hệ thống. Bạn định nghĩa được gọi hàm nào, tham số gì, giới hạn ra sao.

Định nghĩa function (JSON Schema) – OpenAI

Bạn mô tả được tên, mô tả, tham số (schema) để model hiểu: khi nào nên gọi, gọi với dữ liệu nào.

// functions.js
export const functions = [
  {
    name: "get_weather",
    description: "Lấy thời tiết hiện tại theo thành phố",
    parameters: {
      type: "object",
      properties: {
        city: { type: "string", description: "Tên thành phố, ví dụ: Hà Nội" },
        unit: { type: "string", enum: ["c", "f"], default: "c" }
      },
      required: ["city"]
    }
  },
  {
    name: "send_email",
    description: "Gửi email xác nhận cho khách hàng",
    parameters: {
      type: "object",
      properties: {
        to: { type: "string", description: "Địa chỉ email" },
        subject: { type: "string" },
        body: { type: "string" }
      },
      required: ["to", "subject", "body"]
    }
  }
];

Best-practices viết description:

  • Viết ngắn, rõ hành vi → giúp model routing chính xác.
  • Miêu tả điều kiện gọi (ví dụ: “Chỉ gọi khi người dùng hỏi về…”) để giảm sai lệch.

OpenAI – luồng gọi hàm end-to-end (Node.js, chạy được)

Ví dụ dưới dùng openai SDK (JS), minh họa: hỏi thời tiết → model gợi ý get_weather → bạn gọi API thật → nhét kết quả vào “tool message” → model tổng hợp câu trả lời.

// app.js
import OpenAI from "openai";
import fetch from "node-fetch";
import { functions } from "./functions.js";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function getWeather({ city, unit = "c" }) {
  // Ví dụ mock hoặc gọi API thật (OpenWeather, etc.)
  // const resp = await fetch(`https://api.example.com/weather?city=${encodeURIComponent(city)}`);
  // const data = await resp.json();
  const data = { city, temp_c: 29, condition: "Nắng nhẹ" };
  return unit === "f" ? { ...data, temp_f: 84.2 } : data;
}

async function sendEmail({ to, subject, body }) {
  // Gọi email provider của bạn (ví dụ BizMail, SES, Mailgun…)
  return { status: "ok", messageId: "abc-123", to, subject };
}

const toolMap = {
  get_weather: getWeather,
  send_email: sendEmail,
};

export async function ask(messages) {
  // 1) Gọi model với khai báo functions
  const resp = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages,
    functions,
    function_call: "auto",
    temperature: 0.2,
  });

  const msg = resp.choices[0]?.message;
  if (msg?.function_call) {
    // 2) Model đề nghị gọi hàm
    const { name, arguments: argsJson } = msg.function_call;
    const args = JSON.parse(argsJson || "{}");

    if (!toolMap[name]) {
      // guard: nếu model gọi hàm không tồn tại
      return { messages: [...messages, msg, { role: "assistant", content: "Hàm không hỗ trợ." }] };
    }

    // 3) Thực thi hàm thật
    const toolResult = await toolMap[name](args);

    // 4) Trả kết quả (tool) về cho model để nó viết câu trả lời tốt hơn
    const followResp = await client.chat.completions.create({
      model: "gpt-4o-mini",
      temperature: 0.2,
      functions,
      function_call: "auto",
      messages: [
        ...messages,
        msg,
        {
          role: "function",
          name,
          content: JSON.stringify(toolResult)
        }
      ]
    });

    return followResp.choices[0]?.message;
  }

  // Không cần gọi function → trả lời trực tiếp
  return msg;
}

// Demo:
(async () => {
  const messages = [{ role: "user", content: "Hà Nội hôm nay bao nhiêu độ C? Và gửi email xác nhận cho tôi ở abc@demo.com" }];
  const first = await ask(messages);

  // Nếu model chưa đủ thông tin để gửi email, bạn có thể lặp vòng:
  // thêm chỉ dẫn “nếu muốn gửi email, hãy gọi send_email với subject/body ngắn gọn…”
  console.log(first);
})();

Điểm mấu chốt:

  • Không tự động tin mọi đề nghị gọi hàm → luôn có toolMap và kiểm tra tham số.
  • Tách logic gọi API thật ra khỏi layer LLM để dễ test.

Ollama – function calling cục bộ (Python, tối giản)

Ollama cho phép tương tác local; dưới đây là cách mô phỏng function calling (nhiều bản phân phối thực hiện qua “tool spec” hoặc tiền xử lý prompt).

# ollama_fc.py
import json
import ollama

def get_time(_args=None):
    return {"now": "2025-10-16 10:30:00"}

TOOLS = {
    "get_time": get_time
}

SYSTEM_HINT = """\
Bạn là trợ lý dùng công cụ. Khi cần thời gian hệ thống, hãy xuất JSON:
{"call": "get_time", "args": {}}
Nếu không cần công cụ, trả lời bình thường.
"""

def route_and_call(model, user_text):
    res = ollama.chat(
        model=model,
        messages=[
            {"role":"system","content": SYSTEM_HINT},
            {"role":"user","content": user_text}
        ]
    )
    content = res["message"]["content"]
    try:
        blob = json.loads(content)
        if "call" in blob:
            fn = blob["call"]
            args = blob.get("args", {})
            if fn in TOOLS:
                tool_out = TOOLS[fn](args)
                # cho LLM thấy kết quả tool để nó diễn giải
                res2 = ollama.chat(
                    model=model,
                    messages=[
                        {"role":"system","content": SYSTEM_HINT},
                        {"role":"user","content": user_text},
                        {"role":"assistant","content": content},
                        {"role":"system","content": f"Kết quả công cụ: {json.dumps(tool_out, ensure_ascii=False)}"}
                    ]
                )
                return res2["message"]["content"]
    except Exception:
        pass
    return content

if __name__ == "__main__":
    print(route_and_call("llama3", "Bây giờ là mấy giờ?"))

Lưu ý: Cách trên dùng “protocol tự quy ước” (JSON trigger) nếu bản Ollama/model bạn dùng chưa hỗ trợ tool natively. Ở sản xuất, bạn nên đóng khung JSON bằng regex/guard rails, hoặc dùng tool spec nếu có.

Xác thực tham số và chống prompt injection

Vấn đề: Model có thể “bịa” tham số, hoặc bị người dùng chèn prompt injection (ép gọi hàm nguy hiểm).
Giải pháp:

  • Luôn validate đầu vào bằng Zod/Ajv (JS) hay pydantic (Python).
  • Thêm whitelist tên hàm có thể gọi.
  • Không bao giờ thực thi shell/file I/O nguy hiểm nếu không có xác thực người dùng/role.

Ví dụ (Node.js + zod):

import { z } from "zod";

const SendEmailSchema = z.object({
  to: z.string().email(),
  subject: z.string().min(1).max(120),
  body: z.string().min(1).max(5000)
});

async function sendEmailSafe(args) {
  const parsed = SendEmailSchema.safeParse(args);
  if (!parsed.success) throw new Error("Invalid arguments");
  return sendEmail(parsed.data);
}

Logging, quan sát & kiểm thử

  • Structured logs (JSON) gồm: request-id, user-id, function_name, args_hash, latency, status.
  • Replay test: Lưu cặp (messages, tool_result) để replay khi nâng cấp model.
  • Kịch bản kiểm thử:
    • Model nên gọi đúng get_weather khi hỏi “thời tiết…”.
    • Không gọi send_email nếu thiếu địa chỉ email hợp lệ.
    • Dừng sau N bước để tránh vòng lặp vô hạn.

Ví dụ log nhanh:

console.log(JSON.stringify({
  level: "info",
  event: "tool_call",
  fn: name,
  args: args,
  ts: Date.now()
}));

Mẫu triển khai (patterns) thực chiến

  • Single-Tool Router: Chỉ có 1–3 tool quan trọng (CRM/Email/Weather). Tối ưu prompt: nêu điều kiện gọi rõ ràng.
  • ReAct + Function Calling: LLM suy luận (Reason) → quyết định (Act = call function) → quan sát (Observation) → lặp cho đến khi xong. Dùng khi cần chuỗi tác vụ (multi-step), ví dụ: “tạo báo giá → gửi email → log CRM”.
  • Guarded Tools: Các tool nhạy cảm (ghi/xóa dữ liệu) yêu cầu xác nhận 2 bước: model đề xuất → hệ thống kiểm tra điều kiện → mới thực thi.
  • Caching Tool Results: Nếu cùng câu hỏi lặp lại (ví dụ thời tiết, tỉ giá), cache 1–5 phút để giảm chi phí & latency.

Quy trình đa bước cho bài toán thật (ví dụ CRM thực tế)

Mục tiêu: Người dùng nhắn “Tạo lead mới tên Minh, email minh@acme.com, sau đó gửi email chào mừng”.

  1. Model trích ý định & tham số → gọi create_lead({name, email}).
  2. Hệ thống gọi BizCRM API → trả về {leadId: 1001, status:"created"}.
  3. Model thấy đã có leadId → gọi send_email({to, subject, body}).
  4. Hệ thống gửi BizMail → trả {status:"ok"}.
  5. Model tổng hợp trả lời cuối: “Đã tạo lead #1001 và gửi email chào mừng.”

Streaming & UX

  • Streaming (SSE) cho phép render dần: hiển thị tiến trình “Đang tạo lead…”, “Đã gửi email…”.
  • UI thân thiện giúp người dùng tin tưởng vào tự động hóa (vd. báo rõ action, có nút “Hủy”).

Bảo mật & tuân thủ

  • Rate limit & quota cho từng tool.
  • RBAC: chỉ role “CSKH” mới được send_refund, v.v.
  • Audit log lưu 90 ngày.
  • PII: mã hóa email/điện thoại khi lưu log.

Tối ưu chi phí và độ chính xác

  • Dùng model nhẹ (ví dụ gpt-4o-mini) cho routing function; chỉ nâng cấp model khi cần văn phong cao cấp.
  • System prompt rõ ràng = ít vòng gọi → tiết kiệm token.
  • Eval nội bộ: đo tỉ lệ route đúng hàm, tỉ lệ lỗi thực thi, độ dài bước.

Checklist triển khai nhanh

  1. Liệt kê tác vụ có thể tự động hóa (CRUD CRM, Email, Ticket…).
  2. Viết function schema ngắn, rõ; thêm mô tả điều kiện gọi.
  3. Thêm validation tham số.
  4. Dựng toolMap/whitelist.
  5. Thiết lập log + replay tests.
  6. Giới hạn max_steps và timeout.
  7. Viết kịch bản kiểm thử theo intent người dùng thật.

Mẫu prompt hệ thống (gợi ý áp dụng ngay)

Bạn là Trợ lý Tác vụ. Khi người dùng yêu cầu thao tác thực tế (lấy dữ liệu, tạo lead, gửi email...), hãy chọn một function phù hợp từ danh sách.
Chỉ gọi function khi:
- Người dùng hỏi đúng ngữ cảnh của function và dữ liệu đủ tối thiểu.
- Nếu thiếu dữ liệu, hãy hỏi lại đúng trường còn thiếu.
Sau khi nhận được "function result", hãy viết câu trả lời cuối ngắn gọn, có số liệu quan trọng.
Không bịa thông tin. Không gọi function ngoài whitelist.

Kết bài

Function Calling biến mô hình ngôn ngữ từ công cụ “hỏi–đáp” thành Agent có thể hành động. Bằng cách định nghĩa schema rõ ràng, xác thực tham số, logging đầy đủ và áp dụng các pattern như ReAct + Guarded Tools, bạn có thể tự động hóa quy trình thực trong CRM/Email/Support một cách an toàn, chính xác và tiết kiệm chi phí bất kể bạn chọn OpenAI (cloud) hay Ollama (cục bộ).

Đỗ Minh Đức
Tác giả
Đỗ Minh Đức

Với gần 20 năm kinh nghiệm trong ngành công nghệ, Đỗ Minh Đức hiện là Giám đốc Sản phẩm Bizfly Martech tại VCCorp. Anh được biết đến là một trong bốn người đặt nền móng cho BizChatAI, giải pháp ứng dụng trí tuệ nhân tạo để chăm sóc khách hàng tự động đa kênh.

Anh tập trung phát triển BizChatAI như một "trợ lý ảo" cho doanh nghiệp, giúp tự động hóa việc tương tác và CSKH. Công nghệ này đang thay đổi mạnh mẽ cách doanh nghiệp tiếp cận khách hàng, từ việc gửi tin nhắn, quà tri ân tự động đến ứng dụng hiệu quả cho các chuỗi bán lẻ và nhà hàng... Qua các bài viết của mình, anh chia sẻ sâu hơn về những lợi ích và cách thức hoạt động của chatbot trong kinh doanh.