深入了解 CodeGraphWiki 如何将一堆源代码文件,变成 AI 助手能理解和搜索的结构化知识。
源代码
语法树解析
知识图谱
AI 可搜索
向下滚动开始学习 ↓
想象你有一个巨大的代码仓库,里面有成千上万个函数——CodeGraphWiki 能让 AI 真正"读懂"它们
你让 AI 助手帮你改一个 bug,但它不知道你的项目里有哪些函数、它们之间怎么调用、谁依赖谁。它只能一个文件一个文件地"扫",效率很低,还经常遗漏关键信息。
CodeGraphWiki 解决的就是这个问题:它给代码仓库做一次彻底的"体检",把所有函数、类、调用关系整理成一张关系网络——然后 AI 助手就能在这张网上精准搜索。
用 Tree-sitter 把每个文件的函数、类、调用关系提取出来,存入图数据库
查询图数据库,为每个函数自动生成文档——包含签名、调用关系树、参数说明
把函数文档转化为向量嵌入,让你用自然语言搜索代码——比如问"怎么解析类型"就能找到 parse_btype 函数
用 LLM 自动写一套多页技术文档,解释整个项目的架构和设计思路
想象一个巨大的图书馆,几十万本书(代码文件)杂乱无章地堆在地上。
CodeGraphWiki 就像一个图书管理员:它先给每本书编号(解析),然后制作一套卡片目录记录"哪本书引用了哪本书"(知识图谱),再写一本使用指南(Wiki),最后装上一个智能搜索终端,让你用普通话就能找到想要的书(语义搜索)。
假设你在用 Claude Code,对一个已经被 CodeGraphWiki 索引过的项目说:
"这个项目里怎么处理函数调用关系的?"
把你的问题发给 CodeGraphWiki 的 MCP 服务器
把问题转化为向量,在索引里找到最相关的函数——比如 CallProcessor、call_resolver
不仅返回函数名,还附带完整的 API 文档、调用关系树、源代码位置
有了这些上下文,Claude 能告诉你具体哪个文件、哪一行在处理调用关系
五个核心组件,各司其职,像一条精密的流水线
这个项目的所有功能,归根结底由五个"角色"完成。想象一个快递分拣中心:每个角色负责流水线上的一个环节。
"验货员"——把源代码文件拆解成结构化的语法树,提取出函数、类、调用关系
"档案馆"——把所有代码实体和它们的关系存成一张巨大的网络图
"文书"——查询图数据库,为每个函数写出详细的技术文档
"翻译官"——把文档变成数字向量,让计算机能通过"比较数字"来理解"意思相近"
"前台接待"——对外暴露 19 个工具,让 Claude Code 等 AI 助手随时查询
每个角色对应项目中特定的文件和文件夹:
注意每个文件夹只负责一件事。这种"把大问题拆成小问题"的策略,工程师叫它关注点分离。下次你让 AI 搭建新项目时,可以明确要求:"把解析逻辑、数据库操作、对外接口分开放在不同的文件夹里。"
builder.py 里的 CodeGraphBuilder 类是整个项目的起点:
class CodeGraphBuilder:
def __init__(self,
repo_path: str | Path,
backend: str = "kuzu",
backend_config: dict | None = None,
scan_config: dict | None = None,
) -> None:
定义一个叫 CodeGraphBuilder 的"蓝图"(类)
它的初始化需要以下材料:
repo_path——你想分析的代码仓库在哪个文件夹
backend——用什么数据库存储,默认是 "kuzu"(一个不需要安装服务器的轻量数据库)
backend_config——数据库的配置细节(可选)
scan_config——扫描时要排除哪些文件夹(可选)
你想给 CodeGraphWiki 换一个不同的向量搜索引擎(比如从内存搜索换成 Qdrant 云服务),你应该主要修改哪个目录?
跟踪一次完整的代码索引过程——从"一堆文件"到"AI 能搜索的知识"
当你执行 initialize_repository 时,幕后发生的事就像这段对话:
当 AI 助手显示"索引中... 35%"时,这个数字是怎么算出来的?看看 pipeline.py 里的权重分配:
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 %
"""
定义一个"进度回调"——用来向外界报告"做到哪了"
它接收两个参数:一条消息描述和一个 0-100 的百分比
权重怎么分:
图谱构建 + 文档生成 = 0% 到 15%(最快的步骤)
向量嵌入 = 15% 到 40%(需要调 API,比较慢)
Wiki 生成 = 40% 到 100%(需要 LLM 逐页生成,最耗时)
图谱构建是纯本地计算(快),向量嵌入要调外部 API(中等),Wiki 生成要让 LLM 逐页思考并写长文(慢)。所以 Wiki 占了 60% 的进度权重。下次你看到进度条前 40% 跑得飞快、后面慢下来,就知道原因了!
索引一个大项目时,进度条卡在 42% 很长时间不动。根据你了解的进度权重分配,最可能是哪个步骤正在运行?
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 不只是生成语法树——它还支持查询模式,让你可以精确地说"我要找所有函数定义"。
# Python 函数查询模式
(function_definition
name: (identifier) @func_name
parameters: (parameters) @params
body: (block) @body
)
在语法树里寻找所有"函数定义"节点
把函数名捕获到变量 @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 理解整个语法结构,所以永远不会搞混。
class DefinitionProcessor:
def __init__(self,
ingestor: IngestorProtocol,
repo_path: Path,
project_name: str,
function_registry: FunctionRegistryTrieProtocol,
simple_name_lookup: SimpleNameLookup,
import_processor: ImportProcessor,
):
定义"定义处理器"——它的工作是从语法树中提取函数和类
它需要这些"工具"才能工作:
ingestor——用来把结果写入数据库的接口
repo_path——代码仓库的路径
function_registry——一个"电话簿",记录所有已知函数的完整名称
simple_name_lookup——一个快速查找表,函数简称→完整名
import_processor——处理 import 语句的伙伴,帮助解析跨模块调用
你想让 CodeGraphWiki 支持一门新的编程语言(比如 Kotlin)。你需要添加什么?
图数据库和向量搜索——两种截然不同但互补的记忆方式
CodeGraphWiki 用两种完全不同的方式存储知识,就像人脑的两种记忆方式:
像一张城市地图——记录"A 调用 B"、"C 继承 D"这样的精确关系。适合回答"谁调用了这个函数?"
像一个"意思相似度"引擎——你说"处理类型",它就能找到"parse_btype"。适合回答"哪个函数做了类似的事?"
图数据库用Cypher 语言查询。节点是代码实体,边是它们之间的关系:
Function 函数Class 类Module 模块/文件Type 类型定义CALLS 函数 A 调用了函数 BDEFINES 模块定义了某个函数INHERITS 类继承自另一个类IMPORTS 模块导入了另一个模块向量搜索的关键是余弦相似度——比较两个向量的"方向"是否接近:
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)
比较两个向量(两列数字)有多"相似"
第一步:把两列数字对应相乘再加起来(点积——衡量方向一致性)
第二步:计算每个向量的"长度"(就像测量箭头有多长)
第三步:用同样的方法算另一个向量的长度
最后:点积除以两个长度的乘积——结果越接近 1,两段文字的"意思"越接近
向量嵌入并不只看函数名——它为每个函数构建一份丰富的上下文"简历":
比如:parse_btype,位于 tccgen.c
"Parse base type declaration including struct/union/enum specifiers"
谁在调用我?我又调用了谁?最多各 10 个名字
函数的实际代码——让嵌入能理解"这个函数具体在做什么"
如果只用函数名去生成向量,"parse_btype" 和 "parse_string" 看起来很像(都有 parse)。但加上调用关系和源代码后,嵌入能区分一个是处理类型,一个是处理字符串。信息越丰富,搜索越精准。
用户在 AI 助手里问"找出所有调用了 database_connect 的函数"。这个查询更适合用哪种方式处理?
MCP 服务器如何把所有能力包装成 AI 助手能调用的"工具"
你有没有用过万能充电器?不管是 iPhone 还是安卓,插上同一个USB-C 就能充电。
MCP 对 AI 工具的作用是一样的:它是一个标准协议,让任何 AI 助手(Claude Code、Cursor、OpenCode)都能调用 CodeGraphWiki 的能力,不需要各写一套适配器。
MCP 服务器对外暴露了 19 个工具,分为四大类:
initialize_repository、get_repository_info、list_repositories、switch_repository
find_api、semantic_search、query_code_graph、get_code_snippet、locate_function
list_api_docs、get_api_doc、list_api_interfaces、generate_api_docs
list_wiki_pages、get_wiki_page、generate_wiki、rebuild_embeddings、build_graph、prepare_guidance
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")
定义一个异步的启动函数(异步 = 能同时处理多件事)
先确定工作空间在哪——默认是你电脑的 ~/.code-graph-builder/ 文件夹
也可以通过环境变量 CGB_WORKSPACE 自定义位置
创建工具注册表——它管理所有 19 个工具和已索引的仓库
创建 MCP 服务器实例,给它起名 "code-graph-builder"
MCP 服务器使用 stdio 通信——不需要网络端口,直接通过标准输入/输出交换 JSON 消息。
HTTP 需要开端口、处理网络安全。stdio 更简单——AI 助手直接启动服务器进程,通过管道通信。没有端口冲突,没有防火墙问题,也不需要用户做任何网络配置。
你想让一个不支持 MCP 的 AI 助手(比如一个自定义的 AI 工具)也能使用 CodeGraphWiki 的能力。你需要做什么?
把所有线索串起来——完整的架构和调试直觉
有了对架构的理解,你可以用"排除法"快速定位问题。以下是最常见的故障场景:
如果 0-15%:图谱构建问题,检查文件权限和 Tree-sitter 语言支持。如果 15-40%:嵌入 API 问题,检查 DASHSCOPE_API_KEY。如果 40-100%:LLM 问题,检查 LLM_API_KEY。
向量嵌入可能过期了——代码改了但没重新索引。运行 rebuild_embeddings 重新生成向量。
Kuzu 是文件级锁——如果另一个进程在用数据库,会报锁定错误。系统会自动重试最多 5 次,指数退避(1秒、2秒、4秒...)。如果仍然失败,手动关闭其他使用数据库的进程。
所有索引结果都保存在工作空间目录下:
学完这七个模块,你现在有了一些新"超能力":
"先用 find_api 搜索相关函数,再用 query_code_graph 看调用关系"——你能给出具体的工具调用建议
索引失败?你知道先看哪个步骤、检查哪个 API 密钥、是不是数据库锁冲突
"为什么用嵌入式数据库不用 PostgreSQL?"——因为不需要用户装 Docker,降低使用门槛
AST、知识图谱、余弦相似度、MCP 协议、向量嵌入——这些概念你都能用来和工程师沟通了
你刚用 CodeGraphWiki 索引了一个 TypeScript 项目,但 AI 搜索"用户认证"相关的代码时,结果完全不相关。你会怎么排查?
你想给 CodeGraphWiki 添加一个新功能:"显示某个函数被修改的 Git 历史"。根据项目的架构,你应该怎么向 AI 助手描述这个需求?