编程 code-review-graph 深度拆解:Tree-sitter + MCP 如何把代码库编译成图,让 AI 审查省下几百倍 token

2026-07-24 04:14:57 +0800 CST views 6

code-review-graph 深度拆解:Tree-sitter + MCP 如何把代码库编译成图,让 AI 审查省下几百倍 token

一句话先说清楚:code-review-graph 干的事,就是把「让大模型自己 grep 整个仓库找上下文」这套暴力打法,换成「先把代码编译成一张持久化的依赖图,审查时只喂 AI 真正相关的那几个文件」。GitHub Trending 单日 +1800 星不是偶然——它戳中了每个用 AI 写代码的人最疼的地方:token 在无意义的翻仓上被烧光了

这篇文章我不打算写成 README 的中文复读机。我想从一个后端 + 工程效率的视角,把它的架构、数据结构、增量算法、MCP 集成、embedding 选型这些东西一层层剥开,配上能跑的代码,讲清楚它「为什么快、为什么省、什么时候不该用」。


一、背景:AI 代码审查的 token 是怎么被烧掉的

先还原一个真实场景。你在 Cursor 或 Claude Code 里改了一个 login() 函数,然后让 AI「帮我审查一下这次改动有没有风险」。

AI 助手拿到这个请求后,它并不知道 login() 被谁调用、影响了哪些测试、依赖了哪些模块。于是它开始做一件非常「诚实」但极其低效的事:

  1. grep 一遍 login 这个关键词,命中几十个文件;
  2. 挨个 read_file 读进上下文;
  3. 为了理解调用链,又顺藤摸瓜读了一堆间接依赖;
  4. 上下文窗口被塞满,模型开始「注意力稀释」,审查质量反而下降。

我给一组实测感受一下量级。官方对自己这个仓库(174 文件、3107 个节点、22227 条边)跑基准测试,其中「影响半径分析」这一场景:

场景传统全量读取 token图查询 token节省比例
影响半径分析99,3759999.9%
架构探索87,7691,25998.6%
查找调用者2,06644978.3%(且速度 5.7x)

在 monorepo 里更夸张:源码本身 208,821 个 token,图响应只有约 2,495 token——单个问题 token 减少 93 倍;跨 6 个真实仓库的横向评估里,token 削减在 38× 到 528× 之间波动。

这里的核心洞察其实很朴素:代码不是自然语言,它是有强结构的。函数调用、类继承、import、测试覆盖,这些关系天然是一张图。既然是图,那就没必要每次都用「全文扫描 + 语言模型硬读」去重建这张图——你完全可以一次性把它解析出来存好,之后按需查询。这就是 code-review-graph 的全部立论基础。


二、核心概念:从 AST 到「代码知识图谱」

2.1 节点与边的定义

code-review-graph 用 Tree-sitter 把每个源文件解析成 AST,然后从 AST 里抽取两类东西:

  • 节点(Node):函数、类、导入。每个节点带上 idnamekindfileline_start/line_endsignature 等属性。
  • 边(Edge):调用(calls)、继承(inherits)、导入(imports)、测试覆盖(tests)。边是有向的,并且带置信度

置信度这个设计值得单独说。真实世界的代码解析没法做到 100% 精确——动态分发、反射、鸭子类型都会让「A 调用 B」这件事变得模糊。所以它给边分了三级:

  • EXTRACTED:直接从 AST 里能确定抽出来的(比如显式的函数调用点);
  • INFERRED:通过启发式推断的(比如同名方法的可能调用);
  • AMBIGUOUS:存在歧义的(比如多个候选目标)。

每条边还附一个浮点分数。这让下游做「影响半径」计算时能按置信度加权,而不是把所有边一视同仁。

用一段伪代码表达这个数据模型:

from dataclasses import dataclass
from enum import Enum

class EdgeKind(str, Enum):
    CALLS = "calls"
    INHERITS = "inherits"
    IMPORTS = "imports"
    TESTS = "tests"

class Confidence(str, Enum):
    EXTRACTED = "extracted"   # 从 AST 直接可得
    INFERRED = "inferred"     # 启发式推断
    AMBIGUOUS = "ambiguous"   # 有歧义

@dataclass
class GraphNode:
    id: str            # 稳定 id,例如 "auth/login.py::login"
    name: str
    kind: str          # function | class | import
    file: str
    line_start: int
    line_end: int
    signature: str     # "login function (user: User) returns Session"

@dataclass
class GraphEdge:
    src: str
    dst: str
    kind: EdgeKind
    confidence: Confidence
    score: float       # 0.0 ~ 1.0

2.2 为什么用 Tree-sitter 而不是 LSP 或正则

这是个值得展开的工程决策。抽代码结构,理论上有几条路:

  1. 正则/字符串匹配——快但脆,遇到嵌套、多行签名就崩。
  2. 语言原生编译器/LSP——最准,但你得为每种语言起一个 language server,环境依赖爆炸,Python 项目里跑 Go 的 gopls 是灾难。
  3. Tree-sitter——增量式、容错式的解析器生成器,一套 API 覆盖几十种语言,解析速度是毫秒级,而且能容忍语法不完整的代码(你写到一半的文件也能解析出大部分结构)。

code-review-graph 选了第三条,然后在 Tree-sitter 覆盖不到的边角用「针对性回退解析」补齐。它的语言面非常宽:Python、JS/TS/TSX、Go、Rust、Java、C/C++、C#、Ruby、Kotlin、Swift、PHP、Scala、Solidity、Dart、R、Perl、Lua、Objective-C、shell、Elixir、Zig、PowerShell、Julia、Nix、Verilog/SystemVerilog、SQL、Vue/Svelte 单文件组件,甚至 Jupyter 的 .ipynb

对于 Python,它还有个可选增强 [enrichment],用 Jedi 做调用解析——Jedi 能做静态类型推断,把「这个 self.repo.save() 到底调的哪个类的 save」这种模糊调用解析得更准,从而把边从 INFERRED 升级成 EXTRACTED


三、架构拆解:一条从代码到最小审查集的流水线

整体架构是一条清晰的单向流水线:

代码库
  │  git ls-files(只索引已跟踪文件)
  ▼
Tree-sitter 解析器 ──► AST
  │  抽取节点 + 边
  ▼
SQLite 图存储(.code-review-graph/)
  │  后处理:社区检测 / 执行流 / FTS5 索引
  ▼
影响半径查询
  │
  ▼
最小审查集(喂给 AI)

3.1 为什么是 SQLite 而不是 Neo4j

这是我最欣赏的一个决定。图数据库领域大家第一反应是 Neo4j、或者上个 graph engine。但 code-review-graph 把核心图直接存在 SQLite 文件里,放在仓库的 .code-review-graph/ 目录下。

理由很硬核:

  • 零外部依赖。开发者装个 pip 包就能用,不用起数据库、不用连云。这对「本地优先(local-first)」是硬约束——你的代码结构不该为了跑个审查而上传到别人的服务器。
  • SQLite 完全够用。代码图的规模通常在几千到几十万节点,SQLite 的 B-tree 索引 + 递归 CTE 完全扛得住图遍历。
  • 可移植。整个图就是一个文件,删掉重建、拷贝、diff 都很自然。

它还用了 SQLite 的 FTS5 全文索引做混合搜索——关键词匹配 + 向量相似度结合。这意味着「语义搜索」和「精确名字搜索」用的是同一套存储引擎,不用再引第二个系统。

一个简化版的 schema 大概长这样:

CREATE TABLE nodes (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    kind        TEXT NOT NULL,
    file        TEXT NOT NULL,
    line_start  INTEGER,
    line_end    INTEGER,
    signature   TEXT,
    file_hash   TEXT       -- SHA-256,用于增量比对
);

CREATE TABLE edges (
    src         TEXT NOT NULL,
    dst         TEXT NOT NULL,
    kind        TEXT NOT NULL,
    confidence  TEXT NOT NULL,
    score       REAL NOT NULL,
    PRIMARY KEY (src, dst, kind)
);

CREATE INDEX idx_edges_dst ON edges(dst);   -- 反向查「谁调用了我」
CREATE INDEX idx_nodes_file ON nodes(file);

-- FTS5 混合搜索
CREATE VIRTUAL TABLE nodes_fts USING fts5(
    name, signature, content='nodes', content_rowid='rowid'
);

注意那个 idx_edges_dst 索引。查「谁调用了 login」(callers)本质是 SELECT src FROM edges WHERE dst = ? AND kind = 'calls',有了 dst 索引这就是一次 O(log n) 查找。这就是为什么「查找调用者」场景能做到 5.7x 加速——它把线性全文扫描换成了索引查找。

3.2 影响半径:一次有边界的图遍历

「影响半径(blast radius)」是这个工具最核心的算法概念。当 login() 改动时,需要找出所有可能受影响的:

  • 上游调用者:谁调了 login(顺着 dst=login 的边反向找);
  • 下游依赖:login 调了谁(顺着 src=login 的边正向找);
  • 相关测试:哪些 test 覆盖了这条链。

本质是从变更节点出发做一次有界 BFS。「有界」很关键——如果不设边界,一个核心工具函数的影响半径可能是整个仓库。所以它用 token 预算和深度来剪枝:

def impact_radius(graph, changed_nodes, max_depth=3, token_budget=4000):
    """从变更节点出发,做有界反向+正向 BFS,返回最小审查集。"""
    visited = set()
    frontier = list(changed_nodes)
    result = []
    spent = 0

    for depth in range(max_depth):
        next_frontier = []
        # 按边置信度 * score 排序,优先展开高置信关系
        ranked = rank_by_confidence(graph, frontier)
        for node in ranked:
            if node in visited:
                continue
            visited.add(node)

            cost = estimate_tokens(node)        # 该节点摘要的 token 成本
            if spent + cost > token_budget:     # 预算耗尽,停止扩张
                return result
            spent += cost
            result.append(node)

            # 反向:调用者;正向:被调用者与测试
            next_frontier += graph.callers(node)
            next_frontier += graph.callees(node)
            next_frontier += graph.tests_of(node)
        frontier = next_frontier
    return result

这段逻辑里藏着两个我认为很聪明的取舍:

  1. confidence * score 排序再展开——预算有限时,优先把 token 花在「我很确定相关」的节点上,而不是被一堆 AMBIGUOUS 的边带偏。
  2. token 预算是硬边界——它保证了无论仓库多大,喂给 AI 的上下文永远可控。这就是「AI 只读约 15 个文件」的秘密,不是它猜得准,而是它按预算截断了一次拓扑排序。

四、增量更新:2900 文件的仓库重索引不到 2 秒

全量构建一个 500 文件的项目大约 10 秒,这不算慢,但如果每次保存都全量重建就没法用了。所以增量更新是它能进入日常工作流的关键。

机制是这样的:

  1. 启用 watch 模式或 git 提交钩子;
  2. 文件保存 / commit 触发,先做 git diff 拿到变更文件列表;
  3. 对每个变更文件算 SHA-256,和上次存的 file_hash 比对;
  4. 只重新解析 hash 变了的文件,跳过其余;
  5. 重解析后,只更新这些文件相关的节点和边,再局部重跑受影响的后处理。

用代码表达核心的「哈希门控」逻辑:

import hashlib

def incremental_update(graph, repo):
    changed = git_diff_names(repo)          # 例如 5 个文件
    reparsed = 0
    for path in changed:
        new_hash = sha256_file(path)
        old_hash = graph.get_file_hash(path)
        if new_hash == old_hash:
            continue                        # 内容没变(比如只动了 mtime),跳过
        ast = tree_sitter_parse(path)
        nodes, edges = extract(ast, path)
        graph.replace_file_subgraph(path, nodes, edges, new_hash)
        reparsed += 1
    # 只有真的有节点变化时,才局部重跑社区/执行流后处理
    if reparsed:
        graph.postprocess(scope="incremental")
    return reparsed

def sha256_file(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            h.update(chunk)
    return h.hexdigest()

一个 2900 文件的项目里,git 只报了 5 个变更文件,其中可能还有 2 个 hash 没变(格式化工具动了但内容等价),最后只重解析 3 个——这就是「2910 个文件被跳过、重索引 < 2 秒」的来源。

这里有个容易被忽略的工程细节:它默认只索引 git 已跟踪的文件(git ls-files。也就是说,.gitignore 里的东西自动被跳过,node_modulesvendor、构建产物根本不进图。如果你想在已跟踪文件里再排除一部分(比如 codegen 出来的 *.generated.ts),就写个 .code-review-graphignore

generated/**
*.generated.ts
vendor/**
node_modules/**

五、MCP 集成:AI 助手如何「自动」用上这张图

前面讲的都是「图怎么建」,这一节讲「图怎么被 AI 用起来」。答案是 MCP(Model Context Protocol)

5.1 一条命令自动配置全平台

pip install code-review-graph          # 或 pipx / uvx
code-review-graph install              # 自动检测并配置所有 AI 编码工具
code-review-graph build                # 解析代码库

install 这一步做了三件容易被低估的脏活:

  1. 检测你机器上装了哪些 AI 编码工具(Codex、Claude Code、Cursor、Windsurf、Zed、Continue、Gemini CLI、Qwen、Kiro、Copilot……);
  2. 为每个工具写入正确格式的 MCP server 配置;
  3. 把「图感知指令」注入到各平台的规则文件里(比如 CLAUDE.mdAGENTS.md),告诉 AI「优先用图工具,别自己乱 grep」。

它甚至会判断你是用 uvx 还是 pip 装的,从而生成不同的启动命令。生成的 MCP 配置大致是这个形状:

{
  "mcpServers": {
    "code-review-graph": {
      "command": "uvx",
      "args": ["code-review-graph", "serve"]
    }
  }
}

5.2 30 个 MCP 工具与「先要最小上下文」的纪律

图建好后,AI 助手能调用大约 30 个 MCP 工具。这里我挑几个关键的讲,因为它们的调用顺序本身就是一套方法论:

工具作用token 量级
get_minimal_context_tool超紧凑上下文,第一个调~100
get_impact_radius_tool变更文件的影响半径~几百
detect_changes_tool差异映射到受影响函数/执行流/测试缺口,带风险评分~几百
query_graph_tool查调用者/被调用者/测试/导入/继承按需
traverse_graph_tool任意节点 BFS/DFS,可设 token 预算受控
get_hub_nodes_tool连接最多的节点(架构热点)
get_bridge_nodes_tool介数中心性找架构瓶颈
get_knowledge_gaps_tool孤立节点、未测热点、薄弱社区

官方明确建议的调用链是:get_minimal_context(约 100 token)拿全局骨架 → 再 detect_changes 定位变更影响 → 只有需要细节时才 query_graphtraverse_graph 逐点展开

这套顺序背后是一个我很认同的理念:别让模型一上来就要全量信息。先给它一张便宜的「地图」,让它自己决定要放大看哪一块。这跟人类 senior 工程师做 code review 的方式几乎一样——先看 PR 的 diff 摘要和影响面,而不是一头扎进每一行。

一个典型的审查会话,AI 内部大概是这么跑的(伪代码还原):

# AI 助手收到「审查这次改动」后的内部编排
ctx = mcp.call("get_minimal_context_tool", task="review changes",
               repo_root=".")                 # ~100 tokens
changes = mcp.call("detect_changes_tool")      # 拿到风险评分 + 受影响函数
for hotspot in changes["high_risk"]:           # 只对高风险点深挖
    detail = mcp.call("query_graph_tool",
                      node=hotspot, relation="callers",
                      detail_level="minimal")   # 关键:minimal 把 token 压到最低
    review(detail)
gaps = mcp.call("get_knowledge_gaps_tool")     # 顺带检查测试缺口

注意 detail_level="minimal" 这个参数——它是把 token 消耗压到地板的关键旋钮。审查场景下你往往不需要函数完整实现,只需要签名和关系。

5.3 5 个提示模板

它还内置了 5 个 MCP 提示模板,本质是把常见工作流固化成一键流程:

  • review_changes:审查自上次提交以来的变更;
  • architecture_map:生成架构概览;
  • debug_issue:从入口点追调用链定位问题;
  • onboard_developer:新人入职,快速理解代码结构;
  • pre_merge_check:合并前检查。

onboard_developer 这个我觉得被严重低估了。新人接手一个陌生 monorepo 最痛的就是「不知道从哪看起」。有了图,你可以直接问「这个仓库的核心 hub 节点是什么、主要执行流有哪些」,AI 靠 get_hub_nodeslist_flows 就能给你画出主干,比读一周文档快得多。


六、进阶能力:不只是省 token,还是代码质量雷达

如果只把它理解成「省 token 工具」,那就低估它了。当代码变成图之后,一堆图算法就能直接落地成「代码质量洞察」。

6.1 社区检测(Leiden 算法)

它用 Leiden 算法 把关联紧密的代码聚成「社区(community)」——本质是无监督地发现你代码里的「模块」。有意思的是,当一个社区大到超过整张图的 25% 时,它会递归再分割,并自动调节分辨率。这解决了社区检测的经典问题:大仓库容易聚出一个吞掉半个项目的「巨型社区」,没有分析价值。

6.2 Hub 与 Bridge:两种不同的架构风险

  • Hub 节点:连接最多的节点(高度数)。这些是「牵一发动全身」的核心,改它们风险最高。
  • Bridge 节点:通过**介数中心性(betweenness centrality)**发现的架构瓶颈——它们不一定连接多,但删掉它们会切断两个模块间的唯一通路。

这两个是不同的风险信号。Hub 告诉你「哪里改动影响大」,Bridge 告诉你「哪里是架构上的单点」。一个健康的架构应该 Bridge 尽量少(模块间有多条解耦路径),Hub 有意识地控制(核心抽象稳定)。

6.3 异常评分与知识缺口

  • 异常评分(surprising connections):检测「意料之外的耦合」——跨社区的边、跨语言的边、从外围直连核心的边。这些往往是架构腐化的早期信号,比如某个 UI 组件直接 import 了数据库层。
  • 知识缺口(knowledge gaps):孤立节点(没人调用的死代码候选)、未测试热点(高连接但零测试覆盖)、结构性弱点。

这两个能力我会把它当成 CI 里的「架构 linter」用。传统 linter 看的是单文件的代码风格,而这些看的是跨文件的结构健康度,是完全不同维度的检查。

6.4 导出到你熟悉的工具

图不是黑盒,它能导出成多种格式:

code-review-graph visualize                    # 交互式 HTML(D3.js 力导向图)
code-review-graph visualize --format graphml   # Gephi / yEd
code-review-graph visualize --format cypher    # 导进 Neo4j 继续玩
code-review-graph visualize --format obsidian  # Obsidian 知识库,带 wikilinks
code-review-graph visualize --format svg       # 静态图
code-review-graph wiki                          # 从社区结构自动生成 Markdown Wiki

那个 Obsidian 导出很有意思——它把每个代码实体变成一个笔记,用 wikilinks 连接依赖关系。你可以像逛知识库一样在 Obsidian 里「漫游」自己的代码库。


七、性能与选型:embedding 那些坑

语义搜索是可选能力(pip install "code-review-graph[embeddings]"),支持 sentence-transformers、Google Gemini、MiniMax,以及任何 OpenAI 兼容端点(真 OpenAI、Azure、new-api、LiteLLM、vLLM、LocalAI、Ollama 的 openai 模式)。

配 OpenAI 兼容端点不用装额外依赖,设几个环境变量就行:

export CRG_OPENAI_BASE_URL=http://127.0.0.1:3000/v1   # 或 https://api.openai.com/v1
export CRG_OPENAI_API_KEY=sk-...
export CRG_OPENAI_MODEL=text-embedding-3-small
# 可选
export CRG_OPENAI_DIMENSION=1536      # v3 模型支持维度缩减
export CRG_OPENAI_BATCH_SIZE=100      # 网关有更严格批次限制时下调

当 base URL 指向 localhost 时,它会自动跳过「数据出境」警告——一个很贴心的 local-first 细节。

但这里有两个坑,README 里点到了,我展开说:

坑一:别用 preview / beta / exp 模型做长期 embedding。gemini-embedding-2-preview 这种,供应商可能悄悄换权重(维度一变你就得全量重新 embed 整个图),或者无预警下架。生产用就老老实实上 GA 模型:text-embedding-3-small/large、自宿主的 Qwen3-Embedding-8B、或原生 gemini-embedding-001

坑二:目前只 embed 函数签名,不 embed 函数体。 这是个很重要的认知——每个节点嵌入的只是类似 "parse_file function (path: str) returns Tree" 这样约 10 token 的签名。这意味着那些靠「读懂长函数体」来拉开差距的顶级 embedding 模型(比如在 MTEB-code 上刷 SOTA 的 Gemini 2、Qwen3-8B),在这种超短输入下的优势会被大幅抹平——你花大钱上大模型 embedding,收益可能远不如预期。所以在当前版本,embedding 选型上『够用就好』是理性的,函数体/docstring 嵌入还在 roadmap 上。

这个坦诚我很欣赏。很多项目会把「支持顶级 embedding 模型」当卖点吹,而它直接告诉你「在我的输入长度下,大模型和小模型差距不大,别浪费钱」。


八、什么时候不该用它

工程上没有银弹,讲讲它的边界:

  1. 超小项目没必要。 几十个文件的仓库,AI 全量读一遍也就几千 token,建图的开销反而不划算。它的价值随仓库规模非线性增长——monorepo 才是主场。
  2. 高度动态的语言精度会打折。 重反射、重元编程、大量运行时动态分发的代码,静态解析出来的边会有很多 INFERRED/AMBIGUOUS,影响半径可能偏大或偏小。Python 上可以靠 Jedi 增强缓解,其他语言就得接受一定误差。
  3. 它不理解运行时行为。 图是静态结构,捕捉不到「这条路径实际上从没被执行过」这类信息。它是「结构地图」,不是「profiler」。
  4. 签名级 embedding 的语义搜索有天花板。 如前所述,当前版本对「按语义模糊找一段实现逻辑」的能力有限,别指望它替代读代码本身。

理解这些边界,才能把它用在刀刃上:大仓库 + AI 辅助审查 + 频繁增量改动,这三个条件叠加时它的 ROI 最高。


九、上手清单:从零到跑通

给个可复制的最小实践路径:

# 1. 安装(推荐配合 uv,MCP 会自动用 uvx 启动)
pip install code-review-graph
# 或者:pipx install code-review-graph

# 2. 自动配置你所有的 AI 编码工具
code-review-graph install
# 只想配某一个:
code-review-graph install --platform claude-code

# 3. 首次全量构建(500 文件约 10 秒)
code-review-graph build

# 4. 开启 watch,之后保存自动增量更新(< 2 秒)
code-review-graph watch

# 5. 看看图长啥样
code-review-graph status          # 节点/边统计、健康度
code-review-graph visualize       # 交互式 HTML

# 6. 做一次带风险评分的变更分析
code-review-graph detect-changes

然后回到你的 AI 编辑器,直接说「Build the code review graph for this project」或「审查这次改动的影响半径」,它就会自动走 MCP 工具链,而不是傻乎乎地 grep 全仓。

多仓库场景还能注册进一个注册表做跨仓搜索:

code-review-graph register ~/work/service-a
code-review-graph register ~/work/service-b
code-review-graph repos                        # 列出已注册仓库
# AI 侧可用 cross_repo_search_tool 跨仓查询

十、总结与展望:AI 编程基础设施的一个缩影

把 code-review-graph 放到更大的图景里看,它代表了 2026 年一个很清晰的趋势:AI 编码工具的竞争,正在从「模型多聪明」转向「喂给模型的上下文多精准」

同一时间段 GitHub Trending 上冒头的项目——AI 记忆平台 cognee、代码智能图谱 code-review-graph、各种 AI 网关——本质上都在解决同一个问题的不同侧面:大模型的上下文窗口是稀缺资源,谁能把最相关的信息用最少的 token 喂进去,谁就赢。code-review-graph 选择的路径是「用确定性的图结构,替代概率性的全文检索」,这是非常工程师的思路:能用数据结构精确解决的,就别丢给大模型去猜。

往前看,几个值得期待的方向:

  • 函数体 / docstring embedding——补上当前语义搜索的短板;
  • 运行时信息融合——如果能把 profiler 或 trace 数据叠加到静态图上,「影响半径」就能从「理论可达」收敛到「实际热路径」,价值再上一个台阶;
  • 更细的置信度校准——随着各语言 enrichment 的完善,INFERRED 边转 EXTRACTED 的比例会越来越高,影响半径会越来越准。

最后回到那个最朴素的判断:如果你每天在一个中大型仓库里用 AI 写代码、审代码,并且经常觉得「AI 好像读了一堆没用的文件、上下文一下就满了」,那这个工具值得你花十分钟装上试试。它不改变你的工作流,只是在你和 AI 之间插了一层「代码地图」——而这层地图,可能就是把你的 token 账单砍掉一个数量级的那块拼图。

pip install code-review-graph && code-review-graph install——十秒的事,省下的可能是你接下来每天几十万个被浪费的 token。

推荐文章

PHP来做一个短网址(短链接)服务
2024-11-17 22:18:37 +0800 CST
一键配置本地yum源
2024-11-18 14:45:15 +0800 CST
用 Rust 玩转 Google Sheets API
2024-11-19 02:36:20 +0800 CST
Python上下文管理器:with语句
2024-11-19 06:25:31 +0800 CST
【SQL注入】关于GORM的SQL注入问题
2024-11-19 06:54:57 +0800 CST
程序员茄子在线接单