# Ingest

> 将 raw/ 目录下的原始资料编译到 wiki/ 中。处理完成后，将源文件自动移动到 raw/09-archive/ 归档。支持 `/ingest` (扫描 raw/ 下所有未归档文件) 或 `/ingest <path>` (处理指定文件)。当用户提到"摄取"、"导入"、"收入"资料，或要求将文件加入知识库时，也应该触发此技能。绝对忽略 raw/09-archive/ 目录。

- **Type:** Skill
- **Install:** `agentstack add skill-levi-qiao-obsidian-llm-wiki-ingest`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [levi-qiao](https://agentstack.voostack.com/s/levi-qiao)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [levi-qiao](https://github.com/levi-qiao)
- **Source:** https://github.com/levi-qiao/obsidian-llm-wiki/tree/main/.claude/skills/ingest

## Install

```sh
agentstack add skill-levi-qiao-obsidian-llm-wiki-ingest
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Ingest 技能

## 核心目标

你正在维护一个 **LLM Wiki**（Obsidian 知识库）。`raw/` 目录是"待处理收件箱"，`wiki/` 是"编译输出层"。本技能负责将原始资料编译到知识库中，并自动归档。

## 目录结构

- `raw/` — 用户自定义的分类结构（可动态调整）
- `raw/09-archive/` — **已处理文件的归档目录，禁止读取**（固定，写死）
- `wiki/concepts/` — 概念（框架、方法论、理论）
- `wiki/entities/` — 实体（人物、公司、工具、产品）
- `wiki/sources/` — 资料摘要
- `wiki/syntheses/` — 综合研究报告

## 触发条件

1. 用户执行 `/ingest` — 扫描 `raw/` 所有子目录（排除 `09-archive/`），找出待处理文件
2. 用户执行 `/ingest ` — 仅处理指定文件
3. 隐式触发 — 用户说"把这个资料摄入知识库"、"导入这篇文章"时，自动执行

## 工作流程

### 前置检查：扫描待处理文件

**关键准则**：
1. 使用 Glob 扫描 `raw/**/*.{md,pdf,txt}`（自动排除 `raw/09-archive/`）
2. 读取 `raw/.processed.json`，识别已处理的文件
3. **为每个待处理文件创建处理清单**，确保不遗漏任何文件
4. 按文件名字母顺序处理，便于追踪进度

**处理清单示例**：
```
📋 待处理文件清单：
- [ ] raw/01-articles/文件A.md (ID: file-a)
- [ ] raw/01-articles/文件B.md (ID: file-b)
- [ ] raw/02-papers/文件C.pdf (ID: file-c)
```

**重要**：在开始处理前，向用户展示这个清单，让用户确认要处理的文件列表。

---

对每个待处理源文件，严格按以下步骤执行：

### 步骤 1：读取源文件

- 如果是 `.md` 文件：使用 Read 工具完整读取内容
- 如果是 `.pdf` 文件：使用 Read 工具尝试提取文本。如果无法提取或内容为空，改为记录文件元信息（文件名、页数）
- **检查点 ✓**：确认文件内容已完整读取，记录文件路径和字数

### 步骤 2：内容分析与 ID 生成

**生成稳定 ID**：
- 从文件名提取（去除扩展名和路径）
- 转换为 kebab-case
- 示例：`raw/01-articles/Attention Is All You Need.md` → ID: `attention-is-all-you-need`
- **检查点 ✓**：确认 ID 已生成，且在 `.processed.json` 中不存在（避免重复处理）

**从源文件中提取**：
- **核心主旨** — 这段资料讲什么（1-2句话）
- **实体** — 人物、公司、工具、产品等具体名词（列出所有实体）
- **概念** — 框架、方法论、理论等抽象名词（列出所有概念）
- **关键标签** — 3-5个细粒度标签（优先复用 index.md 标签云中的现有标签）

如果是非中文内容，则翻译成中文。

**检查点 ✓**：确认已提取实体列表、概念列表、标签列表

### 步骤 3：知识网络化（动态 Top-K）

**读取总索引**：
```bash
Read wiki/index.md
```

**智能定位候选页面**：
1. 用步骤 2 提取的实体、概念、标签作为关键词
2. 在 index.md 中匹配相关页面（不限数量）
3. 按相关性排序，动态决定更新数量：
   - **简单博客/短文**（ 5000 字）：15-30 个页面
4. 如果需要更新超过 20 个页面 → 询问用户是否批量处理

**只读取 Top-K 页面的内容**（避免全量扫描）

对于步骤 2 提取的每个实体和概念：

**目标目录**：
- 实体 → `wiki/entities/`
- 概念 → `wiki/concepts/`

**处理逻辑**：
1. 页面不存在 → 按照 CLAUDE.md 的 Frontmatter 规范创建新页面
2. 页面已存在 → 读取现有内容，增量合并新信息
3. 发现冲突 → 根据冲突类型处理：
   - **时间性冲突**（旧版本 vs 新版本）→ 直接更新，在页面中标注 `## 历史版本`
   - **观点性冲突**（A 说法 vs B 说法）→ 在页面中新建 `## 知识冲突` 区块
   - **复杂冲突**（无法判断）→ 在 `wiki/_conflicts/` 创建冲突文件，暂停并询问用户

**页面模板**：

```markdown
---
type: entity | concept
tags: [标签1, 标签2, 标签3]
source_id: 稳定ID
created: 2026-04-29
updated: 2026-04-29
---

## 定义
[对该实体/概念的定义]

## 关键信息
[从源文件中提取的详细信息]

## 关联连接
- [[摘要-source-slug]] — 来源
- [[RelatedEntity]] — 相关实体
```

**标签使用原则**：
- 优先复用已存在的标签
- 每个页面 3-5 个标签
- 标签使用中文或英文，保持一致性

**检查点 ✓**：确认所有实体和概念页面已创建或更新，记录创建/更新的页面列表

### 步骤 4：创建来源摘要

在 `wiki/sources/` 创建 Markdown 文件：

```markdown
---
type: source
tags: [标签1, 标签2, 标签3]
source_id: 稳定ID
created: 2026-04-29
updated: 2026-04-29
---

## 核心摘要
[3-5句话的核心总结]

## 关联连接
- [[EntityName]] — 关联实体
- [[ConceptName]] — 关联概念
```

文件名使用 kebab-case：`摘要-{稳定ID}.md`

**检查点 ✓**：确认来源摘要页面已创建

### 步骤 5：更新索引与日志

**更新总索引** `wiki/index.md`

按照 type（类型）分组，将新增页面添加到对应分类下：
- 格式：`- [[页面名称]] — 一句话描述`
- 更新标签云：重新统计所有标签使用频率

**检查点 ✓**：确认 index.md 已更新，新增页面已添加到索引

**更新操作日志** `wiki/log.md`（Append-only）：
```markdown
## [YYYY-MM-DD] ingest | 操作简述
- 变更: 新增 [[PageName]] (N个); 更新 [[ExistingPage]] (M个)
- 标签: #标签1 #标签2 #标签3
- 冲突: 无 (或: 时间性冲突已更新; 或: 复杂冲突已生成报告到 _conflicts/)
- 源文件: raw/xx/文件名.md
```

**检查点 ✓**：确认 log.md 已追加新记录

### 步骤 6：更新处理状态并归档源文件

**更新 `raw/.processed.json`**：

使用稳定 ID（从文件名提取），记录处理状态：
```json
{
  "文件ID": {
    "original_path": "raw/01-articles/xxx.md",
    "archived_path": "raw/09-archive/xxx.md",
    "processed_at": "2026-04-29T14:30:00Z",
    "pages_created": ["页面1", "页面2"],
    "pages_updated": ["页面3"]
  }
}
```

**时间戳格式**：使用 ISO 8601 格式（`YYYY-MM-DDTHH:MM:SSZ`）

**检查点 ✓**：确认 `.processed.json` 已更新

**归档源文件**：

**关键准则**：只有在以下所有条件都满足时，才能归档源文件：
- ✓ sources 页面已创建
- ✓ 实体/概念页面已创建或更新
- ✓ index.md 已更新
- ✓ log.md 已更新
- ✓ `.processed.json` 已更新

**使用 Bash 工具执行移动操作**：
```bash
# 确保归档目录存在
mkdir -p raw/09-archive

# 移动文件（使用相对路径）
mv "raw/01-articles/文件名.md" raw/09-archive/
```

**验证归档**：
```bash
# 验证文件已移动
test -f "raw/09-archive/文件名.md" && echo "归档成功" || echo "归档失败"
```

**检查点 ✓**：确认源文件已成功移动到 `raw/09-archive/`，原位置不再存在该文件

**绝对禁止修改源文件内部的文字。**

### 步骤 7：完成提示

**对于每个处理完成的文件**，输出：
```
✅ 已完成文件处理：raw/xx/文件名.md
- 稳定ID: xxx
- 新增页面：N 个
- 更新页面：M 个
- 已归档到：raw/09-archive/
```

**所有文件处理完成后**，输出总结：

```
🎉 所有文件摄入完成！

📊 总计：
- 处理文件：X 个
- 新增页面：N 个
- 更新页面：M 个
- 源文件已归档到：raw/09-archive/

📝 下一步：使用 `/sync` 提交到 Git
```

## 冲突处理

当发现新旧知识冲突时，根据冲突类型自动处理：

### 1. 时间性冲突（旧版本 vs 新版本）
**特征**：同一事实的不同时间点描述，新信息明显更新
**处理**：直接更新为新版本，在页面中添加 `## 历史版本` 区块记录旧信息

### 2. 观点性冲突（A 说法 vs B 说法）
**特征**：不同来源对同一问题的不同观点，无法判断对错
**处理**：在页面中新建 `## 知识冲突` 区块，保留两种说法并对比

### 3. 复杂冲突（无法判断类型）
**特征**：涉及多个页面、多个维度的矛盾，LLM 无法自动决策
**处理**：
1. 在 `wiki/_conflicts/` 创建冲突文件：`conflict_YYYY-MM-DD_主题.md`
2. 文件内容包含：
   - 冲突描述
   - 涉及的页面和来源
   - 建议的解决方案
   - 决策选项（供用户勾选）
3. 暂停 ingest 流程，向用户报告冲突位置
4. 等待用户决策后继续

## 细节准则

### 文件处理准则
1. **批量处理**：处理多个文件时，按字母顺序逐个处理，每个文件完成后立即归档
2. **错误恢复**：如果某个文件处理失败，记录错误信息，继续处理下一个文件
3. **进度追踪**：处理多个文件时，实时更新处理清单，标记已完成的文件
4. **文件名编码**：处理中文文件名时，使用相对路径和 `cd` 命令避免编码问题

### 页面创建准则
1. **命名规范**：
   - 实体：使用原名（中文或英文），保持 TitleCase
   - 概念：使用中文名称，保持 TitleCase
   - 来源：使用 `摘要-{kebab-case-id}` 格式
2. **去重检查**：创建页面前，检查是否已存在同名或相似页面
3. **链接完整性**：每个页面必须包含 `## 关联连接` 区域，至少链接到来源页面
4. **标签一致性**：优先复用现有标签，避免创建语义相同的新标签

### 索引更新准则
1. **分类准确**：确保页面添加到正确的分类（Concepts/Entities/Sources/Syntheses）
2. **描述简洁**：每个条目的描述控制在 10-15 字以内
3. **标签统计**：标签云按使用频率降序排列，格式为 `#标签 (数量)`
4. **计数准确**：每个分类的计数必须与实际页面数量一致

### 日志记录准则
1. **时间格式**：使用 `[YYYY-MM-DD]` 格式
2. **操作类型**：明确标注操作类型（ingest/query/lint/sync）
3. **变更详情**：列出所有新增和更新的页面，使用 wikilink 格式
4. **源文件追溯**：记录源文件的原始路径，便于追溯

### 归档验证准则
1. **移动前检查**：确认所有相关页面和索引都已更新
2. **移动后验证**：使用 `ls` 或 `find` 命令验证文件已成功移动
3. **原位置清理**：确认原位置不再存在该文件
4. **归档记录**：在 `.processed.json` 中记录归档路径和时间

## 注意事项

- 绝对不读取 `raw/09-archive/` 下的任何文件
- 所有 wiki 页面必须包含 `## 关联连接` 区域，不能产生孤岛页面
- 使用简体中文编写所有内容
- 实体命名使用 TitleCase，概念和来源使用 kebab-case
- 使用 Glob 工具扫描文件，使用 Read 工具读取内容，使用 Write/Edit 工具创建/更新页面
- 使用 Bash 工具移动文件到归档目录
- 只读取总索引和相关页面，不要全量扫描
- **处理多个文件时，必须逐个完成，不能跳过任何文件**
- **每个文件处理完成后，立即归档，不要等到所有文件都处理完才归档**

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [levi-qiao](https://github.com/levi-qiao)
- **Source:** [levi-qiao/obsidian-llm-wiki](https://github.com/levi-qiao/obsidian-llm-wiki)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-levi-qiao-obsidian-llm-wiki-ingest
- Seller: https://agentstack.voostack.com/s/levi-qiao
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
