Lompat ke konten
dokumentasi.id
Commit 51864a8
v1
Star1,9 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

9

Knowledge Base RAG

core/knowledge_base.py adalah lapisan retrieval RAG dengan SQLite murni dari stdlib. Korpusnya berisi metadata CVE, CWE, MITRE ATT&CK, dan nuclei template, dan tujuannya agar Analyst berhenti mengarang referensi CVE. FTS5 mengerjakan pencarian leksikal; kolom embedding opsional mempercepat pencocokan semantik fuzzy ketika extra sentence-transformers terpasang. core/knowledge_base.py:1-42

Basis data default ada di ~/.guardian/kb/kb.sqlite3. Direktori induk dibuat saat KnowledgeBase diinstansiasi, dan _connect menerapkan _SCHEMA pada koneksi pertama. core/knowledge_base.py:56 core/knowledge_base.py:165-177

Dua dataclass membentuk antarmuka data: KBEntry untuk satu record dan KBHit untuk satu hasil retrieval. KBHit.score selalu dinormalkan ke higher-is-better, sehingga pemanggil tidak perlu tahu jalur mana yang menghasilkan skor. core/knowledge_base.py:74-84

Halaman ini hanya membahas penyimpanan dan retrieval. Pemakaian KB oleh agen dijelaskan di Sistem Multi-Agen; pembungkusan hasil retrieval sebagai untrusted input dijelaskan di Keamanan, Scope, dan Sanitasi; daftar seluruh perintah ada di Referensi Perintah CLI.

Skema: entries, fts, dan embeddings

erDiagram
    ENTRIES {
        TEXT id PK
        TEXT kind
        TEXT title
        TEXT summary
        TEXT severity
        REAL cvss
        TEXT cwe
        TEXT refs
        TEXT updated
    }
    FTS {
        TEXT id
        TEXT title
        TEXT summary
        TEXT refs
    }
    EMBEDDINGS {
        TEXT id PK
        TEXT vec
    }
    ENTRIES ||--|| FTS : "content=entries"
    ENTRIES ||--o| EMBEDDINGS : "id references entries"
%% fig: Skema tiga tabel knowledge base
%% desc: entries menyimpan record kanonik, fts adalah virtual table FTS5 yang mengindeks title/summary/refs, dan embeddings menyimpan vektor float32 opsional per id.
Fig. 1: Skema tiga tabel knowledge baseentries menyimpan record kanonik, fts adalah virtual table FTS5 yang mengindeks title/summary/refs, dan embeddings menyimpan vektor float32 opsional per id.

entries memakai id TEXT PRIMARY KEY dan index idx_entries_kind pada kolom kind. fts adalah virtual table FTS5 dengan content='entries', content_rowid='rowid', dan tokenizer porter. Tiga trigger — entries_ai, entries_ad, entries_au — menjaga isi fts tetap sinkron pada insert, delete, dan update. core/knowledge_base.py:88-127

embeddings hanya diisi bila extra terpasang. upsert_embedding memak vektor sebagai float32 little-endian lewat struct.pack, lalu menyimpannya dengan INSERT OR REPLACE. core/knowledge_base.py:236-243

⚠️ _connect memanggil sqlite3.connect(self.path) tanpa PRAGMA foreign_keys = ON, sehingga klausa ON DELETE CASCADE pada embeddings hanya berlaku bila pragma itu diaktifkan lebih dulu. Menghapus baris entries tidak otomatis membersihkan embedding-nya. core/knowledge_base.py:172-177

has_corpus(kind) memberi cek murah sebelum corpus rebuild, sedangkan stats() mengembalikan jumlah baris per kind untuk guardian kb status. core/knowledge_base.py:186-198

Jalur tulis: upsert

graph LR
    A["Iterable[KBEntry]"] --> B["KnowledgeBase.upsert"]
    B --> C["susun rows: title 500, summary 5000, refs 20"]
    C --> G{"len(rows) == 0?"}
    G -->|"ya"| H["return 0"]
    G -->|"tidak"| D["INSERT OR REPLACE INTO entries"]
    D --> E["trigger entries_ai / entries_au"]
    E --> F[("fts FTS5")]
    D --> I["return len(rows)"]
    style D fill:#336791,color:#fff
    style F fill:#336791,color:#fff
    style E fill:#f59e0b,color:#000
    style I fill:#2d7d46,color:#fff
    style H fill:#dc2626,color:#fff
%% fig: Jalur tulis upsert ke entries dan fts
%% desc: upsert menormalkan tiap KBEntry, menulis batch dengan INSERT OR REPLACE, lalu trigger FTS5 menyinkronkan tabel fts.
Fig. 2: Jalur tulis upsert ke entries dan ftsupsert menormalkan tiap KBEntry, menulis batch dengan INSERT OR REPLACE, lalu trigger FTS5 menyinkronkan tabel fts.

upsert menormalkan setiap KBEntry menjadi tuple kolom. Bila batch kosong, fungsi mengembalikan 0 tanpa menyentuh koneksi; bila tidak, fungsi menulis dalam satu transaksi dan mengembalikan jumlah baris yang ditulis. core/knowledge_base.py:202-234

KolomPerlakuan di upsert
titledipotong ke 500 karakter
summarydipotong ke 5000 karakter
refsdibatasi 20 URL lalu diserialisasi dengan json.dumps
cvssdisimpan apa adanya, boleh NULL
kindcve / cwe / attck / template

core/knowledge_base.py:209-221

upsert memakai INSERT OR REPLACE, jadi id yang sama menimpa baris lama, bukan menggandakannya. Test membuktikannya: dua upsert berurutan dengan id yang sama menyisakan satu baris cwe, dan judul barunya menang di pencarian. tests/test_knowledge_base.py:69-76

Pemotongan refs ke 20 URL juga diuji langsung. tests/test_knowledge_base.py:218-223

Korpus bawaan untuk seed ada di _seed_entries(): 15 record, terdiri dari 6 CVE, 5 CWE, dan 4 ATT&CK. cli/commands/kb.py:60-160

Sanitasi query FTS5

_fts_sanitize membuang semua karakter di luar [A-Za-z0-9_ ], mempertahankan token dengan panjang minimal 2 karakter, membungkus setiap token dengan tanda kutip ganda, lalu menyambungnya dengan OR. Hasil kosong berarti pemanggil melewati FTS dan query langsung mengembalikan list kosong. core/knowledge_base.py:130-148

Tujuannya menghindari grammar FTS5: tanda kutip, tanda kurung, tanda hubung, dan keyword AND/OR/NOT/NEAR tidak aman dipakai apa adanya. Test memastikan input seperti (injection) OR "foo" dan --; DROP TABLE entries; -- tidak melempar exception. tests/test_knowledge_base.py:107-111

Query: shortlist FTS5 lalu rerank

sequenceDiagram
    participant CLI as guardian kb query
    participant Analyst as AnalystAgent
    participant KB as KnowledgeBase.query
    participant FTS as fts FTS5
    participant EMB as embeddings
    CLI->>KB: query(text, k, kind)
    Analyst->>KB: query(600 char pertama tool + output, k=rag.top_k)
    KB->>KB: _fts_sanitize(text)
    alt tidak ada token tersisa
        KB-->>CLI: []
    else ada token
        KB->>FTS: SELECT bm25(fts) MATCH ? ORDER BY rank LIMIT k*4
        FTS-->>KB: rows
        opt reranker tersedia dan shortlist punya embedding
            KB->>EMB: SELECT id, vec WHERE id IN shortlist
            EMB-->>KB: blob float32
            KB->>KB: score = 0.3 * fts + 0.7 * cosine
        end
        KB-->>Analyst: List[KBHit] ke hits_to_prompt_block
    end
%% fig: Siklus satu query KB sampai blok prompt
%% desc: query menyaring teks ke grammar FTS5 yang aman, mengambil shortlist k*4, lalu melakukan rerank cosine bila embedding tersedia.
Fig. 3: Siklus satu query KB sampai blok promptquery menyaring teks ke grammar FTS5 yang aman, mengambil shortlist k*4, lalu melakukan rerank cosine bila embedding tersedia.

Query SQL mengambil bm25(fts) AS rank, join entries lewat rowid, menambahkan filter e.kind = ? bila kind diberikan, dan mengambil max(k, 1) * 4 baris sebagai shortlist. core/knowledge_base.py:247-295

ParameterDefaultArti
text–query bebas; kosong setelah strip() berarti []
k5jumlah hit akhir yang dikembalikan
kindNonefilter satu jenis korpus

core/knowledge_base.py:247-252

bm25 bersifat lower-is-better, jadi skor dibalik dan dipadatkan ke rentang (0, 1] dengan 1.0 / (1.0 + max(bm25, 0.0)). Test memastikan bm25 0.0 mengalahkan 5.0 dan nilai negatif dijepit ke hasil yang sama dengan 0.0. core/knowledge_base.py:321-325 tests/test_knowledge_base.py:141-149

Rerank hanya aktif bila _maybe_load_reranker berhasil memuat SentenceTransformer("all-MiniLM-L6-v2"). Model di-cache di level modul, dan kegagalan import apa pun menghasilkan None tanpa mengganggu jalur FTS. core/knowledge_base.py:328-343

_rerank_with_embeddings mengambil vec dari embeddings untuk id di shortlist, membongkarnya dengan struct.unpack, lalu memblend 0.3 * skor_fts + 0.7 * cosine. Entry tanpa baris embedding tetap membawa skor FTS-nya; bila tidak satu pun shortlist punya embedding, urutan FTS dipertahankan. core/knowledge_base.py:346-386

Blok referensi untuk prompt

hits_to_prompt_block merender hit sebagai blok berpagar [BEGIN_KB_REFERENCES] dan [END_KB_REFERENCES], satu baris per hit berisi id, kind, severity, cvss bila ada, title, dan summary yang dipotong 300 karakter. Anggaran total dibatasi max_chars=2000, dan hit yang melewati anggaran dilewati. core/knowledge_base.py:404-431

Analyst memanggilnya lewat _retrieve_kb_block, yang memakai 600 karakter pertama gabungan nama tool dan output sebagai query, serta rag.top_k dengan default 5 sebagai k. Hasilnya dibungkus wrap_untrusted karena teks CVE bisa tercemar di sisi upstream. core/analyst_agent.py:179-204

Grounding bersifat opt-in: KnowledgeBase() hanya dibuat bila rag.enabled true, dan default-nya false sehingga perilaku Analyst tidak berubah sampai korpus di-seed. core/analyst_agent.py:31-39

guardian kb query --prompt mencetak blok yang sama apa adanya untuk inspeksi manual, tanpa pembungkusan untrusted; pembungkusan hanya dilakukan oleh Analyst. cli/commands/kb.py:311-313

Perintah guardian kb

kb_app didaftarkan di aplikasi utama dengan nama kb. cli/main.py:82

SubcommandOpsiPerilaku
status–tabel baris per kind; KB kosong mencetak KB is empty lalu exit 0
seed–upsert(_seed_entries()) lalu mencetak jumlah baris
update--kind, --accept-network, --fileingest saja; tanpa --file tidak ada unduhan
queryTEXT, --top/-k, --kind, --prompttabel skor/ID/kind/severity/title, atau blok prompt bila --prompt

cli/commands/kb.py:39-54 cli/commands/kb.py:163-168 cli/commands/kb.py:174-223 cli/commands/kb.py:295-329

status juga mencetak path basis data yang sedang dibaca, sehingga operator tahu berkas mana yang dipakai. cli/commands/kb.py:39-54

⚠️ guardian kb update --kind cve tanpa --file selalu gagal: exit 1 bila --accept-network tidak diberikan, exit 2 bila diberikan. Klien tidak pernah mengunduh feed sendiri; operator mengunduh manual lalu memberi --file. cli/commands/kb.py:193-208

⚠️ Docstring modul menyebut korpus bawaan di data/kb_seed/, tetapi direktori itu tidak ada di repository. Isi seed sebenarnya adalah list Python di _seed_entries(). cli/commands/kb.py:11-18 cli/commands/kb.py:60-160

update memakai adapter longgar _normalize_payload yang menerima list, items, CVE_Items, atau objects. Item tanpa id dilewati; title jatuh ke name, lalu ke 120 karakter pertama summary, lalu ke id. cli/commands/kb.py:226-289

Contoh urutan pemakaian dari dokumen fitur v4: kb seed, lalu kb update --kind cve --file ./nvd-2025.json, lalu kb query "log4j JNDI" --top 5, lalu menambahkan rag.enabled: true dan rag.top_k: 5 ke config. docs/V4_FEATURES.md:41-63

Metrik penerimaan yang ditetapkan untuk A1 adalah CVE-match rate di atas 85% pada 50 finding nuclei yang di-hold-out, dengan baseline tanpa RAG di bawah 50%. docs/V4_FEATURES.md:65-68