)
1. 為什么我要自己動手做一個 open-code-review 工具代碼審查這件事做過團(tuán)隊協(xié)作的人都有體會。提了 PR 之后等 reviewer 有空、reviewer 看漏了邊界條件、同一個低級問題在三個文件里重復(fù)出現(xiàn)——這些場景幾乎每天都在發(fā)生。市面上的商業(yè)代碼審查服務(wù)不少但要么按席位收費貴得離譜要么把代碼傳到別人的服務(wù)器上公司安全規(guī)范直接卡死。我所在的團(tuán)隊就屬于后者私有倉庫連外部 CI 都不讓接更別說把 diff 發(fā)給第三方 API 了。于是我開始琢磨能不能用本地能跑的 LLM配合 Git 的命令行能力做一個完全離線的代碼審查工具這就是open-code-review的起點。它的定位很明確——一個跑在終端里的 CLI 工具讀取git diff的輸出把變更內(nèi)容喂給本地或自建的 LLM Agent讓它按預(yù)設(shè)規(guī)則逐文件、逐 hunk 地給出審查意見最后匯總成一份可讀的報告。整個鏈路不依賴任何外部服務(wù)代碼不出內(nèi)網(wǎng)。這篇文章適合三類人看一是想給自己團(tuán)隊搭一套私有代碼審查流水線的工程師二是正在研究 LLM Agent 怎么和 Git 工作流結(jié)合的技術(shù)愛好者三是單純想搞清楚 CLI 工具怎么調(diào)用大模型、prompt 怎么設(shè)計、diff 怎么解析的開發(fā)者。我會把設(shè)計取舍、核心實現(xiàn)、踩過的坑全部攤開講代碼片段可以直接抄。先說清楚一個概念區(qū)分因為熱詞里很多人問agent、LLM、AI 模型到底啥關(guān)系。簡單講LLMLarge Language Model是底層的大語言模型比如 DeepSeek、GPT 系列、Claude 系列它們本質(zhì)是輸入文本、輸出文本的函數(shù)。AI 模型是個更大的范疇LLM 只是其中一類。而Agent是在 LLM 之上加了一層感知—決策—行動的循環(huán)它能調(diào)用工具比如讀文件、執(zhí)行 git 命令、跑測試根據(jù)工具返回的結(jié)果決定下一步做什么。open-code-review 里的審查邏輯就是一個輕量 Agent——它不只是把 diff 丟給模型要一段評論而是會先解析 diff 結(jié)構(gòu)、按文件分組、對每個 hunk 單獨提問、再把結(jié)果聚合。這個分而治之的調(diào)度邏輯就是 Agent 和裸調(diào) API 的區(qū)別。2. 整體架構(gòu)CLI 外殼 Git 解析層 LLM 調(diào)度層2.1 三層職責(zé)劃分我把整個工具拆成三層每層只干一件事方便單獨測試和替換。第一層是CLI 外殼負(fù)責(zé)參數(shù)解析、配置加載、輸出格式化。用戶敲ocr review --staged或者ocr review main..feature這一層把命令翻譯成內(nèi)部調(diào)用。我選的是 Python 的argparse加rich做終端渲染沒上 Click 是因為 argparse 零依賴、夠用rich 負(fù)責(zé)把審查結(jié)果渲染成帶顏色和表格的漂亮輸出。第二層是Git 解析層負(fù)責(zé)調(diào)用 git 命令拿到 diff然后把 unified diff 格式解析成結(jié)構(gòu)化的數(shù)據(jù)結(jié)構(gòu)。這一層是整個工具的地基也是最容易出 bug 的地方后面單獨展開講。第三層是LLM 調(diào)度層負(fù)責(zé)把結(jié)構(gòu)化的變更內(nèi)容組裝成 prompt調(diào)用模型解析模型返回做結(jié)果聚合。這一層要處理超長 diff 的分片、并發(fā)調(diào)用、失敗重試、結(jié)果去重。三層之間用簡單的 dataclass 傳遞數(shù)據(jù)不引入消息隊列或復(fù)雜框架。理由很直接這是個單人維護(hù)的工具過度設(shè)計只會增加維護(hù)成本。2.2 為什么不用現(xiàn)成的 diff 解析庫Python 生態(tài)里有unidiff、patch-ng這類庫我一開始也用了unidiff但很快發(fā)現(xiàn)兩個問題。一是它對二進(jìn)制文件、重命名、模式變更file mode change的處理不夠細(xì)而這些恰恰是代碼審查里需要特別標(biāo)注的地方——比如一個文件從 644 變成 755可能是誤操作。二是它把整個 diff 一次性加載進(jìn)內(nèi)存遇到幾千行的巨型 diff 會卡。所以我最后自己寫了個流式解析器逐行掃描 diff 文本用狀態(tài)機(jī)識別diff --git、index、---、、、、-、 這些前綴。核心邏輯大概是這樣import re from dataclasses import dataclass, field dataclass class Hunk: old_start: int old_lines: int new_start: int new_lines: int lines: list field(default_factorylist) dataclass class FileDiff: old_path: str new_path: str is_new: bool False is_deleted: bool False is_renamed: bool False is_binary: bool False hunks: list field(default_factorylist) HUNK_HEADER re.compile(r^ -(\d)(?:,(\d))? \(\d)(?:,(\d))? ) def parse_diff(text: str) - list: files, current, hunk [], None, None for line in text.splitlines(): if line.startswith(diff --git): if current: files.append(current) parts line.split( b/, 1) current FileDiff(old_pathparts[0][11:], new_pathparts[1] if len(parts) 1 else ) hunk None elif line.startswith(new file mode): current.is_new True elif line.startswith(deleted file mode): current.is_deleted True elif line.startswith(rename from): current.is_renamed True elif line.startswith(Binary files): current.is_binary True elif HUNK_HEADER.match(line): m HUNK_HEADER.match(line) hunk Hunk(int(m.group(1)), int(m.group(2) or 1), int(m.group(3)), int(m.group(4) or 1)) current.hunks.append(hunk) elif hunk is not None and line[:1] in (, -, ): hunk.lines.append(line) if current: files.append(current) return files這段代碼看著簡單但有幾個細(xì)節(jié)值得說。diff --git a/foo b/foo這行里路徑可能帶空格所以用split( b/, 1)而不是按空格切。頭里的行數(shù)可能省略比如 -1 1 表示各一行所以正則里用了(?:,(\d))?并給默認(rèn)值 1。二進(jìn)制文件沒有 hunk直接標(biāo)記跳過。提示解析 diff 時一定要處理\ No newline at end of file這種特殊行它不屬于任何 hunk 的內(nèi)容但會影響行號計算。我一開始忽略了它導(dǎo)致審查意見里的行號全部偏移。2.3 數(shù)據(jù)流全景從用戶敲命令到看到報告數(shù)據(jù)流是這樣的CLI 解析參數(shù) → 調(diào)用git diff拿到原始文本 → 解析器轉(zhuǎn)成FileDiff列表 → 過濾器按規(guī)則剔除不需要審查的文件比如 lock 文件、圖片→ 分片器把大 diff 切成模型能吃的塊 → 調(diào)度器并發(fā)調(diào)用 LLM → 結(jié)果聚合器合并同一文件的意見 → 渲染器輸出。這個鏈路里過濾器和分片器是兩個容易被低估的環(huán)節(jié)。過濾器決定了審什么分片器決定了怎么審得動。下一節(jié)專門講這兩個。3. Git 變更獲取與 diff 分片策略3.1 拿到正確的 diff 范圍git diff的參數(shù)組合非常多工具要支持幾種常見場景。我定義了三個入口ocr review --staged審查暫存區(qū)等價于git diff --cached適合提交前自查。ocr review --range main..feature審查兩個分支之間的差異適合 PR 場景。ocr review --commit abc123審查單個提交等價于git show abc123。底層統(tǒng)一走一個函數(shù)根據(jù)參數(shù)拼 git 命令。這里有個坑git diff默認(rèn)會調(diào)用外部 diff 工具或受.gitattributes影響輸出格式可能不是標(biāo)準(zhǔn) unified diff。所以我在命令里強(qiáng)制加了幾個參數(shù)git -c diff.mnemonicprefixfalse -c core.quotepathfalse \ --no-optional-locks diff --no-color --no-ext-diff \ --unified3 --find-renames --find-copies main..feature逐個解釋。diff.mnemonicprefixfalse保證路徑前綴是標(biāo)準(zhǔn)的a/和b/而不是i/、w/這種隨場景變化的助記前綴否則解析器要處理多種前綴。core.quotepathfalse讓中文路徑不被轉(zhuǎn)義成八進(jìn)制否則文件名會變成\346\226\207...這種鬼東西。--no-optional-locks避免在只讀操作時去搶 index 鎖多人同時跑不會互相阻塞。--no-ext-diff禁用外部 diff 工具。--unified3固定上下文行數(shù)方便后續(xù)按 hunk 處理。--find-renames和--find-copies讓 git 識別重命名和復(fù)制這樣審查時能知道這個文件只是改名了內(nèi)容沒動避免誤報。注意如果你的倉庫很大git diff本身可能就要跑好幾秒。我加了個--no-optional-locks之后在 CI 環(huán)境里并發(fā)跑多個審查任務(wù)時不再出現(xiàn) index.lock 沖突導(dǎo)致的失敗。3.2 哪些文件不該送進(jìn)模型不是所有變更都值得讓 LLM 看。我維護(hù)了一個默認(rèn)忽略列表同時允許用戶在配置文件里覆蓋類別匹配規(guī)則忽略理由鎖文件*.lock,package-lock.json,poetry.lock機(jī)器生成無審查價值且極長壓縮產(chǎn)物*.min.js,*.min.css,dist/**壓縮后不可讀浪費 token二進(jìn)制圖片、字體、.so,.dll模型讀不了解析器已標(biāo)記生成代碼*_pb2.py,*.generated.*由工具生成改也是改生成器快照測試__snapshots__/**,*.snap內(nèi)容隨實現(xiàn)變審查意義低這個列表不是拍腦袋定的。我統(tǒng)計過團(tuán)隊三個月的 PR鎖文件和 dist 產(chǎn)物占了 diff 總行數(shù)的 40% 以上全部剔除后 token 消耗直接砍半審查速度提升明顯。但要注意忽略規(guī)則不能一刀切——有些團(tuán)隊把dist也納入版本管理且需要審查所以必須可配置。配置用 YAML長這樣ignore: - *.lock - package-lock.json - dist/** - **/*.min.js max_hunk_lines: 200 max_file_lines: 2000max_hunk_lines和max_file_lines是分片閾值下面講。3.3 大 diff 怎么切才不丟上下文LLM 有上下文窗口限制一個幾千行的 diff 塞進(jìn)去要么超限要么模型注意力被稀釋、審查質(zhì)量下降。所以必須分片。但分片有個矛盾切得太碎模型看不到跨函數(shù)的調(diào)用關(guān)系切得太粗又超限。我的策略是按文件優(yōu)先、按 hunk 兜底。具體規(guī)則單個文件的所有 hunk 加起來不超過max_file_lines默認(rèn) 2000 行就整個文件作為一個審查單元。超過的話按 hunk 切但每個分片盡量包含連續(xù)的 hunk且分片之間保留 3 行重疊上下文。單個 hunk 超過max_hunk_lines默認(rèn) 200 行的說明這是個巨型改動單獨成片并標(biāo)記oversized在報告里提示人工重點看。為什么按文件優(yōu)先因為代碼審查里很多問題是文件級的——比如這個文件新增了 500 行但沒加任何測試這個模塊的 import 順序亂了。如果按 hunk 切碎模型就看不到文件全貌這類意見就提不出來。只有文件實在太大時才退化成 hunk 級。分片時還要注意保留 hunk 頭里的行號信息因為最終報告要定位到具體行。我在每個分片的 prompt 里都會帶上 -old_start,old_lines new_start,new_lines 這行讓模型知道它看的是文件的哪一段。def split_file(fd: FileDiff, max_file_lines: int, max_hunk_lines: int): total sum(len(h.lines) for h in fd.hunks) if total max_file_lines: return [fd] # 整文件一片 chunks, buf, buf_lines [], [], 0 for h in fd.hunks: hlen len(h.lines) if hlen max_hunk_lines: if buf: chunks.append(buf); buf, buf_lines [], 0 chunks.append([h]) # 巨型 hunk 單獨成片 continue if buf_lines hlen max_file_lines: chunks.append(buf); buf, buf_lines [], 0 buf.append(h); buf_lines hlen if buf: chunks.append(buf) return chunks這段邏輯我調(diào)了好幾版。最早是簡單按行數(shù)累加切結(jié)果把一個函數(shù)的開頭和結(jié)尾切到兩個片里模型對著半截函數(shù)提了一堆莫名其妙的意見。后來改成hunk 是不可分割的最小單位因為一個 hunk 通常對應(yīng)一處連續(xù)改動切開就失去語義了。再后來加了巨型 hunk 的單獨處理因為確實遇到過自動生成的遷移腳本單 hunk 上千行的情況。4. 把 diff 變成模型能懂的審查指令4.1 prompt 結(jié)構(gòu)設(shè)計prompt 設(shè)計是這個工具的靈魂。我試過很多版本最后穩(wěn)定下來的結(jié)構(gòu)是四段式角色設(shè)定 審查規(guī)則 變更內(nèi)容 輸出格式。角色設(shè)定要具體不能只說你是一個代碼審查員。我寫的是你是一位有十年經(jīng)驗的資深工程師擅長發(fā)現(xiàn)并發(fā)問題、邊界條件、資源泄漏和安全隱患。你的評論要具體、可操作指出問題所在行并給出修改建議。 這樣模型輸出的意見會明顯更聚焦而不是泛泛地說代碼可以更清晰。審查規(guī)則是可配置的默認(rèn)包含這些維度正確性邏輯錯誤、邊界條件、空指針、類型不匹配安全性注入風(fēng)險、敏感信息硬編碼、權(quán)限校驗缺失性能不必要的循環(huán)、重復(fù)計算、N1 查詢可維護(hù)性命名、重復(fù)代碼、過長函數(shù)、缺失注釋測試新增邏輯是否有對應(yīng)測試變更內(nèi)容部分我會把文件路徑、hunk 頭、具體增刪行都帶上并且用明確的標(biāo)記區(qū)分新增和刪除文件: src/auth/login.py 變更類型: 修改 -45,7 45,9 def verify_token(token): if not token: return None - payload jwt.decode(token, SECRET) payload jwt.decode(token, SECRET, algorithms[HS256]) if payload.get(exp, 0) time.time(): raise TokenExpired() return payload輸出格式我要求模型返回 JSON字段固定file、line、severitycritical/warning/info、category、message、suggestion。用 JSON 是為了后續(xù)能程序化聚合和渲染而不是讓模型自由發(fā)揮寫一段散文。但這里有個坑——模型經(jīng)常在 JSON 外面包一層 markdown 代碼塊或者加一句好的以下是審查結(jié)果。所以解析時要先剝離代碼塊標(biāo)記再用容錯的方式提取 JSON。import json, re def extract_json(text: str): text text.strip() text re.sub(r^(?:json)?\s*, , text) text re.sub(r\s*$, , text) try: return json.loads(text) except json.JSONDecodeError: m re.search(r\[.*\], text, re.DOTALL) if m: return json.loads(m.group(0)) return []提示讓模型輸出 JSON 時一定要在 prompt 里給一個完整的示例并且明確說只輸出 JSON不要任何解釋文字。即便如此仍要寫容錯解析因為模型偶爾會忘記。4.2 并發(fā)調(diào)用與限流一個 PR 可能有幾十個文件串行調(diào)用模型太慢。我用concurrent.futures.ThreadPoolExecutor做并發(fā)默認(rèn) 4 個 worker。為什么是 4 而不是更多因為本地跑的模型比如通過 Ollama 或 vLLM 部署的通常只有有限的并發(fā)能力開太多反而排隊。如果是調(diào)用遠(yuǎn)程 API可以調(diào)到 8 到 16但要配合限流。限流我用的是簡單的令牌桶每秒放行 N 個請求。這里踩過一個坑一開始沒做限流20 個文件同時發(fā)出去遠(yuǎn)程 API 直接返回 429然后我的重試邏輯又同時重試形成雪崩。后來加了指數(shù)退避加隨機(jī)抖動才穩(wěn)定下來。import time, random def call_with_retry(fn, max_retries3): for i in range(max_retries): try: return fn() except RateLimitError: sleep (2 ** i) random.uniform(0, 1) time.sleep(sleep) raise RuntimeError(重試耗盡)指數(shù)退避的2 ** i保證每次等待翻倍隨機(jī)抖動避免多個線程同時重試撞在一起。這個模式在任何調(diào)用外部服務(wù)的場景都適用不只是 LLM。4.3 結(jié)果聚合與去重并發(fā)調(diào)用回來的是一個個分片的審查結(jié)果需要合并。合并時有兩個問題同一文件多個分片的意見要按行號排序不同分片可能對同一行提出重復(fù)意見因為分片有重疊上下文。去重策略是按(file, line, category)三元組做鍵保留 severity 最高的那條。如果兩條意見 message 高度相似用簡單的編輯距離或 Jaccard 相似度判斷也合并。實測下來重疊上下文導(dǎo)致的重復(fù)意見大概占 5% 到 10%不去重的話報告會很啰嗦。聚合后按 severity 排序critical 在前info 在后。渲染時用 rich 的表格critical 標(biāo)紅warning 標(biāo)黃info 標(biāo)藍(lán)。最后給一個統(tǒng)計摘要本次審查共發(fā)現(xiàn) X 個 critical、Y 個 warning、Z 個 info涉及 N 個文件。5. 實測中暴露的問題與修復(fù)過程5.1 模型對刪除行的誤判最早版本里我把 diff 的增刪行原樣喂給模型結(jié)果模型經(jīng)常對被刪除的代碼提意見。比如刪掉了一行有 bug 的代碼模型卻說這行代碼有風(fēng)險。原因是它沒理解-前綴的含義。修復(fù)方法是在 prompt 里明確說明 diff 格式并且在變更內(nèi)容前加一段說明以下 diff 中以-開頭的行是被刪除的舊代碼以開頭的行是新增代碼以空格開頭的是未改動的上下文。請只對新增代碼和上下文提出審查意見不要對被刪除的代碼提意見。 加了這段之后誤判率從大概 15% 降到 2% 以下。這個坑的本質(zhì)是模型不知道你的輸入格式約定。任何把結(jié)構(gòu)化數(shù)據(jù)喂給模型的場景都要顯式說明格式不能假設(shè)模型應(yīng)該懂。5.2 行號對不上的問題報告里的行號一開始經(jīng)常對不上用戶點過去發(fā)現(xiàn)是空行或者別的函數(shù)。排查后發(fā)現(xiàn)兩個原因。一是模型返回的行號是相對于 hunk 的偏移而不是文件絕對行號。二是\ No newline at end of file這類特殊行影響了計數(shù)。修復(fù)方案是在 prompt 里明確要求模型返回文件絕對行號并且在變更內(nèi)容里把每個 hunk 的起始行號標(biāo)出來。同時在解析器里正確處理特殊行不把它們計入行號。為了驗證我寫了個測試構(gòu)造一個已知行號的 diff跑一遍審查斷言返回的行號落在預(yù)期范圍內(nèi)。這個測試后來成了回歸測試的一部分。5.3 大文件導(dǎo)致的超時有個 PR 改了一個 3000 行的配置文件雖然我做了分片但每個分片仍然很大模型響應(yīng)慢加上并發(fā)整體超時。后來我加了兩條規(guī)則一是對配置文件、數(shù)據(jù)文件這類低審查價值的大文件默認(rèn)只審查前 500 行并提示文件過大僅審查前 500 行二是給每個 LLM 調(diào)用設(shè)置獨立的超時默認(rèn) 60 秒超時就跳過該分片并在報告里標(biāo)記未完成審查。這里的原則是寧可漏審不可卡死。一個審查工具如果跑十分鐘還沒結(jié)果用戶就不會再用第二次??焖俳o出 80% 的價值比慢慢給出 100% 更有用。5.4 中文注釋和字符串的處理團(tuán)隊代碼里有大量中文注釋早期版本模型對中文的處理不穩(wěn)定有時把中文注釋當(dāng)成亂碼。排查發(fā)現(xiàn)是core.quotepath的問題——git 默認(rèn)會把非 ASCII 路徑轉(zhuǎn)義。加上-c core.quotepathfalse之后路徑正常了但文件內(nèi)容里的中文本來就沒問題。另外在 prompt 里加一句代碼中可能包含中文注釋和字符串請正常理解模型的表現(xiàn)會更好。6. 配置、集成與日常使用心得6.1 配置文件長什么樣工具的所有行為都通過一個.ocr.yaml配置放在倉庫根目錄。完整示例如下model: provider: openai_compatible base_url: http://localhost:8000/v1 api_key: ${OCR_API_KEY} name: deepseek-coder temperature: 0.2 max_tokens: 2048 timeout: 60 review: concurrency: 4 max_file_lines: 2000 max_hunk_lines: 200 rules: - correctness - security - performance - maintainability - testing ignore: - *.lock - dist/** - **/*.min.js output: format: table show_info: true fail_on_critical: truetemperature設(shè) 0.2 是因為代碼審查需要穩(wěn)定、可復(fù)現(xiàn)的輸出太高會每次給出不同意見。fail_on_critical設(shè)為 true 時如果發(fā)現(xiàn) critical 問題進(jìn)程退出碼非零可以直接接進(jìn) CI 流水線做門禁。api_key用${OCR_API_KEY}從環(huán)境變量讀取避免密鑰寫進(jìn)配置文件提交到倉庫。這是基本的安全習(xí)慣但確實見過有人把 key 硬編碼進(jìn)去然后推到公開倉庫。6.2 接進(jìn) CI 和 Git hooks最常見的兩種集成方式。一是pre-commit hook提交前自動跑一次審查發(fā)現(xiàn) critical 就阻止提交#!/bin/sh ocr review --staged --fail-on-critical放在.git/hooks/pre-commit并加執(zhí)行權(quán)限即可。但要注意pre-commit 里跑 LLM 會拖慢提交速度如果模型響應(yīng)慢開發(fā)者會煩。我的做法是 pre-commit 只跑快速規(guī)則比如正則匹配敏感信息完整審查放到 CI。二是CI 集成在流水線里加一步- name: Code Review run: | pip install open-code-review ocr review --range origin/main..HEAD --format markdown review.md env: OCR_API_KEY: ${{ secrets.OCR_API_KEY }}輸出 markdown 后可以貼到 PR 評論里。這里的關(guān)鍵是模型服務(wù)要能被 CI 訪問——如果模型跑在本地開發(fā)機(jī)CI 就夠不著得部署一個內(nèi)網(wǎng)可訪問的推理服務(wù)。6.3 幾個提升效果的小技巧第一給模型提供項目背景。在配置里加一個context字段寫清楚項目是干什么的、用了什么框架、有哪些約定。比如這是一個 Django 項目所有數(shù)據(jù)庫操作必須走 ORM禁止裸 SQL。模型知道這些之后提的意見會貼合項目規(guī)范而不是泛泛而談。第二定期更新審查規(guī)則。團(tuán)隊踩過的坑應(yīng)該沉淀成規(guī)則。比如曾經(jīng)因為沒做參數(shù)校驗出過線上問題就把所有外部輸入必須校驗加進(jìn)規(guī)則。規(guī)則越貼合團(tuán)隊實際工具越有用。第三不要迷信模型的每一條意見。LLM 會有幻覺會提出不存在的問題。我的做法是把 severity 為 info 的意見默認(rèn)折疊只展開 critical 和 warning減少噪音。審查意見是輔助最終判斷還是人來做。第四關(guān)注 token 成本。如果用的是按量計費的 API一個大 PR 可能花掉幾塊錢。我統(tǒng)計過剔除鎖文件和產(chǎn)物后平均每個 PR 的審查成本在可接受范圍內(nèi)。但如果團(tuán)隊 PR 特別頻繁建議用本地模型邊際成本幾乎為零。6.4 關(guān)于模型選型的實際對比我試過幾種模型跑同一批 diff感受如下僅代表個人實測環(huán)境模型類型審查質(zhì)量速度部署難度適合場景本地 7B 代碼模型中等能抓明顯問題快低日??焖僮圆楸镜?32B 代碼模型較好邊界條件也能抓中等中團(tuán)隊內(nèi)網(wǎng)部署遠(yuǎn)程大模型 API最好理解力強(qiáng)取決于網(wǎng)絡(luò)低對質(zhì)量要求高的場景選型的核心權(quán)衡是質(zhì)量、速度、隱私三者不可兼得。內(nèi)網(wǎng)部署犧牲一點質(zhì)量換隱私和成本遠(yuǎn)程 API 換質(zhì)量但代碼要出內(nèi)網(wǎng)。沒有標(biāo)準(zhǔn)答案看團(tuán)隊約束。7. 后續(xù)可以繼續(xù)打磨的方向工具目前能穩(wěn)定跑但還有幾個我想做的改進(jìn)。一是增量審查——記住上次審查到哪個 commit只審新增部分避免重復(fù)勞動。二是意見學(xué)習(xí)——記錄開發(fā)者對每條意見的采納或忽略用這些反饋微調(diào) prompt讓工具越來越懂團(tuán)隊的偏好。三是多語言規(guī)則包——不同語言Python、Go、Java的常見問題不一樣做成可插拔的規(guī)則包會更專業(yè)。我在實際使用中最大的體會是代碼審查工具的價值不在于替代人而在于把人從重復(fù)勞動里解放出來。格式問題、明顯的空指針、忘記加校驗這類低級錯誤交給工具架構(gòu)設(shè)計、業(yè)務(wù)邏輯是否合理這類需要上下文判斷的留給人。分工清楚了工具才真正有用而不是變成一個制造噪音的負(fù)擔(dān)。