Lewati ke konten utama

Tutorial OpenAI Agents SDK: Membangun Sistem AI yang Dapat Bertindak

Pelajari cara membangun aplikasi AI cerdas dengan Agents SDK dari OpenAI. Panduan komprehensif ini mencakup pembuatan agen, penerapan tools, output terstruktur, dan koordinasi beberapa agen.
Diperbarui 28 Sep 2026  · 12 mnt Baca

Jelajahi bersama AI

ChatGPTClaudePerplexity

Bidang kecerdasan buatan sedang bergeser dari sistem yang sekadar merespons pertanyaan menjadi sistem yang dapat bertindak secara mandiri. Agents SDK terbaru dari OpenAI berada di garis depan tren ini, memberikan kerangka kerja praktis bagi developer untuk membangun aplikasi AI yang membuat keputusan dan melakukan tindakan sendiri. Toolkit ini mewakili evolusi berikutnya dalam pengembangan AI, di mana sistem tidak hanya menjawab pertanyaan tetapi juga secara aktif memecahkan masalah.

Agents SDK menggabungkan model bahasa besar dengan kemampuan menggunakan tools dan berkoordinasi antar agen spesialis. Dibangun dengan Python, SDK ini menawarkan keseimbangan antara kesederhanaan dan kekuatan—menggunakan hanya beberapa konsep inti seperti agen, tools, handoff, dan guardrail. Pendekatan ini membuatnya mudah diakses oleh developer yang ingin menciptakan sistem AI canggih tanpa harus menangani mekanisme perilaku agen yang rumit di balik layar.

Dalam tutorial ini, kita akan menjelajahi cara membangun aplikasi praktis dengan OpenAI Agents SDK. Mulai dari pembuatan agen dasar, kita akan melangkah ke penerapan tools, koordinasi beberapa agen, dan memastikan keselamatan melalui guardrail. Pengetahuan yang diperoleh akan memungkinkan Anda mengembangkan sistem AI yang dapat menangani berbagai tugas, memberi Anda keterampilan yang relevan dengan pendekatan pengembangan AI saat ini.

Jika Anda baru mulai bekerja dengan OpenAI, lihat jalur keterampilan OpenAI Fundamentals kami untuk mempercepat pemahaman Anda. Anda juga dapat menonton video tutorial OpenAI Agents SDK kami di bawah ini.

Prasyarat untuk Bekerja dengan OpenAI Agents SDK

Sebelum mendalami OpenAI Agents SDK, penting untuk memahami beberapa konsep dasar dan menyiapkan lingkungan Anda dengan benar. Bagian ini mencakup semua yang perlu Anda ketahui sebelum menulis agen pertama Anda.

Pengetahuan yang diperlukan

Agar mendapatkan hasil maksimal dari tutorial ini, Anda sebaiknya nyaman dengan:

  • Pemrograman Python tingkat menengah: Anda memahami fungsi, kelas, pola async/await, dan type hints
  • Penggunaan OpenAI API dasar: Familiar dengan cara mengirim request ke model OpenAI dan menangani respons
  • Large language models (LLM): Memahami cara kerja LLM secara konseptual serta kemampuan/limitasinya
  • Pydantic: Pengetahuan dasar penggunaan Pydantic untuk validasi data akan membantu, karena Agents SDK banyak menggunakannya
  • Pemrograman asinkron: Familiar dengan pola async/await di Python, karena Agents SDK dibangun di atas eksekusi asinkron

Jika Anda perlu memperkuat pengetahuan di beberapa area ini, DataCamp menawarkan sejumlah sumber daya yang sangat baik:

Konsep kunci untuk agen

Sebelum mulai ngoding, mari perjelas beberapa konsep kunci yang menjadi pusat Agents SDK:

  1. Agen: Sistem AI yang dapat menggunakan tools dan membuat keputusan untuk menyelesaikan tugas
  2. Tools: Fungsi yang dapat dipanggil agen untuk melakukan aksi seperti menelusuri web atau mengakses database
  3. Handoffs: Mekanisme untuk mengalihkan kontrol di antara agen spesialis
  4. Guardrails: Langkah-langkah keamanan yang memvalidasi input dan output untuk memastikan perilaku yang tepat

Memahami konsep-konsep ini dan hubungannya akan membantu Anda membangun aplikasi berbasis agen yang lebih efektif. Anda dapat mempelajari lebih lanjut di panduan kami tentang memahami agen AI. 

Penyiapan Lingkungan OpenAI Agents

Mari siapkan lingkungan pengembangan dengan semua yang dibutuhkan untuk bekerja dengan OpenAI Agents SDK:

Menginstal paket yang diperlukan

Pertama, buat dan aktifkan virtual environment:

# Create and activate a virtual environment (run in your terminal)
python -m venv agents-env
source agents-env/bin/activate  # On Windows: agents-env\Scripts\activate

Selanjutnya, instal OpenAI Agents SDK dan python-dotenv:

pip install openai-agents python-dotenv

Menggunakan python-dotenv untuk pengelolaan kunci API

Saat bekerja dengan kunci API, praktik terbaiknya adalah menyimpannya di luar kode Anda. Paket python-dotenv menyediakan cara yang aman untuk mengelola variabel lingkungan:

  1. Buat file bernama .env di direktori proyek Anda
  2. Tambahkan kunci API OpenAI Anda ke file ini:

OPENAI_API_KEY=your-api-key-here

3. Muat dan gunakan variabel lingkungan di kode Anda:

import os
from dotenv import load_dotenv
from agents import Agent, Runner

# Load environment variables from .env file
load_dotenv()

# Access your API key
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
   raise ValueError("OPENAI_API_KEY not found in .env file")

Pendekatan ini menjaga informasi sensitif tetap di luar kode Anda, yang sangat penting jika Anda membagikan atau memublikasikan pekerjaan Anda.

Bekerja dengan fungsi async

OpenAI Agents SDK sangat menggunakan pemrograman asinkron. Berikut cara bekerja dengan fungsi async di berbagai lingkungan:

Di skrip Python (file .py)

Di skrip Python biasa, Anda perlu menggunakan asyncio.run() untuk mengeksekusi fungsi async:

import asyncio
from agents import Agent, Runner

async def main():
   agent = Agent(
       name="Test Agent",
       instructions="You are a helpful assistant that provides concise responses."
   )
   result = await Runner.run(agent, "Hello! Are you working correctly?")
   print(result.final_output)

if __name__ == "__main__":
   asyncio.run(main())  # Run the async function

Di Jupyter notebook

Jupyter notebook sudah memiliki event loop yang berjalan, jadi Anda TIDAK boleh menggunakan asyncio.run(). Sebagai gantinya, Anda bisa langsung menunggu fungsi async:

from agents import Agent, Runner

# No need for asyncio.run() in notebooks
agent = Agent(
   name="Test Agent",
   instructions="You are a helpful assistant that provides concise responses."
)
result = await Runner.run(agent, "Hello! Are you working correctly?")
print(result.final_output)

Jika Anda mencoba menggunakan asyncio.run() di notebook, Anda akan menemui error seperti:

RuntimeError: asyncio.run() cannot be called from a running event loop

Ini adalah salah satu penyesuaian paling umum yang perlu Anda lakukan saat menyalin kode antara skrip dan notebook. Sepanjang tutorial ini, saya terutama akan menggunakan sintaks yang ramah notebook karena sebagian besar pembaca akan menggunakan Jupyter.

Menguji instalasi Anda

Mari pastikan semuanya sudah disiapkan dengan benar:

from agents import Agent, Runner
from dotenv import load_dotenv

load_dotenv()

# For Jupyter notebooks:
agent = Agent(
   name="Test Agent",
   instructions="You are a helpful assistant that provides concise responses."
)
result = await Runner.run(agent, "Hello! Are you working correctly?")
print(result.final_output)

# For Python scripts, you'd use:
# async def test_installation():
#     agent = Agent(
#         name="Test Agent",
#         instructions="You are a helpful assistant that provides concise responses."
#     )
#     result = await Runner.run(agent, "Hello! Are you working correctly?")
#     print(result.final_output)
#
# if __name__ == "__main__":
#     asyncio.run(test_installation())

# Hello! Yes, I'm here and ready to help. How can I assist you today?

Dengan penyiapan ini selesai, Anda siap mulai membangun aplikasi dengan OpenAI Agents SDK.

Memulai dengan OpenAI Agents SDK

Inti dari OpenAI Agents SDK adalah kelas Agent, yang menjadi antarmuka utama Anda untuk membuat sistem AI yang dapat memahami dan menindaklanjuti instruksi. Mari jelajahi cara membuat dan mengonfigurasi agen untuk berbagai skenario.

Agen dalam SDK ini merepresentasikan sistem AI yang mampu mengikuti instruksi dan, opsional, menggunakan tools untuk menyelesaikan tugas. Membuat agen dasar hanya memerlukan beberapa parameter penting:

from agents import Agent

basic_agent = Agent(
   name="My First Agent",
   instructions="You are a helpful assistant that provides factual information.",
   model="gpt-4o"  # Optional: defaults to "gpt-4o" if not specified
)

Tiga komponen utama dari sebuah agen adalah:

  1. Nama: Pengenal untuk agen Anda yang membantu logging dan debugging
  2. Instruksi: “system prompt” inti yang mendefinisikan perilaku dan tujuan agen
  3. Model: Model bahasa yang mendasari agen (default GPT-4o)

Meskipun pengaturan dasar ini sudah cukup untuk memulai, kelas Agent menawarkan beberapa opsi konfigurasi model tambahan yang memberi Anda kontrol lebih atas perilaku LLM:

from agents import ModelSettings


advanced_agent = Agent(
   name="Advanced Assistant",
   instructions="""You are a professional, concise assistant who always provides
   accurate information. When you don't know something, clearly state that.
   Focus on giving actionable insights when answering questions.""",
   model="gpt-4o",
   model_settings=ModelSettings(
       temperature=0.3,  # Lower for more deterministic outputs (0.0-2.0)
       max_tokens=1024,  # Maximum length of response
   ),
   tools=[]  # We'll cover tools in a later section
)

Instruksi dan konfigurasi

Parameter instruksi mungkin merupakan aspek terpenting dari desain agen. Ia berfungsi sebagai “system prompt” yang memandu perilaku, nada, dan kemampuan agen. Menulis instruksi yang efektif adalah seni sekaligus sains:

Praktik terbaik untuk menulis instruksi

  1. Spesifik: Jelaskan dengan jelas peran, kepribadian, dan keterbatasan agen
  2. Tetapkan batasan: Nyatakan secara eksplisit topik atau tindakan yang harus dihindari agen
  3. Tentukan pola interaksi: Jelaskan bagaimana agen harus menangani berbagai jenis input
  4. Tetapkan batas pengetahuan: Perjelas apa yang seharusnya diketahui agen dan kapan harus mengakui ketidakpastian

Agent juga menyertakan parameter description, yaitu deskripsi yang dapat dibaca manusia tentang agen, digunakan saat agen dipakai di dalam tools/handoffs.

Opsi konfigurasi

Selain instruksi, Anda dapat menyetel perilaku agen menggunakan beberapa parameter konfigurasi:

  • temperature: Mengendalikan tingkat keacakan respons (0,0–2,0)
    • Nilai rendah (0,1–0,4) menghasilkan respons yang lebih deterministik dan fokus
    • Nilai tinggi (0,7–1,0) menciptakan keluaran yang lebih kreatif dan bervariasi
  • max_tokens: Membatasi panjang respons agen
    • Berguna untuk memastikan keluaran ringkas atau mengendalikan biaya
    • Default bervariasi menurut model tetapi umumnya cukup tinggi untuk sebagian besar kasus penggunaan
  • model: Memilih LLM yang mendasari
    • “gpt-4o” menawarkan performa terbaik untuk sebagian besar kasus
    • “gpt-4o-mini” memberikan keseimbangan yang baik antara performa dan biaya
    • “gpt-3.5-turbo” tersedia untuk tugas yang kurang kompleks dengan prioritas kecepatan dan biaya

Opsi konfigurasi ini memberikan kerangka kerja yang kuat untuk menyesuaikan perilaku agen agar sesuai dengan berbagai aplikasi. Kami mendorong Anda bereksperimen dengan berbagai pengaturan untuk melihat pengaruhnya terhadap respons agen dan menemukan konfigurasi optimal untuk kasus penggunaan Anda.

Contoh Open AI Agents SKD: Membangun asisten cuaca khusus

Sekarang, mari gabungkan konsep-konsep ini dengan membangun contoh praktis: asisten informasi cuaca khusus. Contoh ini menunjukkan cara membuat agen dengan keahlian, kemampuan, dan batasan yang terdefinisi dengan baik:

from agents import Agent, Runner
from dotenv import load_dotenv

# Load environment variables (API key)
load_dotenv()

# Define detailed instructions for our weather assistant
weather_instructions = """
You are a weather information assistant who helps users understand weather patterns and phenomena.

YOUR EXPERTISE:
- Explaining weather concepts and terminology
- Describing how different weather systems work
- Answering questions about climate and seasonal patterns
- Explaining the science behind weather events

LIMITATIONS:
- You cannot provide real-time weather forecasts for specific locations
- You don't have access to current weather data
- You should not make predictions about future weather events

STYLE:
- Use clear, accessible language that non-meteorologists can understand
- Include interesting weather facts when relevant
- Be enthusiastic about meteorology and climate science
"""

# Create our specialized weather assistant
weather_assistant = Agent(
   name="WeatherWise",
   instructions=weather_instructions,
   model="gpt-3.5-turbo",
   model_settings=ModelSettings(
       temperature=0.5,  # Balanced temperature for natural but focused responses
       max_tokens=256,  # Maximum length of response
   )
)

Menjalankan agen pertama Anda

Setelah Anda membuat agen, Anda dapat menjalankannya menggunakan kelas Runner. Kelas ini menangani eksekusi tugas agen dan mengelola alur percakapan:

# For Jupyter notebooks:
result = await Runner.run(
   weather_assistant, "Can you tell me about the relationship between climate change and extreme weather events?"
)

print(result.final_output)

—--------

Absolutely! Climate change is closely linked to the increase in frequency and intensity of extreme weather events. As the Earth's climate warms due to the buildup of greenhouse gases in the atmosphere, it disrupts the balance of our planet's climate systems.

Here's how climate change influences extreme weather events:
1. **Heatwaves**: Rising global temperatures lead to more frequent and severe heatwaves. These events can have serious impacts on human health, agriculture, and ecosystems.

2. **Intense Storms**: Warmer oceans provide more energy to fuel hurricanes, typhoons, and other tropical storms, leading to stronger and more destructive events.

3. **Heavy Rainfall**: A warmer atmosphere can hold more moisture, resulting in heavier rainfall during storms. This can lead to flooding and landslides.

4. **Droughts**: Climate change can exacerbate drought conditions in certain regions, impacting water resources, agriculture, and ecosystems.

5. **Wildfires**: Higher temperatures and drier conditions increase the likelihood of wildfires, which can be more frequent and intense.

It's important to note that while climate change doesn't directly cause specific weather events, it can increase the likelihood and severity of extreme weather occurrences. Scientists continue to study these connections to better understand and prepare for the impacts of

Untuk skrip Python, Anda perlu menggunakan pendekatan asyncio:

# For Python scripts:
import asyncio

async def run_agent_example():
   result = await Runner.run(weather_assistant, "Can you tell me about the relationship between climate change and extreme weather events?")
   print(result.final_output)

if __name__ == "__main__":
   asyncio.run(run_agent_example())

Pada bagian ini, kita telah membahas dasar-dasar membuat dan menjalankan agen dengan OpenAI Agents SDK. Di bagian berikutnya, kita akan memperluas konsep ini dengan menambahkan tools ke agen kita, yang akan secara signifikan meningkatkan kemampuannya.

Bekerja dengan Tools

Kekuatan sebenarnya dari OpenAI Agents SDK muncul saat Anda membekali agen dengan tools. Tools memungkinkan agen berinteraksi dengan sistem eksternal, mengakses data, dan melakukan tindakan yang melampaui sekadar generasi teks. Agents SDK mendukung tiga jenis tools utama: hosted tools, function tools, dan agen sebagai tools.

Hosted tools

Hosted tools berjalan di server OpenAI bersama model bahasa. Tools ini menyediakan kapabilitas bawaan tanpa mengharuskan Anda menerapkan fungsionalitas yang kompleks.

WebSearchTool: Membangun asisten riset

WebSearchTool memberi agen Anda kemampuan untuk menelusuri web guna mendapatkan informasi terbaru. Ini sangat berharga untuk tugas yang memerlukan pengetahuan terkini di luar data pelatihan model.

from agents import Agent, Runner, WebSearchTool
from dotenv import load_dotenv

load_dotenv()

# Create a research assistant with web search capability
research_assistant = Agent(
   name="Research Assistant",
   instructions="""You are a research assistant that helps users find and summarize information.
   When asked about a topic:
   1. Search the web for relevant, up-to-date information
   2. Synthesize the information into a clear, concise summary
   3. Structure your response with headings and bullet points when appropriate
   4. Always cite your sources at the end of your response
  
   If the information might be time-sensitive or rapidly changing, mention when the search was performed.
   """,
   tools=[WebSearchTool()]
)

async def research_topic(topic):
   result = await Runner.run(research_assistant, f"Please research and summarize: {topic}. Only return the found links with very minimal text.")
   return result.final_output

# Usage example (in Jupyter notebook)
summary = await research_topic("Latest developments in personal productivity apps.")
print(summary[:512])

—----------------

Here are some recent developments in personal productivity apps:

- **AI Integration in Productivity Tools**: Startups like Anthropic are developing AI agents capable of performing routine tasks across different applications, aiming to streamline workflows and reduce the need for multiple apps. ([ft.com](https://www.ft.com/content/a0e54dd5-b270-42cc-8c4c-18a0b8b3e6cc?utm_source=openai))

- **Read AI's Expansion**: Read AI, a productivity startup, secured $50 million in funding, valuing the company at $450 m

Pada contoh ini, kita membuat asisten riset yang dapat menelusuri web dan menyintesis informasi menjadi ringkasan koheren. WebSearchTool tidak memerlukan parameter apa pun untuk berfungsi, tetapi Anda dapat menyesuaikan perilakunya sesuai kebutuhan:

# Customized search tool with location context
location_aware_search = WebSearchTool(
   user_location="San Francisco, CA",  # Provides geographic context for local search queries
   search_context_size=3  # Number of search results to consider in the response
)

Parameter user_location sangat berguna untuk kueri yang diuntungkan oleh konteks geografis, seperti layanan lokal atau informasi regional. Parameter search_context_size membantu mengendalikan berapa banyak hasil pencarian yang dipertimbangkan model saat merumuskan respons.

Function tools

Function tools memungkinkan Anda memperluas agen dengan fungsi Python apa pun. Di sinilah fleksibilitas sejati Agents SDK tampak, memungkinkan integrasi dengan API, database, atau layanan lokal apa pun.

Tool prakiraan cuaca

Mari buat contoh praktis yang terintegrasi dengan API cuaca pihak ketiga:

import os
import requests
from datetime import datetime
from typing import Optional, List
from dataclasses import dataclass

from agents import Agent, Runner, function_tool
from dotenv import load_dotenv

load_dotenv()


@dataclass
class WeatherInfo:
   temperature: float
   feels_like: float
   humidity: int
   description: str
   wind_speed: float
   pressure: int
   location_name: str
   rain_1h: Optional[float] = None
   visibility: Optional[int] = None


@function_tool
def get_weather(lat: float, lon: float) -> str:
   """Get the current weather for a specified location using OpenWeatherMap API.

   Args:
       lat: Latitude of the location (-90 to 90)
       lon: Longitude of the location (-180 to 180)
   """
   # Get API key from environment variables
   WEATHER_API_KEY = os.getenv("OPENWEATHERMAP_API_KEY")

   # Build URL with parameters
   url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={WEATHER_API_KEY}&units=metric"

   try:
       response = requests.get(url)
       response.raise_for_status()
       data = response.json()

       # Extract weather data from the response
       weather_info = WeatherInfo(
           temperature=data["main"]["temp"],
           feels_like=data["main"]["feels_like"],
           humidity=data["main"]["humidity"],
           description=data["weather"][0]["description"],
           wind_speed=data["wind"]["speed"],
           pressure=data["main"]["pressure"],
           location_name=data["name"],
           visibility=data.get("visibility"),
           rain_1h=data.get("rain", {}).get("1h"),
       )

       # Build the response string
       weather_report = f"""
       Weather in {weather_info.location_name}:
       - Temperature: {weather_info.temperature}°C (feels like {weather_info.feels_like}°C)
       - Conditions: {weather_info.description}
       - Humidity: {weather_info.humidity}%
       - Wind speed: {weather_info.wind_speed} m/s
       - Pressure: {weather_info.pressure} hPa
       """
       return weather_report

   except requests.exceptions.RequestException as e:
       return f"Error fetching weather data: {str(e)}"

Function tool API cuaca ini berfungsi sebagai jembatan antara OpenWeatherMap API dan agen kita. Ia mengambil data cuaca waktu nyata berdasarkan koordinat geografis dan memformatnya menjadi laporan yang mudah dibaca. 

Kodenya mendefinisikan data class WeatherInfo untuk menstrukturkan data dengan field bertipe (suhu, kelembapan, dll.), sehingga rapi dan mudah dipelihara. Dekorator @function_tool mengubah fungsi Python kita menjadi tool yang dapat digunakan agen, secara otomatis membuat skema dari signature fungsi dan docstring.

Saat dipanggil, fungsi ini mengambil kunci API secara aman dari variabel lingkungan, melakukan permintaan HTTP ke OpenWeatherMap, dan memproses respons JSON. 

Ia menangani field data opsional seperti curah hujan dengan aman dan memformat semuanya menjadi laporan cuaca yang jelas, termasuk penanganan error jika request API gagal. Ini memungkinkan agen kita memberikan informasi cuaca terkini dalam format yang konsisten dan mudah dibaca tanpa perlu memahami detail API.

# Create a weather assistant
weather_assistant = Agent(
   name="Weather Assistant",
   instructions="""You are a weather assistant that can provide current weather information.
  
   When asked about weather, use the get_weather tool to fetch accurate data.
   If the user doesn't specify a country code and there might be ambiguity,
   ask for clarification (e.g., Paris, France vs. Paris, Texas).
  
   Provide friendly commentary along with the weather data, such as clothing suggestions
   or activity recommendations based on the conditions.
   """,
   tools=[get_weather]
)

Untuk menjalankan asisten cuaca:

async def main():
   runner = Runner()
  
   simple_request = await runner.run(weather_assistant, "What are your capabilities?")
  
   request_with_location = await runner.run(weather_assistant, "What's the weather like in Tashkent right now?")
  
   print(simple_request.final_output)
   print("-"*70)
   print(request_with_location.final_output)

await main()

Output:

I can provide you with the current weather conditions for any location around the world. Just give me the location details, and I'll fetch the weather data for you. I can also offer tips on activities or clothing based on the weather. If you have a specific city in mind, let me know, and I'll get to work!
----------------------------------------------------------------------
The weather in Tashkent is currently lovely with a clear sky. It's around 19.8°C, but it might feel slightly cooler at 18.6°C. The humidity is quite low at 26%, making it a pleasant day to be outdoors. A gentle breeze is blowing at 3.09 m/s, and the air pressure is steady at 1023 hPa.

It's a great time for a picnic in the park or a leisurely walk. Light clothing should be perfect for this weather. Enjoy your day in Tashkent!

Respons tersebut membuktikan bahwa agen berhasil menggunakan tool cuaca. Ia mengekstrak nama kota dari prompt, menggunakan basis pengetahuannya sendiri untuk mendapatkan koordinat, dan mengoperkannya sebagai lat dan lon ke tool.

Agen sebagai tools

Agents SDK memungkinkan Anda menggunakan agen itu sendiri sebagai tools, sehingga memungkinkan struktur hierarkis di mana agen spesialis bekerja di bawah koordinator. Ini adalah pola yang kuat untuk alur kerja yang kompleks.

from agents import Agent, Runner
from dotenv import load_dotenv

load_dotenv()

# Specialist agents
note_taking_agent = Agent(
   name="Note Manager",
   instructions="You help users take and organize notes efficiently.",
   # In a real application, this agent would have note-taking tools
)

task_management_agent = Agent(
   name="Task Manager",
   instructions="You help users manage tasks, deadlines, and priorities.",
   # In a real application, this agent would have task management tools
)

# Coordinator agent that uses specialists as tools
productivity_assistant = Agent(
   name="Productivity Assistant",
   instructions="""You are a productivity assistant that helps users organize their work and personal life.
  
   For note-taking questions or requests, use the note_taking tool.
   For task and deadline management, use the task_management tool.
  
   Help the user decide which tool is appropriate based on their request,
   and coordinate between different aspects of productivity.
   """,
   tools=[
       note_taking_agent.as_tool(
           tool_name="note_taking",
           tool_description="For taking, organizing, and retrieving notes and information"
       ),
       task_management_agent.as_tool(
           tool_name="task_management",
           tool_description="For managing tasks, setting deadlines, and tracking priorities"
       )
   ]
)

Dalam contoh ini:

  1. Kita membuat dua agen spesialis, masing-masing dengan domain keahlian yang fokus.
  2. Kemudian kita membuat agen koordinator yang dapat mendelegasikan ke para spesialis ini.
  3. Kita mengonversi setiap agen spesialis menjadi tool menggunakan metode .as_tool(), dengan menentukan:
  • tool_name yang akan digunakan koordinator untuk merujuk tool.
  • tool_description yang membantu koordinator memahami kapan menggunakan tool tersebut.

Pola ini memungkinkan Anda membuat sistem agen yang kompleks sambil mempertahankan pemisahan concern. Setiap agen spesialis dapat memiliki kumpulan tools dan keahlian sendiri, sementara koordinator mengelola interaksi dengan pengguna dan mendelegasikan ke spesialis yang tepat.

Untuk menggunakan productivity assistant:

async def main():
   runner = Runner()
  
   result = await runner.run(productivity_assistant, "I need to keep track of my project deadlines")
   print(result.final_output)

await main()

Tools mengubah agen dari asisten percakapan sederhana menjadi sistem kuat yang mampu mengambil tindakan bermakna di dunia nyata. Meskipun kita telah membahas dasar-dasar membuat dan menggunakan tools, ini baru permulaan dari apa yang mungkin. Untuk penggunaan lebih lanjut, termasuk penanganan error pada function tools, lihat dokumentasi resmi.

Sekarang kita memahami cara membekali agen dengan tools, tantangan berikutnya adalah menangani dan menstrukturkan output mereka. Di bagian selanjutnya, kita akan mengeksplorasi teknik memproses respons agen, dari output teks dasar hingga data terstruktur yang kompleks, memastikan kita mendapatkan format informasi yang tepat yang dibutuhkan aplikasi kita.

Memahami Output Agen OpenAI

Saat bekerja dengan agen, memperoleh informasi terstruktur alih-alih teks bebas dapat membuat aplikasi Anda lebih andal. OpenAI Agents SDK menyediakan cara bawaan yang bersih untuk menerima output terstruktur langsung dari agen.

Output terstruktur dengan model Pydantic

SDK memungkinkan Anda menentukan dengan tepat struktur data yang Anda inginkan dari agen dengan menyetel parameter output_type saat membuat agen:

from pydantic import BaseModel
from typing import List, Optional
from agents import Agent, Runner
from dotenv import load_dotenv

load_dotenv()
True
First, we define our data models using Pydantic:
# Define person data model
class Person(BaseModel):
   name: str
   role: Optional[str]
   contact: Optional[str]


# Define meeting data model
class Meeting(BaseModel):
   date: str
   time: str
   location: Optional[str]
   duration: Optional[str]


# Define task data model
class Task(BaseModel):
   description: str
   assignee: Optional[str]
   deadline: Optional[str]
   priority: Optional[str]


# Define the complete email data model
class EmailData(BaseModel):
   subject: str
   sender: Person
   recipients: List[Person]
   main_points: List[str]
   meetings: List[Meeting]
   tasks: List[Task]
   next_steps: Optional[str]

Model-model ini mendefinisikan struktur yang kita inginkan untuk data yang diekstrak. Setiap kelas merepresentasikan jenis informasi spesifik yang ingin kita ekstrak dari email.

Sekarang, kita membuat agen yang akan menghasilkan data dalam format terstruktur kita dengan menyetel parameter output_type:

# Create an email extraction agent with structured output
email_extractor = Agent(
   name="Email Extractor",
   instructions="""You are an assistant that extracts structured information from emails.
  
   When given an email, carefully identify:
   - Subject and main points
   - People mentioned (names, roles, contact info)
   - Meetings (dates, times, locations)
   - Tasks or action items (with assignees and deadlines)
   - Next steps or follow-ups
  
   Extract this information as structured data. If something is unclear or not mentioned,
   leave those fields empty rather than making assumptions.
   """,
   output_type=EmailData,  # This tells the agent to return data in EmailData format
)

Saat Anda menentukan output_type, agen akan secara otomatis menghasilkan data terstruktur alih-alih respons teks biasa. Ini menghilangkan kebutuhan parsing JSON manual atau ekstraksi regex.

Mari gunakan extractor ini dengan email contoh:

sample_email = """
From: Alex Johnson <alex.j@techcorp.com>
To: Team Development <team-dev@techcorp.com>
CC: Sarah Wong <sarah.w@techcorp.com>, Miguel Fernandez <miguel.f@techcorp.com>
Subject: Project Phoenix Update and Next Steps

Hi team,

I wanted to follow up on yesterday's discussion about Project Phoenix and outline our next steps.

Key points from our discussion:
- The beta testing phase has shown promising results with 85% positive feedback
- We're still facing some performance issues on mobile devices
- The client has requested additional features for the dashboard

Let's schedule a follow-up meeting this Friday, June 15th at 2:00 PM in Conference Room B. The meeting should last about 1.5 hours, and we'll need to prepare the updated project timeline.

Action items:
1. Sarah to address the mobile performance issues by June 20th (High priority)
2. Miguel to create mock-ups for the new dashboard features by next Monday
3. Everyone to review the beta testing feedback document and add comments by EOD tomorrow

If you have any questions before Friday's meeting, feel free to reach out.

Best regards,
Alex Johnson
Senior Project Manager
(555) 123-4567
"""

Kini memproses email menjadi jauh lebih sederhana karena SDK menangani konversinya:

async def process_email(email_text):
   runner = Runner()
   result = await runner.run(
       email_extractor,
       f"Please extract information from this email:\n\n{email_text}"
   )

   # The result is already a structured EmailData object
   return result


# Process the sample email
result = await process_email(sample_email)

# Display the extracted information
result = result.final_output

print(f"Subject: {result.subject}")
print(f"From: {result.sender.name} ({result.sender.role})")
print("\nMain points:")
for point in result.main_points:
   print(f"- {point}")

print("\nMeetings:")
for meeting in result.meetings:
   print(f"- {meeting.date} at {meeting.time}, Location: {meeting.location}")

print("\nTasks:")
for task in result.tasks:
   print(f"- {task.description}")
   print(
       f"  Assignee: {task.assignee}, Deadline: {task.deadline}, Priority: {task.priority}"
   )
—----------

Subject: Project Phoenix Update and Next Steps
From: Alex Johnson (Senior Project Manager)

Main points:
- 85% positive feedback from beta testing
- Performance issues on mobile devices
- Client requested additional dashboard features

Meetings:
- June 15th at 2:00 PM, Location: Conference Room B

Tasks:
- Address mobile performance issues
 Assignee: Sarah, Deadline: June 20th, Priority: High
- Create mock-ups for new dashboard features
 Assignee: Miguel, Deadline: Next Monday, Priority: None
- Review beta testing feedback document and add comments
 Assignee: Everyone, Deadline: EOD tomorrow, Priority: None

Kode ini jauh lebih bersih daripada pendekatan sebelumnya karena:

  1. Kita tidak perlu mengekstrak dan mem-parsing JSON dari respons secara manual.
  2. SDK menangani konversi dari output agen ke model Pydantic kita.
  3. Kita dapat langsung mengakses properti data terstruktur (seperti result.final_output.subject).
  4. Validasi tipe terjadi secara otomatis, sehingga kita tahu data cocok dengan model kita.

Bekerja dengan berbagai tipe output

Parameter output_type bekerja dengan tipe apa pun yang dapat dibungkus dalam Pydantic TypeAdapter:

# For simple lists
agent_with_list_output = Agent(
   name="List Generator",
   instructions="Generate lists of items based on the user's request.",
   output_type=list[str],  # Returns a list of strings
)

# For dictionaries
agent_with_dict_output = Agent(
   name="Dictionary Generator",
   instructions="Create key-value pairs based on the input.",
   output_type=dict[
       str, int
   ],  # Returns a dictionary with string keys and integer values
)

# For simple primitive types
agent_with_bool_output = Agent(
   name="Decision Maker",
   instructions="Answer yes/no questions with True or False.",
   output_type=bool,  # Returns a boolean
)

Manfaat output terstruktur

Menggunakan parameter output_type menawarkan beberapa keunggulan:

  1. Integrasi langsung: Output agen Anda otomatis tersedia sebagai objek Python.
  2. Keamanan tipe: SDK memastikan output sesuai dengan struktur yang Anda definisikan.
  3. Kode lebih sederhana: Tidak perlu parsing JSON manual atau penanganan error.
  4. Performa lebih baik: SDK menangani konversi secara efisien.
  5. Dukungan IDE: IDE Anda dapat memberikan autocompletion untuk properti output terstruktur.

Dengan mendefinisikan model data yang jelas dan menggunakan parameter output_type, agen Anda dapat menghasilkan struktur data persis seperti yang dibutuhkan aplikasi Anda, membuat integrasi mulus dan mengurangi kompleksitas kode.

Pada bagian selanjutnya, kita akan mengeksplorasi handoff antar agen, memungkinkan Anda membuat agen spesialis yang dapat bekerja sama pada tugas kompleks. Handoff ini dapat menggunakan output terstruktur untuk meneruskan informasi antar agen secara konsisten dan aman tipe.

Handoffs: Mendelegasikan Antar Agen

Dalam aplikasi kompleks, tugas yang berbeda sering kali memerlukan area keahlian yang berbeda. OpenAI Agents SDK mendukung “handoff,” yang memungkinkan satu agen mengalihkan kontrol ke agen spesialis lain. Fitur ini sangat berharga saat membangun sistem yang menangani berbagai permintaan pengguna, seperti aplikasi dukungan pelanggan di mana agen berbeda mungkin menangani penagihan, dukungan teknis, atau manajemen akun.

Membuat handoff dasar

Secara sederhana, handoff memungkinkan Anda menghubungkan beberapa agen sehingga mereka dapat mentransfer kontrol saat tepat. Mari buat sistem layanan pelanggan sederhana dengan agen triase yang dapat melakukan handoff ke spesialis:

from agents import Agent, handoff, Runner
from dotenv import load_dotenv

load_dotenv()

# Create specialist agents
billing_agent = Agent(
   name="Billing Agent",
   instructions="""You are a billing specialist who helps customers with payment issues.
   Focus on resolving billing inquiries, subscription changes, and refund requests.
   If asked about technical problems or account settings, explain that you specialize
   in billing and payment matters only.""",
)

technical_agent = Agent(
   name="Technical Agent",
   instructions="""You are a technical support specialist who helps with product issues.
   Assist users with troubleshooting, error messages, and how-to questions.
   Focus on resolving technical problems only.""",
)

# Create a triage agent that can hand off to specialists
triage_agent = Agent(
   name="Customer Service",
   instructions="""You are the initial customer service contact who helps direct
   customers to the right specialist.
  
   If the customer has billing or payment questions, hand off to the Billing Agent.
   If the customer has technical problems or how-to questions, hand off to the Technical Agent.
   For general inquiries or questions about products, you can answer directly.
  
   Always be polite and helpful, and ensure a smooth transition when handing off to specialists.""",
   handoffs=[billing_agent, technical_agent],  # Direct handoff to specialist agents
)

Dalam contoh ini, kita membuat sistem dengan tiga agen:

  • Dua agen spesialis dengan keahlian fokus
  • Satu agen triase yang dapat mendelegasikan ke spesialis

Perhatikan bagaimana kita cukup memasukkan agen spesialis ke parameter handoffs milik agen triase. Agents SDK secara otomatis membuat tool handoff yang sesuai yang dapat digunakan agen triase saat diperlukan.

Mari lihat sistemnya beraksi:

async def handle_customer_request(request):
   runner = Runner()
   result = await runner.run(triage_agent, request)
   return result


# Example customer inquiries
billing_inquiry = (
   "I was charged twice for my subscription last month. Can I get a refund?"
)
technical_inquiry = (
   "The app keeps crashing when I try to upload photos. How can I fix this? Give me the shortest solution possible."
)
general_inquiry = "What are your business hours?"

# Process the different types of inquiries
billing_response = await handle_customer_request(billing_inquiry)
print(f"Billing inquiry response:\n{billing_response.final_output}\n")

technical_response = await handle_customer_request(technical_inquiry)
print(f"Technical inquiry response:\n{technical_response.final_output}\n")

general_response = await handle_customer_request(general_inquiry)
print(f"General inquiry response:\n{general_response.final_output}")

Saat agen triase menerima pertanyaan penagihan, ia akan mengenali bahwa Billing Agent lebih cocok menanganinya dan akan memicu handoff. Pertanyaan teknis akan dialihkan ke agen teknis. Ia akan menjawab pertanyaan umum secara langsung sesuai kemampuannya. Berikut outputnya:

Billing inquiry response:
I can help with that. Could you please provide the transaction details or the date of the charges? This will help me locate the duplicate charge and process a refund for you.

Technical inquiry response:
Try these steps:

1. Restart the app.
2. Update to the latest app version.
3. Clear the app cache.
4. Restart your device.

If it persists, reinstall the app.

General inquiry response:
Our business hours are Monday to Friday, 9 AM to 5 PM. If you need assistance outside these hours, feel free to reach out and we'll get back to you as soon as possible. Is there anything else I can help you with?

Menyesuaikan handoff

Untuk kontrol lebih atas handoff, Anda dapat menggunakan fungsi handoff() alih-alih langsung mengoper agen ke parameter handoffs:

from agents import Agent, handoff, RunContextWrapper
from datetime import datetime

# Create an agent that handles account-related questions
account_agent = Agent(
   name="Account Management",
   instructions="""You help customers with account-related issues such as
   password resets, account settings, and profile updates.""",
)


# Custom handoff callback function
async def log_account_handoff(ctx: RunContextWrapper[None]):
   print(
       f"[LOG] Account handoff triggered at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
   )
   # In a real app, you might log to a database or alert a human supervisor


# Create a triage agent with customized handoffs
enhanced_triage_agent = Agent(
   name="Enhanced Customer Service",
   instructions="""You are the initial customer service contact who directs
   customers to the right specialist.
  
   If the customer has billing or payment questions, hand off to the Billing Agent.
   If the customer has technical problems, hand off to the Technical Agent.
   If the customer needs to change account settings, hand off to the Account Management agent.
   For general inquiries, you can answer directly.""",
   handoffs=[
       billing_agent,  # Basic handoff
       handoff(  # Customized handoff
           agent=account_agent,
           on_handoff=log_account_handoff,  # Callback function
           tool_name_override="escalate_to_account_team",  # Custom tool name
           tool_description_override="Transfer the customer to the account management team for help with account settings, password resets, etc.",
       ),
       technical_agent,  # Basic handoff
   ],
)

result = await Runner.run(
   enhanced_triage_agent, "I need to change my password."
)

Output:

[LOG] Account handoff triggered at 2025-03-16 17:45:48

Fungsi handoff() memungkinkan Anda untuk:

  • Menentukan callback kustom dengan on_handoff
  • Menimpa nama tool default (biasanya “transfer_to_[agent_name]”)
  • Menyediakan deskripsi tool kustom
  • Mengonfigurasi penanganan input (dibahas di bawah)

Meneruskan data saat handoff

Terkadang, Anda ingin agen pertama memberikan konteks tambahan atau metadata saat mengalihkan ke agen lain. Agents SDK mendukung ini melalui parameter input_type:

from pydantic import BaseModel
from typing import Optional
from agents import Agent, handoff, RunContextWrapper


# Define the data structure to pass during handoff
class EscalationData(BaseModel):
   reason: str
   priority: Optional[str]
   customer_tier: Optional[str]


# Handoff callback that processes the escalation data
async def process_escalation(ctx: RunContextWrapper, input_data: EscalationData):
   print(f"[ESCALATION] Reason: {input_data.reason}")
   print(f"[ESCALATION] Priority: {input_data.priority}")
   print(f"[ESCALATION] Customer tier: {input_data.customer_tier}")

   # You might use this data to prioritize responses, alert human agents, etc.


# Create an escalation agent
escalation_agent = Agent(
   name="Escalation Agent",
   instructions="""You handle complex or sensitive customer issues that require
   special attention. Always address the customer's concerns with extra care and detail.""",
)

# Create a service agent that can escalate with context
service_agent = Agent(
   name="Service Agent",
   instructions="""You are a customer service agent who handles general inquiries.
  
   For complex issues, escalate to the Escalation Agent and provide:
   - The reason for escalation
   - Priority level (Low, Normal, High, Urgent)
   - Customer tier if mentioned (Standard, Premium, VIP)""",
   handoffs=[
       handoff(
           agent=escalation_agent,
           on_handoff=process_escalation,
           input_type=EscalationData,
       )
   ],
)

Dengan penyiapan ini, saat agen layanan memutuskan untuk mengalihkan ke agen eskalasi, ia akan memberikan data terstruktur tentang alasan eskalasi. Sistem akan memvalidasi data ini terhadap model EscalationData sebelum meneruskannya ke callback process_escalation.

Kapan menggunakan handoff vs. agen-sebagai-tool

Agents SDK menawarkan dua cara agar agen dapat bekerja sama: handoff dan menggunakan agen sebagai tools (seperti yang kita lihat di bagian sebelumnya). Berikut kapan menggunakan masing-masing pendekatan:

Gunakan handoff saat:

  • Anda ingin sepenuhnya mentransfer kontrol ke agen lain
  • Percakapan perlu dilanjutkan dengan spesialis
  • Anda membangun alur kerja di mana agen berbeda menangani tahapan berbeda

Gunakan agen-sebagai-tools saat:

  • Agen utama perlu berkonsultasi dengan spesialis tetapi tetap mempertahankan kontrol
  • Anda ingin memasukkan respons spesialis sebagai bagian dari jawaban yang lebih besar
  • Anda membangun sistem hierarkis di mana koordinator mendelegasikan sub-tugas

Kedua pendekatan ini dapat digabung dalam sistem yang canggih, dengan agen utama yang kadang berkonsultasi dengan spesialis (menggunakannya sebagai tools) dan kadang mengalihkan kontrol sepenuhnya saat sesuai.

Handoff menyediakan mekanisme yang kuat untuk membangun sistem agen kompleks di mana tanggung jawab berpindah antar spesialis. Saat merancang arsitektur agen Anda, pertimbangkan pendekatan mana yang paling sesuai dengan kasus penggunaan spesifik Anda untuk menciptakan pengalaman pengguna yang paling efektif.

Penutup dan Langkah Berikutnya

Kita telah mengeksplorasi komponen inti OpenAI Agents SDK, mulai dari membuat agen dasar hingga menerapkan function tools dan mengelola handoff antar agen spesialis. Kita telah melihat bagaimana output terstruktur menggunakan model Pydantic dapat membuat aplikasi lebih andal dan mudah dipelihara, serta bagaimana merancang sistem agen yang dapat menangani tugas kompleks melalui delegasi dan spesialisasi.

Namun, kita baru menggaruk permukaan dari kemungkinan kerangka kerja yang kuat ini. Bagi developer yang siap membawa sistem agen ke tingkat berikutnya, beberapa topik lanjutan menanti untuk dieksplorasi: streaming responses untuk pembaruan real-time, tracing dan observabilitas untuk debugging, orkestrasi multi-agen untuk alur kerja kompleks, manajemen konteks untuk mempertahankan state percakapan, dan guardrails untuk memastikan perilaku agen yang aman dan tepat.

Seiring agen AI menjadi semakin sentral dalam aplikasi modern, keterampilan yang Anda kembangkan melalui tutorial ini memberikan fondasi yang kuat untuk menciptakan sistem AI yang canggih. Lanjutkan perjalanan belajar Anda dengan sumber daya seperti panduan DataCamp untuk belajar AI, dan ingat bahwa desain agen yang efektif sama-sama merupakan seni dan sains—membutuhkan iterasi, pengujian, serta pemahaman mendalam tentang kebutuhan pengguna dan kapabilitas AI.

FAQ OpenAI Agents SDK

Apa itu OpenAI Agents SDK?

OpenAI Agents SDK adalah kerangka kerja Python yang memungkinkan developer membangun aplikasi AI yang mampu membuat keputusan dan mengambil tindakan. SDK ini menggabungkan model bahasa besar dengan tools dan kapabilitas koordinasi, sehingga Anda dapat membuat sistem yang dapat memecahkan masalah kompleks secara otonom alih-alih sekadar merespons pertanyaan.

Jenis tools apa yang dapat digunakan agen dalam SDK?

SDK mendukung tiga jenis tools utama: hosted tools (seperti WebSearchTool yang berjalan di server OpenAI), function tools (fungsi Python kustom yang memperluas kapabilitas agen), dan agen-sebagai-tools (menggunakan agen spesialis sebagai tools untuk agen lain). Ini memungkinkan agen melakukan aksi seperti menelusuri web, mengakses API, atau mendelegasikan ke agen spesialis.

Bagaimana cara kerja output terstruktur dalam Agents SDK?

Output terstruktur menggunakan model Pydantic untuk menentukan secara tepat struktur data yang Anda inginkan dari agen. Dengan menyetel parameter output_type saat membuat agen, Anda akan menerima objek data yang terformat dengan benar alih-alih teks bebas. Ini membuat aplikasi lebih andal dan menghilangkan kebutuhan parsing manual atas respons agen.

Apa perbedaan antara handoff dan agen-sebagai-tools?

Handoff sepenuhnya mengalihkan kontrol ke agen lain, sehingga ideal saat percakapan perlu dilanjutkan dengan spesialis. Agen-sebagai-tools memungkinkan agen utama berkonsultasi dengan spesialis sambil mempertahankan kontrol, menggabungkan respons spesialis ke dalam jawaban yang lebih besar. Handoff paling cocok untuk transisi alur kerja, sedangkan agen-sebagai-tools lebih baik untuk sistem hierarkis.

Fitur lanjutan apa yang tersedia di OpenAI Agents SDK?

Di luar fungsi inti, SDK menawarkan streaming untuk pembaruan real-time, tracing untuk debugging dan observabilitas, orkestrasi multi-agen untuk alur kerja kompleks, manajemen konteks untuk mempertahankan state percakapan, dan guardrails untuk memastikan perilaku agen yang aman. Fitur-fitur ini membantu developer membangun sistem agen yang canggih dan siap produksi.


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

Saya adalah kreator konten data science dengan pengalaman lebih dari 2 tahun dan salah satu dengan jumlah pengikut terbesar di Medium. Saya suka menulis artikel mendalam tentang AI dan ML dengan sedikit gaya sarkastik—karena harus ada sesuatu untuk membuatnya tidak terlalu membosankan. Saya telah menghasilkan lebih dari 130 artikel dan satu kursus di DataCamp, dengan satu lagi sedang dalam proses. Konten saya telah dilihat oleh lebih dari 5 juta pasang mata, dan 20 ribu di antaranya menjadi pengikut di Medium dan LinkedIn. 

Topik
Agen AI
OpenAI
Kecerdasan Buatan

Kursus Teratas di DataCamp

Kursus

Bekerja dengan OpenAI API

3 jam
178.4K
Mulai perjalanan Anda mengembangkan aplikasi berbasis AI dengan OpenAI API. Pelajari fungsionalitas yang menjadi dasar aplikasi AI populer seperti ChatGPT.
Lihat DetailRight Arrow
Mulai Kursus
Lihat SelengkapnyaRight Arrow
Terkait

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

Lihat SelengkapnyaLihat Selengkapnya