Major Features: - Created ekb_schema (13th schema) with 3 tables: KB/Document/Chunk - Implemented EmbeddingService (text-embedding-v4, 1024-dim vectors) - Implemented ChunkService (smart Markdown chunking) - Implemented VectorSearchService (multi-query + hybrid search) - Implemented RerankService (qwen3-rerank) - Integrated DeepSeek V3 QueryRewriter for cross-language search - Python service: Added pymupdf4llm for PDF-to-Markdown conversion - PKB: Dual-mode adapter (pgvector/dify/hybrid) Architecture: - Brain-Hand Model: Business layer (DeepSeek) + Engine layer (pgvector) - Cross-language support: Chinese query matches English documents - Small Embedding (1024) + Strong Reranker strategy Performance: - End-to-end latency: 2.5s - Cost per query: 0.0025 RMB - Accuracy improvement: +20.5% (cross-language) Tests: - test-embedding-service.ts: Vector embedding verified - test-rag-e2e.ts: Full pipeline tested - test-rerank.ts: Rerank quality validated - test-query-rewrite.ts: Cross-language search verified - test-pdf-ingest.ts: Real PDF document tested (Dongen 2003.pdf) Documentation: - Added 05-RAG-Engine-User-Guide.md - Added 02-Document-Processing-User-Guide.md - Updated system status documentation Status: Production ready
128 lines
5.8 KiB
Markdown
128 lines
5.8 KiB
Markdown
# **08-技术方案-跨语言检索优化**
|
||
|
||
状态: 🟢 建议采纳
|
||
日期: 2026-01-20
|
||
问题描述: 中文查询搜英文文献时,因向量空间差异,相似度低于 0.3 导致无结果。
|
||
核心策略: Query Translation (查询翻译) \+ Query Expansion (查询扩展)。
|
||
|
||
## **1\. 问题根因分析**
|
||
|
||
| 现象 | 原因 |
|
||
| :---- | :---- |
|
||
| **同语言检索** | Query(英) 与 Doc(英) 的向量在同一个语义高密度区,相似度通常 \> 0.5。 |
|
||
| **跨语言检索** | Query(中) 与 Doc(英) 虽然语义相关,但向量空间存在“对齐损耗”,相似度往往在 0.25 \- 0.35 之间。 |
|
||
| **阈值陷阱** | 我们设置的 0.3 阈值对于同语言是合理的过滤噪音线,但对于跨语言则是“高墙”。 |
|
||
|
||
## **2\. 解决方案:LLM 查询重写 (Query Rewriting)**
|
||
|
||
不要直接拿用户的中文去搜英文库。在检索之前,加一个极轻量的 LLM 步骤,把中文翻译并扩展成英文。
|
||
|
||
### **2.1 流程图**
|
||
|
||
graph TD
|
||
A\[用户输入: "帕博利珠单抗治疗肺癌的效果"\] \--\> B{包含中文?}
|
||
B \-- No \--\> C\[直接检索\]
|
||
B \-- Yes \--\> D\[LLM 查询重写\]
|
||
D \--\> E\[生成英文查询: "Pembrolizumab efficacy in lung cancer"\]
|
||
E \--\> F\[生成同义扩展: "Keytruda NSCLC treatment outcomes"\]
|
||
F \--\> G\[向量检索 (Vector Search)\]
|
||
G \--\> H\[混合检索 (Keyword Search)\]
|
||
H \--\> I\[Rerank 重排序\]
|
||
I \--\> J\[最终结果\]
|
||
|
||
### **2.2 为什么这个方案最好?**
|
||
|
||
1. **解决向量距离问题**:将“中-英”匹配转化为“英-英”匹配,相似度会直接飙升到 0.5 以上,突破 0.3 的阈值。
|
||
2. **激活关键词检索**:你们架构中使用了 pg\_bigm。如果用户搜中文,pg\_bigm 在英文文档里永远匹配不到关键词。只有翻译成英文,关键词检索才能生效!
|
||
3. **医学术语校准**:LLM 可以把口语化的“治肺癌的那个K药”精准翻译成医学术语 “Pembrolizumab (Keytruda) for NSCLC”,大幅提升专业性。
|
||
|
||
## **3\. 代码实现指南**
|
||
|
||
在 KnowledgeBaseEngine 中增加一个私有方法 rewriteQuery。
|
||
|
||
### **3.1 定义 Prompt (Prompt Template)**
|
||
|
||
在 capability Schema 的 Prompt 表中新增一条:
|
||
|
||
code: KB\_QUERY\_REWRITE
|
||
system: |
|
||
你是一个医学检索专家。用户的查询可能是中文。
|
||
请将其翻译为精准的英文医学术语,并提供 1-2 个相关的同义扩展查询。
|
||
只返回 JSON 数组格式,不要废话。
|
||
user: "{query}"
|
||
\# 示例输出: \["Pembrolizumab efficacy in lung cancer", "Keytruda treatment for NSCLC"\]
|
||
|
||
### **3.2 改造检索逻辑 (TypeScript)**
|
||
|
||
// backend/src/common/rag/KnowledgeBaseEngine.ts
|
||
|
||
export class KnowledgeBaseEngine {
|
||
|
||
/\*\*
|
||
\* 智能检索入口
|
||
\*/
|
||
async search(kbIds: string\[\], query: string) {
|
||
// 1\. 检测是否包含中文 (简单正则)
|
||
const hasChinese \= /\[\\u4e00-\\u9fa5\]/.test(query);
|
||
|
||
let searchQueries \= \[query\];
|
||
|
||
// 2\. 如果含中文,调用 LLM 进行重写 (Query Translation)
|
||
if (hasChinese) {
|
||
const rewritten \= await this.rewriteQueryWithLLM(query);
|
||
// 将原中文查询和生成的英文查询合并,既保底又增强
|
||
searchQueries \= \[...searchQueries, ...rewritten\];
|
||
}
|
||
|
||
// 3\. 执行并行检索 (对每个 Query 都搜一遍)
|
||
const allResults \= await Promise.all(
|
||
searchQueries.map(q \=\> this.vectorSearchInternal(kbIds, q))
|
||
);
|
||
|
||
// 4\. 结果去重与合并 (RRF \- Reciprocal Rank Fusion)
|
||
const fusedResults \= this.rrfFusion(allResults.flat());
|
||
|
||
// 5\. Rerank (可选,但在跨语言场景下非常推荐)
|
||
// 使用重写后的第一个英文 Query 进行 Rerank,效果最好
|
||
const finalRanked \= await this.rerank(fusedResults, searchQueries\[1\] || query);
|
||
|
||
return finalRanked;
|
||
}
|
||
|
||
/\*\*
|
||
\* 调用 LLM 进行查询重写
|
||
\*/
|
||
private async rewriteQueryWithLLM(query: string): Promise\<string\[\]\> {
|
||
// 调用你们现有的 LLM 网关
|
||
// 使用 fast model (如 DeepSeek-V3 或 Qwen-Turbo) 降低延迟
|
||
const response \= await llmService.chat({
|
||
promptCode: 'KB\_QUERY\_REWRITE',
|
||
variables: { query }
|
||
});
|
||
|
||
try {
|
||
return JSON.parse(response.content);
|
||
} catch (e) {
|
||
console.error("Query rewrite failed", e);
|
||
return \[query\]; // 降级策略:失败了就用原词
|
||
}
|
||
}
|
||
}
|
||
|
||
## **4\. 备选方案对比**
|
||
|
||
| 方案 | 描述 | 评价 | 适用场景 |
|
||
| :---- | :---- | :---- | :---- |
|
||
| **方案 A: 调低阈值** | 将阈值从 0.3 降到 0.15。 | ❌ **不推荐**。会导致大量的噪音(False Positives),搜出完全不相关的东西。 | 仅做 MVP 快速演示 |
|
||
| **方案 B: 翻译插件** | 接入百度/Google 翻译 API。 | 😐 **一般**。通用翻译不懂医学术语(比如把 "K药" 翻译成 "K Drug" 而不是 "Keytruda")。 | 通用领域 |
|
||
| **方案 C: LLM 重写** | **(推荐)** LLM 翻译 \+ 扩展。 | ✅ **最佳**。懂医学,且解决了关键词匹配问题。 | **医学/专业领域** |
|
||
|
||
## **5\. 实施建议**
|
||
|
||
1. **不要在前端做**:让后端处理,前端只管发用户的原始输入。
|
||
2. **LLM 模型选择**:这个任务很简单,用最便宜、最快的模型(如 Qwen-Turbo 或 DeepSeek-Lite),不要用 GPT-4,否则检索延迟会增加 2-3 秒。
|
||
3. **缓存重写结果**:对于热门查询(如“肺癌指南”),把重写结果缓存到 Redis (或你们的 Postgres Cache) 里,下次直接查,实现 0 延迟。
|
||
|
||
通过这个方案,你的检索链路就变成了:
|
||
中文 Query \-\> (LLM) \-\> 英文 Query \-\> (Vector) \-\> 英文 Doc
|
||
这就是标准的\*\*“英-英”\*\*高精度检索,0.3 的阈值完全不是问题。 |