Files
AIclinicalresearch/docs/08-项目管理/08-技术方案-跨语言检索优化.md
HaHafeng 40c2f8e148 feat(rag): Complete RAG engine implementation with pgvector
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
2026-01-21 20:24:29 +08:00

128 lines
5.8 KiB
Markdown
Raw Permalink 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.
# **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 的阈值完全不是问题。