LLM Wiki:把代码仓库编译成可阅读、可检索的知识系统
LLM Wiki 不该停在批量生成代码摘要。本文从结构解析、页面规划、证据组装、分层生成和增量更新出发,说明它与 RAG、FastCode 的关系,以及怎样构建一套能被人和 Agent 共同使用的代码知识库。
接手一个陌生仓库时,我通常先找 README,再看目录、启动入口和几个核心模块。README 如果只写了安装命令,后面的理解几乎都靠搜索:某个接口从哪里进入,数据在哪一层落库,缓存何时失效,模块之间为什么这样依赖。
Coding Agent 也在做同一件事,只是速度更快,代价更隐蔽。它可以不停地 list、grep 和 read,每一步都在消耗上下文。仓库缺少结构化说明时,人要重复理解,Agent 也要重复探索。
LLM Wiki 想把这部分成本提前支付:读取仓库,恢复代码结构,生成一套有目录、有交叉链接、有图、有源码引用的说明。开发者可以浏览,Agent 也可以按需检索。DeepWiki、DeepWiki-Open、RepoAgent 和 CodeWiki 都属于这条路线,只是产品形态和技术重点不同。
这篇文章讨论的是 LLM Wiki 这一类系统,不局限于某个具体产品。我更关心四个问题:它和普通文档生成有什么区别,页面怎样从代码中长出来,代码变更后怎样保持可信,以及它如何同 RAG、FastCode 组成一套代码知识系统。
一、LLM Wiki 解决的是重复理解,不是缺少文字
代码仓库里从来不缺文字。类名、函数名、注释、提交记录和 README 都是文字。真正短缺的是可复用的解释。
比如一个订单系统有这些文件:
api/order_controller.py
application/create_order.py
domain/order.py
domain/pricing.py
infra/order_repository.py
infra/event_publisher.py
workers/order_timeout.py
逐文件摘要可以准确描述每个文件,却未必回答新人最关心的问题:创建订单经历哪些步骤,价格在哪个阶段确定,失败如何回滚,超时关闭和支付回调会不会竞争。
这些答案需要跨文件恢复一条业务链路。好的 Wiki 页面围绕读者问题组织,而不是照着目录逐个复述源码。它可能生成“订单创建流程”“价格计算规则”“订单状态机”“事件发布与补偿”四页,并把相关文件作为证据挂在页面下面。
因此,LLM Wiki 的输入是仓库,输出不是一批摘要,而是一套稳定的知识视图:
- 页面边界来自架构、领域和流程,不等同于文件边界;
- 页面中的结论能够回到代码符号和仓库版本;
- 页面之间有明确关系,读者能从总览进入细节;
- 代码变化后,系统知道哪些页面需要重新检查。
如果生成结果无法追溯来源,也无法随着代码更新,它只是一份长摘要。第一次阅读或许有帮助,几周后就会变成新的信息噪声。
二、它也是知识库,但知识形态和 RAG 不同
把 LLM Wiki、RAG 和 FastCode 放在一起看会更清楚。三者都在建设模型外部知识,也都试图控制 LLM 最终看到的内容。
区别在交付形态。
LLM Wiki 把代码事实加工成可阅读页面。目录、标题、图和交叉链接给读者建立整体认知,适合“我先系统了解这个仓库”。
RAG 根据当前问题即时召回证据。它不要求提前为每个问题写好页面,适合“我现在有一个具体问题”。
FastCode 进一步利用符号、调用、依赖和继承关系做结构导航,在有限 Token 下选择最值得读取的代码,适合“我需要定位实现、还原链路或分析影响范围”。
它们可以共享同一套底层事实:源码、AST、符号表、调用关系、文档、Git 版本和权限。上层分别生成 Wiki 页面、RAG 检索结果和面向 Agent 的最小代码上下文。
这也是我更愿意把它们放进「RAG 与知识库」专栏的原因。它们都在回答同一个工程问题:怎样把仓库中的事实组织成模型和人能够稳定使用的外部知识。
三、逐文件总结为什么做不好仓库 Wiki
最容易实现的方案是遍历文件,把每个文件交给 LLM 总结,再拼成页面:
for path in repository.files():
source = read(path)
summary = llm.generate(SUMMARY_PROMPT, source)
save_markdown(path, summary)
这个版本很快就能跑起来,也会很快遇到四类问题。
1. 局部代码缺少调用背景
一个函数的真实角色取决于谁调用它、它调用谁、参数从哪里来、结果流向哪里。只把函数正文交给模型,它可能正确解释算法,却误判这个函数在系统中的职责。
例如 calculate_price() 看起来负责计算价格,实际可能只是实验分流后的一个实现。页面如果不包含调用方、开关配置和兜底路径,就会把局部实现写成系统规则。
2. 文件结构不是读者的知识结构
目录通常服务于构建系统、依赖管理和团队分工。读者更关心入口、流程、状态、边界和常见改动。一个业务流程可能横跨 controller、service、domain、repository 和 worker,按文件生成文档会把这条链路切碎。
3. 摘要会在向上汇总时失真
很多实现采用“先总结函数,再总结文件,再总结模块,最后总结系统”的树形压缩。每一层都可能丢掉条件、例外和不确定性。到架构总览时,原始证据已经经过几轮改写,读起来连贯,事实却难以核对。
4. 全量重写让 Wiki 越更新越不可信
一次提交只改了支付超时策略,如果系统重新生成整个 Wiki,未受影响的页面也会改变措辞,甚至引入新的解释偏差。评审者无法从庞大的文档 diff 中判断哪些变化来自代码,哪些只是模型换了一种说法。
因此,仓库 Wiki 需要先建立结构模型,再规划页面,并把每一条结论和证据、版本、更新范围绑定起来。
四、第一层:把仓库编译成知识模型
LLM Wiki 的离线阶段很像编译器前端。系统先解析仓库,得到比原始文本更适合推理的中间表示。
1. 解析代码实体
基础实体至少包括:
Repository
└─ Package / Module
└─ File
├─ Class / Interface
├─ Function / Method
├─ Type / Constant
└─ Configuration
Tree-sitter、语言编译器 API 或 LSP 能帮助识别符号、签名、注释和引用。不同语言的可解析程度并不一致,框架生成代码、宏、动态导入和反射还会制造额外困难,所以解析结果要保留语言和解析器置信度。
2. 提取关系
页面质量很大程度取决于关系。常见关系包括:
- 文件导入与包依赖;
- 函数调用与反向调用;
- 接口实现与类继承;
- API 路由到处理函数;
- 数据模型到表、消息或序列化结构;
- 配置项到读取位置;
- 测试到被测符号;
- 文档到代码路径和符号引用。
静态分析无法完整恢复运行时行为。依赖注入、消息路由、反射和配置驱动逻辑往往需要框架适配器、运行时 Trace 或人工补充。知识模型应该允许“不确定关系”,而不是把所有边都伪装成确定事实。
3. 绑定版本与来源
每个实体和关系都应携带版本信息:
{
"id": "symbol:domain.order.Order.confirm",
"kind": "method",
"path": "domain/order.py",
"lines": [84, 121],
"commit": "8f2a7c1",
"language": "python",
"references": [
"symbol:application.confirm_order.handle",
"symbol:domain.order.OrderStatus.CONFIRMED"
]
}
页面引用这个对象时,就能显示对应文件、行号和 commit。用户查看旧版本 Wiki 时也能回到当时的代码,而不是跳到已经变化的主分支。
五、第二层:先规划页面,再生成正文
仓库里有几万个符号,不代表 Wiki 需要几万个页面。页面规划决定哪些知识值得形成稳定入口。
一个实用的页面体系通常包含四类内容。
| 页面类型 | 回答的问题 | 常用证据 |
|---|---|---|
| 系统总览 | 这个项目解决什么问题,主要边界是什么 | README、入口、顶层模块、部署配置 |
| 模块说明 | 某个模块负责什么,对外暴露什么 | 包结构、公开接口、依赖关系 |
| 关键流程 | 一个请求、任务或事件怎样流动 | 路由、调用图、状态变化、消息关系 |
| 核心概念 | 领域对象、规则和约束如何定义 | 类型、校验、状态机、ADR、测试 |
页面规划可以结合三类信号:
- 结构信号:目录、包、依赖簇、中心符号和入口点。
- 语义信号:README 标题、代码注释、类和方法命名。
- 使用信号:提交频率、故障记录、搜索日志和人工指定的重点。
依赖聚类能给出候选模块,模型负责给模块命名并判断是否值得单独成页。模型生成的目录仍需约束,否则容易把同一主题拆成几页近义内容,或者生成“核心功能”“主要模块”这种难以维护的空泛标题。
页面规划最好产出一份可以验证的 manifest:
pages:
- id: order-lifecycle
title: 订单生命周期
type: flow
root_symbols:
- domain.order.Order
- application.create_order.handle
- workers.order_timeout.handle
required_questions:
- 订单有哪些状态
- 哪些事件触发状态变化
- 并发更新如何处理
这样做的好处是,页面生成失败时可以定位到页面规划、证据召回或正文合成中的某一层,而不是只看到一篇“写得不太对”的文章。
六、第三层:为每一页组装证据包
模型不应该直接面对整个仓库。生成某一页前,系统要围绕页面目标构建证据包。
以“订单生命周期”为例,证据包可以包含:
OrderStatus枚举和Order聚合根;- 修改状态的方法及其调用方;
- 订单创建、支付回调、取消和超时任务;
- 乐观锁或事务代码;
- 对应单元测试和状态转换测试;
- 描述状态含义的 ADR 或产品规则。
证据选择不能只靠向量相似度。符号引用、调用图、依赖关系和测试映射提供更稳定的结构信号;关键词与向量检索用于补充注释、文档和命名不一致的实现。
每个证据片段还需要记录用途:
{
"page": "order-lifecycle",
"claim_scope": "payment transition",
"source": "application/payment_callback.py:61-104",
"commit": "8f2a7c1",
"relation": "calls Order.confirm",
"confidence": 0.93
}
正文生成时,Prompt 要求模型只根据证据包写作,并为架构结论、流程步骤和约束附上引用。没有证据的猜测可以单独标成“待确认”,不能混进确定叙述。
七、分层生成不是反复总结
大型仓库无法一次放进上下文,分层生成不可避免。问题在于怎样减少信息经过多轮改写后的漂移。
RepoAgent 的思路是先分析全局结构,再为代码对象生成文档,并利用 Git 变化维护文档。CodeWiki 更进一步,把仓库级文档拆成层次结构,通过递归 Agent 生成文字和可视化内容。DocAgent 则把分层处理用于上下文感知的 docstring 生成。
这些工作共同说明:局部内容需要全局结构,上一层也需要能够重新访问底层证据。
一个较稳妥的分层方式是:
代码事实层
↓
符号卡片:职责、签名、引用、证据位置
↓
模块页:公开能力、内部协作、依赖边界
↓
流程页:跨模块调用、状态变化、失败路径
↓
系统总览:模块地图、关键入口、部署边界
上层页面不只读取下层摘要,还能沿引用回到原始符号。模型写“订单服务通过消息队列通知库存服务”时,系统应当提供发布代码、Topic 配置和消费入口。只有模块摘要而没有底层证据时,这个结论很容易把计划中的设计写成已经运行的事实。
生成顺序也可以根据依赖关系安排。先处理被大量依赖的基础模块,再处理调用它们的上层模块,后续页面会得到更稳定的背景。遇到循环依赖时,可以先生成实体卡片,再以依赖簇为单位完成模块说明。
八、图不是装饰,它应该回答结构问题
自动 Wiki 很喜欢生成大量流程图,但图多并不代表解释清楚。每张图应当回答一个确定问题。
| 问题 | 合适的图 |
|---|---|
| 系统有哪些边界和外部依赖 | C4 Context / Container 图 |
| 请求经过哪些组件 | 时序图或请求路径图 |
| 对象怎样改变状态 | 状态机 |
| 模块之间怎样依赖 | 依赖图 |
| 数据从哪里产生、流向哪里 | 数据流或事件流图 |
图中的节点最好来自已经解析出的实体,边来自静态分析、配置或 Trace。让模型凭正文自由生成图,常见问题是节点名称与源码不一致、箭头方向错误、把同一模块画成几个不同组件。
一个可验证的生成过程应先得到结构化图数据:
{
"nodes": [
{"id": "api", "label": "Order API", "source": "api/order_controller.py"},
{"id": "app", "label": "Create Order", "source": "application/create_order.py"},
{"id": "repo", "label": "Order Repository", "source": "infra/order_repository.py"}
],
"edges": [
{"from": "api", "to": "app", "type": "calls"},
{"from": "app", "to": "repo", "type": "writes"}
]
}
渲染器只负责布局与样式。这样既能更换图形引擎,也能单独验证节点是否存在、边是否有依据。
九、更新能力决定 Wiki 能活多久
初次生成很容易让人惊艳,长期维护才是难点。代码每天变化,自动 Wiki 必须回答三个问题:
- 哪些知识实体受到影响;
- 哪些页面引用了这些实体;
- 页面中哪些结论需要重写或重新确认。
RepoAgent 使用 Git 追踪代码变化,并根据受影响对象和双向引用更新最小范围文档。这个方向比定时全量重建更适合真实仓库。
一次增量更新可以这样执行:
Git diff
↓
识别新增、修改、移动、删除的符号
↓
更新邻接关系与索引
↓
查找引用这些实体的页面和结论
↓
重建受影响段落与图
↓
检查悬空链接、冲突结论和置信度下降
↓
生成可评审的文档 diff
删除比新增更难处理。一个函数消失后,页面可能仍然描述旧流程;一次重命名可能被误识别为删除加新增;公共接口变化还会影响调用方页面。实体要有尽量稳定的身份标识,更新算法也要结合路径、签名和代码相似度识别移动与重命名。
文档 diff 应尽量小。未受代码变化影响的段落不要重新生成,否则评审者会被措辞变化淹没。模型和 Prompt 版本也要记录,因为换模型可能让相同证据产生不同叙述。
十、可信度不能只看文章是否流畅
LLM 很擅长把零散代码写成连贯说明,流畅恰好会掩盖错误。评测需要拆成几层。
1. 事实准确度
- 页面引用的文件、类和函数是否存在;
- 参数、返回值、异常和配置项是否与代码一致;
- 调用与依赖关系是否有静态或运行时证据;
- 页面版本是否对应正确 commit。
这部分尽量用确定性检查完成,不要再让另一个 LLM 全权裁判。
2. 覆盖度
- 关键入口、核心模块和主要流程是否有页面;
- 页面是否遗漏失败分支、权限检查和异步路径;
- 架构图是否覆盖实际部署组件;
- 重要术语是否能从索引进入对应解释。
3. 可用性
- 新人能否根据 Wiki 找到修改入口;
- Agent 能否借助页面减少文件读取和无效搜索;
- 用户提问后能否得到带来源的答案;
- 页面是否在合理步数内从总览导航到实现。
4. 新鲜度
- 代码提交后多久更新相关页面;
- 受影响页面召回率是多少;
- 是否存在指向已删除符号的链接;
- 文档版本落后主分支多少个提交。
还要专门设计拒答样本。仓库中没有证据时,系统应明确说不知道,不能为了补齐页面结构而生成一个合理故事。
十一、LLM Wiki 和 RAG 应该怎样连接
Wiki 页面本身可以进入 RAG,但不能只索引最终文章。更好的做法是保留三个检索层次。
第一层:页面索引
用于寻找“订单状态机”“权限模型”这类稳定主题。它有清晰标题和完整上下文,适合先确定知识位置。
第二层:结论与证据索引
把页面中的关键结论拆成带来源的记录。查询命中结论后,可以一起返回支持它的代码片段、版本和关系边。
第三层:原始代码索引
当页面没有覆盖新问题,或者 Agent 要执行修改时,继续搜索符号和代码正文。FastCode 一类结构导航工具适合承担这一层。
一次查询可以按下面的顺序路由:
def query_code_knowledge(question):
page = wiki_index.search(question)
if page.confidence >= 0.85 and page.is_fresh:
return answer_with_citations(question, page)
evidence = claim_index.search(question)
if evidence.coverage >= 0.75:
return answer_with_sources(question, evidence)
return fastcode.scout_and_read(question)
阈值只是示意,重点是查询路径。系统先利用已经沉淀的稳定解释,证据不足时再下钻到代码结构和正文。Agent 不必每次从目录开始重新认识仓库。
十二、Wiki 还需要吸收代码之外的知识
只读取源码,系统能解释“现在怎么做”,很难准确解释“为什么这样做”。很多重要知识并不在代码中:
- 架构决策记录中的方案取舍;
- 故障复盘中的真实失效模式;
- 产品规则中的边界和例外;
- 迁移文档中的临时兼容逻辑;
- 团队 Skills 中的修改方法和验证清单。
这些材料应当和代码事实分开标记。代码可以证明某个分支存在,ADR 可以解释为什么保留它,测试可以证明哪种行为受到保护。模型生成页面时要区分事实来源,遇到冲突时显示版本和时间,而不是自动把几份材料揉成一个结论。
权限也必须在索引阶段执行。私有文档、敏感配置和跨团队代码不能先进入统一上下文,再依赖 Prompt 要求模型保密。页面缓存、向量索引、问答历史和生成结果都要继承原始数据的访问边界。
十三、一套可落地的最小实现
如果要在团队内部试做,我会把第一版控制在下面这条链路:
Git 仓库
↓
Tree-sitter / LSP 提取文件、符号、引用
↓
关键词索引 + 向量索引 + 简单依赖图
↓
人工指定 5 到 10 个高价值页面
↓
为每页构建可追溯证据包
↓
LLM 生成 Markdown 与结构图数据
↓
静态检查 + 人工评审
↓
Git diff 驱动增量更新
第一版不要急着自动发现全部页面。选一个边界清楚、日常会被问到的仓库,先覆盖系统总览、两个关键流程、两个核心概念。每页保留源码链接、commit 和生成时间。
验收也可以很具体:找三名不熟悉仓库的开发者,让他们分别完成定位入口、解释流程和判断改动影响的任务;再让 Coding Agent 在有 Wiki 和没有 Wiki 的条件下执行同一组问题,比较文件读取次数、Token、引用准确率和最终结论。
效果稳定后再增加自动页面规划、更多语言适配、运行时 Trace 和跨仓库关系。过早追求“一键生成整个公司代码百科”,会把精力耗在页面数量和模型成本上,忽略最重要的可信度与更新链路。
十四、几个容易忽略的失败模式
1. 页面正确,却对应错误分支
用户在看 release 分支,Wiki 来自主分支。两边内容都正确,结合起来就是错误答案。页面上必须显示仓库、分支和 commit,检索时也要把版本作为过滤条件。
2. 图和正文各自生成,互相矛盾
流程图显示 A 调用 B,正文写 B 通过事件触发 A。图与正文应共享同一份结构化事实,不能各写一遍 Prompt。
3. 生成页面很多,却没有稳定入口
自动目录如果每次更新都改变页面层级和标题,外部链接、团队记忆和 Agent 引用都会失效。页面 ID 应保持稳定,标题变化通过重定向处理。
4. Wiki 代替了源码验证
Wiki 是导航和解释层。Agent 真正改代码前仍要读取相关实现、测试和配置,并执行验证。页面可能过期,也可能遗漏动态行为。
5. 把“没有写下来”误判成“不存在”
静态分析没找到调用,不等于线上没有调用。反射、插件、脚本和外部消费者都可能绕开可见关系。页面需要说明分析边界,并允许维护者补充人工关系。
十五、我对 LLM Wiki 的理解
LLM Wiki 可以看成一种面向代码仓库的知识编译器。
源码、文档和版本历史是原材料。解析器恢复符号与关系,页面规划确定知识边界,检索为每页准备证据,LLM 把证据编排成人能读懂的解释。Git diff 和验证器负责让这份解释继续跟得上代码。
这套系统同时服务两类读者。人通过目录和图建立心智模型,Agent 通过页面索引、结论索引和源码关系图取得上下文。Wiki 负责沉淀稳定解释,RAG 负责按问题取证,FastCode 负责在代码结构中继续下钻。三者共享底层事实时,代码知识才不会被重复生成、重复索引和重复理解。
如果只记住一个判断标准,我会选这个:一条 Wiki 结论能否回到对应版本的代码证据,代码变化后系统又能否找到并更新它。
能做到这两点,LLM Wiki 才是一套可以长期使用的代码知识库。
参考资料
- Cognition,DeepWiki
- AsyncFuncAI,DeepWiki-Open
- Qinyu Luo 等,RepoAgent: An LLM-Powered Open-Source Framework for Repository-level Code Documentation Generation
- Nguyen Hoang Anh 等,CodeWiki: Automated Repository-Level Documentation at Scale
- Meta Research,DocAgent: Agentic Hierarchical Docstring Generation System
- Zhonghang Li 等,FastCode: Fast and Cost-Efficient Code Understanding and Reasoning
如果这篇文章对你有帮助