From 22932e34ebd5062934ac1114978a87e62567e1bd Mon Sep 17 00:00:00 2001 From: claude_dev Date: Fri, 26 Jun 2026 10:11:41 +0800 Subject: [PATCH] docs: add design document (Phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 架构设计:MCP Server + Wiki Skills 混合架构 - 模块设计:4 层架构(Protocol/Tool/Service/Storage) - 数据模型:WikiPage 和 WikiIndex - 接口设计:MCP Tool 接口规范 - 核心算法:增量更新、查询、cross_linker - 配置设计:YAML 配置 + 环境变量 - 错误处理:5 类错误处理策略 - 性能优化:FTS5、缓存、WAL 并发优化 - Wiki Skills 设计:11 个 Skills 优先级划分 - 测试设计:单元/集成/E2E 三层测试 - 部署设计:开发 + 生产环境配置 Phase 2: 设计评审 Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 6 +- docs/02-design.md | 552 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 docs/02-design.md diff --git a/README.md b/README.md index a266266..f11e5e5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 状态 -🚧 开发中 - Phase 1: 需求评审 +🚧 开发中 - Phase 2: 设计评审 ## 项目概述 @@ -52,8 +52,8 @@ python -m mcp_server.main ## 文档 -- [需求文档](docs/01-requirements.md) -- [设计文档](docs/02-design.md) - 待完成 +- [需求文档 v1.1](docs/01-requirements.md) ✅ 评审通过 +- [设计文档 v1.0](docs/02-design.md) 🚧 评审中 ## 开发流程 diff --git a/docs/02-design.md b/docs/02-design.md new file mode 100644 index 0000000..028bd8a --- /dev/null +++ b/docs/02-design.md @@ -0,0 +1,552 @@ +# sanguo_llmwiki 设计文档 + +## 1. 架构设计 + +### 1.1 系统架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Claude Code │ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────────┐ │ +│ │ Wiki Skills (11) │ │ Wiki MCP Server │ │ +│ │ (一次性操作) │ │ (Python 服务) │ │ +│ └─────────────────────┘ └─────────────────────────┘ │ +│ │ │ +└───────────────────────────────────────┼─────────────────────┘ + │ MCP 协议 + │ (stdio/SSE) +┌───────────────────────────────────────▼─────────────────────┐ +│ Wiki MCP Server │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ MCP Protocol Layer │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ Tool Layer │ │ +│ │ query │ status │ lint │ linker │ taxonomy │ synthesize│ │ +│ └───────────────────────┬───────────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ Service Layer │ │ +│ │ indexer │ cache │ graph │ parser │ │ +│ └───────────────────────┬───────────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ Storage Layer │ │ +│ │ SQLite (WAL 模式) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────┼─────────────────────┘ + │ +┌───────────────────────────────────────▼─────────────────────┐ +│ Obsidian Wiki Vault │ +│ /Volumes/KnowledgeBase/wiki-vault │ +│ │ +│ practices/ │ concepts/ │ entities/ │ projects/ │ skills/ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.2 部署架构 + +**开发阶段:** +``` +Claude Code --stdio--> Wiki MCP Server (手动启动) +``` + +**生产阶段:** +``` +PM2 --> Wiki MCP Server (SSE 模式) + │ + └──> Claude Code --SSE--> Wiki MCP Server +``` + +--- + +## 2. 模块设计 + +### 2.1 MCP Protocol Layer + +**职责:** +- MCP 协议解析和封装 +- 工具注册和路由 +- 错误处理和日志 + +**接口:** +```python +class MCPServer: + def register_tool(self, name: str, handler: Callable) + def handle_call(self, name: str, params: dict) -> dict + def log(self, level: str, message: str) +``` + +### 2.2 Tool Layer + +**工具列表:** + +| 工具 | 输入 | 输出 | 实现模块 | +|------|------|------|----------| +| wiki_query | query, tags, limit | results, citations | QueryTool | +| memory_bridge | tool_name, date_range | entries | MemoryTool | +| wiki_status | - | stats, pending | StatusTool | +| wiki_lint | path, level | issues, fixes | LintTool | +| cross_linker | path, dry_run | missing_links | LinkerTool | +| tag_taxonomy | path, enforce | conflicts | TaxonomyTool | +| wiki_synthesize | concepts, threshold | synthesis | SynthesizeTool | +| daily_update | - | updated, new | DailyTool | + +### 2.3 Service Layer + +**IndexerService(索引服务):** +```python +class IndexerService: + def index_page(self, path: str) -> None + def index_batch(self, paths: List[str]) -> None + def rebuild_index(self) -> None + def get_dirty_pages(self) -> List[str] # 增量更新 +``` + +**CacheService(缓存服务):** +```python +class CacheService: + def get(self, key: str) -> Optional[Any] + def set(self, key: str, value: Any, ttl: int) + def invalidate(self, pattern: str) +``` + +**GraphService(图服务):** +```python +class GraphService: + def get_links(self, path: str) -> Set[str] + def get_backlinks(self, path: str) -> Set[str] + def find_orphans(self) -> Set[str] + def find_missing_links(self) -> List[Tuple[str, str]] +``` + +**ParserService(解析服务):** +```python +class ParserService: + def parse_frontmatter(self, content: str) -> dict + def extract_links(self, content: str) -> List[str] + def validate_page(self, path: str) -> List[str] # 返回问题列表 +``` + +### 2.4 Storage Layer + +**数据库连接(带并发保护):** +```python +class Database: + def __init__(self, path: str): + self.conn = sqlite3.connect(path, check_same_thread=False) + self.conn.execute("PRAGMA journal_mode=WAL") + self.conn.execute("PRAGMA busy_timeout=10000") # 10s + self.lock = asyncio.Lock() + + async def execute(self, sql: str, params: tuple): + async with self.lock: + return self.conn.execute(sql, params) +``` + +--- + +## 3. 数据模型 + +### 3.1 WikiPage(页面模型) + +```python +@dataclass +class WikiPage: + path: str # wiki 相对路径 + title: str # 标题 + category: str # 分类(practices/concepts/...) + tags: List[str] # 标签列表 + summary: str # 摘要(≤200 字符) + content_hash: str # MD5 哈希 + lifecycle: str # draft/verified/archived/disputed + sources: List[str] # 来源页面 + created_at: datetime + updated_at: datetime + indexed_at: datetime + + def is_stale(self, days: int = 90) -> bool: + return (datetime.now() - self.updated_at).days > days +``` + +### 3.2 WikiIndex(索引模型) + +```python +@dataclass +class WikiIndex: + pages: Dict[str, WikiPage] + links: Dict[str, Set[str]] # source -> {targets} + backlinks: Dict[str, Set[str]] # target -> {sources} + tags: Dict[str, Set[str]] # tag -> {pages} + orphans: Set[str] # 无反向链接的页面 + stats: IndexStats + +@dataclass +class IndexStats: + total_pages: int + total_links: int + total_tags: int + last_indexed: datetime + dirty_pages: int +``` + +--- + +## 4. 接口设计 + +### 4.1 MCP Tool 接口 + +**wiki_query:** +```json +{ + "name": "wiki_query", + "inputSchema": { + "type": "object", + "properties": { + "query": {"type": "string"}, + "tags": {"type": "array", "items": {"type": "string"}}, + "limit": {"type": "integer", "default": 10} + } + } +} +``` + +**wiki_status:** +```json +{ + "name": "wiki_status", + "inputSchema": { + "type": "object", + "properties": {} + } +} +``` + +### 4.2 内部服务接口 + +```python +# IndexerService +async def index_page(path: str) -> IndexResult +async def get_dirty_pages() -> List[str] + +# QueryService +async def search(query: str, limit: int) -> List[WikiPage] +async def search_by_tags(tags: List[str]) -> List[WikiPage] + +# GraphService +async def get_links(path: str) -> Set[str] +async def find_orphans() -> Set[str] +``` + +--- + +## 5. 核心算法 + +### 5.1 增量更新算法 + +```python +async def incremental_update(): + # 1. 获取所有 wiki 页面 + all_pages = scan_wiki_vault() + + # 2. 检查每个页面的哈希 + for page in all_pages: + current_hash = md5(page.content) + stored = db.get_page_hash(page.path) + + if stored != current_hash: + # 3. 只重索引变化的页面 + await index_page(page) + + # 4. 处理删除的页面 + indexed_paths = db.get_all_indexed_paths() + for path in indexed_paths: + if path not in all_pages: + db.delete_page(path) +``` + +### 5.2 查询算法 + +```python +async def wiki_query(query: str, tags: List[str], limit: int): + # 1. FTS5 全文搜索 + if query: + results = fts_search(query, limit) + + # 2. 标签过滤 + if tags: + results = filter_by_tags(results, tags) + + # 3. 按相关性排序 + sorted_results = rank_by_relevance(results, query) + + # 4. 返回带 [[wikilink]] 的结果 + return format_results(sorted_results[:limit]) +``` + +### 5.3 cross_linker 算法 + +```python +async def find_missing_links(): + # 1. 获取所有页面内容 + pages = load_all_pages() + + # 2. 提取所有 [[wikilinks]] + all_links = extract_all_links(pages) + + # 3. 找出缺失的链接 + missing = [] + for source, targets in all_links.items(): + for target in targets: + if not page_exists(target): + missing.append((source, target)) + + return missing +``` + +--- + +## 6. 配置设计 + +### 6.1 配置文件结构 + +```yaml +# ~/.sanguo-llmwiki/config.yaml +wiki: + vault_path: "/Volumes/KnowledgeBase/wiki-vault" + index_path: "~/.sanguo-llmwiki/index.db" + max_page_size_kb: 500 + +mcp: + mode: "stdio" # stdio 或 SSE + host: "localhost" + port: 8080 + +performance: + query_timeout_ms: 5000 + cache_ttl_seconds: 3600 + fts_cache_size_mb: 100 + +logging: + level: "INFO" + file: "~/.sanguo-llmwiki/wiki-mcp.log" +``` + +### 6.2 环境变量 + +```bash +# 环境变量优先级高于配置文件 +WIKI_VAULT_PATH=/custom/path +WIKI_INDEX_PATH=/custom/index.db +MCP_MODE=sse +LOG_LEVEL=DEBUG +``` + +--- + +## 7. 错误处理 + +### 7.1 错误分类 + +| 错误类型 | 处理方式 | +|----------|----------| +| Wiki 路径不存在 | 启动失败,返回友好错误 | +| 索引文件损坏 | 自动重建 + WARN 日志 | +| SQLite 写入失败 | 回滚事务 + 重试 1 次 | +| 页面解析失败 | 记录日志 + 跳过该页面 | +| MCP 协议错误 | 返回标准错误格式 | +| 查询超时 | 返回部分结果 + WARN | + +### 7.2 错误响应格式 + +```json +{ + "success": false, + "error": { + "code": "INDEX_CORRUPTED", + "message": "索引文件损坏,正在自动重建", + "details": {"rebuilding": true} + } +} +``` + +--- + +## 8. 安全考虑 + +虽然是本地系统,但仍需考虑: + +1. **路径安全**:验证路径在 wiki vault 范围内(防止路径遍历) +2. **资源限制**:限制查询返回数量、内存使用 +3. **日志脱敏**:日志中不记录敏感内容 + +--- + +## 9. 性能优化 + +### 9.1 索引优化 + +- 使用 FTS5 全文搜索索引 +- 标签单独建立索引 +- 定期 VACUUM(每周) + +### 9.2 查询优化 + +- 查询结果缓存(TTL 1 小时) +- 限制返回数量(默认 10) +- 使用 prepared statements + +### 9.3 并发优化 + +- SQLite WAL 模式 +- asyncio.Lock 写入串行化 +- busy_timeout=10s + +--- + +## 10. Wiki Skills 设计 + +### 10.1 Skill 模板 + +每个 Skill 遵循统一结构: + +```markdown +--- +name: wiki-xxx +description: > + 简短描述(1-2 句) + 触发条件 +--- + +# Wiki XXX + +## 使用场景 +用户何时触发这个 Skill + +## 操作步骤 +1. ... +2. ... + +## 输出格式 +... +``` + +### 10.2 Skills 列表 + +**优先级 P0(核心):** +1. wiki-setup - 初始化 wiki +2. wiki-ingest - 蒸馏文档 +3. wiki-capture - 保存对话 + +**优先级 P1(重要):** +4. wiki-rebuild - 重建 wiki +5. data-ingest - 录入数据 +6. ingest-url - 抓取 URL +7. wiki-agent - 录入历史 +8. wiki-export - 导出 + +**优先级 P2(可选):** +9. wiki-research - 研究 +10. impl-validator - 验证 +11. graph-colorize - 着色 + +--- + +## 11. 测试设计 + +### 11.1 单元测试 + +覆盖所有 Service 层的核心逻辑: +- IndexerService 测试 +- QueryService 测试 +- GraphService 测试 +- ParserService 测试 + +### 11.2 集成测试 + +- MCP 协议层测试(使用 MCP SDK mock) +- SQLite 操作测试 +- Wiki 解析测试 + +### 11.3 E2E 测试 + +使用真实 wiki 数据集测试: +- 查询场景 +- 搜索场景 +- 索引更新场景 + +--- + +## 12. 部署设计 + +### 12.1 开发部署 + +```bash +# 手动启动 +cd ~/.openclaw/sanguo_projects/sanguo_llmwiki +python -m mcp_server.main +``` + +### 12.2 生产部署 + +```bash +# PM2 配置 +cat > ecosystem.config.cjs << 'EOF' +module.exports = { + apps: [{ + name: 'wiki-mcp', + script: 'python', + args: '-m mcp_server.main', + cwd: '/Users/chufeng/.openclaw/sanguo_projects/sanguo_llmwiki', + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '500M', + env: { + PYTHONUNBUFFERED: '1' + } + }] +} +EOF + +pm2 start ecosystem.config.cjs +pm2 save +``` + +### 12.3 MCP 配置 + +**stdio 模式(开发):** +```json +{ + "mcpServers": { + "wiki": { + "command": "python", + "args": ["-m", "mcp_server.main"], + "cwd": "/Users/chufeng/.openclaw/sanguo_projects/sanguo_llmwiki", + "env": { + "WIKI_VAULT_PATH": "/Volumes/KnowledgeBase/wiki-vault" + } + } + } +} +``` + +**SSE 模式(生产):** +```json +{ + "mcpServers": { + "wiki": { + "type": "sse", + "url": "http://localhost:8080/sse" + } + } +} +``` + +--- + +*文档版本:v1.0* +*创建时间:2026-06-26*