18. Sistem Multi-Agen — Pola Supervisor
Coba ingat kembali agen layanan pelanggan yang kita bangun di Bab 16. Agen itu hanya punya satu alat — pencarian pesanan — jadi ketika pelanggan bertanya soal pesanan, agen melaporkan status pengiriman. Dengan hanya satu alat, memang tidak ada cara baginya untuk memilih alat yang salah.
Sekarang bayangkan kita mengembangkan agen itu menjadi layanan produksi sungguhan. Pencarian pesanan saja tidak cukup. Kita akan butuh pembatalan pesanan, pelacakan pengiriman, perubahan alamat, permintaan penukaran, permintaan pengembalian barang, pengecekan kelayakan refund, pemrosesan refund, pengecekan stok, penerbitan kupon, pencarian poin, pembuatan tiket dukungan, dan banyak lagi. Mekanismenya sederhana: terus tambahkan alat dan tambahkan aturan bisnis ke system prompt.
Namun seiring alat dan aturan menumpuk, muncul tiga masalah.
-
Pemilihan alat jadi kurang akurat. Pada setiap giliran, model membaca semua deskripsi alat dan memutuskan mana yang akan dipanggil. Saat kamu menambahkan alat yang menerima input yang sama dan tumpang tindih dalam tujuannya — seperti permintaan penukaran dan permintaan pengembalian barang — peluang memilih yang salah pun meningkat.
-
Konteks terisi dengan informasi yang saat ini tidak kamu butuhkan. Bahkan saat memproses refund, skema alat pengecekan stok, aturan penerbitan kupon, prosedur permintaan penukaran, dan segala hal lain ikut terbawa pada setiap panggilan. Kamu mengirimkan seluruh set skema alat dan aturan bisnis setiap domain setiap kali. Ketika konteks penuh dengan materi yang tidak relevan dengan tugas saat ini, bagian yang penting justru terkubur dan akurasi menurun.
-
Sulit untuk diubah. Menyesuaikan satu aturan refund berarti mengedit prompt tempat aturan setiap domain saling terjalin, dan kamu tidak bisa memastikan perubahan itu tidak merembet ke penukaran atau pengiriman. Kalau domain yang berbeda punya pemilik yang berbeda, masalahnya makin besar.
Bab ini mengajarkan satu cara untuk menangani hal ini. Alih-alih menumpuk lebih banyak alat dan aturan pada satu agen, kita membagi pekerjaan menjadi satu agen per domain. Melanjutkan skenario layanan pelanggan dari Bab 16, kita akan membangun sebuah tim yang terdiri dari agen yang hanya menangani pencarian pesanan dan agen yang hanya menangani refund. Masing-masing punya alat dan prompt-nya sendiri. Lalu kita tambahkan satu agen lagi untuk mengarahkan mereka — pengarah ini disebut supervisor. Susunan dengan beberapa agen seperti ini adalah sistem multi-agen(multi-agent system), dan pengaturan di mana supervisor memimpin yang lain adalah pola supervisor(supervisor pattern).
Sistem multi-agen memang punya biayanya. Karena supervisor harus memutuskan pekerja mana yang akan didelegasikan pada setiap langkah, panggilan LLM jadi lebih banyak, dan itu berarti latensi lebih tinggi serta biaya lebih besar. Jadi kalau alat dan aturanmu tidak terlalu banyak, tidak perlu langsung beralih ke sistem multi-agen sama sekali.
Berikut rencananya. Di 18.1 kita melihat apa itu sistem multi-agen dan cara kerjanya. Di 18.2 kita membangun agen pekerja. Di 18.3 kita membangun supervisor secara manual dengan objek Command milik LangGraph. Di 18.4 kita membungkus para pekerja sebagai tools dan membangun ulang tim yang sama dengan kode yang jauh lebih sedikit.
18.1) Memahami Sistem Multi-Agen
18.1.1) Apa Itu Sistem Multi-Agen
Mari kita rancang sebuah agen yang bisa menangani permintaan, "Pesanan saya tidak pernah sampai — kalau ada yang salah, refund saja." Kita bisa membangunnya dengan semua yang sudah kita pelajari sejauh ini: pasang alat pencarian pesanan dan alat refund pada satu agen, lalu biarkan agen berulang sampai pekerjaannya selesai. Satu agen mencari pesanan, memastikan pengiriman gagal, melihat hasilnya, meminta refund, lalu menulis balasan akhir.
Sistem multi-agen adalah struktur di mana beberapa agen membagi pekerjaan ini di antara mereka. Kamu membagi agen berdasarkan domain dan menempatkan supervisor di atas mereka, dan setiap agen hanya memegang alat yang dibutuhkannya. Agen yang baru saja kita gambarkan, misalnya, terbagi secara alami menjadi agen pencarian pesanan dan agen refund. Supervisor pertama-tama memanggil agen pencarian pesanan untuk mengecek status pengiriman; ketika ia mendengar bahwa pengiriman gagal, ia memanggil agen refund untuk memproses refund; lalu ia mengumpulkan kedua hasil itu untuk menjawab pelanggan. Yang tadinya satu agen memanggil alat secara berurutan kini menjadi supervisor memanggil agen secara berurutan.
Kalau hanya ada dua alat, tidak ada alasan untuk membagi seperti ini. Satu agen sudah cukup, dan menambahkan supervisor hanya akan menambah panggilan LLM. Namun seperti yang kita lihat di pendahuluan, begitu ada banyak alat, sebuah agen bisa memilih yang salah, konteksnya terisi dengan informasi yang tidak berkaitan dengan tugas yang sedang dikerjakan, dan prompt-nya jadi sulit diubah.
Membagi menyelesaikan masalah-masalah ini. Agen pencarian pesanan hanya melihat beberapa alat terkait pesanan, sehingga memilih dari daftar berisi lusinan menyusut menjadi memilih di antara segelintir. Prompt-nya hanya memuat aturan pencarian pesanan, sehingga kebijakan refund, syarat kupon, dan hal-hal lain yang tidak berkaitan dengan pencarian pesanan tidak memenuhi konteksnya. Dan ketika kamu perlu mengubah aturan refund, kamu hanya menyentuh agen refund, sehingga perubahannya tidak sampai ke yang lain.
Agen pencarian pesanan dan agen refund di sini bukanlah sesuatu yang istimewa. Keduanya adalah jenis agen yang sama seperti yang kamu bangun di Bab 16. Kamu membuatnya dengan mengoper model, tools, dan prompt ke create_agent, dan kamu memanggilnya dengan invoke. Mereka hanya mencakup wilayah yang lebih sempit.
Jadi apa yang dilakukan supervisor? Agen pencarian pesanan dan agen refund tidak tahu bahwa yang lain ada. Masing-masing hanya melakukan tugasnya sendiri; tak satu pun tahu siapa yang harus jalan lebih dulu. Pada contoh di atas, memanggil pencarian pesanan lebih dulu dan — baru setelah melihat hasilnya — memanggil refund adalah keputusan supervisor.
Siapa memanggil siapa, dan kapan. Inilah yang kita sebut orkestrasi.
18.1.2) Pola Supervisor
Pola supervisor adalah struktur di mana satu supervisor pusat mengorkestrasi beberapa agen pekerja. Ia mengikuti aturan-aturan ini:
- Supervisor tidak pernah melakukan pekerjaannya sendiri. Ia tidak mencari pesanan atau memproses refund. Ia hanya memutuskan kepada siapa harus diserahkan, lalu mengumpulkan hasil yang dikembalikan menjadi jawaban akhir.
- Pekerja tidak pernah saling memanggil. Agen pencarian pesanan tidak pernah memanggil agen refund secara langsung. Setiap jalur berjalan melalui supervisor.
- Hanya supervisor yang berbicara dengan pelanggan. Pekerja melapor ke supervisor, bukan ke pelanggan.
Jadi bagaimana supervisor memutuskan agen mana yang akan dipanggil? LLM yang memutuskan. Supervisor membaca seluruh percakapan sejauh ini dan menilai. Kalau ia belum tahu status pesanan, ia memanggil agen pencarian pesanan; begitu terkonfirmasi bahwa pengiriman gagal dan refund dibutuhkan, ia memanggil agen refund.
Supervisor mengulang penilaian ini sampai permintaan pengguna selesai. Ia memanggil agen, menerima laporan, membaca ulang percakapan yang kini telah bertambah laporan itu, dan memutuskan agen mana yang akan dipanggil berikutnya.
Loop ini punya struktur yang sama dengan yang kamu bangun di Bab 14.
- Think — baca percakapan sejauh ini dan putuskan agen mana yang akan dipanggil.
- Act — jalankan agen yang dipilih.
- Observe — ambil laporan agen dan tambahkan ke percakapan.
Di Bab 14 kamu memanggil tools; di sini kamu memanggil agen. Hanya itu yang berubah.
Perhatikan bagaimana panah-panahnya kembali mengarah ke supervisor. Ketika seorang pekerja selesai, ia melapor ke supervisor, dan supervisor membaca laporan itu lalu memutuskan langkah berikutnya.
Supervisorlah yang mengakhiri loop. Begitu ia menilai bahwa permintaan pelanggan sudah sepenuhnya ditangani, ia menghasilkan jawaban akhir dan berhenti.
18.2) Membangun Agen Pekerja
Mari kita bangun dua agen pekerja yang kita rancang di 18.1: pekerja pencarian pesanan yang mengecek status pesanan, dan pekerja refund yang memproses refund. Kita akan membangun supervisor di 18.3.
Agen pekerja hanyalah agen biasa yang kamu bangun di Bab 16. Kita akan membangunnya dengan cepat menggunakan create_agent.
Pertama, mari kita siapkan data pesanan yang akan dipakai bersama oleh kedua pekerja.
ORDERS = {
"12345": {"item": "Wireless Earbuds", "amount": 89,
"status": "in_transit", "status_text": "Dalam perjalanan (tiba besok)"},
"67890": {"item": "Mechanical Keyboard", "amount": 129,
"status": "delivered", "status_text": "Terkirim"},
"24680": {"item": "Noise-Cancelling Headphones", "amount": 249,
"status": "delivery_failed", "status_text": "Pengiriman gagal (dikembalikan — penerima tidak ditemukan)"},
}Pekerja pencarian pesanan hanya punya satu alat.
from langchain.tools import tool
from langchain.agents import create_agent
@tool
def get_order_status(order_id: str) -> str:
"""Cari item, jumlah yang dibayar, dan status pengiriman untuk sebuah nomor pesanan."""
order = ORDERS.get(order_id)
if order is None:
return f"Pesanan {order_id} tidak ditemukan."
return (f"Pesanan {order_id}: {order['item']}, "
f"${order['amount']:,}, status: {order['status_text']}")
order_agent = create_agent(
name="order_expert",
model="openai:gpt-5.4-mini",
tools=[get_order_status],
system_prompt=(
"Kamu adalah spesialis pencarian pesanan. Cari status pesanan dan jawab.\n"
"Sertakan nomor pesanan, item, jumlah yang dibayar, dan status pengiriman dalam jawaban akhirmu.\n"
"Jangan menilai apakah sebuah refund layak diberikan atau menyebut refund dengan cara apa pun. Peranmu hanyalah pencarian pesanan dan pelaporan status."
),
)Pekerja refund punya dua alat: satu yang memutuskan apakah sebuah pesanan layak di-refund, dan satu yang benar-benar memproses refund-nya.
@tool
def check_refund_eligibility(order_id: str) -> str:
"""Tentukan apakah sebuah pesanan layak untuk refund. Hanya pesanan dengan pengiriman gagal yang memenuhi syarat."""
order = ORDERS.get(order_id)
if order is None:
return f"Pesanan {order_id} tidak ditemukan."
if order["status"] == "delivery_failed":
return f"Pesanan {order_id} layak untuk refund (alasan: pengiriman gagal)."
return f"Pesanan {order_id} tidak layak untuk refund (status saat ini: {order['status_text']})."
@tool
def issue_refund(order_id: str) -> str:
"""Proses sebuah refund. Selalu konfirmasi kelayakan dengan check_refund_eligibility sebelum memanggil ini."""
order = ORDERS.get(order_id)
if order is None:
return f"Pesanan {order_id} tidak ditemukan."
return (f"Refund selesai: ${order['amount']:,} untuk pesanan {order_id} "
f"akan di-refund dalam 3–5 hari kerja. (nomor persetujuan: RF-{order_id})")
refund_agent = create_agent(
name="refund_expert",
model="openai:gpt-5.4-mini",
tools=[check_refund_eligibility, issue_refund],
system_prompt=(
"Kamu adalah spesialis pemrosesan refund.\n"
"Selalu konfirmasi kelayakan dengan check_refund_eligibility terlebih dahulu, dan baru "
"kemudian panggil issue_refund.\n"
"Kalau kamu memproses sebuah refund, sertakan jumlahnya dan nomor persetujuan dalam jawaban akhirmu.\n"
"Kalau pesanan tidak layak, jangan proses — laporkan alasannya sebagai gantinya."
),
)Kita memberi kedua pekerja sebuah name. Nama inilah yang dipakai create_supervisor di 18.5 sebagai nama node dan nama alat handoff.
Empat Hal yang Dibutuhkan System Prompt Seorang Pekerja
System prompt seorang pekerja dibangun dari empat elemen — prinsip yang disaring Anthropic saat membangun sistem risetnya sendiri yang multi-agen. Ketika elemen-elemen ini lemah, pekerja menggandakan pekerjaan, membiarkan tugas tidak selesai, atau gagal menemukan informasi yang mereka butuhkan.
| Elemen | order_agent | refund_agent |
|---|---|---|
| Peran | Kamu adalah spesialis pencarian pesanan | Kamu adalah spesialis pemrosesan refund |
| Panduan alat | Selalu konfirmasi dengan check_refund_eligibility terlebih dahulu, dan baru kemudian panggil issue_refund | |
| Format output | Sertakan nomor pesanan, item, jumlah yang dibayar, dan status pengiriman dalam jawaban akhirmu | Kalau kamu memproses sebuah refund, sertakan jumlahnya dan nomor persetujuan dalam jawaban akhirmu |
| Batas tugas | Jangan menilai apakah sebuah refund layak diberikan atau menyebut refund dengan cara apa pun. Peranmu hanyalah pencarian pesanan dan pelaporan status. | Kalau pesanan tidak layak, jangan proses — laporkan alasannya sebagai gantinya |
Peran menetapkan, dalam satu kalimat, siapa pekerja ini. Mematok identitasnya dengan "Kamu adalah spesialis pencarian pesanan" menjaga model tetap fokus pada tugasnya sendiri dan lebih kecil kemungkinannya menyimpang ke tugas orang lain.
Panduan alat. Tulis ini ketika ada sesuatu yang tidak bisa disampaikan oleh skema alat saja — urutan dan syarat penggunaan alat, misalnya. Kalau tidak ada yang perlu ditambahkan di luar skema, kamu bisa membiarkannya kosong.
Format output dan batas tugas sangat penting dalam pengaturan multi-agen.
Format output. Jawaban akhir seorang pekerja bukanlah balasan kepada pelanggan — melainkan laporan yang diserahkan kepada supervisor. Apa pun yang tidak tertulis di sana tidak akan pernah sampai ke supervisor. Kalau seorang pekerja mencari jumlah dengan sebuah alat tetapi tidak mencantumkannya dalam jawaban akhirnya, supervisor tidak punya cara untuk mengetahuinya.
Batas tugas. Inilah cakupan yang menentukan seberapa jauh seorang pekerja boleh melangkah dan apa yang tidak boleh dilakukannya. Pekerja pencarian pesanan seharusnya hanya mencari — tidak pernah refund. Itulah sebabnya kita tidak memberinya alat refund. Namun menahan alat saja tidak cukup, karena model tetap bisa mengatakan "Pengiriman gagal, jadi saya akan menerbitkan refund untuk Anda" tanpa alat sama sekali. Kalau kalimat itu sampai ke supervisor, supervisor mungkin mengira refund sudah berjalan dan tidak pernah memanggil pekerja refund. Maka prompt juga berkata, "Jangan menilai apakah sebuah refund layak diberikan atau menyebut refund dengan cara apa pun," menghalanginya bahkan sekadar membahas refund dalam kata-kata.
Memilih Model
Kita menggunakan gpt-5.4-mini untuk para pekerja dan gpt-5.4 untuk supervisor. Seorang pekerja mengerjakan tugas sederhana yaitu memanggil beberapa alat dalam urutan tertentu, jadi model kecil sudah lebih dari cukup. Supervisor harus membaca seluruh percakapan dan menilai siapa yang akan dipanggil berikutnya, jadi ia butuh model yang lebih besar. Bisa memilih model per agen, disesuaikan dengan tingkat kesulitan tugasnya, adalah manfaat lain dari membagi.
Sekarang kita bisa menambahkan supervisor.
18.3) Membangun Supervisor Secara Manual
Mari kita bangun supervisor secara manual. Dalam praktiknya kamu sebagian besar akan memakai pendekatan di mana framework menangani ini untukmu (dibahas di bagian berikutnya), tetapi untuk memahami apa yang terjadi di balik layar, kamu perlu membangunnya sendiri sekali.
Seperti yang kita lihat di 18.1, apa yang dilakukan supervisor adalah satu loop tunggal: panggil pekerja, baca laporan, dan putuskan siapa yang akan dipanggil berikutnya — atau apakah harus berhenti — berulang kali.
18.3.1) Handoff dan Command
Agar loop ini berputar, kendali harus berpindah bolak-balik antara supervisor dan para pekerja. Supervisor menyerahkan kendali — "pekerja ini yang jalan berikutnya" — dan ketika pekerja selesai, ia menyerahkan kendali kembali ke supervisor. Perpindahan kendali dari satu node ke node lain ini disebut handoff.
Sebuah handoff membutuhkan dua potong informasi: ke mana harus pergi (tujuan) dan apa yang harus diteruskan (payload). Tujuan selalu wajib; payload disertakan hanya ketika ada sesuatu untuk diteruskan. Di LangGraph, sebuah node menentukan keduanya dengan mengembalikan sebuah Command.
from typing import Literal
from langgraph.graph import MessagesState
from langgraph.types import Command
def some_node(state: MessagesState) -> Command[Literal["refund_expert_proxy"]]:
return Command(
goto="refund_expert_proxy", # ke mana: node yang akan dijalankan berikutnya
update={"messages": [...]}, # apa: laporan pekerja (payload yang ditambahkan ke State)
)goto adalah tujuannya; update adalah payload-nya. Hint tipe kembalian Command[Literal["refund_expert_proxy"]] mencantumkan, di awal, tujuan-tujuan yang bisa dituju node ini. Kita akan melihat bagaimana Command ini sebenarnya digunakan di bagian berikutnya, di mana kita membangun node supervisor dan proxy pekerja.
18.3.2) Membangun Loop Supervisor
Strukturnya sendiri sederhana. Kita membuat satu node supervisor dan sebanyak-banyaknya proxy pekerja yang kita butuhkan. Sebuah proxy pekerja adalah node yang memanggil agen pekerja yang ditugaskan kepadanya atas nama supervisor. Titik masuknya adalah supervisor, dan setiap proxy pekerja, begitu selesai, kembali ke supervisor — membentuk loop. Command yang dikembalikan setiap node itulah yang menentukan ke mana kendali pergi berikutnya.
Garis solid adalah handoff antar node (goto); garis putus-putus adalah proxy pekerja yang memanggil agen pekerjanya (invoke). Supervisor menyerahkan ke proxy pekerja, dan proxy pekerja, begitu selesai, kembali ke supervisor. Ketika supervisor memutuskan FINISH, ia keluar ke END.
Sekarang mari kita ubah gambaran ini menjadi kode. Pertama, kelas Route. Route adalah skema untuk menerima jawaban supervisor sebagai respons terstruktur ketika kita menanyakan ke LLM proxy pekerja mana yang akan dipanggil berikutnya. Kalau LLM menjawab dalam bahasa alami bentuk bebas, akan sulit mengetahui proxy pekerja mana yang harus dijalankan. Route menyimpan proxy pekerja mana yang akan dipanggil berikutnya (next) dan alasan ia memutuskan begitu (reason).
from typing import Literal
from pydantic import BaseModel, Field
class Route(BaseModel):
reason: str = Field(description="Alasan untuk keputusan ini.")
next: Literal["order_expert_proxy", "refund_expert_proxy", "FINISH"] = Field(
description="Node pekerja yang akan dijalankan berikutnya. FINISH jika permintaan sudah sepenuhnya ditangani."
)Ada alasan mengapa reason dideklarasikan sebelum next. Output terstruktur dihasilkan dalam urutan field yang muncul di skema, jadi dengan reason lebih dulu, LLM menuliskan penalarannya sebelum memilih pekerja. Bernalar dulu dan memutuskan kemudian menghasilkan pilihan yang lebih baik. Balik urutannya — taruh next lebih dulu — dan LLM memilih pekerja sebelum ia bernalar sama sekali, lalu mengisi rasionalisasi belakangan agar cocok dengan pilihan yang mungkin sudah salah sejak awal.
Berikutnya, node supervisor.
from typing import Literal
from langgraph.graph import MessagesState, StateGraph, START, END
from langgraph.types import Command
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
supervisor_llm = ChatOpenAI(model="gpt-5.4")
SUPERVISOR_PROMPT = (
"Kamu adalah supervisor sebuah tim layanan pelanggan. Kamu mengelola dua pekerja:\n"
"- order_expert_proxy: mencari status pesanan.\n"
"- refund_expert_proxy: mengecek kelayakan refund dan memproses refund.\n"
"Untuk memutuskan apakah sebuah refund dibutuhkan, kamu harus mengecek status pesanan terlebih dahulu.\n"
"Tugaskan ke satu pekerja setiap kali, dan jawab dengan FINISH begitu permintaan sudah sepenuhnya ditangani."
)
def supervisor(
state: MessagesState,
) -> Command[Literal["order_expert_proxy", "refund_expert_proxy", "__end__"]]:
messages = [{"role": "system", "content": SUPERVISOR_PROMPT}, *state["messages"]]
decision = supervisor_llm.with_structured_output(Route).invoke(messages)
print(f"[supervisor] → {decision.next} ({decision.reason})")
if decision.next == "FINISH":
final = supervisor_llm.invoke(
[{"role": "system", "content": "Berdasarkan percakapan sejauh ini, tulis balasan untuk pelanggan."},
*state["messages"]]
)
return Command(goto=END, update={"messages": [final]}) # serahkan ke END
return Command(goto=decision.next) # serahkan ke proxy pekerjaSampai sekarang, node hanya mengembalikan State yang berubah. Namun node supervisor mengembalikan sebuah Command. Ketika sebuah node mengembalikan Command, LangGraph melakukan dua hal: ia menerapkan isi update ke State, dan ia menjalankan node yang disebutkan dalam goto berikutnya. Pada kode di atas, kita menaruh nama proxy pekerja yang diambil dari decision.next ke dalam goto, sehingga node yang dipilih LLM — decision.next — itulah yang dijalankan.
Berikutnya, proxy pekerja. Sebuah proxy pekerja mengerjakan tugasnya dan mengembalikan kendali ke supervisor — itulah sebabnya ia menyerahkan dengan goto="supervisor".
Tugas sebuah proxy pekerja sederhana: memanggil agen pekerjanya dengan invoke.
def order_expert_proxy(state: MessagesState) -> Command[Literal["supervisor"]]:
result = order_agent.invoke(state)
last = result["messages"][-1]
return Command(
goto="supervisor",
update={"messages": [HumanMessage(content=last.content, name="order_expert")]},
)
def refund_expert_proxy(state: MessagesState) -> Command[Literal["supervisor"]]:
result = refund_agent.invoke(state)
last = result["messages"][-1]
return Command(
goto="supervisor",
update={"messages": [HumanMessage(content=last.content, name="refund_expert")]},
)Perhatikan bahwa kita hanya menarik pesan akhir pekerja dan mengoper itu ke supervisor. Supervisor hanya butuh kesimpulannya; ia tidak perlu tahu berapa kali pekerja memanggil alat secara internal.
18.3.3) Merangkai dan Menjalankan Graph
Kita mendaftarkan ketiga node dan hanya menghubungkan titik masuknya ke supervisor. Setiap perpindahan lainnya diputuskan oleh Command masing-masing node, jadi tidak dibutuhkan edge lain.
builder = StateGraph(MessagesState)
builder.add_node("supervisor", supervisor)
builder.add_node("order_expert_proxy", order_expert_proxy)
builder.add_node("refund_expert_proxy", refund_expert_proxy)
builder.add_edge(START, "supervisor")
team = builder.compile()
result = team.invoke(
{"messages": [HumanMessage(
content="Pesanan 24680 masih belum sampai. Kalau ada yang salah, tolong refund saja."
)]},
config={"recursion_limit": 15},
)Output:
[supervisor] → order_expert_proxy (Perlu mengecek status pesanan sebelum memutuskan soal refund.)
[supervisor] → refund_expert_proxy (Pengiriman gagal sudah terkonfirmasi, jadi cek kelayakan refund dan proses.)
[supervisor] → FINISH (Pengecekan pesanan dan pemrosesan refund sama-sama sudah selesai.)Supervisor pertama-tama merutekan ke pencarian pesanan. Ketika laporan kembali bahwa pengiriman telah gagal, ia merutekan ke refund, dan ketika laporan refund-selesai tiba, ia menyelesaikannya. Ia memilih setiap tujuan berikutnya dengan membaca laporan agen sebelumnya.
Proxy pekerja mengoper seluruh State yang dipakai bersama ke pekerjanya melalui invoke(state), sehingga setiap pekerja melihat seluruh percakapan sejauh ini. Dengan hanya dua pekerja hal ini baik-baik saja, tetapi seiring pekerja dan percakapan bertambah, setiap pekerja akhirnya membaca pesan yang tidak ada hubungannya dengan tugasnya sendiri. Kita akan menyelesaikan ini dengan cara berbeda di bagian berikutnya.
18.4) Mendelegasikan ke Pekerja Melalui Tools
Setelah membangun bagian dalam supervisor secara manual di 18.3, sekarang mari kita bangun ulang tim yang sama dengan cara yang direkomendasikan untuk proyek nyata. Pendekatan ini tidak membutuhkan API baru. Kamu mengubah setiap pekerja menjadi sebuah alat dengan @tool, dan memberikan alat-alat itu ke sebuah agen supervisor. Kita akan membangun agen supervisor dengan sederhana menggunakan create_agent.
Ide kuncinya muat dalam satu kalimat: supervisor itu sendiri hanyalah sebuah agen, dan setiap pekerja menjadi sebuah alat yang dipanggil supervisor.
Dilihat begini, supervisor punya struktur yang sama dengan agen pemanggil alat yang kamu bangun di Bab 16. Ia hanya memegang alat tingkat tinggi yang memanggil agen, alih-alih alat tingkat rendah seperti get_order_status.
18.4.1) Membungkus Pekerja sebagai Tools
Kita menggunakan order_agent dan refund_agent dari 18.2 tanpa perubahan. Yang kita lakukan hanyalah membungkus masing-masing dalam sebuah fungsi @tool.
from langchain.tools import tool
# order_agent dan refund_agent adalah para pekerja dari 18.2
@tool
def lookup_order(request: str) -> str:
"""Cari item, jumlah yang dibayar, dan status pengiriman sebuah pesanan. Gunakan ini ketika kamu perlu tahu status sebuah pesanan.
Input: permintaan pencarian dalam bahasa alami (mis., 'Beri tahu saya status pengiriman pesanan 24680').
"""
print("[tool call] lookup_order")
print(f" request: {request}")
result = order_agent.invoke({"messages": [{"role": "user", "content": request}]})
return result["messages"][-1].content
@tool
def handle_refund(request: str) -> str:
"""Cek kelayakan refund dan proses sebuah refund. Gunakan ini ketika pelanggan ingin refund dan kamu sudah mengonfirmasi status pesanan.
Input: permintaan refund dalam bahasa alami. Sertakan nomor pesanan dan status pengiriman yang telah dikonfirmasi lewat pencarian.
(mis., 'Pesanan 24680 dalam status pengiriman gagal. Proses refund jika layak.')
"""
print("[tool call] handle_refund")
print(f" request: {request}")
result = refund_agent.invoke({"messages": [{"role": "user", "content": request}]})
return result["messages"][-1].contentTiga hal berubah.
-
Deskripsi alat menggantikan logika perutean. Di 18.3 kita menulis
SUPERVISOR_PROMPTdan skemaRoutesecara manual untuk memberi tahu supervisor daftar pekerjanya dan pilihan-pilihannya. Di sini docstring alat yang mengerjakan tugas itu. LLM supervisor membaca deskripsi alat dan memutuskan kapan memanggil apa. -
Setiap pekerja mulai dari konteks yang bersih. Disandingkan dengan 18.3, perbedaannya jelas.
python# 18.3 (graph manual): mengoper seluruh State yang dipakai bersama result = refund_agent.invoke(state) # 18.4 (delegasi via tool): mengoper hanya deskripsi tugas yang ditulis supervisor result = refund_agent.invoke({"messages": [{"role": "user", "content": request}]})Di 18.4 pekerja refund menerima hanya satu kalimat yang menggambarkan tugasnya. Ia tidak pernah melihat kata-kata asli pelanggan, penalaran supervisor, atau riwayat panggilan alat pekerja lain. Bahkan dengan sepuluh pekerja dan seratus giliran percakapan, konteks setiap pekerja tetaplah hanya deskripsi tugas yang satu itu.
-
Sebagai gantinya, supervisor mengambil alih tugas meneruskan informasi. Karena seorang pekerja tidak bisa melihat riwayat percakapan, apa pun yang ia butuhkan harus dikemas ke dalam string
requestoleh supervisor. Itulah sebabnya docstringhandle_refundmengeja secara gamblang, "Sertakan nomor pesanan dan status pengiriman yang telah dikonfirmasi lewat pencarian." Tanpa instruksi itu, supervisor mungkin hanya mengoper"Proses sebuah refund", membuat pekerja refund tidak tahu pesanan yang mana yang sedang dibicarakan.
18.4.2) Menyusun dan Menjalankan Supervisor
Dalam pendekatan ini supervisor pun hanyalah sebuah agen. Tidak butuh Command, tidak butuh skema Route.
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
TOOL_SUPERVISOR_PROMPT = (
"Kamu adalah supervisor sebuah tim layanan pelanggan.\n"
"Untuk memutuskan apakah sebuah refund dibutuhkan, kamu harus mengecek status pesanan terlebih dahulu.\n"
"Jangan kerjakan sendiri — delegasikan ke para pekerja.\n"
"Para pekerja tidak bisa melihat percakapan ini. Ketika kamu mendelegasikan, taruh semua yang mereka butuhkan ke dalam request.\n"
"Ketika semua pekerjaan sudah selesai, sintesiskan hasil para pekerja menjadi balasan untuk pelanggan."
)
supervisor_agent = create_agent(
model="openai:gpt-5.4",
tools=[lookup_order, handle_refund],
system_prompt=TOOL_SUPERVISOR_PROMPT,
)
result = supervisor_agent.invoke(
{"messages": [HumanMessage(
content="Pesanan 24680 masih belum sampai. Kalau ada yang salah, tolong refund saja."
)]}
)
print("\n\n[final response]")
print(result["messages"][-1].content)Hasil akhirnya sama seperti di 18.3.
[tool call] lookup_order
request: Pelanggan mengatakan pesanan 24680 belum sampai. Untuk memutuskan apakah sebuah refund
dibutuhkan, tolong beri saya item, jumlah yang dibayar, dan status pengiriman saat ini untuk pesanan 24680.
[tool call] handle_refund
request: Pesanan 24680 adalah Noise-Cancelling Headphones, $249, dan status pengirimannya
terkonfirmasi sebagai 'Pengiriman gagal (dikembalikan — penerima tidak ditemukan)'. Pelanggan
meminta refund, jadi cek kelayakan dan proses jika layak.
[final response]
Saya sudah mengecek, dan pesanan 24680 berada dalam status pengiriman gagal (dikembalikan — penerima tidak ditemukan).
Pesanan itu memenuhi syarat untuk refund, dan saya sudah menyelesaikan refund-nya.
- Item: Noise-Cancelling Headphones
- Jumlah refund: $249
- Nomor persetujuan refund: RF-24680
Tergantung metode pembayaran Anda, biasanya butuh beberapa hari kerja agar refund tercatat.Namun lihat panggilan alat yang dibuat supervisor sepanjang jalan — di situlah perbedaan dari 18.3 terlihat. Lihat request milik handle_refund. Supervisor merangkum hasil pencarian sebelumnya dan menulis deskripsi tugasnya sendiri. Pekerja refund menerima hanya satu kalimat ini. Di mana 18.3 menyerahkan seluruh percakapan kepada pekerja dan membiarkannya menggali, di sini supervisor memilih hanya apa yang dibutuhkan dan mengoper itu.
18.4.3) Menambahkan Memori ke Supervisor dengan Checkpointer
Mengatakan supervisor adalah agen biasa berarti checkpointing yang kamu pelajari di Bab 17 bekerja padanya apa adanya.
from langgraph.checkpoint.memory import InMemorySaver
supervisor_agent = create_agent(
model="openai:gpt-5.4",
tools=[lookup_order, handle_refund],
system_prompt=TOOL_SUPERVISOR_PROMPT,
checkpointer=InMemorySaver(), # checkpointer hanya dipasang pada agen tingkat atas
)
config = {"configurable": {"thread_id": "cs-1"}}
supervisor_agent.invoke(
{"messages": [HumanMessage(content="Di mana pesanan saya 12345?")]},
config,
)
follow_up = supervisor_agent.invoke(
{"messages": [HumanMessage(content="Berapa harganya?")]},
config,
)
print(follow_up["messages"][-1].content)Output:
Pesanan Anda 12345, Wireless Earbuds, seharga $89.Supervisor dengan benar membaca itu dalam pertanyaan lanjutan sebagai pesanan 12345 dari giliran sebelumnya. Checkpointer bekerja pada supervisor persis sama.
Jangan memasang checkpointer pada agen pekerja (
order_agent,refund_agent). Kalau kamu lakukan, seorang pekerja akan membawa hasil panggilan sebelumnya ke panggilan saat ini, yang bisa mengganggu tugas yang sedang dikerjakan. Tanpa checkpointer, seorang pekerja berjalan hanya berdasarkan request dari supervisor. Untuk subagen, inilah default yang direkomendasikan.
18.5) create_supervisor: Bentuk yang Akan Kamu Temui di Kode Lawas
Di basis kode yang sudah ada dan tutorial lama, kamu akan menjumpai helper create_supervisor dari paket langgraph-supervisor. Beri ia daftar agen dan sebuah prompt, dan ia membangun seluruh graph supervisor untukmu.
from langgraph_supervisor import create_supervisor
from langchain_openai import ChatOpenAI
workflow = create_supervisor(
agents=[order_agent, refund_agent], # setiap agen harus punya name yang di-set
model=ChatOpenAI(model="gpt-5.4"),
prompt="Tugaskan pengecekan pesanan ke order_expert dan refund ke refund_expert.",
)
app = workflow.compile()create_supervisor adalah helper yang menyusun sebuah tim supervisor dalam satu panggilan fungsi (pendekatan handoff dari 18.3). Namun jangan gunakan di proyek baru. Ini adalah warisan yang sudah tidak direkomendasikan LangChain, dan secara internal ia bergantung pada create_react_agent, yang di-deprecate di v1 (penghapusan direncanakan untuk v2). LangChain merekomendasikan supervisor berbasis tool yang kamu pelajari di 18.4.
Di bab ini kita mengambil sebuah agen yang tadinya hidup di dalam satu graph dan memperluasnya menjadi sebuah tim — dibagi berdasarkan domain, diorkestrasi oleh supervisor. Kita membangun tim yang sama dengan tiga cara: graph Command manual di 18.3, pendekatan delegasi via tool di 18.4, dan helper create_supervisor lawas di 18.5. Untuk pekerjaan nyata, jadikan pendekatan delegasi via tool sebagai default-mu.