Chuyển đến nội dung chính

Hướng dẫn Gọi Hàm với GPT-5

Hướng dẫn thực hành gọi hàm trong GPT-5: từ hàm theo JSON Schema đến công cụ tự do, ràng buộc Lark/CFG, preamble và danh sách cho phép công cụ.
· 11 phút đọc

Khám phá cùng AI

Với việc ra mắt GPT-5, OpenAI đã mở rộng khả năng gọi công cụ/hàm trong API để bao gồm các lệnh gọi công cụ tự do (văn bản thô, không JSON), ràng buộc ngữ pháp qua Lark/CFG, danh sách công cụ cho phép, và cải thiện khả năng lập luận khi dùng công cụ. Kết hợp lại, những tính năng này biến GPT-5 thành một mô hình thực sự mang tính tác vụ; bạn có thể kết nối API, cơ sở dữ liệu và công cụ tùy chỉnh vào Responses API để tạo câu trả lời bám dữ liệu hoặc thậm chí tự động hóa quy trình công việc.

Trong hướng dẫn này, tôi sẽ trình bày về các công cụ dạng hàm, công cụ tùy chỉnh, ràng buộc ngữ pháp, danh sách công cụ cho phép và preamble, kèm ví dụ mã và giải thích rõ ràng về cách hoạt động cũng như thời điểm sử dụng.

Gọi hàm trong GPT-5: Là gì & Vì sao

Gọi hàm trong GPT-5 cho phép bạn tăng cường mô hình bằng dữ liệu và hành động của ứng dụng. Mô hình có thể quyết định khi nào gọi công cụ, còn bạn xử lý logic và trả kết quả để tạo phản hồi cuối cùng bám dữ liệu. 

Các loại gọi hàm:

  1. Công cụ hàm (JSON Schema): Cung cấp đầu vào/ra có cấu trúc để mô hình có thể gọi chính xác.
  2. Công cụ tùy chỉnh (tự do): Cung cấp đầu vào/ra văn bản linh hoạt cho tích hợp phi cấu trúc.

Gọi hàm là gì?

GPT-5 hỗ trợ cả công cụ hàm có cấu trúc (dùng JSON Schema) và công cụ tùy chỉnh chấp nhận tải trọng văn bản tự do (như SQL, script, hoặc cấu hình) để tích hợp liền mạch với các runtime bên ngoài. 

Công cụ hàm được định nghĩa bằng JSON Schema, đảm bảo mô hình hiểu chính xác các đối số cần truyền và cho phép xác thực đầu vào nghiêm ngặt. Bạn có thể đọc thêm trong hướng dẫn Gọi hàm OpenAI.

Vì sao dùng gọi hàm?

  • Linh hoạt: Công cụ tự do cho phép mô hình tạo đúng văn bản mà hệ thống của bạn cần, không bị ràng buộc bởi JSON cứng, lý tưởng cho mã, truy vấn hoặc cấu hình.
  • Độ tin cậy và kiểm soát: Công cụ hàm áp đặt đối số có cấu trúc, giúp tăng tính dự đoán và giảm lỗi phân tích cú pháp.
  • Quy trình tác vụ: GPT-5 được thiết kế cho các tác vụ công cụ đa bước, phức tạp và viết mã, khiến gọi công cụ trở thành tính năng trọng tâm.

Gọi hàm hoạt động thế nào?

  1. Gửi prompt bao gồm tất cả công cụ sẵn có.
  2. Mô hình phát lệnh gọi công cụ (kèm đối số hoặc văn bản tự do).
  3. Ứng dụng của bạn thực thi công cụ (chạy hàm Python).
  4. Trả kết quả từ công cụ về cho mô hình.
  5. Mô hình sau đó phản hồi hoặc có thể gọi thêm công cụ.

Nguồn: Function calling - OpenAI API

Khi nào dùng Công cụ Hàm so với Công cụ Tùy chỉnh

  • Dùng công cụ hàm khi bạn muốn xác thực nghiêm ngặt và đối số kiểu rõ ràng, có thể dự đoán qua JSON Schema.
  • Dùng công cụ tùy chỉnh (tự do) khi runtime của bạn cần văn bản thô (như script, SQL, hoặc cấu hình) hoặc khi bạn cần lặp nhanh mà không cần schema.

Công cụ Hàm (JSON Schema): Gửi Dữ liệu Có cấu trúc

Sử dụng Công cụ Hàm với JSON Schema giúp bạn nhận kết quả có cấu trúc và có thể dự đoán từ mô hình. Mô hình đánh giá các công cụ bạn khai báo, quyết định khi nào sử dụng, đề xuất đối số được xác thực theo JSON, rồi thực thi hàm tương ứng trong mã của bạn. 

Sau đó, bạn có thể yêu cầu mô hình tạo phản hồi chỉ dựa trên dữ liệu công cụ trả về, đảm bảo đầu ra bám dữ liệu, máy có thể đọc và dễ tích hợp.

Ví dụ sau minh họa một số khái niệm chính:

  • Khám phá công cụ và sinh đối số: Mô hình chọn công cụ phù hợp và tự động điền các đối số kiểu JSON của nó.
  • Vòng lặp thực thi công cụ: Ứng dụng của bạn thực thi công cụ được yêu cầu và gửi kết quả về cho mô hình.
  • Câu trả lời theo khuôn schema: Phản hồi cuối cùng dựa chặt chẽ trên đầu ra từ các công cụ, giảm sai lệch và đảm bảo độ tin cậy.

1. Thiết lập

  1. Tạo tài khoản OpenAI và tạo khóa API trên bảng điều khiển. Trong bảng điều khiển, mở mục API, sau đó “View API keys” và nhấp “Create new secret key”.
  2. Thêm phương thức thanh toán hoặc tín dụng vào tài khoản để có thể gọi API; mức sử dụng sẽ trừ vào số dư trả trước khi bạn gửi yêu cầu.
  3. Lưu khóa API thành biến môi trường tên OPENAI_API_KEY trước khi khởi chạy ứng dụng. 
  4. Cài OpenAI Python SDK chính thức với pip install openai.

2. Định nghĩa công cụ

Bạn cung cấp danh sách công cụ với tên, mô tả và JSON Schema cho tham số. Schema giúp mô hình cung cấp đối số đúng chuẩn.

  • make_coffee yêu cầu một tham số chuỗi: coffee_type.
  • random_coffee_fact không nhận tham số (đối tượng rỗng). Các định nghĩa này được truyền qua đối số tools trong lệnh gọi API để mô hình biết những gì sẵn có.
import os
from openai import OpenAI
import json

client = OpenAI(api_key = os.environ["OPENAI_API_KEY"])

tools = [
    {
        "type": "function",
        "name": "make_coffee",
        "description": "Gives a simple recipe for making a coffee drink.",
        "parameters": {
            "type": "object",
            "properties": {
                "coffee_type": {
                    "type": "string",
                    "description": "The coffee drink, e.g. espresso, cappuccino, latte"
                }
            },
            "required": ["coffee_type"],
        },
    },
    {
        "type": "function",
        "name": "random_coffee_fact",
        "description": "Returns a fun fact about coffee.",
        "parameters": {"type": "object","properties":{}}
    }
]

3. Triển khai công cụ

Bạn triển khai các hàm Python thực sự thực hiện công việc:

  • make_coffee(coffee_type) trả về chuỗi công thức ngắn gọn theo đồ uống yêu cầu.
  • random_coffee_fact() trả về một payload thông tin thú vị nhỏ. 

Cả hai đều trả về dict có thể tuần tự hóa JSON, rất phù hợp để đưa lại cho mô hình.

def make_coffee(coffee_type):
    recipes = {
        "espresso": "Grind fine, 18g coffee → 36g espresso in ~28s.",
        "cappuccino": "Brew 1 espresso shot, steam 150ml milk, pour and top with foam.",
        "latte": "Brew 1 espresso shot, steam 250ml milk, pour for silky texture.",
    }
    return {"coffee_type": coffee_type, "recipe": recipes.get(coffee_type.lower(), "Unknown coffee type!")}

def random_coffee_fact():
    return {"fact": "Coffee is the second most traded commodity in the world, after oil."}

4. Bắt đầu hội thoại

Khởi tạo hội thoại với câu hỏi của người dùng: {"role": "user", "content": "How do I make a latte?"}

Mô hình có thể trả về các message bao gồm mục function_call, chẳng hạn make_coffee với {"coffee_type":"latte"}. Khi đó bạn nên nối các message của mô hình trở lại vào input_list: input_list += response.output. Cách này giúp duy trì trạng thái hội thoại cho lượt kế tiếp.

# Track used tools
used_tools = []

input_list = [{"role": "user", "content": "How do I make a latte?"}]

response = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=input_list,
)
input_list += response.output

5. Thực thi lệnh gọi công cụ và đính kèm đầu ra

Bạn nên lặp qua response.output và xác định các mục có item.type bằng "function_call". Với các mục này, phân tích item.arguments và chuyển yêu cầu đến các hàm Python của bạn. 

Với hàm make_coffee, gọi bằng make_coffee(args["coffee_type"]). Với hàm random_coffee_fact, chỉ cần gọi random_coffee_fact(). 

Sau khi thực thi hàm, nối thêm một message function_call_output bao gồm các mục sau: 

  • call_id (liên kết đầu ra này với yêu cầu của mô hình) 
  • output (một chuỗi JSON của kết quả hàm).
for item in response.output:
    if getattr(item, "type", "") == "function_call":
        used_tools.append(item.name)
        args = json.loads(item.arguments or "{}")

        if item.name == "make_coffee":
            result = make_coffee(args["coffee_type"])
        elif item.name == "random_coffee_fact":
            result = random_coffee_fact()
        else:
            result = {"error": f"Unknown tool {item.name}"}

        input_list.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(result)
        })

6. Tạo câu trả lời cuối

Bạn thực hiện lệnh gọi client.responses.create lần hai, truyền input_list đã được cập nhật, hiện bao gồm đầu ra công cụ.

Biến used_tools ghi lại tên các công cụ xuất hiện trong lệnh gọi hàm của mô hình. Chúng ta sẽ dùng nó để hiển thị công cụ nào đã được dùng. 

final = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=input_list,
    instructions="Answer using only the tool results."
)

print("Final output:\n", final.output_text)

print("\n--- Tool Usage ---")
for t in tools:
    status = "USED ✅" if t["name"] in used_tools else "NOT USED ❌"
    print(f"{t['name']}: {status}")

Vì người dùng hỏi về latte, mô hình đã chọn make_coffee với coffee_type = "latte", mã của bạn thực thi hàm đó và phản hồi cuối cùng chỉ được xây dựng từ kết quả công cụ đó.

Final output:
 {"coffee_type":"latte","recipe":"Brew 1 espresso shot, steam 250ml milk, pour for silky texture."}

--- Tool Usage ---
make_coffee: USED ✅
random_coffee_fact: NOT USED ❌

Công cụ Tùy chỉnh (Tự do): Gửi Văn bản Thô

Dùng công cụ tùy chỉnh tự do của GPT-5 để cho phép mô hình gửi trực tiếp văn bản thô tới công cụ của bạn, như mã, SQL, lệnh shell, hoặc CSV đơn giản, mà không cần JSON Schema. 

Khả năng này vượt xa kiểu gọi hàm chỉ JSON trước đây, mang lại sự linh hoạt lớn hơn để tích hợp với executor, engine truy vấn hoặc bộ thông dịch ngôn ngữ chuyên biệt (DSL). 

Ở chế độ tự do, mô hình tạo lệnh gọi công cụ tùy chỉnh với payload văn bản phi cấu trúc mà bạn có thể định tuyến đến runtime của mình. Sau đó bạn trả về kết quả để mô hình dùng khi hoàn thiện câu trả lời cho người dùng.

Chúng ta sẽ viết mã cho phép mô hình chọn công cụ phù hợp và trả về văn bản thô không cấu trúc làm đầu ra. Đầu ra này sau đó được truyền vào hàm Python để tạo kết quả. Cách này cho phép người dùng cung cấp nguyên liệu họ có và mô hình sẽ tạo công thức phù hợp với nhu cầu của họ.

Trong ví dụ mã này, chúng ta có:

  1. Khởi tạo client OpenAI và định nghĩa một công cụ tùy chỉnh duy nhất tên meal_planner chấp nhận đầu vào văn bản thô.
  2. Tạo hàm Python plan_meal nhận chuỗi nguyên liệu phân tách bằng dấu phẩy và trả về một ý tưởng bữa ăn.
  3. Bắt đầu hội thoại với message của người dùng liệt kê nguyên liệu sẵn có.
  4. Thực hiện lệnh gọi mô hình đầu tiên hướng dẫn GPT-5 gọi công cụ chỉ bằng danh sách nguyên liệu sạch, và nối đầu ra của mô hình vào lịch sử hội thoại.
  5. Xác định lệnh gọi công cụ tùy chỉnh được phát ra và trích xuất chuỗi đầu vào thô phân tách bằng dấu phẩy.
  6. Thực thi công cụ bằng cách truyền danh sách vào plan_meal, rồi gửi kết quả trở lại mô hình dưới dạng function_call_output được liên kết qua call_id.
  7. Thực hiện lệnh gọi mô hình cuối để chuyển ý tưởng bữa ăn thô thành công thức ngắn gọn, từng bước, và in cả chi tiết lệnh gọi công cụ lẫn đầu ra cuối cùng.
from openai import OpenAI
import json
import random

client = OpenAI()

# --- Custom tool ---
tools = [
    {
        "type": "custom",
        "name": "meal_planner",
        "description": "Takes ONLY a comma-separated list of ingredients and suggests a meal idea."
    }
]

# --- Fake meal planner ---
def plan_meal(ingredients: str) -> str:
    ideas = [
        f"Stir-fry: {ingredients} with garlic & soy sauce.",
        f"One-pot rice: cook {ingredients} together in broth until fluffy.",
        f"Soup: simmer {ingredients} in stock with herbs.",
        f"Sheet-pan bake: roast {ingredients} at 200°C for ~20 min."
    ]
    return random.choice(ideas)

# --- Start conversation ---
messages = [
    {"role": "user", "content": "I only have chicken, rice, and broccoli. Any dinner ideas?"}
]

# 1) Ask model with clear instruction
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="If the user mentions ingredients, call the meal_planner tool with ONLY a comma-separated list like 'chicken, rice, broccoli'."
)

messages += resp.output

# 2) Find the tool call
tool_call = next((x for x in resp.output if getattr(x, "type", "") == "custom_tool_call"), None)
assert tool_call, "No tool call found!"

# Get clean CSV input
ingredients_csv = tool_call.input.strip()

# 3) Run the tool
meal_result = plan_meal(ingredients_csv)

# 4) Send tool output back
messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": meal_result
})

# 5) Final model response
final = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="Turn the meal idea into a short recipe with 3-4 steps."
)

print("\n--- Tool Call ---")
print("Name:", tool_call.name)
print("Input:", ingredients_csv)
print("Output:", meal_result)


print("\n--- Final Output ---\n", final.output_text)

Kết quả là, chúng ta nhận được thống kê về công cụ đã dùng, bao gồm cài đặt, đầu vào và đầu ra. Bên cạnh đó, đầu ra cuối cùng cung cấp một công thức hoàn chỉnh để nấu món ăn.

--- Tool Call ---
Name: meal_planner
Input: chicken, rice, broccoli
Output: One-pot rice: cook chicken, rice, broccoli together in broth until fluffy.

--- Final Output ---
 One-Pot Chicken, Rice & Broccoli

- Season bite-size chicken pieces with salt and pepper; sear in a little oil in a pot until lightly browned.
- Add 1 cup rinsed rice and 2 cups broth (or water + salt). Bring to a boil, then cover and simmer on low for 12 minutes.
- Scatter 2 cups small broccoli florets on top, cover, and cook 5-7 more minutes until rice is fluffy and broccoli is tender.
- Rest 5 minutes off heat, fluff, and adjust seasoning. Optional: stir in a knob of butter or a splash of soy.

Ràng buộc Ngữ pháp (Lark): Ràng buộc Đầu ra Công cụ

GPT-5 hiện hỗ trợ ngữ pháp phi ngữ cảnh (CFG) để đảm bảo định dạng đầu ra được kiểm soát chặt chẽ. 

Bằng cách áp dụng một ngữ pháp, như SQL hoặc DSL, vào phản hồi, GPT-5 đảm bảo đầu ra luôn tuân theo cấu trúc yêu cầu. CFG đặc biệt quan trọng cho quy trình tự động và các tác vụ quan trọng. 

GPT-5 có thể dùng ngữ pháp được định nghĩa ở các định dạng như Lark để hạn chế đầu ra của công cụ trong quá trình sinh. Cách giải mã có ràng buộc này tăng độ tin cậy nhờ ngăn trôi định dạng và đảm bảo nhất quán cấu trúc.

Chúng ta sẽ viết mã để mô hình chọn một công cụ và chỉ xuất ra một biểu thức số học hợp lệ theo ngữ pháp; biểu thức này sau đó được đánh giá trong Python, kết quả được trả về hội thoại và phản hồi cuối cùng được trình bày rõ ràng bằng ngôn ngữ tự nhiên.

Trong ví dụ mã này, chúng ta có:

  1. Khởi tạo client OpenAI và định nghĩa công cụ tùy chỉnh math_solver buộc phải chỉ xuất một biểu thức số học hợp lệ theo ngữ pháp (Lark CFG).
  2. Bắt đầu hội thoại bằng câu hỏi toán học ngôn ngữ tự nhiên từ người dùng.
  3. Thực hiện lệnh gọi mô hình đầu tiên hướng dẫn GPT-5 gọi math_solver với một biểu thức tuân theo ngữ pháp.
  4. Xác định lệnh gọi công cụ và trích xuất chuỗi biểu thức thô từ payload của công cụ.
  5. Đánh giá biểu thức cục bộ trong môi trường hạn chế để tạo kết quả cụ thể.
  6. Trả kết quả thực thi công cụ về hội thoại, liên kết qua call_id ban đầu.
  7. Thực hiện lệnh gọi mô hình cuối để trình bày kết quả đã đánh giá rõ ràng bằng ngôn ngữ tự nhiên.
from openai import OpenAI
import json

client = OpenAI()

# --- Local fake evaluator ---
def eval_expression(expr: str) -> str:
    try:
        # Evaluate safely using Python's eval on restricted globals
        result = eval(expr, {"__builtins__": {}}, {})
        return f"{expr} = {result}"
    except Exception as e:
        return f"Error evaluating expression: {e}"

# --- Custom tool with grammar constraint ---
tools = [
    {
        "type": "custom",
        "name": "math_solver",
        "description": "Solve a math problem by outputting ONLY a valid arithmetic expression.",
        "format": {
            "type": "grammar",
            "syntax": "lark",
            "definition": r"""
start: expr

?expr: term
     | expr "+" term   -> add
     | expr "-" term   -> sub

?term: factor
     | term "*" factor -> mul
     | term "/" factor -> div

?factor: NUMBER
       | "(" expr ")"

%import common.NUMBER
%ignore " "
"""
        },
    }
]

# --- User asks ---
msgs = [
    {"role": "user", "content": "What is (12 + 8) * 3 minus 5 divided by 5?"}
]

# 1) First model call: GPT-5 must emit a grammar-valid expression
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=msgs,
    instructions="Always call the math_solver tool with a grammar-valid arithmetic expression like '(12 + 8) * 3 - 5 / 5'."
)

msgs += resp.output

tool_call = next(
    x for x in resp.output
    if getattr(x, "type", "") in ("custom_tool_call", "tool_call", "function_call")
)

expr = getattr(tool_call, "input", "") or getattr(tool_call, "arguments", "")
print("\n=== Grammar-Constrained Expression ===")
print(expr)

# 2) Run the local evaluator
tool_result = eval_expression(expr)
print("\n=== Tool Execution Result ===")
print(tool_result)

# 3) Return tool output
msgs.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": tool_result
})

# 4) Final pass: GPT-5 presents answer nicely
final = client.responses.create(
    model="gpt-5",
    input=msgs,
    tools=tools,
    instructions="Present the evaluated result clearly in natural language."
)

print("\n=== Final Output ===")
print(final.output_text)

Như bạn thấy, mô hình đã chọn công cụ rồi tạo một biểu thức bị ràng buộc bởi ngữ pháp. Sau đó, nó được đưa qua hàm Python để tạo đáp án. Cuối cùng, chúng ta có câu trả lời đúng bằng ngôn ngữ tự nhiên.

=== Grammar-Constrained Expression ===
(12 + 8) * 3 - 5 / 5

=== Tool Execution Result ===
(12 + 8) * 3 - 5 / 5 = 59.0

=== Final Output ===
59

Danh sách Công cụ Cho phép: Giới hạn an toàn những gì mô hình có thể dùng

Thiết lập allowed_tools cho phép bạn giới hạn mô hình vào một tập công cụ an toàn từ bộ công cụ đầy đủ. Điều này tăng tính dự đoán và ngăn lệnh gọi công cụ ngoài ý muốn, đồng thời vẫn cho mô hình sự linh hoạt trong phạm vi cho phép. 

Bạn có thể cho mô hình chọn trong các công cụ được phép hoặc yêu cầu nó phải dùng một công cụ, phản chiếu hành vi “ép dùng công cụ” trong các stack khác và phù hợp với thực tiễn điều phối gọi công cụ.

Chúng ta sẽ viết mã cho phép một công cụ duy nhất, để mô hình chỉ gọi công cụ đó, chạy nó, đưa kết quả trở lại, rồi tạo bản tóm tắt ngắn gọn cho người dùng.

Lưu ý: Vui lòng tạo tài khoản Firecrawl miễn phí, tạo khóa API, lưu thành biến môi trường FIRECRAWL_API_KEY, và cài Firecrawl Python SDK bằng pip install firecrawl-py.

Trong ví dụ mã này, chúng ta có:

  1. Định nghĩa bộ công cụ đầy đủ gồm cả dummy_web_search và firecrawl_search.
  2. Giới hạn mô hình chỉ dùng firecrawl_search qua tool_choice = { type: "allowed_tools", mode: "required", tools: [...] }.
  3. Thực hiện lệnh gọi mô hình lần đầu, ghi nhận tên lệnh gọi công cụ và đối số, và in ra để quan sát.
  4. Khi firecrawl_search được gọi, giải tuần tự đối số, thực thi Firecrawl, chuyển phản hồi thành dict có thể tuần tự hóa JSON và trích phần dữ liệu an toàn.
  5. Đưa kết quả công cụ trở lại hội thoại bằng function_call_output liên kết bởi call_id, rồi thực hiện lệnh gọi cuối để tóm tắt thành 3–4 gạch đầu dòng với liên kết Markdown.
  6. Bao gồm nhánh dự phòng sẽ chạy công cụ giả (dù danh sách cho phép ngăn điều này trong lượt chạy này).
  7. In câu trả lời cuối cùng bằng ngôn ngữ tự nhiên.
from openai import OpenAI
from firecrawl import Firecrawl
import json
import os

client = OpenAI()
firecrawl = Firecrawl(api_key=os.environ["FIRECRAWL_API_KEY"])

# --- Full toolset ---
tools = [
    {
        "type": "function",
        "name": "dummy_web_search",
        "description": "A fake web search that always returns static dummy results.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
    {
        "type": "function",
        "name": "firecrawl_search",
        "description": "Perform a real web search using Firecrawl.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
]

# --- Dummy implementation ---
def dummy_web_search(query, limit=3):
    return {
        "query": query,
        "results": [f"[Dummy] Result {i+1} for '{query}'" for i in range(limit)]
    }

# --- User asks ---
messages = [
    {"role": "user", "content": "Find me the latest info about the stock market."}
]

# --- Restrict to Firecrawl only ---
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="Only the firecrawl_search tool is allowed. Do not use dummy_web_search.",
    tool_choice={
        "type": "allowed_tools",
        "mode": "required",  # force Firecrawl
        "tools": [{"type": "function", "name": "firecrawl_search"}],
    },
)

for item in resp.output:
    if getattr(item, "type", "") in ("function_call", "tool_call"):
        print("\n--- Tool Call ---")
        print("Tool name:", item.name)
        print("Arguments:", item.arguments)

        if item.name == "firecrawl_search":
            args = json.loads(item.arguments)

            # Firecrawl returns a SearchData object → convert it
            results_obj = firecrawl.search(
                query=args["query"],
                limit=args.get("limit", 3),
            )

            # 🔥 Convert to JSON-serializable dict
            if hasattr(results_obj, "to_dict"):
                results = results_obj.to_dict()
            elif hasattr(results_obj, "dict"):
                results = results_obj.dict()
            else:
                results = json.loads(results_obj.json()) if hasattr(results_obj, "json") else results_obj

            print("\n--- Firecrawl Raw Results ---")
            print(json.dumps(results, indent=2)[:500])

            # ✅ Extract only the data portion for summarization
            safe_results = results.get("data", results)

            # Feed back to GPT
            messages += resp.output
            messages.append({
                "type": "function_call_output",
                "call_id": item.call_id,
                "output": json.dumps(safe_results)  # now safe
            })

            # Final pass
            final = client.responses.create(
                model="gpt-5",
                tools=tools,
                input=messages,
                instructions="Summarize the Firecrawl search results into 3-4 bullet points with clickable [Title](URL) links."
            )

            print("\n--- Final Natural Language Answer ---")
            print(final.output_text)

        elif item.name == "dummy_web_search":
            args = json.loads(item.arguments)
            results = dummy_web_search(args["query"], args.get("limit", 3))
            print("\n--- Dummy Web Search Results ---")
            print(json.dumps(results, indent=2))

Mô hình đã chọn tìm kiếm Firecrawl và từ đó tạo phản hồi cuối cùng theo định dạng markdown.

Preamble: Hướng dẫn mô hình cách gọi công cụ

Preamble là phần giải thích ngắn mà mô hình tạo trước khi gọi công cụ, nêu rõ lý do gọi. Chúng tăng tính minh bạch, xây dựng niềm tin và cải thiện khả năng gỡ lỗi trong quy trình phức tạp. Bạn có thể bật bằng một hướng dẫn đơn giản như “giải thích trước khi gọi công cụ,” thường không làm tăng độ trễ đáng kể. 

Chúng ta sẽ viết mã trong đó mô hình trước tiên tạo một dòng “Preamble:” ngắn giải thích vì sao nó gọi công cụ, sau đó gọi công cụ, bạn thực thi cục bộ, trả về kết quả, và mô hình tạo câu trả lời cuối cùng rõ ràng.

Trong ví dụ mã này, chúng ta có:

  1. Khởi tạo client OpenAI và định nghĩa một công cụ hàm kiểu nghiêm ngặt (medical_advice) với JSON Schema.
  2. Thêm hướng dẫn hệ thống yêu cầu mô hình xuất một dòng “Preamble:” trước mọi lệnh gọi công cụ.
  3. Khởi tạo hội thoại bằng câu hỏi về triệu chứng của người dùng.
  4. Thực hiện lệnh gọi mô hình đầu tiên để nhận cả preamble và lệnh gọi công cụ với đối số.
  5. Trích xuất và thực thi công cụ cục bộ để tạo khuyến nghị.
  6. Trả đầu ra của công cụ về mô hình, liên kết qua call_id.
  7. Thực hiện lệnh gọi mô hình cuối để GPT‑5 trình bày kết quả ngắn gọn, dễ hiểu.
from openai import OpenAI
import json
import random

client = OpenAI()

# --- Fake tool implementation ---
def medical_advice(symptom: str):
    remedies = {
        "headache": "You can take acetaminophen (paracetamol) or ibuprofen, rest in a quiet room, and stay hydrated.",
        "cough": "Drink warm fluids, use honey in tea, and consider over-the-counter cough syrup.",
        "fever": "Use acetaminophen to reduce fever, stay hydrated, and rest. See a doctor if >39°C.",
    }
    return {"advice": remedies.get(symptom.lower(), "Please consult a healthcare provider for guidance.")}

# --- Tool definition ---
tools = [{
    "type": "function",
    "name": "medical_advice",
    "description": "Provide safe, general over-the-counter advice for common symptoms.",
    "parameters": {
        "type": "object",
        "properties": {"symptom": {"type": "string"}},
        "required": ["symptom"],
        "additionalProperties": False
    },
    "strict": True,
}]

# --- Messages (system preamble instruction) ---
messages = [
    {"role": "system", "content": "Before you call a tool, explain why you are calling it in ONE short sentence prefixed with 'Preamble:'."},
    {"role": "user", "content": "What should I take for a headache?"}
]

# 1) First call: expect preamble + tool call
resp = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("=== First Response ===")
for item in resp.output:
    t = getattr(item, "type", None)
    if t == "message":
        # Extract just the model's text
        content = getattr(item, "content", None)
        text = None
        if isinstance(content, list):
            text = "".join([c.text for c in content if hasattr(c, "text")])
        elif content:
            text = str(content)
        if text:
            # ✅ Only print once
            print(text)
    if t in ("function_call", "tool_call", "custom_tool_call"):
        print("Tool:", getattr(item, "name", None))
        print("Args:", getattr(item, "arguments", None))

# Extract tool call
tool_call = next(x for x in resp.output if getattr(x, "type", None) in ("function_call","tool_call","custom_tool_call"))
messages += resp.output

# 2) Execute tool locally
args = json.loads(getattr(tool_call, "arguments", "{}"))
symptom = args.get("symptom", "")
tool_result = medical_advice(symptom)

# 3) Return tool result
messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": json.dumps(tool_result)
})

# 4) Final model call → natural answer
final = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("\n=== Final Answer ===")
print(final.output_text)

Bạn có thể thấy trước khi gọi công cụ, mô hình cung cấp lời giải thích vì sao nó gọi công cụ. Điều này hữu ích cho khả năng quan sát và gỡ lỗi.

Tổng kết

GPT-5 là mô hình tiên tiến nhất của OpenAI tính đến nay, đặc biệt vượt trội trong viết mã và các quy trình dựa trên tác vụ. Những tính năng API mới nhất cho phép phát triển hệ thống cấp sản xuất một cách trọn vẹn, đơn giản.

Trong hướng dẫn này, bạn đã học cách:

  1. Trả đầu ra có cấu trúc bằng Công cụ Hàm và JSON Schema để tự động hóa hạ nguồn đáng tin cậy.
  2. Kích hoạt chế độ tự do với Công cụ Tùy chỉnh (văn bản thô), cho phép mô hình tạo mã, SQL, script shell và tệp CSV vượt ra ngoài chỉ các lệnh gọi JSON.
  3. Ràng buộc định dạng bằng ngữ pháp Lark khi cần độ chính xác, như toán học, SQL hoặc DSL.
  4. Giới hạn truy cập công cụ bằng danh sách cho phép (hoặc ép dùng một công cụ) để tăng an toàn, kiểm soát và tính quyết định.
  5. Cải thiện tính minh bạch với preamble ngắn, để mô hình giải thích vì sao gọi công cụ cụ thể, hỗ trợ quan sát và gỡ lỗi.

Để tìm hiểu thêm về những gì bạn có thể làm với các công cụ khác nhau của OpenAI, tôi khuyến nghị học lộ trình kỹ năng OpenAI Fundamentals và khóa học Làm việc với OpenAI API.

Abid Ali Awan

Tác giả

Abid Ali Awan

Là một nhà khoa học dữ liệu được chứng nhận, tôi đam mê tận dụng công nghệ tiên tiến để tạo ra các ứng dụng học máy đổi mới. Với nền tảng vững chắc về nhận dạng giọng nói, phân tích và báo cáo dữ liệu, MLOps, AI hội thoại và NLP, tôi đã rèn giũa kỹ năng phát triển các hệ thống thông minh có thể tạo ra tác động thực sự. Bên cạnh chuyên môn kỹ thuật, tôi cũng là một người truyền đạt tốt, có khả năng chắt lọc các khái niệm phức tạp thành ngôn ngữ rõ ràng, súc tích. Nhờ đó, tôi trở thành một blogger được nhiều người quan tâm trong lĩnh vực khoa học dữ liệu, chia sẻ góc nhìn và kinh nghiệm với cộng đồng các chuyên gia dữ liệu ngày càng lớn. Hiện tại, tôi tập trung vào sáng tạo và biên tập nội dung, làm việc với các mô hình ngôn ngữ lớn để phát triển nội dung mạnh mẽ và hấp dẫn, giúp doanh nghiệp và cá nhân tận dụng tối đa dữ liệu của mình.

Chủ đề

Liên quan