Modul 3: Membangun Agen AGY dengan SDK¶
Durasi: ~90 menit
Tujuan: Membangun agen AGY siap produksi dari awal menggunakan pustaka Python google-antigravity — alat, hook, kebijakan, status sesi, orkestrasi multi-agen, dan keluaran terstruktur.
Latihan: Latihan 10: Agen Pertama Anda · Latihan 11: Pipeline Multi-Agen
📖 Sumber: Ringkasan SDK · google-antigravity PyPI · Skill
Mengapa Membangun Agen Daripada Hanya Menggunakan CLI?¶
CLI adalah asisten serbaguna. Sebuah agen yang Anda bangun dengan SDK adalah seorang spesialis — ia memiliki pekerjaan yang sempit, alat khusus domain, prompt sistem yang direkayasa dengan cermat, dan dapat di-deploy sebagai layanan yang dapat dipanggil oleh seluruh tim Anda.
| Antigravity CLI | Agen SDK AGY | |
|---|---|---|
| Siapa yang menggunakannya | Pengembang individu | Tim / konsumen API |
| Kustomisasi | AGENTS.md + plugin | Kontrol kode penuh |
| Alat | Alat CLI bawaan | Fungsi Python apa pun yang Anda tulis |
| Kebijakan | Prompt persetujuan interaktif | Aturan policy.* terprogram |
| Deployment | Sesi interaktif lokal | Layanan Cloud Run, dapat dipanggil melalui API |
| Multi-agen | Sub-agen dalam sesi CLI | asyncio.gather + START_SUBAGENT |
3.1 — Pengaturan SDK 10 min¶
Prasyarat¶
- Python 3.11+
- Kunci API Gemini — tetapkan sebagai
GEMINI_API_KEYatau teruskan melaluiapi_key=di konfigurasi
Instalasi¶
Verifikasi¶
from google.antigravity import Agent, LocalAgentConfig
from google.antigravity.hooks import policy
print("google-antigravity installed ✅")
Kunci API vs Vertex AI: Untuk pengembangan lokal yang cepat, gunakan
api_key="AIza..."diLocalAgentConfig. Untuk produksi di GCP, autentikasi dengangcloud auth application-default login— pustaka akan mengambil ADC secara otomatis.
3.2 — Primitif Inti: Agen, Konfigurasi, Alat 20 mnt¶
SDK google-antigravity memiliki tiga blok penyusun: Agent, LocalAgentConfig, dan alat (fungsi Python biasa). Pelajari ini dan Anda dapat membangun apa saja.
Alat¶
Alat adalah fungsi Python biasa. Tanpa kelas pembungkus, tanpa dekorator. Agen memutuskan kapan harus memanggilnya berdasarkan docstring — itulah keseluruhan kontrak antarmuka.
def get_file_contents(file_path: str) -> str:
"""Read and return the contents of a file at the given path.
Args:
file_path: Absolute or relative path to the file.
Returns:
The file contents as a string, or an error message if not found.
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return f"Error: File not found at {file_path}"
Aturan penting untuk alat:
- Gunakan anotasi tipe eksplisit —
str,int,bool,list[str]. Jangan gunakantyping.Optional.- Gunakan nilai default
Noneuntuk parameter opsional:param: str = None- Docstring adalah skema alat — model membacanya untuk memutuskan kapan dan bagaimana memanggil alat tersebut. Tulis ini untuk model, bukan untuk manusia.
- Jaga agar alat tetap sempit dan fokus. Satu pekerjaan per alat.
Alat dengan Status Sesi¶
Untuk membaca/menulis status sesi di dalam alat, deklarasikan parameter dengan tipe ToolContext. SDK mendeteksinya secara otomatis, menyuntikkannya pada saat pemanggilan, dan menghapusnya dari skema yang ditampilkan ke model:
from google.antigravity.tools.tool_context import ToolContext
def record_finding(
severity: str,
message: str,
ctx: ToolContext,
) -> dict:
"""Records a review finding into session state.
Args:
severity: One of 'critical', 'warning', 'info'.
message: Description of the finding.
Returns:
Confirmation dict with the finding index.
"""
findings = ctx.get_state("findings", [])
findings.append({"severity": severity, "message": message})
ctx.set_state("findings", findings)
return {"status": "recorded", "index": len(findings) - 1}
Agen + Konfigurasi¶
Agent adalah titik masuk tunggal. Semua konfigurasi masuk ke dalam LocalAgentConfig:
import asyncio
from google.antigravity import Agent, LocalAgentConfig
from google.antigravity.hooks import policy
config = LocalAgentConfig(
model="gemini-3.5-flash",
system_instructions="""You are a code reviewer specialising in Python.
When given a file path, read the file and provide a structured review covering:
- Correctness and edge cases
- Code style and readability
- Security concerns
- Suggested improvements
Always read the file first before commenting. Be specific — cite line numbers.
""",
tools=[get_file_contents, record_finding],
policies=[policy.allow_all()], # autonomous — no interactive prompts
workspaces=["/path/to/project"], # file ops scoped to this directory
)
async def main():
async with Agent(config) as agent:
response = await agent.chat("Review src/auth/login.py")
print(await response.text())
asyncio.run(main())
async with Agent(config) as agent:— selalu gunakan manajer konteks. Ini memulai jembatan runtime Go (bin/localharness) dan menghentikannya dengan bersih saat keluar.
Pemilihan Model¶
Sesuaikan model dengan pekerjaannya. Kebijakan sadar biaya:
| Peran | Model | Alasan |
|---|---|---|
| Tugas umum, tinjauan kode | gemini-3.5-flash | Default SDK — hemat biaya, cepat |
| Orkestrasi, perutean, perencanaan | gemini-3.1-pro-preview | Penalaran kompleks, keputusan multi-langkah |
| Tugas pembuatan gambar | gemini-3.1-flash-image-preview | Default SDK untuk pembuatan gambar |
| Analisis berisiko tinggi | gemini-3.1-pro-preview dengan ThinkingLevel.HIGH | Penalaran mendalam untuk kepatuhan/keamanan |
Jangan pernah gunakan
gemini-1.5-flash,gemini-1.5-pro. Sudah usang.
Skill¶
Skill adalah file SKILL.md yang dimuat pada saat runtime untuk menyuntikkan pengetahuan domain. Jaga agar prompt sistem Anda tetap ringkas — muat keahlian dari file:
from pathlib import Path
import re
def load_skill(skill_name: str) -> str:
"""Load a SKILL.md and strip YAML frontmatter."""
skill_path = Path("skills") / skill_name / "SKILL.md"
if skill_path.exists():
content = skill_path.read_text(encoding="utf-8")
return re.sub(r'^---\n.*?---\n', '', content, flags=re.DOTALL).strip()
return ""
review_guidelines = load_skill("python-review")
config = LocalAgentConfig(
model="gemini-3.5-flash",
system_instructions=f"""You are a code reviewer.
## Review Guidelines
{review_guidelines}
""",
tools=[get_file_contents],
policies=[policy.allow_all()],
)
Skill juga dapat dimuat secara native melalui LocalAgentConfig(skills_paths=["/path/to/skills/"]) — SDK menemukan file SKILL.md secara otomatis.
3.3 — Kebijakan dan Keamanan 10 min¶
Kebijakan adalah hal pertama yang Anda konfigurasikan — ini mengontrol apa yang diizinkan untuk dilakukan oleh agen tanpa persetujuan manusia. Setiap LocalAgentConfig membutuhkan daftar policies=:
from google.antigravity.hooks import policy
# Fully autonomous — approve all tool calls (use for trusted, sandboxed agents)
policies=[policy.allow_all()]
# Default behaviour — ask user before running shell commands, allow everything else
policies=[policy.confirm_run_command()]
# Fine-grained rules (evaluated in order, first match wins)
async def approval_handler(tool_call) -> bool:
answer = input(f"Allow {tool_call.name}? [y/N]: ")
return answer.lower() == "y"
policies=[
policy.deny("run_command"), # never run shell commands
policy.allow("view_file"), # always allow reading
policy.ask_user("edit_file", handler=approval_handler), # ask before every write
policy.allow("*"), # allow everything else
]
# Conditional deny — block dangerous patterns
policy.deny("run_command", when=lambda args: "rm -rf" in args.get("CommandLine", ""))
# Scope file operations to a specific directory
policy.workspace_only(["/path/to/project"])
Urutan prioritas:
specific_deny>specific_ask>specific_allow>wildcard_deny>wildcard_ask>wildcard_allow
3.4 — Hook: Observabilitas dan Kontrol 10 min¶
Hook memungkinkan Anda mencegat dan bereaksi terhadap setiap peristiwa dalam siklus hidup agen — untuk pencatatan, audit, pagar pengaman, atau alur persetujuan kustom:
from google.antigravity.hooks import hooks
from google.antigravity.types import ToolCall, ToolResult, HookResult
# Block dangerous tool calls BEFORE they execute
@hooks.pre_tool_call_decide
async def security_guard(tool_call: ToolCall) -> HookResult:
if tool_call.name == "run_command":
cmd = tool_call.args.get("CommandLine", "")
if any(danger in cmd for danger in ["rm -rf", "drop table", "DELETE FROM"]):
return HookResult(allow=False, message=f"Blocked dangerous command: {cmd}")
return HookResult(allow=True)
# Log all tool completions (non-blocking, read-only)
@hooks.post_tool_call
async def audit_logger(tool_result: ToolResult) -> None:
print(f"[AUDIT] tool={tool_result.name} success={tool_result.success}")
# Initialise state when a session begins
@hooks.on_session_start
async def initialise_state() -> None:
print("[AGENT] Session started — ready.")
config = LocalAgentConfig(
hooks=[security_guard, audit_logger, initialise_state],
policies=[policy.allow_all()],
model="gemini-3.5-flash",
system_instructions="You are a code reviewer.",
tools=[get_file_contents],
)
Jenis-jenis hook:
| Hook | Memblokir eksekusi | Memodifikasi data | Digunakan untuk |
|---|---|---|---|
@hooks.pre_tool_call_decide | Ya | Tidak | Menyetujui/menolak pemanggilan alat |
@hooks.post_tool_call | Tidak | Tidak | Pencatatan, metrik |
@hooks.pre_turn | Tidak | Tidak | Pencatatan tingkat giliran |
@hooks.post_turn | Tidak | Tidak | Pencatatan respons |
@hooks.on_session_start/end | Tidak | Tidak | Pengaturan/pembongkaran |
@hooks.on_tool_error | Ya | Ya | Pemulihan kesalahan |
3.5 — Orkestrasi Multi-Agen 15 menit¶
google-antigravity tidak memiliki kelas SequentialAgent atau ParallelAgent. Multi-agen dilakukan dengan dua cara: digerakkan oleh model (biarkan agen memunculkan sub-agen) atau digerakkan oleh Python (Anda mengorkestrasi instans Agent secara langsung).
Pola A — Sub-agen Digerakkan oleh Model¶
Aktifkan START_SUBAGENT dalam kapabilitas. Model memanggilnya ketika memutuskan untuk mendelegasikan:
from google.antigravity.types import BuiltinTools, CapabilitiesConfig
config = LocalAgentConfig(
capabilities=CapabilitiesConfig(
enable_subagents=True,
enabled_tools=BuiltinTools.all_tools(),
),
policies=[policy.allow_all()],
model="gemini-3.1-pro-preview",
system_instructions="""You are an engineering lead.
For complex tasks, spawn focused subagents to handle each part in parallel.
Synthesise their outputs into a final summary.""",
)
Pola B — Pipeline Sekuensial (Digerakkan oleh Python)¶
Teruskan output dari satu agen sebagai input ke agen berikutnya:
async def sequential_review(file_path: str):
# Step 1 — read and summarise the file
async with Agent(reader_config) as reader:
r1 = await reader.chat(f"Read and summarise {file_path}")
summary = await r1.text()
# Step 2 — security audit using the summary
async with Agent(security_config) as auditor:
r2 = await auditor.chat(f"Security audit this code summary:\n\n{summary}")
report = await r2.text()
return report
Pola C — Analisis Paralel¶
Jalankan agen independen secara bersamaan dengan asyncio.gather:
async def parallel_analysis(file_path: str):
async with (
Agent(security_config) as security_agent,
Agent(style_config) as style_agent,
Agent(perf_config) as perf_agent,
):
results = await asyncio.gather(
security_agent.chat(f"Security review: {file_path}"),
style_agent.chat(f"Style review: {file_path}"),
perf_agent.chat(f"Performance review: {file_path}"),
)
texts = await asyncio.gather(*[r.text() for r in results])
return {
"security": texts[0],
"style": texts[1],
"perf": texts[2],
}
Kapan menggunakan paralel: Kapan pun Anda memiliki N analisis independen. Ini memangkas waktu nyata sebesar 60–80% dibandingkan dengan menjalankannya secara sekuensial.
3.6 — Streaming dan Output Terstruktur 5 min¶
Respons Streaming¶
async with Agent(config) as agent:
response = await agent.chat("Write a detailed security report...")
# Stream text deltas as they arrive
async for delta in response:
print(delta, end="", flush=True)
# Stream reasoning/thinking (if thinking enabled)
async for thought in response.thoughts:
print(f"[thinking] {thought}")
Output Terstruktur¶
Ikat output agen ke skema Pydantic:
import asyncio
import pydantic
from google.antigravity import Agent, LocalAgentConfig
from google.antigravity.hooks import policy
class ReviewResult(pydantic.BaseModel):
issues: list[str]
severity: str # 'critical' | 'warning' | 'info'
recommendation: str
config = LocalAgentConfig(
response_schema=ReviewResult,
system_instructions="Analyse the code and return structured output via the finish tool.",
policies=[policy.allow_all()],
model="gemini-3.5-flash",
tools=[get_file_contents],
)
async def main():
async with Agent(config) as agent:
response = await agent.chat("Review src/auth/login.py")
result = await response.structured_output() # dict matching ReviewResult schema
print(result["severity"], result["issues"])
asyncio.run(main())
3.7 — Melanjutkan Sesi dan Persistensi 5 min¶
# First session — save the conversation ID
async with Agent(config) as agent:
await agent.chat("Analyse this codebase and build a mental model.")
conv_id = agent.conversation_id # persist this
# Later session — resume exactly where you left off
resume_config = LocalAgentConfig(
conversation_id=conv_id,
save_dir="/path/where/first/session/was/saved",
model="gemini-3.5-flash",
policies=[policy.allow_all()],
)
async with Agent(resume_config) as agent:
await agent.chat("Now suggest the top 3 refactoring priorities.")
3.8 — Pemicu: Agen Latar Belakang Otonom 5 menit¶
import asyncio
from google.antigravity import Agent, LocalAgentConfig
from google.antigravity.hooks import policy
from google.antigravity.triggers import every, on_file_change, TriggerContext
# Poll every 60 seconds
async def check_for_new_issues(ctx: TriggerContext) -> None:
await ctx.send("Scan the repo for any new TODO comments added since last run.")
# React to file changes
async def on_code_change(ctx: TriggerContext, changes) -> None:
paths = [c.path for c in changes]
await ctx.send(f"Files changed: {paths}. Run quick security check.")
config = LocalAgentConfig(
triggers=[
every(60.0, check_for_new_issues),
on_file_change("/path/to/src", on_code_change),
],
policies=[policy.allow_all()],
model="gemini-3.5-flash",
system_instructions="You are a background code monitor.",
)
async def main():
# The agent runs indefinitely, responding to triggers
async with Agent(config) as agent:
await asyncio.Event().wait() # keep alive
asyncio.run(main())
3.9 — Konvensi Struktur Proyek 5 min¶
Strukturkan proyek agen Anda untuk kemudahan pemeliharaan:
my_agent/
├── main.py # entry point — asyncio.run(main())
├── config.py # LocalAgentConfig construction
├── tools/
│ ├── __init__.py
│ ├── file_reader.py # one tool per file
│ └── search_tool.py
├── hooks/
│ ├── __init__.py
│ └── security_guard.py # pre_tool_call_decide hooks
├── skills/
│ └── domain-expertise/
│ └── SKILL.md # portable skill packs
├── tests/
│ ├── test_file_reader.py
│ └── test_search_tool.py
├── requirements.txt # google-antigravity + deps
└── README.md
Penerapan ke Cloud Run¶
Terapkan sebagai aplikasi asinkronus Python standar:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "main.py"]
gcloud run deploy my-code-reviewer \
--source . \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_REGION \
--allow-unauthenticated
Tips: Tetapkan
GOOGLE_CLOUD_PROJECTdanGOOGLE_CLOUD_REGION(misalnyaus-central1) sebelum menjalankan.
Latihan Praktik¶
Latihan 10: Agen AGY Pertama Anda¶
Berkas: ex10_first_agent.md
Durasi: 45 menit
Bangun: Sebuah Agen Tinjauan Kode yang membaca berkas, mengidentifikasi masalah, dan menghasilkan laporan tinjauan terstruktur.
Apa yang akan Anda implementasikan:
- Tentukan 3 alat:
read_file,list_directory,record_finding(denganToolContext) - Tulis prompt sistem dengan rubrik tinjauan (dimuat dari sebuah
SKILL.md) - Konfigurasikan
LocalAgentConfigdenganpolicy.allow_all()danCapabilitiesConfig - Tambahkan penjaga keamanan
@hooks.pre_tool_call_decide - Jalankan dengan keluaran streaming dan skema Pydantic
ReviewResultterstruktur
Latihan 11: Pipeline Multi-Agen¶
Berkas: ex11_multi_agent_pipeline.md
Durasi: 45 menit
Bangun: Sebuah Pipeline Tulis-lalu-Audit — agen Penulis Teknis menghasilkan dokumen, kemudian Analis Kepatuhan mengauditnya untuk celah GDPR.
Apa yang akan Anda implementasikan:
- Bangun agen
technical_writerdengan SKILL.md GDPR yang dimuat melaluiskills_paths - Bangun agen
compliance_analystdenganresponse_schema=ComplianceReport - Hubungkan secara berurutan: keluaran dari penulis diteruskan sebagai masukan ke analis
- Tambahkan varian paralel menggunakan
asyncio.gatheruntuk draf + pemeriksaan hukum secara bersamaan - Tambahkan pelanjutan sesi: analis membaca
conversation_idpenulis untuk memuat konteks - Terapkan ke Cloud Run sebagai
my-pipelinemenggunakangcloud run deploy
Ringkasan: Blok Pembangun SDK¶
| Primitif | Apa yang Dilakukannya | Kapan Digunakan |
|---|---|---|
Agent | Agen LLM tunggal dengan alat, hook, kebijakan | Inti — setiap agen dimulai di sini |
LocalAgentConfig | Semua konfigurasi di satu tempat (model, alat, kebijakan, hook) | Selalu |
tools=[fn] | Callable Python biasa, docstring adalah skemanya | Operasi eksternal apa pun |
ToolContext | Baca/tulis status yang disuntikkan ke dalam alat | Alat stateful dalam pipeline |
policy.allow_all() | Menyetujui semua panggilan alat secara otonom | Agen yang tepercaya dan di-sandbox |
policy.deny("run_command") | Memblokir jenis alat tertentu | Pagar pengaman keselamatan |
@hooks.pre_tool_call_decide | Memblokir/menyetujui panggilan alat sebelum eksekusi | Penjaga keamanan |
@hooks.post_tool_call | Mengamati panggilan alat yang selesai | Pencatatan audit |
response_schema= | Mengikat output ke skema Pydantic | Ekstraksi data terstruktur |
async for delta in response: | Melakukan streaming teks saat tiba | Pembuatan bentuk panjang |
asyncio.gather(...) | Menjalankan agen secara paralel | Analisis independen |
every(60, handler) | Memicu agen pada interval | Monitor latar belakang |
on_file_change(path, fn) | Memicu agen pada peristiwa sistem file | Pengamat kode langsung |
skills_paths=[...] | Memuat file SKILL.md pada saat runtime | Keahlian domain portabel |
conversation_id= | Melanjutkan sesi sebelumnya | Alur kerja multi-sesi |
Langkah Selanjutnya¶
→ Lanjutkan ke Modul 4: Multi-Agen & Pola Lanjutan
→ Referensi: Lembar Contekan — semua perintah di satu tempat