docs(§02): v1.2 设计文档修订 - 修复第二轮评审发现的 3 个 Major 问题

- M1: 修复 FTS5 表结构语法错误(删除重复定义,调整表创建顺序)
- M2: 在 WikiPage 中添加 source_tool 字段,解决 memory_bridge 的 tool_name 数据来源问题
- M3: 补充 CacheService 详细实现(LRU 缓存 + 最大容量 1000 条 + TTL 3600 秒 + asyncio.Lock)

两轮独立评审均通过,设计文档 v1.2 完成。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 11:15:23 +08:00
parent 941febf63c
commit 67a53d82de
3 changed files with 120 additions and 33 deletions
+80 -20
View File
@@ -99,7 +99,7 @@ class MCPServer:
```python
class MemoryBridgeTool:
"""memory_bridge 工具实现"""
"""memory_bridge 工具实现 - 按 AI 工具来源浏览和对比 wiki 知识"""
async def handle(self, tool_name: str, date_range: str) -> dict:
"""
@@ -125,12 +125,14 @@ class MemoryBridgeTool:
"date_range": "2024-01-01:2024-12-31"
}
"""
# 1. 查询符合条件的 wiki 页面
# 2. 按 tool_name 和 date_range 过滤
# 3. 计算相关性分数
# 1. 从索引中查询 source_tool = tool_name 的页面
# 2. 按 updated_at 在 date_range 过滤
# 3. 计算相关性分数(基于摘要匹配)
# 4. 返回结果列表
```
**数据来源:** WikiPage.source_tool 字段(新增),记录页面来源的 AI 工具名称
### 2.3 Service Layer
**IndexerService(索引服务):**
@@ -196,12 +198,68 @@ class QueryService:
**CacheService(缓存服务):**
```python
from functools import lru_cache
from collections import OrderedDict
import asyncio
class CacheService:
async def get(self, key: str) -> Optional[Any]
async def set(self, key: str, value: Any, ttl: int)
async def invalidate(self, pattern: str)
"""缓存服务 - LRU 缓存 + TTL 过期"""
def __init__(self, max_size: int = 1000):
self.cache: OrderedDict[str, tuple] = OrderedDict() # key -> (value, expire_time)
self.max_size = max_size
self.lock = asyncio.Lock()
async def get(self, key: str) -> Optional[Any]:
"""获取缓存值(异步,带锁)"""
async with self.lock:
if key not in self.cache:
return None
value, expire_time = self.cache[key]
# 检查是否过期
if expire_time and time.time() > expire_time:
del self.cache[key]
return None
# LRU: 移到末尾
self.cache.move_to_end(key)
return value
async def set(self, key: str, value: Any, ttl: int = 3600) -> None:
"""设置缓存值(异步,带锁)"""
async with self.lock:
expire_time = time.time() + ttl if ttl else None
# 如果缓存已满,删除最旧的条目
if len(self.cache) >= self.max_size and key not in self.cache:
self.cache.popitem(last=False) # FIFO 删除
self.cache[key] = (value, expire_time)
self.cache.move_to_end(key)
async def invalidate(self, pattern: str) -> int:
"""按模式清除缓存(支持 * 通配符)"""
async with self.lock:
if pattern == "*":
count = len(self.cache)
self.cache.clear()
return count
keys_to_delete = [k for k in self.cache.keys() if fnmatch.fnmatch(k, pattern)]
for key in keys_to_delete:
del self.cache[key]
return len(keys_to_delete)
```
**缓存策略:**
- **存储方式**:内存 LRU 缓存(OrderedDict
- **最大容量**:1000 条(可配置)
- **淘汰策略**:FIFO 淘汰最旧条目
- **TTL**:默认 3600 秒(1 小时)
- **线程安全**asyncio.Lock 保护
**GraphService(图服务):**
```python
class GraphService:
@@ -312,6 +370,7 @@ class WikiPage:
summary: str # 摘要(≤200 字符)
content_hash: str # MD5 哈希
lifecycle: str # draft/verified/archived/disputed
source_tool: str # 来源工具(claude/web_reader/gitea/other
created_at: datetime
updated_at: datetime
indexed_at: datetime
@@ -320,7 +379,7 @@ class WikiPage:
return (datetime.now() - self.updated_at).days > days
```
> **注:** 删除了 `sources` 字段(v1.0 遗留问题),页面来源可通过 backlinks 推断
> **注:** source_tool 字段用于 memory_bridge 功能,记录页面来源的 AI 工具
### 3.2 WikiIndex(索引模型)
@@ -355,6 +414,7 @@ CREATE TABLE wiki_pages (
summary TEXT,
content_hash TEXT NOT NULL,
lifecycle TEXT DEFAULT 'draft', -- draft|verified|archived|disputed
source_tool TEXT DEFAULT 'other', -- claude/web_reader/gitea/other
created_at TIMESTAMP,
updated_at TIMESTAMP,
indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
@@ -364,20 +424,12 @@ CREATE TABLE wiki_pages (
CREATE INDEX idx_pages_category ON wiki_pages(category);
CREATE INDEX idx_pages_lifecycle ON wiki_pages(lifecycle);
CREATE INDEX idx_pages_updated ON wiki_pages(updated_at);
CREATE INDEX idx_pages_source_tool ON wiki_pages(source_tool); -- memory_bridge 查询优化
```
**FTS5 全文搜索表:**
```sql
-- 使用 FTS5 创建全文搜索虚拟表
CREATE VIRTUAL TABLE wiki_fts USING fts5(
path UNINDEXED, -- 路径不参与全文搜索
title, -- 标题参与搜索
content, -- 内容参与搜索
summary, -- 摘要参与搜索
tokenize = 'porter unicode61' -- 英文词干 + Unicode 分词
);
-- 内容表(FTS5 外部内容表)
-- 内容表(FTS5 外部内容表)- 必须先创建
CREATE TABLE wiki_content (
path TEXT PRIMARY KEY,
content TEXT NOT NULL
@@ -390,7 +442,8 @@ CREATE VIRTUAL TABLE wiki_fts USING fts5(
content,
summary,
content=wiki_content,
content_rowid=rowid
content_rowid=rowid,
tokenize = 'porter unicode61' -- 英文词干 + Unicode 分词
);
```
@@ -985,6 +1038,13 @@ pm2 save
## 14. 修订记录
### v1.22026-06-26- 第二轮评审修复
**Major(已修复):**
-**M1**: 修复 FTS5 表结构语法错误(第 3.3 节)- 删除重复定义,调整表创建顺序
-**M2**: 解决 memory_bridge 的 tool_name 数据来源问题(第 2.2 节 / 3.1 节)- 在 WikiPage 中添加 source_tool 字段
-**M3**: 明确 QueryService 缓存策略(第 2.3 节)- 补充 CacheService 详细实现,包括 LRU 缓存和大小限制
### v1.12026-06-26
**修复的问题:**
@@ -1009,6 +1069,6 @@ pm2 save
---
*文档版本:v1.1*
*文档版本:v1.2*
*创建时间:2026-06-26*
*更新时间:2026-06-26*