← Kembali ke Blog

Delegasi ke Subagent dengan Claude Agent SDK

Agent yang harus baca 40 file cuma buat jawab satu pertanyaan adalah agent yang bakal lupa pertanyaannya sendiri. Tiap file yang dia buka, tiap hasil grep, tiap log test yang gagal masuk ke context window yang sama. Di turn ke-12, rencana dari turn pertama sudah tergeser keluar, dan agent mulai mengulang pekerjaan yang tadi sudah dia selesaikan.

Claude Agent SDK punya jawaban bawaan untuk ini: subagent. Subagent adalah instance agent terpisah dengan percakapan sendiri, set tool sendiri, dan system prompt sendiri. Dia menghabiskan context-nya untuk bagian yang berantakan secara terisolasi, lalu mengembalikan ringkasan. Parent tetap bersih.

Panduan ini membangun agent code review yang menyerahkan kerja ke dua subagent, membuktikan delegasinya benar-benar terjadi, dan membatasi biaya supaya run tanpa pengawasan tidak bikin kejutan.

Kalau kamu belum pernah menyentuh SDK ini, dasar tool calling (custom tool, MCP server in-process) sudah dibahas di artikel lain di situs ini. Artikel ini mulai dari titik kamu sudah bisa menjalankan query() dan mau melangkah lebih jauh.

Prasyarat

  • Python 3.10 atau lebih baru
  • Anthropic API key di environment variable ANTHROPIC_API_KEY
  • Satu repo git untuk jadi target agent
  • Venv, karena Debian dan Ubuntu terbaru menolak pip install ke Python sistem
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk

CLI Claude Code sudah ikut di dalam paket, jadi tidak ada langkah install kedua.

Langkah 1: Baseline yang mau kamu perbaiki

import asyncio
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query


async def main():
    options = ClaudeAgentOptions(
        system_prompt="You are a reviewer. Cite file:line for every claim.",
        allowed_tools=["Read", "Grep", "Glob"],
        permission_mode="acceptEdits",
        max_turns=15,
    )

    async for message in query(
        prompt="Review the last commit for security issues and failing tests",
        options=options,
    ):
        if isinstance(message, ResultMessage):
            print(message.result)
            print(f"turns={message.num_turns} est=${message.total_cost_usd:.4f}")


asyncio.run(main())

Ini jalan. Masalahnya, satu agent mengerjakan audit keamanan, menjalankan test, dan menulis kesimpulan di dalam satu percakapan. Semua yang dia baca tetap tinggal di situ sampai run selesai.

Langkah 2: Definisikan subagent

Subagent ditaruh di parameter agents. Tiap entri butuh description dan prompt. description bukan dokumentasi untuk manusia, itu sinyal routing yang dibaca model saat memutuskan mau mendelegasikan atau tidak.

from claude_agent_sdk import AgentDefinition, ClaudeAgentOptions

options = ClaudeAgentOptions(
    system_prompt="Review code changes. Delegate focused work to your subagents.",
    allowed_tools=["Read", "Grep", "Glob", "Agent"],
    agents={
        "security-scanner": AgentDefinition(
            description=(
                "Audits a diff for injection flaws, leaked secrets, and unsafe deserialization. "
                "Use whenever the change touches auth, crypto, or input parsing."
            ),
            prompt=(
                "You audit code. For each finding report file:line, a severity, and a one-line fix. "
                "Ignore style. Report only what you can point at in the diff."
            ),
            tools=["Read", "Grep", "Glob"],
            model="sonnet",
            maxTurns=15,
        ),
        "test-runner": AgentDefinition(
            description=(
                "Runs the test suite and reports failures. Use when the review needs a pass or fail signal."
            ),
            prompt=(
                "Run the project tests. Report failing test names with the assertion message. "
                "Never edit source files."
            ),
            tools=["Bash", "Read"],
        ),
    },
)

Ada empat hal di blok itu yang menentukan hidup matinya setup, dan semuanya pernah bikin orang kejebak:

Agent wajib ada di allowed_tools. Tanpa itu model tidak punya cara memanggil subagent, jadi konfigurasi agents yang sudah kamu susun rapi tidak pernah jalan, dan tidak ada apa pun di output yang memberi tahu kenapa.

AgentDefinition memakai camelCase untuk nama field (maxTurns, disallowedTools, permissionMode) karena langsung dipetakan ke wire format yang dipakai bareng TypeScript SDK. Menulis max_turns=15 langsung melempar TypeError saat konstruksi. Sementara ClaudeAgentOptions tepat di sebelahnya pakai snake_case. Dua konvensi dalam satu file.

List tools adalah batas keras. Scanner yang cuma pegang Read, Grep, dan Glob tidak bisa menulis ulang source kamu, apa pun isi prompt-nya dan apa pun yang disuruh file yang dia baca.

Satu description per pekerjaan, dengan kondisi pemicunya ditulis jelas. Description kabur seperti "helps with code" menghasilkan agent yang mengerjakan semuanya sendiri.

Langkah 3: Paksa delegasinya, lalu buktikan

Model bisa menilai tugasnya cukup kecil dan dikerjakan sendiri. Untuk menguji sambungannya, sebutkan maunya di prompt:

prompt = (
    "Review the staged changes. "
    "Run security-scanner and test-runner, then merge their findings into one report "
    "with a section per subagent."
)

Jawaban bagus tanpa delegasi terlihat sama persis dengan delegasi yang bagus, kecuali kamu mengukurnya. Daftarkan hook dan catat apa yang benar-benar jalan:

from claude_agent_sdk import HookMatcher


async def on_subagent_start(input_data, tool_use_id, context):
    print(f"[subagent] {input_data['agent_type']} started ({input_data['agent_id']})")
    return {}


async def on_subagent_stop(input_data, tool_use_id, context):
    print(f"[subagent] {input_data['agent_id']} finished")
    return {}


options.hooks = {
    "SubagentStart": [HookMatcher(hooks=[on_subagent_start])],
    "SubagentStop": [HookMatcher(hooks=[on_subagent_stop])],
}

agent_id dan agent_type adalah field wajib di SubagentStart dan SubagentStop versi Python, jadi akses key langsung aman di situ. Di event hook lain keduanya opsional.

Kalau tidak ada baris [subagent] di output, artinya model mempertahankan pekerjaannya sendiri. Pertajam description biar pemicunya tidak ambigu, atau sebut nama subagent-nya di prompt seperti contoh di atas.

Langkah 4: Jalankan subagent independen secara paralel

Review yang independen tidak seharusnya mengantre. Ada dua tuas:

Tool use paralel dalam satu turn. Saat model memanggil dua subagent di turn yang sama, keduanya jalan bersamaan dan waktu totalnya ikut yang paling lambat, bukan jumlah keduanya. Ini perilaku default begitu subagent-nya jelas independen dari description.

Subagent background. Mengisi background di AgentDefinition membuat subagent tidak memblokir, jadi parent bisa lanjut kerja sementara subagent jalan:

"test-runner": AgentDefinition(
    description="Runs the test suite and reports failures.",
    prompt="Run the tests. Report failing test names with the assertion message.",
    tools=["Bash", "Read"],
    background=True,
),

Hasilnya datang sebagai result message yang origin-nya menandai task notification. Berguna diketahui sebelum kamu mencari output di result milik parent dan tidak menemukannya.

Langkah 5: Batasi biaya, lalu baca field yang benar

options = ClaudeAgentOptions(
    max_turns=25,
    max_budget_usd=2.00,
)

max_budget_usd menghentikan query saat estimasi biaya di sisi client melewati ambang itu. Estimasi bisa lewat sedikit, jadi sisakan ruang, jangan pasang tepat di plafon kamu.

Ada dua jebakan akurasi di pelaporannya:

total_cost_usd dihitung lokal dari tabel harga yang dibundel bareng SDK. Pakai untuk budgeting saat development. Jangan menagih siapa pun dari angka itu. Kalau uang benar-benar bergantung pada angkanya, pakai usage and cost API dari provider.

usage di result message hanya mencakup loop agent utama. Biaya subagent masuk ke model_usage. Menjumlahkan usage antar turn akan undercount diam-diam di setiap run yang mendelegasikan, dan itu bikin delegasi terlihat lebih murah daripada kenyataannya waktu kamu review angka sendiri.

Langkah 6: Tentukan kapan delegasi justru rugi

Ini lebih dekat ke aturan daripada preferensi: delegasikan kalau subagent bakal menghasilkan output tool lebih besar daripada ringkasan yang dia kembalikan.

Itu berlaku untuk audit keamanan atas diff 30 file, atau test suite yang gagalnya berhalaman-halaman. Bacaannya lebar, laporannya sempit.

Justru rugi untuk pekerjaan berurutan yang murah. Subagent mulai dengan percakapan kosong kecuali dikonfigurasi sebagai fork dari percakapan yang ada, jadi dia harus menemukan ulang apa yang parent sudah tahu. Untuk edit dua file, biaya menemukan ulang itu lebih besar daripada context yang kamu hemat. Memecah tiga langkah yang saling bergantung ke tiga subagent malah lebih buruk, karena tiap subagent membangun ulang gambaran kodenya sendiri.

Langkah 7: Apa yang ditambahkan berikutnya

Dua tambahan bikin setup ini tahan lama. Pindahkan policy dari prompt ke hook: matcher PreToolUse pada Write|Edit yang menolak path .env, plus deny rule spesifik seperti Bash(rm *) yang tetap berlaku di semua permission mode. Lalu commit CLAUDE.md berisi konvensi repo, supaya tiap subagent mewarisi aturan yang sama daripada tiap prompt mengarang sendiri.

Jaga prompt agent review tetap pendek. Tugasnya routing, dan detailnya tempatnya di prompt subagent, di mana baru dimuat saat subagent itu jalan.

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis