基于 Andrej Karpathy LLM Wiki 方法论,将原始办公文档编译为结构化、可检索、可生长的 Markdown 知识库。
采用 Karpathy 三层架构(详细原理见 references/karpathy-methodology.md):
KNOWLEDGE_BASE_ROOT/ # 知识库根目录(由用户指定,如 C:\Users\xx\Documents\工作文档)
├── raw/ # Layer 1: 不可变原始文档
│ └── {category}/{subdir}/*.docx, *.xlsx
├── wiki/ # Layer 2: LLM 生成与维护的知识层
│ ├── _index.md # 主编排索引(渐进式披露 L1)
│ ├── _log.md # 仅追加操作日志
│ ├── _overview.md # 知识库概况与统计
│ ├── sources/ # 源文档摘要(markitdown 转换产物)
│ │ └── {category}/{subdir}/*.md
│ ├── concepts/ # 概念页(LLM 生成)
│ ├── entities/ # 实体页(LLM 生成)
│ └── comparisons/ # 对比分析页(LLM 生成)
├── update/ # 增量更新投放区
├── _schema.md # Layer 3: dochub 治理规则
└── _dochub_knowledge_base.md # 友好概览(向后兼容旧版)
| 层 | 目录 | 角色 | 可变性 |
|----|------|------|--------|
| 原始层 | raw/ | 源文档仓库,所有知识的真相来源 | 不可变 — LLM 只读不写 |
| 知识层 | wiki/ | LLM 生成的结构化 Markdown | 完全由 LLM 维护 |
| Schema 层 | _schema.md | 治理规则、模板、工作流定义 | 人与 LLM 共同迭代 |
| 格式 | 状态 | 说明 |
|------|------|------|
| .docx | 支持 | Word 现代格式,使用 python-docx 转换 |
| .xlsx | 支持 | Excel 现代格式,使用 openpyxl 转换 |
| .doc / .xls / .pdf / .pptx / 其他 | 不支持 | 跳过转换,但建立索引(可定位 raw/ 原文) |
将工作目录中原有的原始文档转化为完整知识库。
触发方式: 用户在 KNOWLEDGE_BASE_ROOT 下运行初始化,或明确说「初始化知识库」「dochub init」。
完整流程:
[1/7] 安全确认 → [2/7] 移动原始文档到 raw/ → [3/7] 文件名规范化 → [4/7] 检测不支持格式 → [5/7] MD转换 → [6/7] 生成知识层 → [7/7] 创建Schema
raw/ 目录
.fnsync_temp_dir 等临时目录
对 raw/ 下所有文件执行文件名规范化:
-
-
- 为单个 -
-
raw/ 下所有文件
.docx / .xlsx 文件(如 .pptx、.pdf、.doc、.xls、.txt、.jpg)
将 raw/ 下所有 .docx 和 .xlsx 转换为 Markdown,输出到 wiki/sources/,保持相同目录结构。
转换工具选择:
由于 markitdown 的 extra dependencies 在部分环境下解析不稳定,优先使用原生库:
| 格式 | 工具 | 方法 |
|------|------|------|
| .docx | python-docx | 提取段落(Heading 样式 -> # 标题)+ 提取表格(-> Markdown 表格) |
| .xlsx | openpyxl | 遍历 Sheet,每 Sheet 一个 ## 标题 + Markdown 表格 |
如原生方案失败,可回退尝试 markitdown[all]。
Python 路径: 始终使用 C:/Users/skya2/.workbuddy/binaries/python/envs/default/Scripts/python.exe
如需安装依赖:pip install python-docx openpyxl
写脚本执行: 中文文件名在 here-doc 中可能编码异常,将转换脚本写入临时 .py 文件后执行,完成后删除临时脚本。
转换规则:
raw/{category}/{subdir}/file.docx
wiki/sources/{category}/{subdir}/file.md
read_only=True, data_only=True 参数避免内存溢出
生成 wiki/ 下的核心文件:
6a. 生成 wiki/sources/ 下的源文档摘要页
对每个转换后的 MD 文件,在文件头部添加 YAML frontmatter:
---
title: {文档标题,从原始文件名推导}
type: source-summary
sources:
- raw/{category}/{subdir}/{filename}.docx
domain: {从目录结构推导领域}
tags: [{从内容提取 3-5 个关键词}]
created: {转换日期}
updated: {转换日期}
confidence: high
---
6b. 生成 wiki/_overview.md(知识库概览)
---
title: 知识库概览
type: overview
updated: {日期}
---
## 文档统计
- 总文档数: N
- 总大小: X MB
- 分类数: N
- 最后更新: {日期}
## 分类目录树
(可视化 ASCII 树状结构)
## 高频关键词标签云
(按频率排列的关键词列表)
## 文档索引
(按分类组织的完整文件清单,每项包含文件名、一句话摘要、标签)
6c. 生成 wiki/_index.md(主编排索引 — Karpathy 核心)
这是 LLM 检索的入口点,采用两级索引结构:
---
title: 知识库索引
type: index
updated: {日期}
---
# 知识库索引
> **检索优先级:** 先查 MD 文档(可全文检索),无结果时再查原始文档(仅索引)。
## 分类导航
### {分类1}
#### MD 文档(可全文检索)
- [[{path}.md]] {文档名}
...
#### 原始文档(仅索引,不可搜索内容)
- [{格式}] `raw/{path}` {文档名}
...
### {分类2}
...
## 文档统计
- 原始文档: N 个 (X MB)
- MD 可检索: N 个
- 原始仅索引: N 个
- 分类: N 个
- 初始化: YYYY-MM-DD | 更新: YYYY-MM-DD
索引规则:
[格式] 标签标注(如 [PPTX], [PDF], [TXT], [JPG], [DOC], [XLS])
6d. 初始化 wiki/_log.md
---
title: 操作日志
type: log
created: {日期}
---
## [{日期}] init | 知识库初始化
- 操作: 初始化知识库
- 文档总数: N
- 成功转换: X
- 跳过: Z (不支持格式)
_schema.md
在 KNOWLEDGE_BASE_ROOT 下创建治理规则文件。
参考 references/entity-types.md 中的模板定义,生成适用于当前文档领域的 schema 文件。内容包括:
将新文档放入 update/ 目录后运行。
触发方式: 用户说「dochub update」「增量更新」「有新文档」或明确指向 update/ 目录。
完整流程:
[1/6] 安全确认 → [2/6] 扫描update/ → [3/6] 检测不支持格式 → [4/6] MD转换 → [5/6] 更新知识层 → [6/6] 追加日志
同 init 操作。确认新文档已脱敏。
update/ 下所有文件
raw/ 和 wiki/sources/ 中已有文件的对应关系
文件对应判定:
同 init 操作,列出非 .docx/.xlsx 文件。
仅转换新增或已修改的文件:
转换方法同 init 操作 [5/7],使用 python-docx + openpyxl 原生方案。
文档归类(重要): 新文档不直接以其 update/ 中的目录结构存放,而应根据文档名/内容分析归入已有分类。规则:
wiki/sources/{分类}/{YYYY-MM-DD-子主题}/文件.md
级联更新受影响的 wiki 页面:
必须更新的文件:
wiki/_index.md — 添加新条目
wiki/_overview.md — 更新统计
wiki/sources/ — 新文档的摘要页
建议更新的文件(LLM 自行判断):
新类型页面的创建条件:
更新完成后,逐个更新页面的 YAML frontmatter 中的 updated 字段。
索引结构规范:
_index.md 中每个分类下分两层,严格执行:
### {分类名}
#### MD 文档(可全文检索)
- [[xxx.md]] 文档名
...
#### 原始文档(仅索引,不可搜索内容)
- [格式] `raw/xxx` 文档名
...
关键规则:
[格式] 标签标注
## [{日期}] update | {源文件名}
- 变更类型: 新增/更新
- 归类: {分类名}
- 影响页面: [{受影响 wiki 页面列表}]
- 摘要: {一句话描述}
重要: 更新完成后,将 update/ 中的原始文件移动到 raw/ 对应分类目录。不支持格式文件也一并移动并加入索引。
触发方式: 用户提问涉及文档内容,说「查一下」「搜索」「dochub search」等。
检索策略(Karpathy 渐进式披露):
L0 → L1 → L2 → L3
| 级别 | 内容 | Token 预算 | 何时加载 |
|------|------|-----------|---------|
| L0 | 工作上下文(用户 profile) | ~200 | 每次会话 |
| L1 | wiki/_index.md | 1-2K | 每次检索开始 |
| L2 | 匹配页面内容(grep 定位) | 2-5K | 索引命中后 |
| L3 | 完整源文档(wiki/sources/) | 5-20K | 需要原始细节时 |
检索流程:
wiki/_index.md,确定相关页面
wiki/ 目录下使用 grep 搜索用户问题的关键词
wiki/sources/ 中对应源文档验证细节
回答格式遵循 Karpathy 双输出原则:
## 回答
{基于文档内容的综合回答}
## 来源
- [[wiki/sources/{path}/file]] — {一句话说明}
- [[wiki/concepts/{concept}]] — {一句话说明}
## 置信度
{high / medium / low} — {简要说明}
特别规则:
[[wikilinks]] 格式
定期对知识库进行全面体检。
触发方式: 用户说「dochub lint」「知识库检查」「健康检查」。
检查维度(Karpathy Lint 操作):
| 检查项 | 说明 | 输出 |
|--------|------|------|
| 矛盾检测 | 扫描 wiki 页面间的冲突声明 | 矛盾清单(页面A vs 页面B,具体冲突点) |
| 孤立页面 | 无 [[wikilinks]] 入链的 wiki 页面 | 孤立页面清单 |
| 缺失概念 | 被引用但尚未创建页面的主题 | 缺失概念清单及引用来源 |
| 过期声明 | updated 超过 90 天的页面 | 过期页面清单 |
| 置信度异常 | confidence: low 且被广泛引用的页面 | 需复核页面清单 |
| 格式一致性 | YAML frontmatter 完整性、命名规范、交叉引用有效性 | 格式问题清单 |
| 知识缺口 | 建议深入研究或补充的领域 | 研究建议清单 |
输出格式: 生成 wiki/_lint-{YYYY-MM-DD}.md 文件,包含所有检查结果。
同时追加到 wiki/_log.md:
## [{日期}] lint | 知识库健康检查
- 矛盾: N 处
- 孤立页面: N 个
- 缺失概念: N 个
- 过期页面: N 个
- 格式问题: N 处
- 建议: {摘要}
| 问题 | 简单文件夹方案 | dochub 三层架构 |
|------|---------------|----------------|
| 知识积累 | 每次搜索需要重新扫文档 | wiki 层持续生长,知识复利 |
| 交叉引用 | 无结构化关系 | [[wikilinks]] 预建关联 |
| 可追溯性 | 无法验证信息来源 | 每个声明可追溯到 raw/ |
| 维护成本 | 人工手动组织 | LLM 自动维护索引和交叉引用 |
| 置信度 | 无法判断可靠性 | 每页标注 confidence |
| 知识衰减 | 无法发现过时信息 | lint 操作定期体检 |
dochub 不依赖向量数据库或嵌入管道。它的策略是编译,而非检索——LLM 提前将知识组织好,而非每次查询时临时拼接。
_index.md 是解决上下文窗口退化的核心机制。LLM 始终先读索引(1-2K token),而非加载整个知识库。索引质量随知识库增长自动提升——因为它是 LLM 自己维护的。
必需依赖(原生方案,推荐):
pip install python-docx openpyxl
可选依赖(markitdown 回退方案):
pip install "markitdown[all]"
Python 路径: C:/Users/skya2/.workbuddy/binaries/python/envs/default/Scripts/python.exe
[X/N] 格式显示进度
由于中文文件名在 bash heredoc 中可能编码出错,转换脚本应写入临时 .py 文件后执行:
# docx 转换
import docx
doc = docx.Document(path)
for para in doc.paragraphs:
if para.style.name.startswith('Heading'):
level = para.style.name.split()[-1]
lines.append('#' * level + ' ' + para.text)
else:
lines.append(para.text)
for table in doc.tables:
# 转为 Markdown 表格
# xlsx 转换
import openpyxl
wb = openpyxl.load_workbook(path, read_only=True, data_only=True)
for ws_name in wb.sheetnames:
lines.append(f'## {ws_name}\n')
for row in ws.iter_rows(values_only=True):
# 首行作表头,后续行作数据
load_workbook(path, read_only=True, data_only=True)
markitdown 基础安装不含 docx/xlsx 支持,需 markitdown[all]
markitdown[all],部分环境仍报 MissingDependencyException
dangerouslyDisableSandbox
共 2 个版本