Tất cả bài viết
API AI

Tool calling với Claude và GPT qua endpoint OpenAI-compatible: ví dụ tra đơn hàng

Hồ Lê Minh Tuấn··4 phút đọc

Khai báo hàm trong tham số tools theo chuẩn OpenAI, gửi kèm câu hỏi của người dùng; nếu model trả về tool_calls, bạn chạy hàm thật, gửi kết quả lại với role tool, rồi model viết câu trả lời cuối. Cách này chạy giống nhau với GPT và Claude khi gọi qua endpoint /v1/chat/completions của MonkeyTech API, chỉ khác tên model.

Tool calling với Claude và GPT qua endpoint OpenAI-compatible: ví dụ tra đơn hàng

Bot trả lời khách hỏi "đơn của em tới đâu rồi" mà chỉ dựa vào prompt thì sẽ bịa. Cách đúng là cho model một hàm tra_don_hang(ma_don) và để nó quyết định khi nào gọi. Đó là tool calling, còn gọi là function calling.

Bài này đi từ ví dụ nhỏ nhất chạy được, dùng thư viện openai của Python trỏ về MonkeyTech API. Phần cài đặt key và base URL đã có trong bài gọi Claude API và GPT bằng Python, Node.js.

Khai báo hàm

from openai import OpenAI
import json

client = OpenAI(
    api_key="sk-mtk-...",
    base_url="https://api.monkeytech.vn/v1",
)

tools = [{
    "type": "function",
    "function": {
        "name": "tra_don_hang",
        "description": "Tra trạng thái đơn hàng theo mã đơn của khách",
        "parameters": {
            "type": "object",
            "properties": {
                "ma_don": {"type": "string", "description": "Mã đơn, ví dụ DH10234"}
            },
            "required": ["ma_don"],
        },
    },
}]

def tra_don_hang(ma_don: str) -> dict:
    # Thay bằng truy vấn thật vào hệ thống của bạn
    return {"ma_don": ma_don, "trang_thai": "Đang giao", "du_kien": "hôm nay trước 17 giờ"}

Mô tả hàm và mô tả tham số là thứ model đọc để quyết định có gọi hay không, nên viết rõ bằng tiếng Việt, kèm ví dụ giá trị.

Vòng gọi hai bước

messages = [
    {"role": "system", "content": "Bạn là nhân viên hỗ trợ của cửa hàng. Chỉ trả lời trạng thái đơn khi đã tra bằng công cụ."},
    {"role": "user", "content": "Đơn DH10234 của em tới đâu rồi?"},
]

first = client.chat.completions.create(
    model="monkeytech/gpt-5.6-luna",
    messages=messages,
    tools=tools,
)
msg = first.choices[0].message

if msg.tool_calls:
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = tra_don_hang(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })
    final = client.chat.completions.create(
        model="monkeytech/gpt-5.6-luna",
        messages=messages,
        tools=tools,
    )
    print(final.choices[0].message.content)
else:
    print(msg.content)

Bước một: model đọc câu hỏi, thấy khớp mô tả hàm, trả về tool_calls với tham số {"ma_don": "DH10234"} thay vì trả lời bằng chữ. Bước hai: bạn chạy hàm thật, nối kết quả vào hội thoại với role: "tool", gọi lại, và model viết câu trả lời cho khách: "Đơn DH10234 đang giao, dự kiến tới trước 17 giờ hôm nay."

Đổi sang Claude: chỉ đổi tên model

Vì gọi qua endpoint tương thích OpenAI, đoạn code trên không đổi khi dùng Claude. Thay model="monkeytech/gpt-5.6-luna" bằng tên model Claude trong danh mục, ví dụ claude-fable-5. Gateway chuyển định dạng tools sang chuẩn Anthropic phía sau.

Hãy chạy thử với từng model bạn định dùng trước khi đưa vào sản phẩm. Cách kiểm tra nhanh: gửi đúng câu hỏi ở trên và in msg.tool_calls; nếu ra None và model trả lời bằng chữ, model đó không gọi được tool qua đường này, hoặc mô tả hàm của bạn chưa đủ rõ.

Những chỗ hay sai

  • Quên nối tin nhắn của model vào hội thoại trước khi thêm tin role: "tool". Thiếu bước messages.append(msg) thì lần gọi thứ hai báo lỗi vì tool_call_id không khớp.
  • Trả kết quả bằng chuỗi tự ghép thay vì JSON. Model vẫn đọc được, nhưng JSON giúp nó không hiểu nhầm số và ngày.
  • Model gọi nhiều tool trong một lượt. Vòng for ở trên xử lý được; đừng giả định chỉ có một tool_call.
  • Cho model quyền gọi hàm có tác dụng phụ (huỷ đơn, hoàn tiền) mà không xác nhận với người dùng. Tách hàm đọc và hàm ghi; hàm ghi nên trả về "cần xác nhận" rồi để người dùng bấm.
  • Mô tả hàm mơ hồ. "Lấy thông tin" là chưa đủ; "Tra trạng thái đơn hàng theo mã đơn, chỉ dùng khi khách cung cấp mã" rõ hơn nhiều.

Tính tiền

Tool calling tốn hai lượt gọi, và phần khai báo tools được gửi kèm mỗi lượt nên tính vào token đầu vào. Với một hàm như ví dụ, phần khai báo khoảng 80-100 token. Nếu bạn có 20 hàm, hãy cân nhắc chỉ gửi những hàm liên quan tới ngữ cảnh, thay vì gửi cả 20 mỗi lượt. Dashboard API hiện token từng lượt để bạn thấy phần này.

Câu hỏi thường gặp

Có. Tham số tool_choice cho phép ép gọi một hàm cụ thể hoặc cấm gọi. Dùng khi bạn đã biết chắc bước tiếp theo, ví dụ luôn tra đơn khi khách nhắn mã đơn.

Cùng cấu trúc: thư viện openai cho Node nhận tools và trả tool_calls y hệt. Chỉ khác cú pháp ngôn ngữ.

Không. n8n có node AI Agent, Dify có Agent với tool, hai bên tự lo vòng gọi. Bài này dành cho người viết code trực tiếp; cách nối n8n chúng tôi đã viết trong bài kết nối n8n với Claude, GPT qua MonkeyTech API.

Bài viết hữu ích với bạn? Thả tim để chúng tôi viết thêm chủ đề này.

Cần tự động hóa quy trình cho doanh nghiệp?

Monkey Tech build AI Agent, bot Zalo/Telegram và luồng n8n chạy thật cho bạn.

Bài viết khác