Files
sanguo_llmwiki/docs/02-design.md
T
claude_dev 22932e34eb docs: add design document (Phase 2)
- 架构设计: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) <noreply@anthropic.com>
2026-06-26 10:11:41 +08:00

553 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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*