Quay lại blog

API & SDKHướng dẫn

Function calling trong Python với API tương thích OpenAI

Gửi tools, đọc tool_calls và trả kết quả với role tool: một vòng lặp function calling hai bước trong Python với MiniMax-M2.7.

Bởi CheapTokens TeamĐã xuất bản Đã cập nhật 7 phút đọc

Sơ đồ vòng lặp gọi tool hai bước: gửi tools, đọc tool_calls, trả kết quả dưới dạng tin nhắn tool
Trên trang này
  1. Cách function calling hoạt động
  2. Thiết lập client
  3. Khai báo một hàm và gửi yêu cầu đầu tiên
  4. Những gì bạn sẽ thấy
  5. Nhiều lệnh gọi và vòng lặp dài hơn
  6. Vì sao MiniMax-M2.7 phù hợp với vòng lặp tool
  7. Nếu nó không hoạt động
  8. Các bước tiếp theo

Function calling (tool calling) cho phép mô hình yêu cầu mã của bạn chạy một hàm. Bạn gửi một danh sách tools cùng với yêu cầu, mô hình trả lời bằng tool_calls thay vì văn bản, bạn tự chạy hàm, rồi gửi kết quả trả về trong một tin nhắn với role: tool. Sau đó mô hình viết câu trả lời cuối cùng. Hướng dẫn này trình bày toàn bộ vòng lặp bằng Python với endpoint tương thích OpenAI https://api.cheaptokens.dev/v1/chat/completions cùng MiniMax-M2.7 (id MiniMaxAI/MiniMax-M2.7, ngữ cảnh 180K, tối đa 8.192 token đầu ra mỗi câu trả lời).

Tool calling được liệt kê là được hỗ trợ cho mọi mô hình trong danh mục (CheapTokens docs, kiểm tra ngày 22 tháng 10 năm 2026). Bạn cần một API key; nếu bạn chưa từng gửi yêu cầu nào, hãy bắt đầu với yêu cầu API đầu tiên của bạn.

Cách function calling hoạt động#

Mô hình không bao giờ chạy bất cứ thứ gì. Nó chỉ tạo ra một yêu cầu có cấu trúc: "gọi get_weather với {\"city\": \"Paris\"}". Chương trình của bạn làm công việc thực sự và báo cáo lại. Điều đó tạo thành một vòng lặp gồm hai yêu cầu:

  1. Yêu cầu 1. Bạn gửi tin nhắn của người dùng cùng danh sách tools. Mô hình hoặc trả lời bằng văn bản, hoặc trả về một hay nhiều tool_calls.
  2. Mã của bạn. Bạn phân tích từng lệnh gọi, chạy hàm tương ứng và thu thập kết quả.
  3. Yêu cầu 2. Bạn gửi lại toàn bộ cuộc hội thoại: các tin nhắn gốc, tin nhắn của assistant với tool_calls, và một tin nhắn role: tool cho mỗi lệnh gọi. Mô hình dùng các kết quả để viết câu trả lời cuối cùng.

Mỗi yêu cầu đều gửi lại toàn bộ lịch sử, nên các vòng lặp tool dùng nhiều token đầu vào hơn một câu hỏi đơn lẻ. Nếu đây là điều mới với bạn, hãy đọc token là gì.

Thiết lập client#

Cài OpenAI SDK (pip install openai) và đặt key của bạn vào một biến môi trường thay vì trong mã nguồn:

bash
export CHEAPTOKENS_API_KEY="your-key-here"

Base URL là https://api.cheaptokens.dev/v1. Lấy id mô hình từ tài liệu CheapTokens: các nhà cung cấp khác nhau đặt tên khác nhau cho cùng một mô hình. Hướng dẫn này dùng MiniMaxAI/MiniMax-M2.7.

Khai báo một hàm và gửi yêu cầu đầu tiên#

Một hàm được mô tả trong mảng tools. Mỗi mục có type: function và một đối tượng function với name, description và parameters được viết dưới dạng JSON Schema. Phần mô tả là thứ mô hình đọc để quyết định khi nào gọi hàm, nên hãy viết nó thành một câu ngắn gọn, cụ thể.

Dưới đây là một script hoàn chỉnh. Hàm thời tiết là một stub với dữ liệu giả, nên ví dụ không cần thêm dịch vụ nào:

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'


def get_weather(city: str) -> dict:
    # Stub with fake data: replace with a real lookup
    fake = {'Paris': 14, 'Tokyo': 19}
    return {'city': city, 'temp_c': fake.get(city, 10)}


FUNCTIONS = {'get_weather': get_weather}

tools = [
    {
        'type': 'function',
        'function': {
            'name': 'get_weather',
            'description': 'Get the current temperature in Celsius for a city.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'city': {'type': 'string', 'description': 'City name, for example Paris'},
                },
                'required': ['city'],
            },
        },
    }
]

messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]

# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message

if not message.tool_calls:
    print(message.content)
    raise SystemExit

# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.tool_calls:
    args = json.loads(call.function.arguments)
    result = FUNCTIONS[call.function.name](**args)
    messages.append({
        'role': 'tool',
        'tool_call_id': call.id,
        'content': json.dumps(result),
    })

final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)

Ba chi tiết quyết định vòng lặp có hoạt động hay không:

  • call.function.arguments là một chuỗi JSON, không phải một dictionary. Hãy phân tích nó bằng json.loads.
  • Mỗi tin nhắn role: tool phải mang tool_call_id của lệnh gọi mà nó trả lời, tức là call.id.
  • content của một tin nhắn tool là một chuỗi. Dùng json.dumps cho các kết quả có cấu trúc.

Những gì bạn sẽ thấy#

Nếu bạn in các trường sau yêu cầu đầu tiên, chúng trông như thế này (id lệnh gọi khác nhau ở mỗi lần chạy):

text
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}

Sau đó script in câu trả lời cuối cùng, một câu đơn giản đại loại như "Hiện tại ở Paris là 14 °C." Cách diễn đạt thay đổi theo từng lần chạy; con số đến từ hàm của bạn.

Nhiều lệnh gọi và vòng lặp dài hơn#

Mô hình có thể trả về nhiều hơn một lệnh gọi trong một phản hồi, ví dụ khi bạn hỏi về thời tiết ở hai thành phố. Đó là lý do ví dụ lặp qua message.tool_calls và trả lời từng lệnh gọi theo id riêng của nó. Hướng dẫn function calling của OpenAI mô tả cùng hành vi: với tool_choice mặc định là auto, mô hình có thể gọi không, một hoặc nhiều hàm (tài liệu OpenAI, kiểm tra ngày 22 tháng 10 năm 2026; trang không có ngày cập nhật lần cuối và mô tả dạng Responses API mới hơn, trong khi hướng dẫn này dùng dạng Chat Completions của endpoint /v1/chat/completions).

Các agent lặp lại hai bước cho đến khi mô hình ngừng yêu cầu tool. Hãy bọc các yêu cầu trong một vòng lặp và luôn đặt giới hạn số bước:

python
for _ in range(5):  # safety limit against endless loops
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = FUNCTIONS[call.function.name](**args)
        messages.append({'role': 'tool', 'tool_call_id': call.id, 'content': json.dumps(result)})

Nếu bạn muốn câu trả lời hiện ra từng token một, hãy kết hợp điều này với streaming trong Python.

Vì sao MiniMax-M2.7 phù hợp với vòng lặp tool#

Một vòng lặp tool gửi nhiều yêu cầu với lịch sử ngày càng tăng. Với mẫu đó, CheapTokens cung cấp MiniMax-M2.7: ngữ cảnh 180K, tối đa 8.192 token đầu ra mỗi câu trả lời, một mức giá ở mọi thời điểm trong ngày không có giờ cao điểm, và một số dư cho tất cả mô hình. Tính cả đầu vào và đầu ra cộng lại, nó rẻ hơn giá tham chiếu của OpenRouter (so sánh giá CheapTokens, kiểm tra ngày 31 tháng 10 năm 2026).

Giá hiện tại trên 1M token: đầu vào $0.05, đầu ra $0.15. Như một khối lượng công việc ví dụ (không phải đo lường), vòng đi-về hai yêu cầu ở trên với tổng cộng khoảng 2.700 token đầu vào và 350 token đầu ra tốn $0.00019. Danh sách giá đầy đủ ở trang giá.

Để thử một mô hình khác, chỉ cần đổi id; danh sách id ở trang mô hình.

Nếu nó không hoạt động#

  • message.tool_calls là None. Mô hình đã trả lời bằng văn bản. Hãy làm cho mô tả hàm description cụ thể hơn, và đảm bảo câu hỏi thực sự cần đến hàm.
  • json.JSONDecodeError trên arguments. Mô hình tạo ra JSON không hợp lệ. Hãy bọc json.loads trong try/except và gửi văn bản lỗi trở lại làm kết quả tool, để mô hình có thể thử lại với đối số đã sửa.
  • Yêu cầu thứ hai thất bại hoặc mô hình bỏ qua kết quả. Kiểm tra rằng mỗi lệnh gọi có đúng một tin nhắn role: tool với tool_call_id khớp, và tin nhắn của assistant với tool_calls nằm trong lịch sử trước chúng.
  • KeyError trong FUNCTIONS[...]. Mô hình đã gọi tên một hàm bạn chưa đăng ký. Hãy trả về một chuỗi lỗi ngắn làm kết quả tool thay vì bị crash.
  • Câu trả lời bị cắt cụt. Kiểm tra finish_reason: giá trị length nghĩa là đã đạt giới hạn token đầu ra. Xem vì sao câu trả lời của AI dừng giữa câu.
  • Lỗi 401 hoặc lỗi xác thực. Kiểm tra rằng key nằm trong CHEAPTOKENS_API_KEY và base URL kết thúc bằng /v1.

Các bước tiếp theo#

Hãy thay stub bằng một hàm thực sự: một truy vấn cơ sở dữ liệu, một lệnh gọi tìm kiếm hoặc một máy tính. Thêm nhiều mục vào tools và vào FUNCTIONS, và giữ mọi kết quả hàm ngắn gọn, vì nó được đưa trở lại lịch sử ở mỗi yêu cầu tiếp theo. Để lấy key và thực hiện lệnh gọi đầu tiên, hãy mở tài liệu CheapTokens.