這趟旅程不是為了證明 LLM 有多神奇,恰恰相反,是為了證明「唯有把 LLM 限制在扎實的軟體工程框架內,它才具備真正的實用價值」:
若把程式碼當成一般純文字文件,隨機以 500 字元切塊,得到的檢索結果只是一堆斷章取義的碎屑。
我們選擇使用 Python AST 解析,讓每個 Chunk 天生具備「這是 class 還是 function」、「起訖行號在哪裡」、「它的全名是什麼」。語法邊界,才是程式碼 RAG 不產生幻覺的根本基礎。
在第三週之前,我們也曾陷入「改一下 Prompt,好像變好了」的玄學調優。
直到 Day 15 建立 data/eval_cases.json、Day 16 寫出 Hit@k 與 MRR 指標、Day 17 跑出自動化評估報表,我們才第一次用數據看清現實:LLM Reranker 在語意題大幅進步,卻帶來了將近 70 倍的推論延遲。這讓我們放棄「全部丟給 AI」的幻想,確立了「精準走規則、模糊走模型」的動態路由策略。
本地跑小模型最現實的問題就是超時、當機、把 JSON 格式輸出成聊天客套話。
在 Day 18 與 Day 19 中,我們落實了正則萃取、索引邊界檢查與安全降級(Fallback):一旦模型輸出格式損壞,系統自動退回第一階段排序。讓工具永遠不崩潰,比模型偶爾靈光一閃更重要。
在 Day 27 中,我們沒有假裝靜態分析能解決 Python 裡所有的 getattr、字串反射或動態匯入。
我們選擇實作 DynamicDetector,主動揪出動態特徵並降級置信度(Confidence: LOW)。一個合格的工程工具,必須在有證據時給出解答,在看不透的地方主動提醒風險。
整套專案包含以下完整成果:
codebase-agent/
├── app/
│ ├── codebase.py # AST 走訪、Symbol 與 Import 提取、SQLite 儲存
│ ├── embeddings.py # Ollama bge-m3 向量嵌入與 Hash 測試分層
│ ├── hybrid_search.py # Lexical + Vector 的 RRF 混合檢索
│ ├── reranker.py # 定義優先的 Deterministic Rerank
│ ├── evaluation.py # EvalCase 載入器與 Hit@k / MRR / Precision@k 計算
│ ├── eval_runner.py # 自動化一鍵評估與 A/B 對照報表
│ ├── llm_reranker.py # 本地 gemma4 模型精排與正則 Fallback 防線
│ ├── call_graph.py # ast.Call 節點萃取與 Import-Aware 符號解析
│ ├── impact_analyzer.py # 反向 BFS 呼叫鏈影響傳播演算法
│ ├── git_diff_analyzer.py # Git Diff 變更行號解析與 Symbol 映射
│ ├── dynamic_detector.py # getattr / 動態特性檢測與置信度標記
│ ├── agent_loop.py # 4 輪上限的受限唯讀 ReAct 決策迴圈
│ ├── agent_service.py # 整合檢索、符號與衝擊分析的 Agent 工具工廠
│ └── cli.py # 終端機統一指令入口
├── data/
│ └── eval_cases.json # 標準化、可重複執行的評估題庫
└── tests/
├── unit/ # 涵蓋所有核心模組的離線單元測試
└── integration/ # 全管線端到端 (E2E) 整合驗收測試 (0.18s 全通)
這 30 天真正想練習的,不是把模型包裝成聊天視窗,而是學會把模型、工具、資料、索引、測試和限制組合成一個可維護的工程系統。
今天,這個目標已經達成。
我們沒有停留在複製貼上 Prompt,而是建立了一座穩固的工程橋樑:讓 AST 負責結構、SQLite 負責記憶、RRF 負責召回、評估資料集負責驗證、唯讀沙盒負責安全,最後才讓本地 LLM 在受控的邊界內發揮語意推理的長處。
這是一個真正「能回答問題、能拿出證據、不知道時坦白承認、出錯時安全退回」的工程系統。
感謝陪伴這個專案走完完整的 30 天!