7
Sistem Multi-Agen
Guardian tidak menjalankan satu model besar, melainkan empat agen dengan peran sempit yang berbagi satu objek PentestMemory. WorkflowEngine membuat keempat agen itu sekali di awal sesi dan memberi mereka satu GeminiClient serta satu memory yang sama core/workflow.py:41-501. Semua agen mewarisi BaseAgent, yang mewajibkan execute dan menyediakan dua mode penalaran core/agent.py:16-362.
Halaman ini membahas lapisan agen dan memori. Orkestrasi langkah workflow, termasuk pemilihan type dan agent per langkah, ada di Engine Workflow DSL v2. Pemilihan tool beserta registry dan kelas risiko ada di Integrasi Tool dan Pemilihan Tool.
BaseAgent dan dua mode penalaran
think adalah satu ronde panggilan ke gemini.generate_with_usage, lalu hasilnya dicatat lewat _record_usage_and_step dan dikembalikan apa adanya core/agent.py:40-563. think_deeply menjalankan hingga max_rounds ronde: ronde pertama menjawab, ronde berikutnya mengkritik jawaban sebelumnya dan meminta perbaikan core/agent.py:62-914. Jika judge_model tersedia, satu provider terpisah membaca seluruh transkrip dan memilih ronde terbaik; jawaban yang dipilih menggantikan response, sementara ronde yang kalah tetap disimpan di thinking_chain untuk audit, dan total_tokens ikut menghitung panggilan judge core/agent.py:142-1725.
Setiap panggilan menulis empat jejak sekaligus lewat _record_usage_and_step: satu TokenUsage ke token_ledger, satu ThinkingStep ke thinking_chain, satu entri ai_decisions, dan satu baris audit log berisi potongan prompt, token, biaya, model, serta nomor ronde core/agent.py:267-3016.
⚠️
thinkdanthink_deeplymenagih setiap panggilan model.think_deeplydenganmax_roundsbesar plus judge pass berarti biaya berlipat pada satu keputusan; angkatotal_cost_usdbaru terlihat setelah semua ronde selesai.
Perpindahan peran dari rencana sampai temuan tersimpan
Diagram berikut menunjukkan satu putaran kerja penuh. Perhatikan bahwa PentestMemory menjadi satu-satunya penghubung antar agen: PlannerAgent dan ToolAgent tidak saling memanggil, keduanya membaca dan menulis ke memori yang sama.
sequenceDiagram
participant P as PlannerAgent
participant T as ToolAgent
participant A as AnalystAgent
participant D as DebateTriage
participant R as ReporterAgent
participant M as PentestMemory
P->>M: get_context_for_ai
P->>P: decide_next_action
P->>M: add_ai_decision + add_thinking_step
T->>M: baca tool_executions sebelumnya
T->>T: execute objective dan target
A->>A: interpret_output tool dan output
A->>M: add_finding
D->>M: baca findings dengan fp prob MEDIUM
D->>D: triage red lalu blue lalu judge
D->>M: tulis ulang severity dan false_positive
R->>M: get_findings_summary
R->>R: execute format markdown html json
%% fig: Siklus satu putaran kerja empat agen
%% desc: PlannerAgent memutuskan langkah, ToolAgent memilih tool, AnalystAgent menyimpan temuan, triase menyesuaikan temuan, lalu ReporterAgent membaca hasil akhir dari PentestMemory yang sama.Empat agen inti
| Agen | Kelas dan metode masuk | Masukan utama | Keluaran |
|---|---|---|---|
| Perencana | PlannerAgent.decide_next_action dan analyze_results | memory.get_context_for_ai, ringkasan finding, aksi per fase | next_action, parameters, expected_outcome, reasoning |
| Pemilih tool | ToolAgent.execute dan configure_tool | objective, target, tipe target, safe_mode, rate_limit, tool_timeout | tool, arguments, expected_output, reasoning |
| Analis | AnalystAgent.interpret_output, correlate_findings, check_false_positive | output mentah tool, temuan sebelumnya, teknologi terdeteksi | daftar Finding, summary, reasoning |
| Pelapor | ReporterAgent.execute | get_findings_summary, temuan per severity, thinking_chain | konten laporan markdown, html, atau json |
PlannerAgent menyusun prompt dari konteks sesi, membungkus ringkasan finding, attack surface, dan teknologi dengan wrap_untrusted karena berasal dari output tool, lalu memanggil think dan mem-parse hasilnya core/planner.py:29-697. Daftar aksi yang boleh dipilih dibatasi per fase, misalnya reconnaissance hanya menawarkan subdomain_enumeration, dns_enumeration, technology_detection, dan port_scanning core/planner.py:113-1448.
ToolAgent mencoba ranker offline lebih dulu. Jika ranker yakin, agen langsung mengembalikan pilihan tanpa memanggil model sama sekali core/tool_agent.py:239-2639. Jika ranker abstain, agen menyusun prompt pemilihan tool dari registry yang terpasang dan batasan konfigurasi, lalu memanggil think core/tool_agent.py:283-30910. Instans tool diimpor secara lazy dari spesifikasi module:Class core/tool_agent.py:157-16211, dan registry digabung dengan plugin yang mendaftar di entry point guardian.tools; nama in-tree menang atas plugin core/tool_agent.py:169-20512.
AnalystAgent.interpret_output memotong output yang melebihi 5000 karakter, menyertakan sepuluh temuan terakhir dan teknologi terdeteksi, membungkus output tool sebagai data tak terpercaya, lalu memanggil think core/analyst_agent.py:58-10413. Setiap Finding hasil parsing beserta execution_id dan potongan output mentah langsung ditambahkan ke memori core/analyst_agent.py:106-11714. Perilaku ini dituntun system prompt yang menetapkan peran analis sebagai gerbang mutu: temuan wajib bersandar pada kutipan bukti, severity mengikuti CVSS 3.1, dan heuristik false positive harus diterapkan ai/prompt_templates/analyst.py:11-1915.
ReporterAgent.execute membangun empat bagian berurutan, yaitu executive summary, technical findings, remediation plan, dan AI trace, lalu merakitnya sesuai format; format di luar tiga pilihan yang dikenal memunculkan ValueError core/reporter_agent.py:27-7616. Rating risiko keseluruhan bukan hasil model, melainkan turunan hitungan temuan: ada critical maka CRITICAL, lebih dari tiga high maka HIGH, ada high atau lebih dari lima medium maka MEDIUM, sisanya LOW core/reporter_agent.py:78-11717. Bagian AI trace dibangun dari ai_decisions dan thinking_chain core/reporter_agent.py:148-16918.
PentestMemory sebagai satu-satunya state
PentestMemory diinisialisasi dengan target dan session_id, lalu menyiapkan fase, completed_actions, tiga penyimpanan inti (findings, tool_executions, ai_decisions), serta token_ledger dan thinking_chain core/memory.py:85-12119.
| Kelompok state | Field | Penulis |
|---|---|---|
| Temuan | findings berisi Finding | add_finding |
| Eksekusi tool | tool_executions berisi ToolExecution | add_tool_execution |
| Keputusan | ai_decisions | add_ai_decision |
| Biaya | token_ledger berisi TokenUsage | add_token_usage |
| Penalaran | thinking_chain berisi ThinkingStep | add_thinking_step |
| Konteks | context, threat_model, attack_surface | update_context, set_threat_model, add_attack_surface_entry |
Mutator untuk ledger, rantai penalaran, threat model, dan attack surface tercantum lengkap di core/memory.py:144-15920. get_token_summary menjumlahkan token dan biaya, lalu mengelompokkannya per provider, per model, dan per agent dengan menghitung jumlah panggilan core/memory.py:163-20221. get_context_for_ai merakit blok teks yang dibaca semua agen: target, fase, aksi selesai, ringkasan finding per severity, aset yang ditemukan, attack surface, potongan threat model maksimum 600 karakter, tiga langkah penalaran terakhir, dan pemakaian token sejauh ini core/memory.py:257-32522. save_state menulis seluruh state termasuk token_summary ke JSON, sehingga sesi bisa dilanjutkan core/memory.py:329-35023.
Schema Pydantic dan jalur penolakan
Schema di core/schemas.py menggantikan parser string yang rapuh: agen yang menghasilkan keluaran terstruktur harus lolos validasi, dan yang tidak sesuai ditolak, bukan dipaksa cocok core/schemas.py:31-3824.
| Model | Dipakai oleh | Aturan yang menolak keluaran |
|---|---|---|
PlannerDecision | PlannerAgent._parse_decision | next_action wajib ada, maksimum 64 karakter, dan harus termasuk KNOWN_ACTIONS |
ToolSelection | ToolAgent.execute | tool harus ada di TOOL_REGISTRY |
FindingModel | AnalystAgent.interpret_output | severity salah satu dari lima nilai, cvss_score antara 0.0 dan 10.0 |
AnalysisResult | hasil agregat satu eksekusi tool | findings, summary, missed_checks |
FalsePositiveVerdict | evaluasi false positive | confidence 0 sampai 100, recommendation salah satu dari KEEP, DISCARD, VERIFY_MANUALLY |
PlannerDecision memvalidasi next_action lewat validator yang menurunkan huruf dan mencocokkannya dengan whitelist core/schemas.py:52-7225, sedangkan whitelist aksi sendiri didefinisikan sekali di core/schemas.py:31-3824. ToolSelection mengambil nama tool yang sah langsung dari TOOL_REGISTRY core/schemas.py:75-92. FindingModel membatasi cvss_score pada rentang 0.0 sampai 10.0 dan menormalkan severity ke huruf kecil core/schemas.py:95-119. parse_or_none mengembalikan objek tervalidasi atau None tanpa melempar exception, sehingga pemanggil bisa mencoba ulang dengan prompt lebih ketat core/schemas.py:148-165. schema_for_prompt membuang kunci title agar schema bisa dipakai sebagai response_format, response_schema, atau input_schema core/schemas.py:168-176.
PlannerAgent._parse_decision mencoba JSON ketat lebih dulu; bila gagal, ia jatuh ke ekstraksi header NEXT_ACTION:, PARAMETERS:, dan EXPECTED_OUTCOME: dengan pembersihan karakter kontrol dan pencocokan whitelist core/planner.py:146-20230.
Triase debat: tiga peran untuk temuan ambigu
Triase debat hanya menyentuh temuan yang ditandai false_positive_probability bernilai MEDIUM. Temuan yang sudah yakin, yaitu LOW dianggap nyata dan HIGH dianggap false positive, melewati debat dan mengembalikan DebateVerdict dengan triggered=False core/agents/debate_triage.py:91-11031.
graph LR
F["Finding dari AnalystAgent"] --> X["_extract_fp_probability"]
X -->|LOW| R1["REAL, triggered=false"]
X -->|HIGH| R2["FALSE_POSITIVE, triggered=false"]
X -->|MEDIUM| RA["_RedAdvocate.think"]
RA --> BA["_BlueAdvocate.think"]
BA --> J["_Judge.think"]
J --> V["_parse_verdict"]
V -->|JSON valid| OK["verdict + adjusted_severity"]
V -->|JSON tidak terbaca| VM["VERIFY_MANUALLY, confidence=50"]
style F fill:#8b5cf6,color:#fff
style R1 fill:#2d7d46,color:#fff
style R2 fill:#dc2626,color:#fff
style VM fill:#f59e0b,color:#000
style OK fill:#2d7d46,color:#fff
%% fig: Percabangan triase debat
%% desc: Hanya temuan berprobabilitas false positive MEDIUM yang masuk debat red, blue, dan judge; verdict yang tidak terbaca turun ke VERIFY_MANUALLY.Tiga peran dijalankan berurutan dengan think terpisah tetapi satu client yang sama: red advocate membela bahwa temuan nyata, blue advocate membela bahwa temuan false positive, lalu judge menerima kedua argumen yang dibungkus wrap_untrusted dan mengeluarkan verdict beserta severity yang disesuaikan core/agents/debate_triage.py:111-16632. Hasilnya adalah dataclass DebateVerdict berisi finding_id, verdict, adjusted_severity, rationale, confidence, kedua argumen, dan penanda triggered core/agents/debate_triage.py:44-5533.
Sinyal pra-debat dibaca dari baris False_Positive: <LOW|MEDIUM|HIGH> di deskripsi finding; bila tidak terbaca, nilainya MEDIUM sehingga debat tetap dijalankan core/agents/debate_triage.py:170-18334. Verdict judge divalidasi terhadap himpunan nilai yang sah, dan bila JSON tidak terbaca hasilnya VERIFY_MANUALLY dengan confidence 50 karena menaikkan ke manusia lebih baik daripada menebak core/agents/debate_triage.py:202-24135.
Di dalam workflow, langkah analysis dengan agent: debate mengumpulkan semua finding MEDIUM, menjalankan triage satu per satu, lalu menerapkan hasilnya ke finding di memori: FALSE_POSITIVE menyalakan false_positive, severity diganti, dan rationale ditambahkan ke description core/workflow.py:338-37736.
⚠️ Satu langkah debat memanggil model tiga kali untuk setiap finding
MEDIUM. Pada sesi dengan banyak temuan ambigu, langkah ini menjadi sumber biaya terbesar setelah analisis awal.
Triase visual: bukti gambar untuk temuan
VisualTriage memperkaya temuan dengan deskripsi yang berasal dari gambar, bukan menggantinya. Alur kerjanya berhenti lebih awal bila syaratnya tidak terpenuhi.
graph TD
A["_active_provider"] --> B{"supports_vision?"}
B -->|tidak| S1["skipped_reason: active provider has no vision support"]
B -->|ya| C["_collect_screenshots"]
C --> D{"ada screenshot?"}
D -->|tidak| S2["skipped_reason: no screenshots in memory"]
D -->|ya| E["_best_finding_for_url"]
E -->|tidak cocok| SKIP["screenshot dilewati"]
E -->|cocok| F["_enrich_one"]
F --> G["_apply_enrichment"]
G --> H["description + raw_evidence"]
style S1 fill:#f59e0b,color:#000
style S2 fill:#f59e0b,color:#000
style SKIP fill:#dc2626,color:#fff
style H fill:#336791,color:#fff
%% fig: Alur triase visual
%% desc: Provider tanpa dukungan vision atau memori tanpa screenshot membuat langkah ini dilewati tanpa kehilangan temuan tekstual, sedangkan temuan yang cocok menerima tambahan deskripsi dan path gambar.triage_findings mengembalikan {"enrichments": [], "skipped_reason": ...} ketika provider aktif tidak mendukung vision atau ketika tidak ada screenshot di memori; pemanggil mencatat alasan itu dan melanjutkan core/agents/visual_triage.py:59-10037. Provider aktif dicari dengan memeriksa atribut provider, active_provider, dan _provider pada client, lalu jatuh ke client itu sendiri bila ia mengimplementasikan supports_vision core/agents/visual_triage.py:104-11938.
Pencocokan gambar ke temuan berlangsung dua tahap: host URL dicocokkan dengan finding.target, dan bila tidak ada yang cocok, temuan pertama dari tool web seperti httpx, nuclei, atau whatweb dipakai core/agents/visual_triage.py:152-18339. _apply_enrichment menambahkan blok [Visual Triage — page_type=..., supports=..., conf=...] beserta indikator yang terlihat ke description, lalu menambahkan baris screenshot: ke raw_evidence agar lampiran laporan tetap menyimpan path gambar core/agents/visual_triage.py:234-24840. Langkah ini terpasang di workflow sebagai langkah analysis dengan agent: visual core/workflow.py:379-39741.
Biaya dan keterlacakan
Karena setiap panggilan think menulis ke token_ledger dan thinking_chain, dua hal bisa dijawab tanpa instrumentasi tambahan. Pertama, get_token_summary memberi total token dan biaya per provider, model, dan agen core/memory.py:163-20221. Kedua, ReporterAgent.generate_ai_trace merangkum ai_decisions dan thinking_chain menjadi bagian laporan yang bisa diaudit, termasuk nomor langkah, agen, dan nomor ronde core/reporter_agent.py:148-16918. Efek sampingnya, semua ronde think_deeply dan panggilan judge ikut terlihat di laporan, sehingga keputusan yang mahal tidak tersembunyi.