背景
- 有一些仓库的 readme 文件很草率,如果作为一个新人去接手一个仓库,看完 readme 连这个仓库的作用都不知道,需要多花时间看代码才能理解这个仓库的功能。但换个角度,作为一个开发人员,大部分时候都会疏于去写文档。
- DeepWiki是专门解决这个问题的,用于为 github/gitlab 仓库生成全面详细文档,但这是个闭源项目,且能集成的平台有限docs.devin.ai/integration…
- DeepWiki-open 是 DeepWiki 的一个开源实现
- 基于 adalflow, 支持本地部署,以及结合 ollama 使用本地模型
- 支持基于仓库像模型提问
技术调研
在决定使用deepwiki之前有了解过一些其他的方式
cursor + rule
- 只用一个rule,不会深入代码比较浅,适合当readme文件
- 通过不通的 rule 来生成不同的内容,再去搜索每个页面信息的时候,多半会超时,可能会需要额外的对话。但 mpa 这类,每个entry都是独立的部分,一个page一个reademe组合起来的效果也能是一个 wiki 了
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 安装很慢,可以使用镜像
pip3 install -r api/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
实践
拿一个项目 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
是一个很典型的 RAG 流程
1.获取仓库,在线的本地公开的私有的(需要提供 token)都可以
- 使用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数量,这个就看电脑配置了
- split_by:默认 word,按词汇拆分,支持的值:
-
/api/embeddings 接口 转化成向量,配置调整可以参考adalflow-embedder和ollom 的文档
-
使用 AdalFlow's LocalDB 进行存储
-
生成FAISS Retriever,Facebook AI Similarity Search 相似性搜索
-
收到用户需求后,先查缓存,如果没有,携带readme + 文件树 + prompt 传递给模型生成 wiki 结构
-
根据结构,一个章节一个章节的进行:prompt 转化 => 向量检索 => 传递给模型 => 持续接收流以及转发给前端
-
prompt 转化为向量,进行FAISS 向量检索, 获取文档索引
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,
)
- 通过索引寻找文档
retrieved_documents[0].documents = [
self.transformed_docs[doc_index]
for doc_index in retrieved_documents[0].doc_indices
]
- 将文件内容填充进prompt
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 接口向模型发起请求,持续接收流以及转发给前端
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 自查github.com/AsyncFuncAI…
速度很慢
从项目实践来看,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 和检索流程js.langchain.com/docs/tutori…
deepwiki的文档,可以帮助了解源码deepwiki.com/AsyncFuncAI…deepwiki.com/AsyncFuncAI…deepwiki.com/AsyncFuncAI…