Wikisage Skill
基于 Karpathy llm-wiki 模式的持久化 Wiki。 LLM 负责写和维护所有内容,用户负责来源、探索方向和提问。 纯本地 markdown 文件,用 index.md 导航,无需向量数据库。
📍 路径约定(环境变量驱动)
本 skill 所有路径都基于环境变量,无硬编码:
| 变量 | 默认值 | 作用 |
|---|---|---|
WIKI_ROOT | $HOME/.openclaw/workspace/wiki | Wiki markdown 根目录 |
MCPORTER_CONFIG | $HOME/.openclaw/workspace/config/mcporter.json | mcporter 配置文件(可选) |
WIKI_SKILL_DIR | $HOME/.openclaw/workspace/skills/wikisage | Skill 自身目录(脚本位置) |
首次部署时,在 shell/agent 环境里 export 一下这三个变量即可(或用默认值)。
下文示例用 $WIKI_ROOT 这种写法代替绝对路径。
🛠 执行通道:Obsidian MCP(首选,强烈推荐)
本 skill 围绕 Obsidian filesystem MCP server 设计。 没装 MCP 也能跑(走
read/write/editfallback),但装了会更稳:allowed-dir 边界兜底、错误更规范、LLM 不会意外写到 wiki 外面。
所有 wiki 文件读写优先走 Obsidian filesystem MCP,而不是通用 read/write 工具。
| 操作 | MCP 调用 |
|---|---|
| 读文件 | mcporter call obsidian.read_text_file path=<abs path> |
| 写/覆盖文件 | mcporter call obsidian.write_file path=<abs> content=<str> |
| 列目录 | mcporter call obsidian.list_directory path=<abs> |
| 搜文件名 | mcporter call obsidian.search_files path=<abs> pattern=<glob> |
| 改文件 | mcporter call obsidian.edit_file path=<abs> edits=... |
| 看边界 | mcporter call obsidian.list_allowed_directories |
所有调用都需要 --config $MCPORTER_CONFIG
(mcporter 有双 config 坑:会同时读 ~/.claude.json 和项目 config,不带 --config 只会看到 claude.json 里的 server)
Fallback:MCP 不可用时(daemon 挂了、server 不 healthy),用通用 read/write/edit/exec grep 兜底,但要在回复里告诉用户"MCP 离线,走 fallback"。
全文搜索不走 MCP:MCP 的 search 只匹配文件名。找内容用:
- qmd-search(workspace 集合,BM25,快但索引可能滞后)
exec grep -rn "关键词" $WIKI_ROOT/
触发条件
| 用户说 | 执行 |
|---|---|
| "加进 wiki" / "ingest" / "摄入这篇" | → ingest 流程 |
| "查 wiki" / "wiki 里有没有" / "从 wiki 查" | → query 流程 |
| "整理 wiki" / "wiki 健康检查" / "lint" | → lint 流程 |
| 涉及客户、历史决策、账号信息的技术问题 | → 先本地查 wiki,再回答 |
| 通用技术问题(无特定上下文) | → 直接 MCP → LLM |
| 回答完有价值的技术问题后 | → 询问"要把这些存进 wiki 吗?" |
三层架构
$WIKI_ROOT/
├── raw/ 原始文档(只读,用户放入,LLM 不修改)
├── pages/ LLM 生成并维护的 markdown 文件集
│ ├── aws/ AWS 服务、架构、合规
│ ├── ai/ AI/LLM 技术
│ ├── clients/ 客户信息(账号、联系人、项目)
│ ├── projects/ 具体项目
│ └── ops/ 运维、kubectl、DevOps
├── index.md 所有页面目录(标题 + 一行描述 + 路径),每次 ingest 后更新
├── log.md 操作日志(append-only,格式:## [YYYY-MM-DD] ingest | 标题)
└── .ingest-cache.json SHA256 去重缓存(dedup.py 维护,不进 Obsidian vault)
只有一个 wiki 目录: $WIKI_ROOT(即 Obsidian MCP 的 allowed dir)
Query 流程
详见 scripts/query.md
核心逻辑:
obsidian.read_text_file读$WIKI_ROOT/index.md,找相关页面obsidian.read_text_file读相关页面全文,综合回答,标注来源> 参考:[[页面名]]- 答案本身有价值 → 询问用户是否存回 wiki
Ingest 流程
详见 scripts/ingest.md
核心逻辑:
0. dedup.py check 去重(来源是文件/URL 时)→ DUPLICATE 就停
obsidian.read_text_file读 index.md,判断是否已有相关页面obsidian.write_file/obsidian.edit_file新建 or 更新页面(一次 ingest 可能触碰 5-15 个页面)obsidian.edit_file更新 index.mdobsidian.edit_file追加 log.md(## [YYYY-MM-DD] ingest | 来源标题)dedup.py record记录 SHA256 缓存(来源是文件/URL 时)
Lint 流程
详见 scripts/lint.md
检查:孤儿页面、缺失概念页、index.md 不一致、矛盾内容、过时内容 (lint.py 脚本走 Python filesystem 直接读,不经过 MCP;LLM Layer 2 整改时走 MCP)
页面模板
# 页面标题
**最后更新:** YYYY-MM-DD
**来源数量:** N
**分类:** aws/security
**置信度:** EXTRACTED <!-- 整页默认值;段落内可局部覆盖 -->
## 概述
## 核心内容
<!-- 置信度可以在段落/句子级别用 inline tag 标注: -->
<!-- [EXTRACTED] 原文直接扒的事实 -->
<!-- [INFERRED] 基于来源推理的结论 -->
<!-- [AMBIGUOUS] 来源本身表述模糊 -->
<!-- [UNVERIFIED] AI 自己补的常识/背景,未经来源验证 -->
## 相关页面
- [[相关页面名]]
## 来源
- [[原始文档页面名]]
- [外部链接](https://...)
置信度标签规则(强制)
| Tag | 含义 | 什么时候用 |
|---|---|---|
EXTRACTED | 从来源原文直接扒的事实 | 定价、API 参数、官方原话 |
INFERRED | 基于来源推理/组合得出 | "所以月成本约 $80"(来源只给了单价) |
AMBIGUOUS | 来源本身说得不清楚 | 文档自相矛盾或写得模糊 |
UNVERIFIED | AI 补的背景常识,没来源 | 写页面时为了通顺加的常识性描述 |
原则:
- 整页默认置信度写在 frontmatter,不要省略
- 页面内如果混合了不同置信度的内容,必须在段落开头/句尾用 inline tag 标注
- Query 时如果引用了
INFERRED/UNVERIFIED的内容,必须在回答里明说("这条是推断的")
log.md 格式
每条记录格式:## [YYYY-MM-DD] {操作} | {标题}
## [2026-04-09] ingest | Karpathy llm-wiki 模式
## [2026-04-09] query | S3 Files POSIX 访问方案
## [2026-04-09] lint | 全库健康检查
可用 grep "^## \[" $WIKI_ROOT/log.md | tail -10 查最近操作。