Menyuntikkan keseluruhan isi berkas repositori ke dalam context window LLM setiap kali interaksi CLI berjalan memboroskan kuota token dan meningkatkan latensi inferensi. Pola arsitektur offline cache context memisahkan pembacaan file dari inferensi utama: UI CLI membaca ringkasan yang telah di-cache, sementara worker di background bertugas mengekstrak konteks dokumen secara asinkron menggunakan TextRank tanpa memanggil model eksternal.
Arsitektur Sistem dan Antrean Event
CLI agent membaca data dari database relasional lokal. Saat berkas pada proyek berubah, sistem tidak langsung memblokir eksekusi CLI. Sebuah event watcher mendeteksi perubahan, menghitung hash SHA-256 berkas, dan memasukkan job ke antrean pemrosesan worker.
Alur data bekerja sebagai berikut:
- Main Process (CLI): Mengambil konteks dari tabel
context_cacheberdasarkanfile_pathdanfile_hash. Jika cache valid, kirimkan langsung ke LLM. Jika cache miss, gunakan isi dokumen mentah sambil mendaftarkan job ekstraksi ke antrean. - Queue Layer: Antrean FIFO berbasis tabel SQLite atau direktori file sementara untuk memicu worker.
- Background Worker: Mengambil isi dokumen, menjalankan algoritma TextRank untuk ekstraksi kalimat kunci, lalu menuliskan ringkasan kembali ke SQLite.
Schema SQLite dan Mitigasi Concurrency dengan WAL Mode
Secara default, SQLite mengunci seluruh database selama operasi penulisan (write transaction), yang dapat memicu galat database is locked pada CLI thread. Mengaktifkan Write-Ahead Logging (WAL) memungkinkan proses pembacaan berjalan serentak bersama satu proses penulisan aktif.
CREATE TABLE IF NOT EXISTS context_cache (
file_path TEXT PRIMARY KEY,
file_hash TEXT NOT NULL,
summary TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS extraction_queue (
file_path TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'PENDING',
created_at INTEGER NOT NULL
);
PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=5000;
PRAGMA synchronous=NORMAL;Konfigurasi PRAGMA busy_timeout=5000; memaksa koneksi menunggu hingga 5 detik saat terjadi locking sebelum melempar exception. Opsi synchronous=NORMAL mempertahankan integritas WAL mode tanpa membebani disk I/O.
Implementasi Worker dan Algoritma TextRank Minimal
TextRank mengekstrak kalimat paling representatif dengan membangun graf kemiripan berbasis overlap token (Jaccard similarity) antar-kalimat, kemudian mengkalkulasi skor PageRank. Implementasi ini berjalan murni di CPU lokal tanpa dependensi API.
import math
import os
import re
import sqlite3
import time
from hashlib import sha256
DB_PATH = "context_cache.db"
def get_connection():
conn = sqlite3.connect(DB_PATH, timeout=5.0)
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA busy_timeout=5000;")
return conn
def compute_hash(content: str) -> str:
return sha256(content.encode("utf-8")).hexdigest()
def textrank_summarize(text: str, top_n: int = 3) -> str:
sentences = [s.strip() for s in re.split(r'[\n.!?]+', text) if len(s.strip()) > 15]
if len(sentences) <= top_n:
return " ".join(sentences)
words = [[w.lower() for w in re.findall(r'\w+', s)] for s in sentences]
n = len(sentences)
weights = [[0.0] * n for _ in range(n)]
for i in range(n):
for j in range(i + 1, n):
set_i, set_j = set(words[i]), set(words[j])
overlap = len(set_i & set_j)
if overlap > 0:
score = overlap / (math.log(len(set_i) + 1) + math.log(len(set_j) + 1))
weights[i][j] = score
weights[j][i] = score
scores = [1.0] * n
d = 0.85
for _ in range(20):
next_scores = []
for i in range(n):
rank_sum = sum(
(weights[j][i] / sum(weights[j])) * scores[j]
for j in range(n)
if sum(weights[j]) > 0
)
next_scores.append((1 - d) + d * rank_sum)
scores = next_scores
ranked_idx = sorted(range(n), key=lambda i: scores[i], reverse=True)[:top_n]
ranked_idx.sort()
return " ".join(sentences[i] for i in ranked_idx)
def process_worker_job():
conn = get_connection()
with conn:
cursor = conn.cursor()
# Cegah race condition antar-worker: atomic fetch-and-lock
cursor.execute(
"SELECT file_path FROM extraction_queue WHERE status = 'PENDING' LIMIT 1"
)
row = cursor.fetchone()
if not row:
return False
file_path = row[0]
cursor.execute(
"UPDATE extraction_queue SET status = 'PROCESSING' WHERE file_path = ? AND status = 'PENDING'",
(file_path,)
)
if cursor.rowcount == 0:
return False
if not os.path.exists(file_path):
with conn:
conn.execute("DELETE FROM extraction_queue WHERE file_path = ?", (file_path,))
return True
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
file_hash = compute_hash(content)
summary = textrank_summarize(content, top_n=3)
with conn:
conn.execute(
"""INSERT INTO context_cache (file_path, file_hash, summary, updated_at)
VALUES (?, ?, ?, ?)
ON CONFLICT(file_path) DO UPDATE SET
file_hash = excluded.file_hash,
summary = excluded.summary,
updated_at = excluded.updated_at""",
(file_path, file_hash, summary, int(time.time()))
)
conn.execute("DELETE FROM extraction_queue WHERE file_path = ?", (file_path,))
return TrueInvalidasi Cache Berbasis Hash dan Penanganan Race Condition
Sistem mengevaluasi validitas cache melalui pemeriksaan kondisi berikut:
- Bila
file_pathtidak ditemukan padacontext_cache, trigger queue event dan fallback ke raw content. - Bila hash berkas lokal tidak cocok dengan
file_hashpada SQLite, dokumen dianggap stale. Masukkan file ke antrean pembaruan. - Jika dua instance worker berjalan paralel, race condition dimitigasi dengan statement
UPDATE extraction_queue SET status = 'PROCESSING' WHERE status = 'PENDING'. Operasi ini bersifat atomik dalam SQLite; worker yang gagal mengklaim baris akan membaca nilairowcount == 0dan beralih ke record berikutnya.
Evaluasi Penghematan Token
Pengurangan ukuran prompt terukur dari perbandingan jumlah token sebelum dan sesudah kompresi teks:
- Dokumen Modul Python (500 baris / ~3.200 token): Ekstraksi TextRank menghasilkan rata-rata 3–5 kalimat kunci yang mewakili interface dan logic utama (~250 token). Kompresi mencapai ~92%.
- Dokumen Dokumentasi/Markdown (1.500 kata / ~2.000 token): Diringkas menjadi ~180 token. Kompresi mencapai ~91%.
Pengurangan token input ini memangkas biaya pemanggilan context LLM dan menjaga token usage tetap berada di bawah context window limit secara deterministik.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!