TONG-H

AI 实践-DeepWiki-open 为项目生成 wiki 文档

1.9k7Frontendai2025-07-202025-08-24

背景

  • 有一些仓库的 readme 文件很草率,如果作为一个新人去接手一个仓库,看完 readme 连这个仓库的作用都不知道,需要多花时间看代码才能理解这个仓库的功能。但换个角度,作为一个开发人员,大部分时候都会疏于去写文档。
  • DeepWiki是专门解决这个问题的,用于为 github/gitlab 仓库生成全面详细文档,但这是个闭源项目,且能集成的平台有限https://docs.devin.ai/integrations/gh
  • DeepWiki-open 是 DeepWiki 的一个开源实现
    • 基于 adalflow, 支持本地部署,以及结合 ollama 使用本地模型
    • 支持基于仓库像模型提问

技术调研

在决定使用deepwiki之前有了解过一些其他的方式

cursor + rule

  • 只用一个rule,不会深入代码比较浅,适合当readme文件
  • 通过不通的 rule 来生成不同的内容,再去搜索每个页面信息的时候,多半会超时,可能会需要额外的对话。但 mpa 这类,每个entry都是独立的部分,一个page一个reademe组合起来的效果也能是一个 wiki 了
Text
1
2
3
readme.mdc //通过 @filename.ts 指向其他的 rule
page-doc-agent.mdc // 生成页面信息的 rule,从入口文件进入根据组件引用以及数据流来获取内容
api-interface.mdc // 生成 api 集成相关信息

其它

gitdiagram 只支持 openai,github 仓库

Context7 不适用,更新库文档的

mintlify, 和 deepwiki 类似,闭源,不支持本地仓库

安装

deepwiki-open + ollama安装和启动,ollama 需要单独配置安装

  • 如果 python 安装很慢,可以使用镜像
Text
1
pip3 install -r api/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 如果中途遇到报错,比如 openai 版本不兼容什么的,可以尝试升级 pip

实践

拿一个项目 A 为案例,本地仓库/多页面模式

从日志可以看出时间线和流程,3点半到5点,一个半

2025-07-2515:30:48 读取仓库, 读取配置过滤文件

2025-07-2515:32:05 有152 个文档,根据配置拆分成 1854 chunk

调用 olamam 的 /api/embeddings 接口,使用 nomic-embed-text 模型,进行 embedding 转化和存储,生成 VectorStore

2025-07-2515:46:21Successfullyprocessed 1854/1854 documents with consistent embeddings

2025-07-2515:46:21FAISS retriever created successfully(Facebook AI Similarity Search, 做相似性搜索的,VectorStore => Retriever)

2025-07-2515:46:21生成 wiki 的结构

按照结构分段式的给提示生成wiki

2025-07-2516:55:51Wiki cachesuccessfullysaved to /.adalflow/wikicache/deepwiki_cache_local_local_A_zh.json

workflow

1.获取仓库,在线的本地公开的私有的(需要提供 token)都可以

  1. 使用text_splitter拆分 chunk,过长的文档可能会难以适配模型的上下文,以及后面检索的步骤
  • 可以通过配置调整传递给 text splitter 的参数

    • split_by:默认 word,按词汇拆分,支持的值:"word","sentence","page","passage", and"token"
    • chunk_size=350
    • chunk_overlap=100,可以和上一个chunk重合的词汇数量,用于维护分块之间的上下文
    • batch_size=1000, 可以批量处理的chunk数量,这个就看电脑配置了
  • /api/embeddings 接口 转化成向量,配置调整可以参考adalflow-embedderollom 的文档

  • 使用 AdalFlow’s LocalDB 进行存储

  • 生成FAISS Retriever,Facebook AI Similarity Search 相似性搜索

  • 收到用户需求后,先查缓存,如果没有,携带readme + 文件树 + prompt 传递给模型生成 wiki 结构

  • 根据结构,一个章节一个章节的进行:prompt 转化 => 向量检索 => 传递给模型 => 持续接收流以及转发给前端

  • prompt 转化为向量,进行FAISS 向量检索, 获取文档索引

Text
1
2
3
4
5
6
7
retrieve_embedder = self.query_embedder if self.is_ollama_embedder else self.embedder
self.retriever = FAISSRetriever(
\*\*configs["retriever"],
embedder=retrieve_embedder,
documents=self.transformed_docs,
document_map_func=lambda doc: doc.vector,
)
  • 通过索引寻找文档
Text
1
2
3
4
retrieved_documents[0].documents = [
self.transformed_docs[doc_index]
for doc_index in retrieved_documents[0].doc_indices
]
  • 将文件内容填充进prompt
Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if retrieved_documents and retrieved_documents[0].documents:
\# Format context for the prompt in a more structured way
documents = retrieved_documents[0].documents
logger.info(f"Retrieved {len(documents)} documents")

\# Group documents by file path
docs_by_file = {}
for doc in documents:
file_path = doc.meta_data.get('file_path', 'unknown')
if file_path not in docs_by_file:
docs_by_file[file_path] = []
docs_by_file[file_path].append(doc)

\# Format context text with file path grouping
context_parts = []
for file_path, docs in docs_by_file.items():
\# Add file header with metadata
header = f"\#\# File Path: {file_path}\n\n"
\# Add document content
content = "\n\n".join([doc.text for doc in docs])

context_parts.append(f"{header}{content}")

\# Join all parts with clear separation
context_text = "\n\n" + "-" \* 10 + "\n\n".join(context_parts)
  • 调用 api/generate 接口向模型发起请求,持续接收流以及转发给前端
Text
1
2
3
4
5
6
7
response = await model.acall(api_kwargs=api_kwargs, model_type=ModelType.LLM)
\# Handle streaming response from Ollama
async for chunk in response:
text = getattr(chunk, 'response', None) or getattr(chunk, 'text', None) or str(chunk)
if text and not text.startswith('model=') and not text.startswith('created_at='):
text = text.replace('<think>', '').replace('</think>', '')
await websocket.send_text(text)

实践问题

No valid XML found in response

只是一个笼统的报错,检查 application.log,可以找到真实的原因,多半和 embedding 模型有关系

  • 有可能 embedding model 不存在

  • 达到最大限制,尤其如果仓库文件包含不寻常的格式很有可能促使达到限制

  • 删除文件 ~/.adalflow

  • 如果日志说No valid document embeddings found,在 read_all_documents 函数里没有找到文档

    • 检查一下配置里的文件过滤file_filters, 以及看下日志里的过滤模式,文件过滤模式分为 include 和 exlcude,优先 include 后 exclude
    • 在界面上设置过的配置会保存在 localstorge,可以清下前端缓存

可以看看这个 issue 自查https://github.com/AsyncFuncAI/deepwiki-open/issues/198

速度很慢

从项目实践来看,152 个文档,1854 chunk,一个半小时

时间问题和仓库大小电脑配置都有关系,从时间线来看,前面消化数据的时间都差不多,主要差异是在后半段耗时会比较长,prompt转化+向量检索+等待模型生成内容,一个生成大概十分钟左右,wiki结构 + 9个page,10个*10分钟

如果前面wiki结构生成花的时间很长,那可能 readme 的内容是不是太多

tips:最好用空闲时间来生成,电脑会很卡

优化措施

  • 尽可能过滤不需要的文件和文件夹,这是最直接的方式,减少chunk减少向量,一些样式 / test / mock 文件….

  • 调整 prompt 删减一些不需要章节

  • 可以尝试在 embedder.json 中减少 num_ctx, chunk_size, and chunk_overlap

  • 尝试切换模型

Model Size Speed Quality Use Case
phi3:mini 1.3GB Fast Good Small projects, quick testing
qwen3:1.7b 3.8GB Medium Better Default, good balance
llama3:8b 8GB Slow Best Complex projects, detailed analysis

Module not found: Can’t resolve ‘@vercel/turbopack-next/internal/font/google/font’

日语字体加载失败,可以删除

deepwiki-open/src/app/layout.tsx

如何调整文档的生成

一开头可以选择类型,全面/简洁,这个类型会影响 prompt

但是文档的结构 以及每个章节的生成,在 ./deepwiki-open/src/app/[owner]/[repo]/page.tsx 里的 RepoWikiPage 和 generatePageContent函数,可以通过修改 promopt 来调整

参考文章

作者简单介绍了他的初衷,deepwiki的功能和机制DeepWiki: Why I Open-Sourced an AI-Powered Wiki Generator

可以用来了解 rag 和检索流程https://js.langchain.com/docs/tutorials/rag

deepwiki的文档,可以帮助了解源码https://deepwiki.com/AsyncFuncAI/deepwiki-open/5-backend-implementation#websocket-communicationhttps://deepwiki.com/AsyncFuncAI/deepwiki-openhttps://deepwiki.com/AsyncFuncAI/deepwiki-open/5-backend-implementation#websocket-communication