AI Agent Roadmap: Triển khai Function Calling với OpenAI và Gemini

Chào mừng các bạn quay trở lại với chuỗi bài viết “AI Agent Roadmap“. Chúng ta đã cùng nhau đi qua nhiều khái niệm nền tảng quan trọng, từ việc AI Agent là gì và Chúng Hoạt Động Như Thế Nào?, hiểu về Vòng Lặp Agent, cách Xây dựng Công cụ AI Tốt hơn, cho đến các kiến trúc nâng cao như ReAct và Chain-of-Thought. Hôm nay, chúng ta sẽ đi sâu vào một kỹ thuật cực kỳ mạnh mẽ giúp AI Agent vượt ra khỏi giới hạn của dữ liệu huấn luyện tĩnh và tương tác với thế giới thực: Function Calling (hay còn gọi là Tool Calling).

Trong bài viết này, chúng ta sẽ tập trung vào cách triển khai Function Calling với hai nền tảng LLM phổ biến nhất hiện nay: OpenAI và Google Gemini. Việc nắm vững kỹ thuật này là bước đệm quan trọng để bạn có thể Xây Dựng AI Agent Từ Đầu Sử Dụng API của LLM thực sự thông minh và hữu ích.

Function Calling là Gì?

Về cơ bản, các Mô hình Ngôn ngữ Lớn (LLM) như GPT-4 hay Gemini không thể tự mình thực hiện các hành động như gửi email, tra cứu thông tin trên web hay truy vấn cơ sở dữ liệu. Chúng chỉ có khả năng xử lý và sinh văn bản dựa trên dữ liệu mà chúng đã được huấn luyện. Tuy nhiên, để xây dựng các AI Agent có thể thực hiện các tác vụ thực tế, chúng cần một cách để tương tác với các hệ thống bên ngoài.

Function Calling chính là cầu nối đó. Nó cho phép bạn mô tả các hàm (functions) hoặc công cụ (tools) mà hệ thống của bạn có thể sử dụng cho LLM dưới dạng cấu trúc dữ liệu (thường là JSON). Khi nhận được yêu cầu từ người dùng, LLM sẽ phân tích yêu cầu và, nếu thấy cần thiết, sẽ không trả về một câu trả lời trực tiếp mà thay vào đó là một đề xuất (recommendation) về việc gọi một hoặc nhiều hàm đã được định nghĩa, kèm theo các tham số phù hợp được trích xuất từ yêu cầu của người dùng.

Quan trọng cần hiểu: LLM không tự mình thực thi hàm. Nó chỉ đưa ra đề xuất gọi hàm. Công việc thực tế là của hệ thống phía bạn (ứng dụng, backend service) nhận được đề xuất này, thực sự gọi hàm tương ứng, và sau đó gửi kết quả của hàm trở lại cho LLM. LLM sẽ sử dụng kết quả này để tiếp tục quá trình suy luận và tạo ra câu trả lời cuối cùng cho người dùng.

Quá trình này diễn ra như sau:

  1. Người dùng gửi yêu cầu đến hệ thống AI Agent của bạn.
  2. Yêu cầu của người dùng và danh sách các hàm (được mô tả chi tiết) được gửi đến API của LLM.
  3. LLM phân tích yêu cầu và đưa ra phản hồi dạng JSON, mô tả tên hàm cần gọi và các tham số.
  4. Hệ thống của bạn nhận phản hồi này, xác định hàm cần gọi và các tham số.
  5. Hệ thống của bạn thực thi hàm thực tế (ví dụ: gọi API thời tiết, truy vấn DB, gửi email).
  6. Kết quả của việc thực thi hàm được gửi trở lại API của LLM (thường là trong một lượt trò chuyện tiếp theo).
  7. LLM xử lý kết quả và tạo ra phản hồi cuối cùng bằng ngôn ngữ tự nhiên cho người dùng.

Đây chính là cách mà các Agent thực hiện bước “Hành động” (Action) trong Vòng Lặp Agent Được Giải Thích sau khi đã hoàn thành bước “Nhận thức” (Perception) và “Suy luận” (Reasoning).

Vì sao Function Calling quan trọng cho AI Agent?

Function Calling là nền tảng cho nhiều khả năng cốt lõi của một AI Agent mạnh mẽ:

  • Truy xuất thông tin thời gian thực: Agent có thể lấy thông tin cập nhật từ các nguồn bên ngoài, như giá cổ phiếu hiện tại, dự báo thời tiết, tin tức mới nhất, thay vì chỉ dựa vào dữ liệu huấn luyện (thường là cũ). Điều này liên quan chặt chẽ đến kỹ thuật RAG (Retrieval Augmented Generation), trong đó việc truy xuất dữ liệu từ Vector Database hay các nguồn khác cũng có thể được xem như một dạng “gọi hàm”.
  • Thực hiện hành động: Agent không chỉ trả lời câu hỏi mà còn có thể làm những việc hữu ích như đặt lịch hẹn, gửi tin nhắn, tạo task trong hệ thống quản lý dự án, mua hàng online, v.v. Tất cả đều thông qua việc gọi các API hoặc hàm backend của bạn. Hiểu về REST API là rất quan trọng ở đây.
  • Tương tác với dữ liệu người dùng: Agent có thể truy cập và xử lý dữ liệu cụ thể của người dùng (ví dụ: lịch sử mua hàng, thông tin tài khoản, ghi chú cá nhân) để đưa ra phản hồi cá nhân hóa hoặc thực hiện các tác vụ liên quan. Kỹ thuật quản lý Bộ nhớ của Agent trở nên cần thiết khi làm việc với dữ liệu người dùng.
  • Kết hợp khả năng của LLM với các hệ thống chuyên biệt: LLM mạnh về ngôn ngữ, suy luận (đã thảo luận trong Lựa chọn LLM phù hợp), nhưng yếu ở các tác vụ tính toán chính xác hoặc truy cập dữ liệu cụ thể. Function Calling cho phép LLM ủy thác những công việc này cho các hệ thống chuyên biệt được thiết kế để thực hiện chúng hiệu quả và chính xác.

Triển khai Function Calling với OpenAI

OpenAI là một trong những nền tảng đầu tiên phổ biến khái niệm Function Calling (ban đầu gọi là “Functions”, sau đổi thành “Tool Calling” với loại `function`). API chat completion của OpenAI cho phép bạn định nghĩa các hàm có sẵn thông qua tham số `tools`.

Định nghĩa Hàm

Bạn định nghĩa các hàm dưới dạng một danh sách trong tham số `tools`. Mỗi mục trong danh sách là một object có `type` là `”function”` và một object `function` mô tả chi tiết hàm đó.

Object `function` yêu cầu các trường sau:

  • `name` (string, bắt buộc): Tên của hàm sẽ được gọi trong code của bạn.
  • `description` (string, khuyến khích): Mô tả rõ ràng chức năng của hàm. Điều này cực kỳ quan trọng để LLM hiểu khi nào cần gọi hàm này. Viết Prompt Hiệu quả và mô tả tool chính là một dạng prompt.
  • `parameters` (object, khuyến khích): Định nghĩa các tham số mà hàm nhận. Đây phải là một object JSON Schema.

Ví dụ về định nghĩa hàm lấy thông tin thời tiết:


{
  "type": "function",
  "function": {
    "name": "get_current_weather",
    "description": "Lấy thông tin thời tiết hiện tại cho một địa điểm cụ thể",
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "Tên thành phố, ví dụ: San Francisco"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "Đơn vị nhiệt độ, celsius hoặc fahrenheit"
        }
      },
      "required": ["location"]
    }
  }
}

Gửi yêu cầu và nhận đề xuất gọi hàm

Bạn gọi API `/v1/chat/completions` như bình thường, nhưng thêm tham số `tools` chứa danh sách các hàm đã định nghĩa. Nội dung cuộc trò chuyện (trong tham số `messages`) phải bao gồm yêu cầu của người dùng.


from openai import OpenAI

client = OpenAI()

# Giả định hàm get_current_weather_from_api() tồn tại trong code của bạn
def get_current_weather_from_api(location, unit="fahrenheit"):
    """Fetch the current weather from a real API."""
    # Đây là nơi bạn gọi API thời tiết thực tế
    # Tạm thời trả về dữ liệu giả
    weather_info = {
        "location": location,
        "temperature": "25",
        "unit": unit,
        "forecast": ["sunny", "windy"]
    }
    return str(weather_info) # Quan trọng: kết quả trả về là string

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Lấy thông tin thời tiết hiện tại cho một địa điểm cụ thể",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "Tên thành phố, ví dụ: San Francisco"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Đơn vị nhiệt độ, celsius hoặc fahrenheit"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

messages = [
    {"role": "user", "content": "Thời tiết ở Hà Nội hôm nay thế nào?"}
]

response = client.chat.completions.create(
    model="gpt-4o", # Hoặc một model hỗ trợ tools
    messages=messages,
    tools=tools,
    tool_choice="auto" # 'auto' cho phép model quyết định có gọi hàm hay không
)

response_message = response.choices[0].message
tool_calls = response_message.tool_calls

# Bước 4: Xử lý đề xuất gọi hàm
if tool_calls:
    # Lưu trữ các message cũ và message mới từ model
    messages.append(response_message)
    
    # Thực thi từng lệnh gọi hàm được đề xuất
    for tool_call in tool_calls:
        function_name = tool_call.function.name
        function_args = tool_call.function.arguments # Dạng string JSON
        
        print(f"Model đề xuất gọi hàm: {function_name} với args: {function_args}")
        
        # Kiểm tra hàm được đề xuất có tồn tại không
        if function_name == "get_current_weather":
            # Bước 5: Thực thi hàm trong code của bạn
            try:
                # Phân tích JSON string arguments thành Python dictionary
                args_dict = json.loads(function_args)
                # Gọi hàm thực tế với các tham số đã phân tích
                function_response = get_current_weather_from_api(
                    location=args_dict.get("location"),
                    unit=args_dict.get("unit")
                )
                print(f"Kết quả thực thi hàm: {function_response}")
                
                # Bước 6: Gửi kết quả trở lại API dưới dạng một message mới
                messages.append({
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": function_response
                })
            except Exception as e:
                 # Xử lý lỗi nếu có
                 error_message = f"Lỗi khi thực thi hàm {function_name}: {e}"
                 print(error_message)
                 messages.append({
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": f"Error: {error_message}" # Gửi thông báo lỗi cho LLM
                })
        else:
            print(f"Lỗi: Hàm {function_name} không tồn tại trong danh sách được cung cấp.")
            # Cần xử lý trường hợp model "ảo tưởng" và đề xuất hàm không có thật
            messages.append({
                 "tool_call_id": tool_call.id,
                 "role": "tool",
                 "name": function_name,
                 "content": "Error: Function not found." # Gửi thông báo lỗi cho LLM
            })


    # Bước 7: Gửi toàn bộ cuộc trò chuyện cập nhật (bao gồm kết quả hàm) trở lại model
    # để nó tạo phản hồi cuối cùng cho người dùng.
    print("\nGửi kết quả hàm trở lại model để hoàn thành phản hồi...")
    second_response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages
    )
    print("Phản hồi cuối cùng từ model:")
    print(second_response.choices[0].message.content)
else:
    # Không có đề xuất gọi hàm, model trả lời trực tiếp
    print("Model không đề xuất gọi hàm. Phản hồi trực tiếp:")
    print(response_message.content)

Lưu ý: Quy trình này thường yêu cầu hai lượt gọi API nếu LLM đề xuất gọi hàm: một để nhận đề xuất, và một để gửi kết quả hàm và nhận phản hồi cuối cùng. Đây là một đặc điểm quan trọng của Function Calling trên OpenAI.

Triển khai Function Calling với Google Gemini

Google Gemini cũng hỗ trợ tính năng tương tự, được gọi là “Tool Calling” hoặc “Function Calling”. API của Gemini có cấu trúc hơi khác so với OpenAI, nhưng nguyên lý hoạt động cơ bản vẫn là mô tả các công cụ/hàm cho model và xử lý đề xuất của model.

Định nghĩa Hàm

Với Gemini API, bạn định nghĩa các hàm trong tham số `tools` khi gọi API, sử dụng loại `Tool` và định nghĩa `function_declarations`.

Ví dụ về định nghĩa hàm lấy thông tin thời tiết với Gemini:


import google.generativeai as genai
import json
import os

# Cấu hình API key của Gemini
# genai.configure(api_key=os.environ["GEMINI_API_KEY"])

# Hàm giả định lấy thời tiết
def get_current_weather_gemini(location: str, unit: str = "fahrenheit"):
    """Fetch the current weather for a specific location."""
    weather_info = {
        "location": location,
        "temperature": "27",
        "unit": unit,
        "forecast": ["cloudy", "chance of rain"]
    }
    return json.dumps(weather_info) # Gemini thường làm việc tốt với string JSON trả về

# Định nghĩa tool (hàm) cho Gemini
weather_tool = genai.GenerativeModel(model_name='gemini-pro')\
    .get_safety_settings()\
    .with_tool(
        genai.Tool.from_function(get_current_weather_gemini)
    )

tools = [weather_tool]

# Hoặc định nghĩa thủ công (ít dùng hơn khi có genai.Tool.from_function)
# tools_manual = [{
#     "function_declarations": [{
#         "name": "get_current_weather_gemini",
#         "description": "Fetch the current weather for a specific location.",
#         "parameters": {
#             "type": "object",
#             "properties": {
#                 "location": {"type": "string"},
#                 "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
#             },
#             "required": ["location"]
#         }
#     }]
# }]

Gemini Python SDK cung cấp helper `genai.Tool.from_function` giúp tự động tạo cấu trúc JSON Schema từ signature của hàm Python, rất tiện lợi.

Gửi yêu cầu và nhận đề xuất gọi hàm

Bạn sử dụng phương thức `generate_content` của model, truyền yêu cầu của người dùng và danh sách `tools`.


# Khởi tạo model
model = genai.GenerativeModel('gemini-pro', tools=tools) # Truyền tools khi khởi tạo hoặc khi gọi generate_content

# Gửi tin nhắn ban đầu
chat_session = model.start_chat()
response = chat_session.send_message("Thời tiết ở Hồ Chí Minh thế nào?")

# Kiểm tra xem response có chứa đề xuất gọi hàm không
if response.candidates[0].content.parts[0].function_call:
    # Bước 4: Xử lý đề xuất gọi hàm
    tool_call = response.candidates[0].content.parts[0].function_call
    function_name = tool_call.name
    function_args = tool_call.args # Dạng dictionary

    print(f"Model đề xuất gọi hàm: {function_name} với args: {function_args}")

    # Kiểm tra hàm được đề xuất và thực thi
    if function_name == "get_current_weather_gemini":
        # Bước 5: Thực thi hàm trong code của bạn
        try:
            # Các tham số đã là dictionary, gọi hàm trực tiếp
            # Đảm bảo tên tham số trong hàm Python trùng với tên trong JSON schema
            function_response = get_current_weather_gemini(**function_args)
            print(f"Kết quả thực thi hàm: {function_response}")

            # Bước 6: Gửi kết quả trở lại API dưới dạng Tool Response
            # Lưu ý: Gemini xử lý trong cùng một chuỗi hội thoại
            response = chat_session.send_message(
                genai.ToolResponse(
                    name=function_name,
                    response={
                        "content": function_response # Kết quả hàm thường được đặt trong field "content" hoặc một cấu trúc tùy ý
                    }
                )
            )
            # Bước 7: Gemini sẽ sử dụng Tool Response để tạo phản hồi cuối cùng
            print("\nGửi kết quả hàm trở lại model để hoàn thành phản hồi...")
            print("Phản hồi cuối cùng từ model:")
            print(response.text)

        except Exception as e:
             error_message = f"Lỗi khi thực thi hàm {function_name}: {e}"
             print(error_message)
             # Cần xử lý gửi lỗi trở lại model hoặc thông báo cho người dùng
             # Gemini có cách xử lý lỗi tool gọi riêng, cần đọc thêm docs cụ thể
             # Ví dụ đơn giản có thể gửi lại lỗi như một message user hoặc tool_response đặc biệt
             pass # Xử lý lỗi chi tiết hơn trong ứng dụng thực tế

    else:
        print(f"Lỗi: Hàm {function_name} không tồn tại trong danh sách được cung cấp.")
        # Xử lý tương tự như OpenAI khi model đề xuất hàm không có thật
        pass # Cần xử lý trong ứng dụng thực tế
elif response.text:
    # Không có đề xuất gọi hàm, model trả lời trực tiếp
    print("Model không đề xuất gọi hàm. Phản hồi trực tiếp:")
    print(response.text)
else:
    # Xử lý các trường hợp phản hồi khác (ví dụ: chặn nội dung)
    print("Nhận phản hồi không chứa văn bản hoặc tool_call.")
    print(response.candidates[0].finish_reason)

Với Gemini API thông qua SDK, việc gửi kết quả hàm trở lại model thường diễn ra trong cùng một `chat_session` bằng cách gửi một message có loại `ToolResponse`. Gemini có thể phản hồi trực tiếp hoặc đề xuất gọi thêm hàm khác dựa trên kết quả này.

So sánh OpenAI và Gemini Function Calling

Tuy cùng phục vụ mục đích kết nối LLM với công cụ bên ngoài, có một vài khác biệt nhỏ trong cách triển khai giữa OpenAI và Gemini:

Tính năng OpenAI Google Gemini
Tên gọi chính thức Tool Calling (loại function) Tool Calling / Function Calling
Tham số định nghĩa hàm tools (list of tool objects with type="function") tools (list of Tool objects)
Định nghĩa chi tiết hàm Object function bên trong tool, sử dụng JSON Schema cho parameters. Object function_declarations bên trong Tool, cũng sử dụng cấu trúc tương tự JSON Schema. SDK có helper genai.Tool.from_function.
Field trong phản hồi chứa đề xuất gọi hàm choices[0].message.tool_calls candidates[0].content.parts[0].function_call
Định dạng tham số đề xuất trả về tool_calls[i].function.arguments (string JSON) function_call.args (dictionary Python, khi dùng SDK)
Cách gửi kết quả hàm trở lại Thêm một message mới vào lịch sử cuộc trò chuyện với role="tool"tool_call_id tương ứng. Yêu cầu gọi API lần 2. Gửi một message ToolResponse trong cùng session chat. Thường chỉ yêu cầu một lượt gọi API tiếp theo trong cùng session.
Hỗ trợ đa hàm trong một lượt gọi Có (danh sách tool_calls) Có (danh sách function_calls, khi có nhiều hơn 1 part)

Mặc dù có khác biệt về cú pháp API, luồng xử lý cốt lõi (“LLM đề xuất -> Bạn thực thi -> Gửi kết quả -> LLM hoàn thành”) là tương tự nhau trên cả hai nền tảng.

Các điểm cần lưu ý và Best Practices

Để triển khai Function Calling hiệu quả, đặc biệt khi Xây Dựng AI Agent Từ Đầu, hãy ghi nhớ các điểm sau:

  • Mô tả hàm (Description) rất quan trọng: Đây là thông tin chính LLM sử dụng để quyết định có gọi hàm hay không và khi nào gọi. Hãy viết mô tả rõ ràng, chính xác về chức năng và mục đích của từng hàm. Mô tả tốt giúp LLM suy luận đúng đắn hơn (Viết Prompt Hiệu Quả là kỹ năng cần thiết ở đây).
  • JSON Schema cho Parameters: Định nghĩa rõ ràng kiểu dữ liệu, các trường bắt buộc, và mô tả (description) cho từng tham số trong JSON Schema. Điều này giúp LLM trích xuất tham số chính xác từ yêu cầu của người dùng.
  • Xử lý lỗi: Hệ thống của bạn cần có khả năng xử lý các trường hợp lỗi xảy ra trong quá trình thực thi hàm (ví dụ: API bên ngoài không phản hồi, dữ liệu không hợp lệ). Bạn nên thông báo lỗi này cho LLM (thường bằng cách gửi kết quả lỗi trong Tool Response/Tool Message) để LLM có thể thông báo lại cho người dùng hoặc thử một phương án khác. Kỹ thuật Thiết Kế Công cụ cho AI Agent – Đầu vào, Đầu ra và Xử lý Lỗi đã nhấn mạnh điều này.
  • Xử lý đa hàm: Các model hiện đại có thể đề xuất gọi nhiều hàm cùng lúc trong một phản hồi. Hệ thống của bạn cần có logic để thực thi tất cả các hàm này (có thể song song) trước khi gửi kết quả trở lại LLM.
  • Bảo mật: Tuyệt đối không cho phép LLM truy cập trực tiếp vào các hàm hoặc dữ liệu nhạy cảm. Luôn có lớp trung gian trong code của bạn kiểm soát việc thực thi hàm và xác thực/phân quyền nếu cần.
  • Quản lý trạng thái (State Management): Đối với các tác vụ phức tạp cần nhiều bước và nhiều lần gọi hàm, bạn cần quản lý trạng thái của cuộc trò chuyện và các thông tin trung gian giữa các lượt gọi API. Điều này liên quan đến việc quản lý Bộ nhớ của Agent AI: Ngắn Hạn và Dài Hạn.

Function Calling trong Vòng Lặp Agent

Function Calling không chỉ là một tính năng độc lập; nó là một phần không thể thiếu của Vòng Lặp Agent (Perception -> Reasoning -> Action).

  • Perception (Nhận thức): Agent nhận yêu cầu từ người dùng và các thông tin từ môi trường (có thể bao gồm kết quả từ các lần gọi hàm trước).
  • Reasoning (Suy luận): LLM xử lý các thông tin này, sử dụng khả năng suy luận của mình (có thể thông qua các kỹ thuật như Chain-of-Thought hay ReAct) để quyết định hành động tiếp theo. Quyết định này có thể là:
    • Trả lời trực tiếp (nếu đủ thông tin).
    • Đề xuất gọi một hoặc nhiều hàm (Function Calling).
    • Yêu cầu làm rõ thêm từ người dùng.
  • Action (Hành động): Nếu LLM đề xuất gọi hàm, hệ thống của bạn sẽ thực thi hàm đó. Kết quả của hành động này sau đó được đưa trở lại bước Perception, làm giàu thêm thông tin cho LLM để tiếp tục vòng lặp cho đến khi hoàn thành tác vụ.

Như vậy, Function Calling chính là cơ chế cho phép Agent thực hiện bước “Action” một cách có chủ đích, dựa trên suy luận của LLM.

Kết luận

Function Calling là một kỹ thuật then chốt giúp biến các LLM từ những mô hình ngôn ngữ thụ động thành những AI Agent chủ động, có khả năng tương tác với thế giới thực. Việc nắm vững cách triển khai Function Calling với các nền tảng phổ biến như OpenAI và Google Gemini là bước tiến lớn trên hành trình xây dựng các ứng dụng AI thông minh và mạnh mẽ hơn.

Qua bài viết này, hy vọng các bạn đã hiểu rõ nguyên lý hoạt động, cách triển khai cơ bản trên hai nền tảng, cùng với những điểm cần lưu ý. Hãy bắt tay vào thử nghiệm và xây dựng các công cụ/hàm cho Agent của riêng bạn. Trong các bài viết tiếp theo của chuỗi “AI Agent Roadmap”, chúng ta sẽ tiếp tục khám phá các chủ đề nâng cao hơn để hoàn thiện bộ kỹ năng xây dựng AI Agent.

Chỉ mục