feat: 搜索改进与代码修复

- 连字符扩展:multi-agent → multi agent 提升搜索召回率
- 中文同义词支持:添加中英文混合查询扩展
- Snippet 高亮预留:为 FTS5 snippet 功能预留接口
- YAML 解析增强:支持 HTML 实体解码
- 标签关联维护:自动维护 wiki_page_tags 关联表
- 缓存失效优化:支持智能前缀匹配
- 设计文档更新:汇总近期改动 (v1.3)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 07:47:28 +08:00
parent b2c3ca02f0
commit 421b3ada3b
10 changed files with 289 additions and 44 deletions
+81 -9
View File
@@ -7,13 +7,32 @@ Service Layer - QueryService(查询服务)
"""
import logging
from typing import List, Set, Optional
import re
from typing import List, Set, Optional, Dict
from ..storage import Database, WikiPage
from .cache import CacheService
logger = logging.getLogger(__name__)
# 中文同义词映射(用于扩展搜索查询)
CHINESE_SYNONYMS: Dict[str, List[str]] = {
"多智能体": ["multi-agent", "多agent", "multi agent"],
"编排": ["orchestration", "调度"],
"协作": ["collaboration", "协同"],
"工具": ["tool", "工具调用"],
"模式": ["pattern", "设计模式"],
"实践": ["practice", "最佳实践"],
"架构": ["architecture", "系统架构"],
"代理": ["agent", "智能体"],
"大模型": ["llm", "language model", "语言模型"],
"向量": ["vector", "embedding", "嵌入"],
"检索": ["retrieval", "search", "搜索"],
"生成": ["generation", "生成式"],
"推理": ["inference", "reasoning"],
}
class QueryService:
"""查询服务 - 负责所有查询逻辑"""
@@ -21,19 +40,51 @@ class QueryService:
self.db = db
self.cache = cache
async def search(self, query: str, limit: int = 10) -> List[WikiPage]:
"""FTS5 全文搜索"""
def _expand_query_with_synonyms(self, query: str) -> str:
"""使用中文同义词扩展查询
例如:"多智能体编排""多智能体编排 multi-agent orchestration"
"""
expanded_terms = []
query_lower = query.lower()
# 检查每个中文词是否有对应的英文同义词
for chinese_term, english_synonyms in CHINESE_SYNONYMS.items():
if chinese_term in query:
expanded_terms.extend(english_synonyms)
if expanded_terms:
# 构建扩展查询(OR 连接)
expanded_query = f"{query} OR {' OR '.join(expanded_terms)}"
logger.debug(f"Expanded query: {query}{expanded_query}")
return expanded_query
return query
async def search(self, query: str, limit: int = 10, use_synonyms: bool = True) -> List[WikiPage]:
"""FTS5 全文搜索
Args:
query: 搜索查询
limit: 返回结果数量限制
use_synonyms: 是否使用中文同义词扩展
"""
# 1. 检查缓存
cache_key = f"search:{query}:{limit}"
cache_key = f"search:{query}:{limit}:{use_synonyms}"
cached = await self.cache.get(cache_key)
if cached:
logger.debug(f"Cache hit for search: {query}")
return cached
# 2. FTS5 搜索
results = await self.db.fts_search(query, limit)
# 2. 查询扩展(中文同义词)
search_query = query
if use_synonyms:
search_query = self._expand_query_with_synonyms(query)
# 3. 缓存结果
# 3. FTS5 搜索
results = await self.db.fts_search(search_query, limit)
# 4. 缓存结果
await self.cache.set(cache_key, results, ttl=3600)
return results
@@ -116,5 +167,26 @@ class QueryService:
return await self.db.get_stats()
async def invalidate_cache(self, pattern: str = "*") -> int:
"""使缓存失效"""
return await self.cache.invalidate(pattern)
"""使缓存失效
如果 pattern 是具体路径(不是通配符),自动尝试匹配所有缓存前缀:
- page:{pattern}
- links:{pattern}
- search:{pattern}
"""
# 清除所有缓存
if pattern == "*":
return await self.cache.invalidate(pattern)
# 对于具体路径,尝试所有可能的前缀
prefixes = ["page:", "links:", "search:"]
total_invalidated = 0
for prefix in prefixes:
count = await self.cache.invalidate(f"{prefix}{pattern}")
total_invalidated += count
# 也尝试原始模式(兼容直接传入完整 key 的情况)
total_invalidated += await self.cache.invalidate(pattern)
return total_invalidated