Đến nội dung chính
AI Agents

Xây AI Agent đầu tiên bằng Python

Tạo agent gọi công cụ tra cứu chỉ đọc bằng Responses API, có validation, timeout và giới hạn số lượt.

Tổng quan

Dành cho người học muốn tự hoàn thành bài thực hành xây ai agent đầu tiên bằng python, với dữ liệu mẫu có thể kiểm tra.

  • Khai báo tool bằng JSON Schema và thực thi ở phía ứng dụng.
  • Gửi tool output đúng call_id và giữ lịch sử phản hồi.
  • Dừng khi có kết quả hoặc hết ngân sách.

Bạn sẽ hoàn thành: agent.py trả lời từ kho hư cấu, không đoán SKU lạ và dừng sau tối đa bốn lượt model.

Chuẩn bị

  • Python 3.10+, môi trường ảo và hiểu dict/function.
  • OPENAI_API_KEY và OPENAI_MODEL được đặt cục bộ; model phải hỗ trợ Responses và function calling.

Ví dụ chính gọi API trả phí nếu bạn chạy kết nối thật. ChatGPT subscription không tự bảo đảm API credits. Không cần key cho kiểm tra hàm execute bằng dữ liệu fixture.

Thực hành từng bước

Luồng thực hành minh họa; không phải kết quả đo thực nghiệm.

Bước 1. Cài SDK trong môi trường riêng

SHELL
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install openai

Các lệnh activate ở trên dành cho macOS/Linux; PowerShell dùng .venv\Scripts\Activate.ps1. Đặt key bằng cơ chế quản lý biến môi trường của máy, không dán vào file hoặc trình duyệt. Đặt OPENAI_MODEL thành ID model có thật trong tài khoản và hỗ trợ tool calling. Không để nguyên chuỗi placeholder.

Bước 2. Viết vòng lặp agent hoàn chỉnh

Lưu đoạn dưới vào agent.py. SDK chỉ import khi chạy file trực tiếp, nên bạn có thể kiểm thử execute mà không cài SDK. Lịch sử giữ cả response.output, bao gồm các mục cần cho lượt tiếp theo; mỗi kết quả tool gắn đúng call_id.

PYTHON
import json
import os

TOOLS = [{"type": "function", "name": "lookup_stock",
    "description": "Read fictional training inventory, never live stock.",
    "strict": True, "parameters": {"type": "object",
    "properties": {"sku": {"type": "string"}},
    "required": ["sku"], "additionalProperties": False}}]

def execute(name, raw):
    if name != "lookup_stock":
        raise ValueError("unknown tool")
    args = json.loads(raw)
    if not isinstance(args, dict) or set(args) != {"sku"}:
        raise ValueError("invalid arguments")
    if not isinstance(args["sku"], str) or not 1 <= len(args["sku"]) <= 40:
        raise ValueError("invalid sku")
    stock = {"DEMO-PEN": 7, "DEMO-BOOK": 0}
    sku = args["sku"]
    return {"sku": sku, "units": stock.get(sku), "found": sku in stock}

def run(client, model, question):
    history = [{"role": "user", "content": question}]
    for turn in range(4):
        response = client.responses.create(model=model, input=history,
            tools=TOOLS, max_output_tokens=500,
            instructions="Use lookup_stock before stating quantities. "
            "Label all inventory as fictional. Unknown means unknown.")
        history.extend(response.output)
        calls = [item for item in response.output if item.type == "function_call"]
        if not calls:
            if not response.output_text.strip():
                raise RuntimeError("no final answer")
            return response.output_text
        if len(calls) > 3:
            raise RuntimeError("too many tool calls")
        for call in calls:
            try:
                result = execute(call.name, call.arguments)
            except (ValueError, TypeError) as error:
                result = {"error": str(error)}
            history.append({"type": "function_call_output",
                "call_id": call.call_id, "output": json.dumps(result)})
    raise RuntimeError("turn limit reached")

if __name__ == "__main__":
    from openai import OpenAI
    client = OpenAI(timeout=20, max_retries=1)
    print(run(client, os.environ["OPENAI_MODEL"],
              "How many DEMO-PEN units are in the training inventory?"))

Bước 3. Kiểm tra executor offline trước

PYTHON
from agent import execute
assert execute("lookup_stock", '{"sku":"DEMO-PEN"}')["units"] == 7
assert execute("lookup_stock", '{"sku":"MISSING"}')["found"] is False
try:
    execute("delete_all", "{}")
except ValueError:
    pass
else:
    raise AssertionError("unknown tool accepted")
print("executor checks passed")

Lưu đoạn kiểm tra thành check_agent.py cạnh agent.py rồi chạy python3 check_agent.py. Đây là kiểm tra executor deterministic, không phải chứng minh model sẽ chọn tool hoặc trả lời đúng trong mọi tình huống.

Bước 4. Chạy một lượt thật và kiểm tra nguồn câu trả lời

SHELL
python3 agent.py

Nếu đã cấu hình API, chạy một lần với dữ liệu giả lập và theo dõi usage trong tài khoản. Câu trả lời phải nói 7 đơn vị trong kho đào tạo; chữ có thể thay đổi. Đổi câu hỏi sang DEMO-BOOK rồi MISSING. Tool schema không thay thế validation trong execute và hướng dẫn không phải biện pháp bảo đảm model sẽ luôn bám nguồn.

Bước 5. Thử giới hạn và lỗi mạng

Thay client bằng fake khi kiểm tra vòng lặp: fake luôn trả tool call phải dẫn tới turn limit reached. Khi chạy thật, lỗi xác thực hoặc timeout phải được quan sát rồi xử lý, không lặp vô hạn. Chỉ log tên tool, call_id và trạng thái; không in key.

Kiểm tra kết quả

  • Executor từ chối tool lạ; SKU thiếu giữ found=false.
  • Kết quả API được đối chiếu với fixture; giới hạn bốn lượt vẫn có hiệu lực.

Xử lý lỗi thường gặp

401 hoặc không tìm thấy model

Nguyên nhân: Key, quyền truy cập hoặc OPENAI_MODEL sai.

Cách xử lý: Kiểm tra cấu hình tài khoản và ID model ở tài liệu chính thức; không ghi key vào log.

Model gọi tool liên tục

Nguyên nhân: Chưa có câu trả lời cuối hoặc dữ liệu không đủ.

Cách xử lý: Giữ giới hạn vòng lặp và báo chưa hoàn thành; không tăng vô hạn để che lỗi.

Bài tập & mở rộng

  • Thêm fake client có hai lượt để kiểm tra đúng call_id.
  • Tiếp tục bài tool calling để thiết kế thêm công cụ và kiểm tra tham số.

Không nối hệ thống kho thật. Những kiểm tra offline không xác minh chi phí, quyền model hay khả năng trả lời của tài khoản; cần cấu hình riêng để kiểm tra API thật.

Nguồn tham khảo

  1. OpenAI function calling
  2. OpenAI text generation
  3. Python virtual environments

Kiến thức liên quan

Tiếp tục thực hành