# Wiki Sync 系统搭建完整记录

> 日期：2026-04-16
> 作者：Hermes Agent
> 用途：记录 llm-wiki-agent 自动化同步系统的完整搭建过程

---

## 背景

用户希望将 Obsidian vault 中的 markdown 文件批量摄入到 LLM Wiki（Karpathy's LLM Wiki）中。原有方案是手动逐篇执行，效率低下。本次目标：搭建自动化系统，实现定时自动摄入。

---

## 系统架构

### 架构图
![Wiki Sync Architecture](IMG-20260418081458052.png)


### 同步时序图

![Wiki Sync Sequence Diagram](IMG-20260418081458100.png)

### Ingest 9 步流程图
![[IMG-20260418081458161.png]]


### 核心组件

| 组件 | 位置 | 职责 |
|------|------|------|
| **llm-wiki-sync skill** | `~/.hermes/skills/research/llm-wiki-sync/SKILL.md` | 执行模板和工作流定义 |
| **sync.py** | `/Users/weishen/Git/llm-wiki-agent/tools/sync.py` | manifest 管理、CLI 工具 |
| **manifest.json** | `/Users/weishen/Git/llm-wiki-agent/tools/manifest.json` | 文件状态追踪（181篇） |
| **Hermes Cron Job** | 内部调度器 | 每 15 分钟触发一次摄入 |


## 一、初始探索（手动执行阶段）

### 1.1 发现 raw 文件

- 路径：`/Users/weishen/Workspace/nexus/raw/`（Obsidian vault）
- 通过 symlink 挂载到：`/Users/weishen/Git/llm-wiki-agent/raw/`
- 文件数：182 个 markdown 文件

### 1.2 创建 manifest.json

手动扫描 raw 目录，生成初始 manifest：

```
# 扫描 raw 目录，提取 frontmatter
for md in Path("raw").glob("**/*.md"):
    # 提取 title, ingested, slug 等字段
    manifest.append({...})
```

### 1.3 测试 /wiki-ingest

Claude Code 的 `/wiki-ingest` 命令执行 9 步流程：
读取 source 文档
读取 wiki/index.md 和 overview.md
生成 wiki/sources/<slug>.md
更新 wiki/index.md
更新 wiki/overview.md
创建/更新 Entity 页面
创建/更新 Concept 页面
检测并记录冲突
追加 wiki/log.md

##  二、遇到的问题和解决方案

### 问题 1：stdin 交互问题

**现象**：`claude --print` 模式在非交互环境下无法正常工作，stdin 被占用导致命令卡住。

**解决方案**：使用 TMUX 交互模式启动 Claude Code：

```bash
# 启动 TMUX session
tmux new-session -d -s wiki-ingest "cd /Users/weishen/Git/llm-wiki-agent && claude --permission-mode bypassPermissions"

# 等待启动完成
sleep 8 && tmux send-keys -t wiki-ingest Enter

# 发送任务
tmux send-keys -t wiki-ingest "请执行任务..."
```

### 问题 2：manifest slug 与实际文件不匹配

**现象**：manifest 中记录的 slug 和 source_path 与 LLM 实际生成的文件名不一致。

**原因**：LLM 根据内容自动生成 slug，而不是简单从文件名转换。

**解决方案**：要求 LLM 在任务完成后输出 `SLUG: xxx`，然后 Hermes 解析并更新 manifest：

```python
def parse_slug_from_output(output: str) -> str:
    """从 LLM 输出中解析实际使用的 slug"""
    match = re.search(r'SLUG:\s*([^\s]+)', output)
    return match.group(1) if match else None
```

### 问题 3：170 条 ingest 错误记录

**现象**：manifest 中有 170 条记录标记为 `ingested=true` 但实际未成功。

**原因**：早期测试时使用 `--print` 模式失败但仍标记为成功。

**解决方案**：使用 `sync.py --reset-failed` 清理错误状态。

---

## 三、自建组件

### 3.1 sync.py CLI 工具

**位置**：`/Users/weishen/Git/llm-wiki-agent/tools/sync.py`

**功能**：
- `--pending`：列出待摄取文件
- `--check`：预览变化
- `--reset-failed`：重置失败记录
- `--bootstrap`：从现有 wiki sources 重建 manifest

**核心函数**：
```python
def get_pending_files() -> list:
    """返回所有未摄入的文件"""

def mark_ingested(file_path: str, slug: str):
    """标记文件为已摄入"""

def reset_failed():
    """重置所有失败状态"""

def parse_slug_from_output(output: str) -> str:
    """从 LLM 输出解析 SLUG"""
```

### 3.2 llm-wiki-sync skill

**位置**：`~/.hermes/skills/research/llm-wiki-sync/SKILL.md`

**版本**：1.4.0

**核心内容**：
- **角色分工**：Hermes 编排流程 → Claude Code 执行 ingest
- **关键设计**：TMUX 交互模式、顺序执行、SLUG 输出要求
- **TMUX 执行流程**：完整的启动、发送任务、监控、清理流程
- **Ingest Workflow 9 步**：标准化执行步骤
- **Cron Job 自动化**：使用 Hermes 原生 cron job

### 3.3 Hermes Cron Job

**创建命令**：
```bash
cronjob create \
  --name wiki-sync-15min \
  --skill llm-wiki-sync \
  --schedule "*/15 * * * *" \
  --repeat 999999 \
  --deliver "telegram:5038825565" \
  --prompt "使用 llm-wiki-sync 技能执行一次 wiki 文章摄入..."
```

**配置**：
- Job ID：`98265b6998c5`
- Schedule：`*/15 * * * *`（每 15 分钟）
- 交付方式：Telegram（ID: 5038825565）
- 技能：llm-wiki-sync

---

## 四、执行流程（自动化阶段）

### 4.1 Cron Job 触发

1. 每 15 分钟（00, 15, 30, 45 分）触发
2. 加载 llm-wiki-sync skill
3. 执行 skill 中的 prompt

### 4.2 实际执行步骤

```
1. 加载 llm-wiki-sync 技能
2. 检查 manifest（python tools/sync.py --pending）
3. 启动 TMUX session
4. 启动 Claude Code（bypassPermissions）
5. 发送任务指令（含 SLUG 输出要求）
6. 监控任务完成（tmux capture-pane）
7. 解析 SLUG 并更新 manifest.json
8. 清理 TMUX session
9. 输出结果（自动发往 Telegram）
```

### 4.3 示例输出

```
## Wiki Sync 完成

| 项目 | 结果 |
|------|------|
| 摄入文件 | raw/Home Office/用Docker中安装Navidrome.md |
| Slug | 用docker中安装navidrome |
| 状态 | ✅ 已完成 |

新增页面：
- wiki/sources/用docker中安装navidrome.md
- wiki/entities/Navidrome.md
- wiki/concepts/Docker-Compose.md

manifest 已更新：ingested: true
剩余待摄入：165 篇
```

---

## 五、当前状态

| 指标 | 值 |
|------|-----|
| 总文件数 | 181 篇 |
| 已摄入 | 16 篇 |
| 待摄入 | 165 篇 |
| Cron Job 状态 | 运行中 |
| 下次运行 | 18:15:00 |

---

## 六、关键命令速查

```bash
# 检查待摄取文件
cd /Users/weishen/Git/llm-wiki-agent && python tools/sync.py --pending

# 预览变化
python tools/sync.py --check

# 重置失败记录
python tools/sync.py --reset-failed

# 查看 cron job 状态
cronjob --list

# 手动触发 cron job
cronjob --run <job_id>
```

---

## 七、注意事项

1. **必须使用 TMUX**：不能用 subprocess 或 --print 模式
2. **必须顺序执行**：并发会触发 529 rate limit
3. **必须解析 SLUG**：LLM 输出的实际 slug 用于更新 manifest
4. **交付方式**：使用 `telegram:5038825565` 发给用户
5. **保留 orphan**：不删除任何原始数据

---

## 八、扩展方向

- [ ] 添加错误重试机制（529 时等待后重试）
- [ ] 支持批量摄入（改为每次 3-5 篇）
- [ ] 添加 webhook 通知（不只是 Telegram）
- [ ] 统计摄入速率和成功率

---

*End of Note*