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*