🔬

把代码变成知识图谱

深入了解 CodeGraphWiki 如何将一堆源代码文件,变成 AI 助手能理解和搜索的结构化知识。

1

源代码

2

语法树解析

3

知识图谱

4

AI 可搜索

向下滚动开始学习 ↓

01

这东西到底干嘛的?

想象你有一个巨大的代码仓库,里面有成千上万个函数——CodeGraphWiki 能让 AI 真正"读懂"它们

问题:AI 看代码就像大海捞针

你让 AI 助手帮你改一个 bug,但它不知道你的项目里有哪些函数、它们之间怎么调用、谁依赖谁。它只能一个文件一个文件地"扫",效率很低,还经常遗漏关键信息。

CodeGraphWiki 解决的就是这个问题:它给代码仓库做一次彻底的"体检",把所有函数、类、调用关系整理成一张关系网络——然后 AI 助手就能在这张网上精准搜索。

它做了什么:从源码到智能搜索的四步

1
解剖代码结构

Tree-sitter 把每个文件的函数、类、调用关系提取出来,存入图数据库

2
生成 API 文档

查询图数据库,为每个函数自动生成文档——包含签名、调用关系树、参数说明

3
创建语义搜索索引

把函数文档转化为向量嵌入,让你用自然语言搜索代码——比如问"怎么解析类型"就能找到 parse_btype 函数

4
生成 Wiki 百科

LLM 自动写一套多页技术文档,解释整个项目的架构和设计思路

打个比方

想象一个巨大的图书馆,几十万本书(代码文件)杂乱无章地堆在地上。

💡
核心洞察

CodeGraphWiki 就像一个图书管理员:它先给每本书编号(解析),然后制作一套卡片目录记录"哪本书引用了哪本书"(知识图谱),再写一本使用指南(Wiki),最后装上一个智能搜索终端,让你用普通话就能找到想要的书(语义搜索)。

真实场景:你在 Claude Code 里问了一个问题

假设你在用 Claude Code,对一个已经被 CodeGraphWiki 索引过的项目说:

"这个项目里怎么处理函数调用关系的?"

1
Claude Code 调用 find_api 工具

把你的问题发给 CodeGraphWiki 的 MCP 服务器

2
语义搜索启动

把问题转化为向量,在索引里找到最相关的函数——比如 CallProcessor、call_resolver

3
返回完整上下文

不仅返回函数名,还附带完整的 API 文档、调用关系树、源代码位置

4
Claude 给出精准回答

有了这些上下文,Claude 能告诉你具体哪个文件、哪一行在处理调用关系

检验一下

你的 AI 助手在大项目里总是找不到关键函数。根据刚才学到的,CodeGraphWiki 主要通过什么方式解决这个问题?

如果你想让 AI 更好地理解一个新项目,你应该先做什么?

02

认识主角们

五个核心组件,各司其职,像一条精密的流水线

五大角色一览

这个项目的所有功能,归根结底由五个"角色"完成。想象一个快递分拣中心:每个角色负责流水线上的一个环节。

🔍
Tree-sitter 解析器

"验货员"——把源代码文件拆解成结构化的语法树,提取出函数、类、调用关系

🗄️
Kuzu 图数据库

"档案馆"——把所有代码实体和它们的关系存成一张巨大的网络图

📝
API 文档生成器

"文书"——查询图数据库,为每个函数写出详细的技术文档

🧠
向量嵌入引擎

"翻译官"——把文档变成数字向量,让计算机能通过"比较数字"来理解"意思相近"

🔌
MCP 服务器

"前台接待"——对外暴露 19 个工具,让 Claude Code 等 AI 助手随时查询

它们住在哪里

每个角色对应项目中特定的文件和文件夹:

code_graph_builder/ Python 主包——所有核心逻辑都在这里
📄 builder.py 核心入口——CodeGraphBuilder 类,启动整个图构建流程
📁 parsers/ Tree-sitter 解析器——拆解代码的"验货员"
📄 definition_processor.py 提取函数、类的定义
📄 call_processor.py 提取函数调用关系
📄 call_resolver.py 解析函数名到完整限定名
📁 services/ 数据库服务——"档案馆"的管理层
📄 kuzu_service.py Kuzu 图数据库的读写操作
📁 embeddings/ 向量嵌入——"翻译官"的工作间
📄 qwen3_embedder.py 调用 Qwen3 API 生成嵌入向量
📄 vector_store.py 存储和搜索向量
📁 mcp/ MCP 服务器——"前台接待"所在地
📄 server.py 服务器启动入口
📄 tools.py 19 个 MCP 工具的定义
📄 pipeline.py 四步流水线的编排
💡
关键洞察:分而治之

注意每个文件夹只负责一件事。这种"把大问题拆成小问题"的策略,工程师叫它关注点分离。下次你让 AI 搭建新项目时,可以明确要求:"把解析逻辑、数据库操作、对外接口分开放在不同的文件夹里。"

看看核心入口长什么样

builder.py 里的 CodeGraphBuilder 类是整个项目的起点:

CODE
class CodeGraphBuilder:
    def __init__(self,
        repo_path: str | Path,
        backend: str = "kuzu",
        backend_config: dict | None = None,
        scan_config: dict | None = None,
    ) -> None:
PLAIN ENGLISH

定义一个叫 CodeGraphBuilder 的"蓝图"(类)

它的初始化需要以下材料:

repo_path——你想分析的代码仓库在哪个文件夹

backend——用什么数据库存储,默认是 "kuzu"(一个不需要安装服务器的轻量数据库)

backend_config——数据库的配置细节(可选)

scan_config——扫描时要排除哪些文件夹(可选)

检验一下

场景

你想给 CodeGraphWiki 换一个不同的向量搜索引擎(比如从内存搜索换成 Qdrant 云服务),你应该主要修改哪个目录?

03

数据如何流动

跟踪一次完整的代码索引过程——从"一堆文件"到"AI 能搜索的知识"

组件之间的"群聊"

当你执行 initialize_repository 时,幕后发生的事就像这段对话:

0 / 10 条消息

数据流动路径

📂
源代码
🔍
Tree-sitter
🗄️
Kuzu DB
🧠
Embedder
🔌
MCP Server
点击"下一步"开始
步骤 0 / 6

进度条背后的秘密

当 AI 助手显示"索引中... 35%"时,这个数字是怎么算出来的?看看 pipeline.py 里的权重分配:

CODE
ProgressCb = Callable[[str, float], None] | None
"""Progress callback: (message, percentage_0_to_100)

Pipeline weight allocation:
    Step 1 (graph + API docs):  0 – 15 %
    Step 2 (embeddings):       15 – 40 %
    Step 3 (wiki generation):  40 – 100 %
"""
PLAIN ENGLISH

定义一个"进度回调"——用来向外界报告"做到哪了"

它接收两个参数:一条消息描述和一个 0-100 的百分比

权重怎么分:

图谱构建 + 文档生成 = 0% 到 15%(最快的步骤)

向量嵌入 = 15% 到 40%(需要调 API,比较慢)

Wiki 生成 = 40% 到 100%(需要 LLM 逐页生成,最耗时)

📌
为什么这样分配?

图谱构建是纯本地计算(快),向量嵌入要调外部 API(中等),Wiki 生成要让 LLM 逐页思考并写长文(慢)。所以 Wiki 占了 60% 的进度权重。下次你看到进度条前 40% 跑得飞快、后面慢下来,就知道原因了!

检验一下

场景

索引一个大项目时,进度条卡在 42% 很长时间不动。根据你了解的进度权重分配,最可能是哪个步骤正在运行?

04

给代码做 X 光

Tree-sitter 是怎么把一段看似普通的代码文本,变成结构化数据的

代码的"骨骼扫描"

人类读代码时,自然而然能看出"这是一个函数"、"这里调用了另一个函数"。但对计算机来说,代码只是一长串字符。

AST(抽象语法树)就是让计算机"读懂"代码结构的方式——把平面文本变成一棵有层次的树。

📄

源代码文本

def add(a, b): return a + b

🌳

语法树

function_definition → name: "add" → params: ["a", "b"] → body: binary_op(a + b)

📊

结构化数据

{name: "add", params: 2, return_type: "inferred", line: 1}

Tree-sitter 的"搜索模式"

Tree-sitter 不只是生成语法树——它还支持查询模式,让你可以精确地说"我要找所有函数定义"。

CODE
# Python 函数查询模式
(function_definition
  name: (identifier) @func_name
  parameters: (parameters) @params
  body: (block) @body
)
PLAIN ENGLISH

在语法树里寻找所有"函数定义"节点

把函数名捕获到变量 @func_name 里

把参数列表捕获到变量 @params 里

把函数体捕获到变量 @body 里

就像一个模板——"找到所有长这个样子的东西"

一个工具,十种语言

CodeGraphWiki 支持从 Python 到 Rust 的 10+ 种编程语言。每种语言有自己的查询模式,因为语法不同:

Python function_definition → 直接匹配 def 关键字
C/C++ function_definition → 还要检查 storage_class_specifier (static 等)
JavaScript function_declaration + arrow_function → 两种函数写法都要匹配
Rust function_item → 还要提取 trait、impl 块
Go function_declaration + method_declaration → 区分普通函数和方法
💡
为什么不用正则表达式?

你可能想:"直接用正则表达式匹配 'def' 不就行了?"问题是正则不理解嵌套——它分不清一个 def 是顶层函数还是嵌套在 if 块里的。Tree-sitter 理解整个语法结构,所以永远不会搞混。

DefinitionProcessor:提取定义的工人

CODE
class DefinitionProcessor:
    def __init__(self,
        ingestor: IngestorProtocol,
        repo_path: Path,
        project_name: str,
        function_registry: FunctionRegistryTrieProtocol,
        simple_name_lookup: SimpleNameLookup,
        import_processor: ImportProcessor,
    ):
PLAIN ENGLISH

定义"定义处理器"——它的工作是从语法树中提取函数和类

它需要这些"工具"才能工作:

ingestor——用来把结果写入数据库的接口

repo_path——代码仓库的路径

function_registry——一个"电话簿",记录所有已知函数的完整名称

simple_name_lookup——一个快速查找表,函数简称→完整名

import_processor——处理 import 语句的伙伴,帮助解析跨模块调用

检验一下

场景

你想让 CodeGraphWiki 支持一门新的编程语言(比如 Kotlin)。你需要添加什么?

05

记忆宫殿

图数据库和向量搜索——两种截然不同但互补的记忆方式

两套记忆系统

CodeGraphWiki 用两种完全不同的方式存储知识,就像人脑的两种记忆方式:

🗺️

Kuzu 图数据库

像一张城市地图——记录"A 调用 B"、"C 继承 D"这样的精确关系。适合回答"谁调用了这个函数?"

🧭

向量存储

像一个"意思相似度"引擎——你说"处理类型",它就能找到"parse_btype"。适合回答"哪个函数做了类似的事?"

图谱里有什么

图数据库用Cypher 语言查询。节点是代码实体,边是它们之间的关系:

节点类型

Function 函数
Class
Module 模块/文件
Type 类型定义

关系类型

CALLS 函数 A 调用了函数 B
DEFINES 模块定义了某个函数
INHERITS 类继承自另一个类
IMPORTS 模块导入了另一个模块

向量搜索的核心魔法

向量搜索的关键是余弦相似度——比较两个向量的"方向"是否接近:

CODE
def cosine_similarity(a: list[float], b: list[float]) -> float:
    dot_product = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(x * x for x in b))
    return dot_product / (norm_a * norm_b)
PLAIN ENGLISH

比较两个向量(两列数字)有多"相似"

第一步:把两列数字对应相乘再加起来(点积——衡量方向一致性)

第二步:计算每个向量的"长度"(就像测量箭头有多长)

第三步:用同样的方法算另一个向量的长度

最后:点积除以两个长度的乘积——结果越接近 1,两段文字的"意思"越接近

每个函数的"简历"

向量嵌入并不只看函数名——它为每个函数构建一份丰富的上下文"简历":

1
函数名 + 文件位置

比如:parse_btype,位于 tccgen.c

2
文档字符串

"Parse base type declaration including struct/union/enum specifiers"

3
调用者和被调用者

谁在调用我?我又调用了谁?最多各 10 个名字

4
前 2000 字符源代码

函数的实际代码——让嵌入能理解"这个函数具体在做什么"

💡
上下文决定精度

如果只用函数名去生成向量,"parse_btype" 和 "parse_string" 看起来很像(都有 parse)。但加上调用关系和源代码后,嵌入能区分一个是处理类型,一个是处理字符串。信息越丰富,搜索越精准。

检验一下

场景

用户在 AI 助手里问"找出所有调用了 database_connect 的函数"。这个查询更适合用哪种方式处理?

06

AI 的接口

MCP 服务器如何把所有能力包装成 AI 助手能调用的"工具"

什么是 MCP?

你有没有用过万能充电器?不管是 iPhone 还是安卓,插上同一个USB-C 就能充电。

MCP 对 AI 工具的作用是一样的:它是一个标准协议,让任何 AI 助手(Claude Code、Cursor、OpenCode)都能调用 CodeGraphWiki 的能力,不需要各写一套适配器

19 个"超能力"

MCP 服务器对外暴露了 19 个工具,分为四大类:

🏗️

仓库管理 (4个)

initialize_repository、get_repository_info、list_repositories、switch_repository

🔍

代码搜索 (5个)

find_api、semantic_search、query_code_graph、get_code_snippet、locate_function

📖

API 文档 (4个)

list_api_docs、get_api_doc、list_api_interfaces、generate_api_docs

📚

Wiki 与构建 (6个)

list_wiki_pages、get_wiki_page、generate_wiki、rebuild_embeddings、build_graph、prepare_guidance

服务器是怎么启动的

CODE
async def main() -> None:
    workspace = Path(
        os.environ.get("CGB_WORKSPACE",
            Path.home() / ".code-graph-builder")
    ).expanduser().resolve()

    registry = MCPToolsRegistry(workspace=workspace)
    server = Server("code-graph-builder")
PLAIN ENGLISH

定义一个异步的启动函数(异步 = 能同时处理多件事)

先确定工作空间在哪——默认是你电脑的 ~/.code-graph-builder/ 文件夹

也可以通过环境变量 CGB_WORKSPACE 自定义位置

创建工具注册表——它管理所有 19 个工具和已索引的仓库

创建 MCP 服务器实例,给它起名 "code-graph-builder"

通信方式:标准输入/输出

MCP 服务器使用 stdio 通信——不需要网络端口,直接通过标准输入/输出交换 JSON 消息。

📌
为什么用 stdio 不用 HTTP?

HTTP 需要开端口、处理网络安全。stdio 更简单——AI 助手直接启动服务器进程,通过管道通信。没有端口冲突,没有防火墙问题,也不需要用户做任何网络配置。

检验一下

场景

你想让一个不支持 MCP 的 AI 助手(比如一个自定义的 AI 工具)也能使用 CodeGraphWiki 的能力。你需要做什么?

07

全局视角

把所有线索串起来——完整的架构和调试直觉

完整架构一览

对外接口层

🔌 MCP Server (server.py)
⌨️ CLI (cli.py)

编排层

🎯 Pipeline (pipeline.py)
📋 Tools Registry (tools.py)

处理层

🔍 Parsers (parsers/)
📝 Doc Generator
🧩 RAG Engine (rag/)

存储层

🗄️ Kuzu DB (services/)
🧠 Vector Store (embeddings/)
点击任意组件查看详细说明

当事情出错时

有了对架构的理解,你可以用"排除法"快速定位问题。以下是最常见的故障场景:

!
索引卡在某个百分比不动

如果 0-15%:图谱构建问题,检查文件权限和 Tree-sitter 语言支持。如果 15-40%:嵌入 API 问题,检查 DASHSCOPE_API_KEY。如果 40-100%:LLM 问题,检查 LLM_API_KEY。

!
搜索结果不相关

向量嵌入可能过期了——代码改了但没重新索引。运行 rebuild_embeddings 重新生成向量。

!
数据库锁定错误

Kuzu 是文件级锁——如果另一个进程在用数据库,会报锁定错误。系统会自动重试最多 5 次,指数退避(1秒、2秒、4秒...)。如果仍然失败,手动关闭其他使用数据库的进程。

数据存在哪里

所有索引结果都保存在工作空间目录下:

~/.code-graph-builder/ 工作空间根目录
📄 active.txt 记录当前活跃仓库的名字
📄 .env API 密钥配置
📁 myproject_a1b2c3d4/ 某个仓库的索引数据(名字+哈希防冲突)
📄 meta.json 仓库路径、索引时间、完成状态
📁 graph.db/ Kuzu 图数据库文件
📄 vectors.pkl 向量存储的序列化文件
📁 api_docs/ 三级 API 文档
📁 wiki/ 生成的 Wiki 页面

你现在能做什么

学完这七个模块,你现在有了一些新"超能力":

🎯

更精准地指导 AI

"先用 find_api 搜索相关函数,再用 query_code_graph 看调用关系"——你能给出具体的工具调用建议

🔧

自主排查问题

索引失败?你知道先看哪个步骤、检查哪个 API 密钥、是不是数据库锁冲突

🏛️

理解架构决策

"为什么用嵌入式数据库不用 PostgreSQL?"——因为不需要用户装 Docker,降低使用门槛

🗣️

掌握技术词汇

AST、知识图谱、余弦相似度、MCP 协议、向量嵌入——这些概念你都能用来和工程师沟通了

最终挑战

综合场景

你刚用 CodeGraphWiki 索引了一个 TypeScript 项目,但 AI 搜索"用户认证"相关的代码时,结果完全不相关。你会怎么排查?

综合场景

你想给 CodeGraphWiki 添加一个新功能:"显示某个函数被修改的 Git 历史"。根据项目的架构,你应该怎么向 AI 助手描述这个需求?