17种经过验证的RAG优化技术,从基础到高级全覆盖
🎯 RAG 高级优化策略大全
17种经过验证的RAG优化技术,从基础到高级全覆盖
📋 目录
🎯 策略分类
按优化阶段分类
文档处理(6种) 检索增强(7种) 生成优化(4种)
↓ ↓ ↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1.文本切分 │ │ 7.二次排序 │ │ 9.上下文压缩 │
│ 2.语义拆分 │ │ 8.连续片段 │ │ 16.Fusion融合 │
│ 3.上下文增强 │ │ 10.用户反馈 │ │ 17.CRAG纠错 │
│ 4.添加描述 │ → │ 11.场景细分 │ → │ 13.知识图谱 │
│ 5.文档增强 │ │ 12.自我检索 │ └──────────────┘
│ 14.层次索引 │ │ 15.HyDE │
└──────────────┘ └──────────────┘
特殊技术:6.查询转换重写(跨阶段)
按难度和效果分类
| 策略 | 难度 | 效果 | 优先级 | 实施时间 |
|---|---|---|---|---|
| 1. 文本切分优化 | ⭐ | ⭐⭐⭐⭐ | 🔥🔥🔥 | 1天 |
| 2. 语义拆分 | ⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 2天 |
| 3. 上下文增强 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 1天 |
| 4. 添加描述标题 | ⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 2天 |
| 5. 文档增强 | ⭐⭐ | ⭐⭐⭐ | 🔥 | 3天 |
| 6. 查询转换 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 2天 |
| 7. 二次排序 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 3天 |
| 8. 连续片段 | ⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 2天 |
| 9. 上下文压缩 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 3天 |
| 10. 用户反馈 | ⭐⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 5天 |
| 11. 场景细分 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 4天 |
| 12. 自我检索 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 5天 |
| 13. 知识图谱 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥 | 2周+ |
| 14. 层次索引 | ⭐⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 3天 |
| 15. HyDE | ⭐⭐⭐ | ⭐⭐⭐⭐ | 🔥🔥 | 2天 |
| 16. Fusion融合 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥🔥 | 4天 |
| 17. CRAG纠错 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🔥🔥 | 5天 |
一、文档处理优化
策略 1:文本切分优化 🔥🔥🔥
原理:合理的文档切分是RAG的基础,影响检索准确率和生成质量。
关键参数:
- chunk_size:每个片段的字符数
- chunk_overlap:片段之间的重叠
- separators:分隔符优先级
实现代码:
/**
* 智能文本切分
* 根据文档类型自适应调整参数
*/
class SmartTextSplitter {
constructor(documentType = 'general') {
// 根据文档类型预设参数
this.configs = {
'general': {
chunkSize: 500,
chunkOverlap: 100,
separators: ['\n\n', '\n', '。', '!', '?', ';', ',', ' '],
},
'technical': {
chunkSize: 800,
chunkOverlap: 150,
separators: ['\n\n', '\n', '。', ';', '.', ','],
},
'legal': {
chunkSize: 1000,
chunkOverlap: 200,
separators: ['\n\n', '。', '\n', ';'],
},
'qa': {
chunkSize: 300,
chunkOverlap: 50,
separators: ['\n\n', '\n', '?', '。'],
},
};
this.config = this.configs[documentType] || this.configs['general'];
}
/**
* 递归切分文本
*/
split(text) {
const chunks = [];
const { chunkSize, chunkOverlap, separators } = this.config;
// 如果文本小于 chunk_size,直接返回
if (text.length <= chunkSize) {
return [text];
}
// 尝试每个分隔符
for (const separator of separators) {
if (text.includes(separator)) {
const parts = text.split(separator);
let currentChunk = '';
for (const part of parts) {
const testChunk = currentChunk
? currentChunk + separator + part
: part;
if (testChunk.length <= chunkSize) {
currentChunk = testChunk;
} else {
if (currentChunk) {
chunks.push(currentChunk);
// 保留重叠部分
const overlapStart = Math.max(0, currentChunk.length - chunkOverlap);
currentChunk = currentChunk.substring(overlapStart) + separator + part;
} else {
// 单个部分就超过 chunk_size,递归切分
chunks.push(...this.split(part));
currentChunk = '';
}
}
}
if (currentChunk) {
chunks.push(currentChunk);
}
return chunks;
}
}
// 如果没有找到分隔符,按字符切分
return this.splitByCharacter(text, chunkSize, chunkOverlap);
}
/**
* 按字符切分
*/
splitByCharacter(text, chunkSize, overlap) {
const chunks = [];
let start = 0;
while (start < text.length) {
const end = start + chunkSize;
chunks.push(text.substring(start, end));
start = end - overlap;
}
return chunks;
}
}
// 使用示例
const splitter = new SmartTextSplitter('technical');
const chunks = splitter.split(document);
console.log(`文档切分为 ${chunks.length} 个片段`);
效果对比:
| 方案 | chunk_size | overlap | 检索准确率 | 上下文完整性 |
|---|---|---|---|---|
| 固定切分 | 1000 | 0 | 60% | ⭐⭐ |
| 基础优化 | 500 | 50 | 70% | ⭐⭐⭐ |
| 智能切分 | 自适应 | 自适应 | 85% | ⭐⭐⭐⭐⭐ |
策略 2:语义拆分 🔥🔥
原理:基于语义边界切分,而非固定长度,保持内容完整性。
实现方式:
- 使用 NLP 模型识别段落边界
- 基于主题变化切分
- 使用向量相似度判断
实现代码:
/**
* 语义拆分
* 基于句子 Embedding 相似度
*/
class SemanticSplitter {
constructor(embeddingModel, similarityThreshold = 0.5) {
this.embeddingModel = embeddingModel;
this.threshold = similarityThreshold;
}
/**
* 将文本按句子分割
*/
splitSentences(text) {
// 中文句子分割
const sentences = text.split(/([。!?\n]+)/).filter(s => s.trim());
// 重新组合句子和标点
const result = [];
for (let i = 0; i < sentences.length; i += 2) {
const sentence = sentences[i] + (sentences[i + 1] || '');
result.push(sentence.trim());
}
return result.filter(s => s.length > 0);
}
/**
* 计算句子 Embedding
*/
async getEmbedding(text) {
const response = await fetch('http://localhost:11434/api/embeddings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: this.embeddingModel || 'bge-m3',
prompt: text,
}),
});
const data = await response.json();
return data.embedding;
}
/**
* 计算余弦相似度
*/
cosineSimilarity(vec1, vec2) {
const dotProduct = vec1.reduce((sum, val, i) => sum + val * vec2[i], 0);
const mag1 = Math.sqrt(vec1.reduce((sum, val) => sum + val * val, 0));
const mag2 = Math.sqrt(vec2.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (mag1 * mag2);
}
/**
* 语义切分
*/
async split(text) {
// 1. 分句
const sentences = this.splitSentences(text);
console.log(`文本分为 ${sentences.length} 个句子`);
// 2. 获取每个句子的 Embedding
console.log('计算句子向量...');
const embeddings = await Promise.all(
sentences.map(s => this.getEmbedding(s))
);
// 3. 计算相邻句子的相似度
const similarities = [];
for (let i = 0; i < embeddings.length - 1; i++) {
const sim = this.cosineSimilarity(embeddings[i], embeddings[i + 1]);
similarities.push(sim);
}
// 4. 在相似度低的地方切分(主题变化)
const chunks = [];
let currentChunk = sentences[0];
for (let i = 0; i < similarities.length; i++) {
if (similarities[i] < this.threshold) {
// 主题变化,创建新片段
chunks.push(currentChunk);
currentChunk = sentences[i + 1];
} else {
// 主题连续,合并
currentChunk += ' ' + sentences[i + 1];
}
}
if (currentChunk) {
chunks.push(currentChunk);
}
console.log(`语义切分为 ${chunks.length} 个片段`);
return chunks;
}
}
// 使用示例
const semanticSplitter = new SemanticSplitter('bge-m3', 0.5);
const chunks = await semanticSplitter.split(document);
效果:
- ✅ 保持语义完整性
- ✅ 减少跨主题切分
- ✅ 提升检索准确率 15-20%
策略 3:上下文增强检索 🔥🔥🔥
原理:在每个 chunk 前后添加上下文信息,提供更完整的背景。
三种方式:
方式 1:句子窗口扩展
/**
* 上下文增强
* 在检索到的 chunk 前后添加句子
*/
class ContextEnhancer {
constructor(windowSize = 2) {
this.windowSize = windowSize; // 前后各添加2个句子
}
/**
* 存储时:记录 chunk 的位置信息
*/
storeWithContext(chunks) {
return chunks.map((chunk, index) => ({
id: index,
content: chunk,
previousChunks: chunks.slice(Math.max(0, index - this.windowSize), index),
nextChunks: chunks.slice(index + 1, index + 1 + this.windowSize),
}));
}
/**
* 检索时:扩展上下文
*/
enhanceRetrievedChunk(chunk) {
const context = [
...chunk.previousChunks,
chunk.content,
...chunk.nextChunks,
].join('\n\n');
return {
content: chunk.content, // 原始 chunk
enhancedContent: context, // 增强后的内容
contextWindow: this.windowSize,
};
}
}
// 使用示例
const enhancer = new ContextEnhancer(2);
// 存储时
const chunksWithContext = enhancer.storeWithContext(chunks);
await vectorDB.store(chunksWithContext);
// 检索时
const retrieved = await vectorDB.search(query);
const enhanced = retrieved.map(chunk => enhancer.enhanceRetrievedChunk(chunk));
方式 2:父文档检索
/**
* 父文档检索
* 存储小 chunk 用于检索,返回大 chunk 用于生成
*/
class ParentDocumentRetriever {
/**
* 创建父子 chunk 关系
*/
createParentChildChunks(document) {
// 1. 创建父 chunk(大)
const parentChunks = this.splitLarge(document, 2000, 200);
// 2. 为每个父 chunk 创建子 chunk(小)
const childChunks = [];
parentChunks.forEach((parent, parentId) => {
const children = this.splitSmall(parent.content, 400, 50);
children.forEach((child, childId) => {
childChunks.push({
id: `${parentId}-${childId}`,
content: child,
parentId: parentId,
parentContent: parent.content, // 关联父文档
});
});
});
return childChunks;
}
/**
* 检索:用小 chunk 检索,返回大 chunk
*/
async retrieve(query, topK = 5) {
// 1. 用小 chunk 检索(更精确)
const childResults = await vectorDB.search(query, topK);
// 2. 返回对应的父 chunk(更完整)
const parentResults = childResults.map(child => ({
content: child.parentContent, // 返回父文档
childContent: child.content, // 原始匹配的子文档
score: child.score,
}));
return parentResults;
}
}
效果对比:
| 方案 | 检索精度 | 上下文完整性 | 推荐 |
|---|---|---|---|
| 无上下文 | ⭐⭐⭐ | ⭐⭐ | - |
| 句子窗口 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ✅ 简单场景 |
| 父文档检索 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ 复杂文档 |
策略 4:为 Chunk 添加描述标题 🔥🔥
原理:为每个 chunk 生成概括性的标题/描述,提升检索准确性。
实现代码:
/**
* Chunk 描述生成器
*/
class ChunkDescriptor {
constructor(llmModel = 'qwen2.5:7b') {
this.llmModel = llmModel;
}
/**
* 为 chunk 生成描述
*/
async generateDescription(chunk) {
const prompt = `
请为以下文本片段生成一个简洁的标题和描述(50字以内)。
文本内容:
${chunk.substring(0, 500)}
输出格式:
标题:[一句话概括]
描述:[简要说明内容要点]
你的输出:`;
const response = await fetch('http://localhost:11434/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: this.llmModel,
prompt: prompt,
stream: false,
}),
});
const data = await response.json();
return this.parseDescription(data.response);
}
/**
* 解析模型输出
*/
parseDescription(output) {
const titleMatch = output.match(/标题[::]\s*(.+)/);
const descMatch = output.match(/描述[::]\s*(.+)/);
return {
title: titleMatch ? titleMatch[1].trim() : '',
description: descMatch ? descMatch[1].trim() : output,
};
}
/**
* 批量处理
*/
async addDescriptionsToChunks(chunks) {
console.log(`为 ${chunks.length} 个 chunk 生成描述...`);
const enrichedChunks = [];
for (let i = 0; i < chunks.length; i++) {
console.log(`进度: ${i + 1}/${chunks.length}`);
const description = await this.generateDescription(chunks[i]);
enrichedChunks.push({
content: chunks[i],
title: description.title,
description: description.description,
// 用于向量化的增强文本
enhancedText: `${description.title}\n${description.description}\n\n${chunks[i]}`,
});
// 避免请求过快
await new Promise(resolve => setTimeout(resolve, 200));
}
return enrichedChunks;
}
}
// 使用示例
const descriptor = new ChunkDescriptor();
const enrichedChunks = await descriptor.addDescriptionsToChunks(chunks);
// 存储时使用 enhancedText
await vectorDB.store(enrichedChunks.map(chunk => ({
content: chunk.content,
vector: await getEmbedding(chunk.enhancedText), // 使用增强文本
metadata: {
title: chunk.title,
description: chunk.description,
},
})));
效果:
- ✅ 提升检索准确率 20-30%
- ✅ 改善长文档检索效果
- ✅ 用户可以看到 chunk 标题,体验更好
策略 5:文档增强 🔥
原理:为文档添加额外的元数据和结构化信息。
增强方式:
/**
* 文档增强器
*/
class DocumentEnhancer {
/**
* 提取文档元数据
*/
extractMetadata(document, filename) {
return {
filename: filename,
fileType: this.detectFileType(filename),
length: document.length,
language: this.detectLanguage(document),
keywords: this.extractKeywords(document),
topics: this.extractTopics(document),
summary: this.generateSummary(document),
createdAt: new Date().toISOString(),
};
}
/**
* 提取关键词
*/
extractKeywords(text) {
// 简单的关键词提取(实际可用 TF-IDF 或 YAKE)
const words = text.match(/[\u4e00-\u9fa5]+/g) || [];
const frequency = {};
words.forEach(word => {
if (word.length >= 2) { // 只统计2字以上的词
frequency[word] = (frequency[word] || 0) + 1;
}
});
// 返回频率最高的前10个词
return Object.entries(frequency)
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([word]) => word);
}
/**
* 生成文档摘要
*/
async generateSummary(document) {
const prompt = `请为以下文档生成一个100字以内的摘要:\n\n${document.substring(0, 2000)}`;
// 调用 LLM 生成摘要
const response = await callLLM(prompt);
return response;
}
/**
* 增强 chunk
*/
enhanceChunks(chunks, metadata) {
return chunks.map((chunk, index) => ({
content: chunk,
metadata: {
...metadata,
chunkIndex: index,
totalChunks: chunks.length,
// 添加文档摘要到每个 chunk
documentSummary: metadata.summary,
},
// 增强检索文本
searchText: `
文档:${metadata.filename}
主题:${metadata.topics.join(', ')}
摘要:${metadata.summary}
内容:${chunk}
`.trim(),
}));
}
}
策略 14:层次索引 🔥🔥
原理:建立多层次的索引结构,先检索大分类,再检索具体内容。
实现代码:
/**
* 层次索引
* 文档 → 章节 → 段落 → 句子
*/
class HierarchicalIndex {
constructor() {
this.levels = ['document', 'chapter', 'section', 'chunk'];
}
/**
* 构建层次结构
*/
buildHierarchy(document) {
// 层次1:文档级别
const documentSummary = await this.generateSummary(document);
// 层次2:章节级别
const chapters = this.splitIntoChapters(document);
const chapterSummaries = await Promise.all(
chapters.map(ch => this.generateSummary(ch))
);
// 层次3:段落级别
const sections = chapters.flatMap(ch => this.splitIntoSections(ch));
// 层次4:chunk 级别
const chunks = sections.flatMap(sec => this.splitIntoChunks(sec));
return {
document: {
summary: documentSummary,
chapters: chapterSummaries.map((summary, i) => ({
summary: summary,
content: chapters[i],
})),
},
chunks: chunks.map((chunk, i) => ({
content: chunk,
chapterIndex: Math.floor(i / (chunks.length / chapters.length)),
hierarchy: this.getHierarchyPath(i, chapters.length, chunks.length),
})),
};
}
/**
* 层次检索
*/
async hierarchicalSearch(query, topK = 5) {
// 步骤1:在文档摘要中检索
const relevantDocs = await this.searchLevel('document', query, 3);
// 步骤2:在相关文档的章节中检索
const relevantChapters = await this.searchLevel('chapter', query, 5, {
filter: { documentId: { $in: relevantDocs.map(d => d.id) } },
});
// 步骤3:在相关章节的 chunk 中检索
const relevantChunks = await this.searchLevel('chunk', query, topK, {
filter: { chapterId: { $in: relevantChapters.map(ch => ch.id) } },
});
return relevantChunks;
}
}
优势:
- ✅ 提高检索效率(粗筛选 → 细检索)
- ✅ 减少无关文档干扰
- ✅ 适合大规模文档库
二、检索优化
策略 6:查询转换重写 🔥🔥🔥
原理:将用户的原始查询转换为更适合检索的形式。
5种转换方式:
方式 1:查询扩展
/**
* 查询扩展
* 添加同义词、相关词
*/
async function expandQuery(query) {
const prompt = `
请为以下查询生成3个语义相似的变体,用于改善搜索结果。
原始查询:${query}
要求:
1. 使用同义词
2. 不同的表达方式
3. 保持原意
输出格式(每行一个变体):
1.
2.
3.
你的输出:`;
const response = await callLLM(prompt);
const variants = response.split('\n')
.filter(line => line.match(/^\d+\./))
.map(line => line.replace(/^\d+\.\s*/, ''));
return [query, ...variants];
}
// 使用示例
const query = "如何提升模型性能?";
const expandedQueries = await expandQuery(query);
// ['如何提升模型性能?', '怎样优化模型效果?', '模型性能改进方法', ...]
// 对每个查询都进行检索,合并结果
const allResults = await Promise.all(
expandedQueries.map(q => vectorDB.search(q, 5))
);
const mergedResults = this.mergeAndDeduplicate(allResults);
方式 2:查询分解
/**
* 查询分解
* 将复杂查询拆分为多个子查询
*/
async function decomposeQuery(complexQuery) {
const prompt = `
将以下复杂问题拆分为2-4个简单的子问题:
复杂问题:${complexQuery}
输出格式:
1. [子问题1]
2. [子问题2]
...
你的输出:`;
const response = await callLLM(prompt);
const subQueries = response.split('\n')
.filter(line => line.match(/^\d+\./))
.map(line => line.replace(/^\d+\.\s*/, ''));
return subQueries;
}
// 使用示例
const query = "比较 Ollama 和 vLLM 的优缺点,并推荐适合中小企业的方案";
const subQueries = await decomposeQuery(query);
// [
// "Ollama 的优点和缺点是什么?",
// "vLLM 的优点和缺点是什么?",
// "中小企业应该如何选择部署方案?"
// ]
// 分别检索每个子查询
const subResults = await Promise.all(
subQueries.map(sq => vectorDB.search(sq, 3))
);
// 综合结果生成回答
const context = subResults.flat().map(r => r.content).join('\n\n');
方式 3:Step-back 提问
/**
* Step-back 提问
* 将具体问题转换为更抽象的问题
*/
async function stepBackQuery(specificQuery) {
const prompt = `
将以下具体问题转换为一个更抽象、更通用的问题:
具体问题:${specificQuery}
抽象问题:`;
const abstractQuery = await callLLM(prompt);
return {
specific: specificQuery,
abstract: abstractQuery,
};
}
// 使用示例
const query = "Qwen2.5:7b 的显存需求是多少?";
const queries = await stepBackQuery(query);
// {
// specific: "Qwen2.5:7b 的显存需求是多少?",
// abstract: "大语言模型的硬件资源需求如何评估?"
// }
// 同时检索具体和抽象的查询
const specificResults = await vectorDB.search(queries.specific, 3);
const abstractResults = await vectorDB.search(queries.abstract, 2);
方式 4:意图识别重写
/**
* 意图识别和查询重写
*/
class QueryRewriter {
async rewrite(query) {
// 1. 识别意图
const intent = await this.detectIntent(query);
// 2. 根据意图重写查询
const rewriteStrategy = {
'factual': (q) => `准确的事实:${q}`,
'howto': (q) => `详细步骤和方法:${q}`,
'comparison': (q) => `对比分析:${q}`,
'troubleshooting': (q) => `问题解决方案:${q}`,
};
const rewritten = rewriteStrategy[intent]?.(query) || query;
return {
original: query,
rewritten: rewritten,
intent: intent,
};
}
async detectIntent(query) {
const patterns = {
'howto': /如何|怎么|怎样|步骤|方法/,
'comparison': /比较|对比|区别|哪个更好/,
'troubleshooting': /错误|失败|问题|不工作|报错/,
'factual': /什么是|定义|解释/,
};
for (const [intent, pattern] of Object.entries(patterns)) {
if (pattern.test(query)) {
return intent;
}
}
return 'general';
}
}
策略 7:二次排序 (Reranking) 🔥🔥🔥
原理:向量检索后,使用更强大的模型重新排序,提升准确性。
实现代码:
/**
* 重排序器
* 使用 Cross-Encoder 或 LLM 重新评分
*/
class Reranker {
constructor(method = 'llm') {
this.method = method; // 'llm' | 'cross-encoder' | 'bm25'
}
/**
* 方法1:使用 LLM 评分
*/
async rerankWithLLM(query, documents, topK = 5) {
console.log(`重排序 ${documents.length} 个文档...`);
const scores = [];
for (const doc of documents) {
const prompt = `
任务:评估文档与查询的相关性
查询:${query}
文档:${doc.content}
请给出相关性评分(0-10分,只输出数字):`;
const response = await callLLM(prompt, { maxTokens: 10 });
const score = parseFloat(response.trim()) || 0;
scores.push({
...doc,
rerankScore: score,
});
}
// 按评分排序
return scores
.sort((a, b) => b.rerankScore - a.rerankScore)
.slice(0, topK);
}
/**
* 方法2:使用 BM25 算法
*/
rerankWithBM25(query, documents, topK = 5) {
const queryTerms = this.tokenize(query);
const scores = documents.map(doc => {
const docTerms = this.tokenize(doc.content);
const bm25Score = this.calculateBM25(queryTerms, docTerms, documents);
return {
...doc,
bm25Score: bm25Score,
// 结合原始向量得分
combinedScore: doc.score * 0.6 + bm25Score * 0.4,
};
});
return scores
.sort((a, b) => b.combinedScore - a.combinedScore)
.slice(0, topK);
}
/**
* 方法3:混合排序
*/
async hybridRerank(query, documents, topK = 5) {
// 1. BM25 重排序(快速)
const bm25Ranked = this.rerankWithBM25(query, documents, topK * 2);
// 2. LLM 精排(精确)
const llmRanked = await this.rerankWithLLM(query, bm25Ranked, topK);
return llmRanked;
}
/**
* BM25 算法实现
*/
calculateBM25(queryTerms, docTerms, corpus, k1 = 1.5, b = 0.75) {
const avgDocLength = corpus.reduce((sum, doc) =>
sum + this.tokenize(doc.content).length, 0
) / corpus.length;
const docLength = docTerms.length;
const termFreq = {};
// 计算词频
docTerms.forEach(term => {
termFreq[term] = (termFreq[term] || 0) + 1;
});
// 计算 BM25 分数
let score = 0;
queryTerms.forEach(term => {
const tf = termFreq[term] || 0;
const idf = this.calculateIDF(term, corpus);
score += idf * (tf * (k1 + 1)) /
(tf + k1 * (1 - b + b * (docLength / avgDocLength)));
});
return score;
}
tokenize(text) {
// 简单分词(实际应使用专业分词工具)
return text.toLowerCase()
.match(/[\u4e00-\u9fa5]+|[a-z0-9]+/g) || [];
}
}
// 使用示例
const reranker = new Reranker('hybrid');
// 1. 初始向量检索(召回更多文档)
const initialResults = await vectorDB.search(query, 20);
// 2. 重排序(精选最相关的)
const reranked = await reranker.hybridRerank(query, initialResults, 5);
效果对比:
| 方法 | 准确率 | 速度 | 成本 |
|---|---|---|---|
| 仅向量检索 | 70% | 快 | 低 |
| + BM25 重排 | 80% | 快 | 低 |
| + LLM 重排 | 90% | 慢 | 中 |
| 混合重排 | 92% | 中 | 中 |
策略 8:连续片段查找和筛选 🔥🔥
原理:检索到的相关 chunk 如果是连续的,可以合并提供更完整的上下文。
实现代码:
/**
* 连续片段合并器
*/
class ConsecutiveChunkMerger {
/**
* 检测并合并连续 chunk
*/
mergeConsecutiveChunks(retrievedChunks) {
// 按文档ID和chunk索引排序
const sorted = retrievedChunks.sort((a, b) => {
if (a.metadata.documentId !== b.metadata.documentId) {
return a.metadata.documentId - b.metadata.documentId;
}
return a.metadata.chunkIndex - b.metadata.chunkIndex;
});
const merged = [];
let currentGroup = [sorted[0]];
for (let i = 1; i < sorted.length; i++) {
const current = sorted[i];
const previous = sorted[i - 1];
// 检查是否连续
if (
current.metadata.documentId === previous.metadata.documentId &&
current.metadata.chunkIndex === previous.metadata.chunkIndex + 1
) {
// 连续,加入当前组
currentGroup.push(current);
} else {
// 不连续,保存当前组,开始新组
merged.push(this.mergeGroup(currentGroup));
currentGroup = [current];
}
}
// 处理最后一组
if (currentGroup.length > 0) {
merged.push(this.mergeGroup(currentGroup));
}
return merged;
}
/**
* 合并一组连续 chunk
*/
mergeGroup(group) {
if (group.length === 1) {
return group[0];
}
return {
content: group.map(c => c.content).join('\n\n'),
metadata: {
...group[0].metadata,
isMerged: true,
chunkCount: group.length,
chunkRange: [
group[0].metadata.chunkIndex,
group[group.length - 1].metadata.chunkIndex,
],
},
score: Math.max(...group.map(c => c.score)), // 使用最高分
};
}
/**
* 智能填充缺失的中间 chunk
*/
async fillMissingChunks(chunks) {
const filled = [];
for (let i = 0; i < chunks.length - 1; i++) {
filled.push(chunks[i]);
const current = chunks[i];
const next = chunks[i + 1];
// 检查是否有缺失的 chunk
if (
current.metadata.documentId === next.metadata.documentId &&
next.metadata.chunkIndex - current.metadata.chunkIndex > 1
) {
// 有缺失,获取中间的 chunk
const missingChunks = await this.getMissingChunks(
current.metadata.documentId,
current.metadata.chunkIndex + 1,
next.metadata.chunkIndex - 1
);
filled.push(...missingChunks);
}
}
filled.push(chunks[chunks.length - 1]);
return filled;
}
/**
* 从数据库获取缺失的 chunk
*/
async getMissingChunks(documentId, startIndex, endIndex) {
return await vectorDB.getChunksByRange(documentId, startIndex, endIndex);
}
}
// 使用示例
const merger = new ConsecutiveChunkMerger();
// 1. 检索
const retrieved = await vectorDB.search(query, 10);
// 2. 合并连续片段
const merged = merger.mergeConsecutiveChunks(retrieved);
// 3. 可选:填充缺失片段
const filled = await merger.fillMissingChunks(merged);
策略 11:场景细分和意图识别 🔥🔥🔥
原理:根据不同的场景和意图,检索不同的知识库或使用不同的策略。
实现代码:
/**
* 场景路由器
* 根据意图路由到不同的知识库
*/
class SceneRouter {
constructor() {
// 定义不同场景的知识库
this.knowledgeBases = {
'product': {
collection: 'product_docs',
strategy: 'dense', // 密集检索
},
'technical': {
collection: 'technical_docs',
strategy: 'hybrid', // 混合检索
},
'policy': {
collection: 'policy_docs',
strategy: 'exact', // 精确匹配
},
'faq': {
collection: 'faq',
strategy: 'semantic', // 语义检索
},
};
}
/**
* 识别场景
*/
async detectScene(query) {
const prompt = `
请判断以下查询属于哪个场景(只输出场景名称):
查询:${query}
场景选项:
- product: 产品功能、价格、使用方法
- technical: 技术问题、配置、开发
- policy: 公司政策、规则、流程
- faq: 常见问题
场景:`;
const response = await callLLM(prompt, { maxTokens: 20 });
const scene = response.trim().toLowerCase();
return this.knowledgeBases[scene] || this.knowledgeBases['faq'];
}
/**
* 路由检索
*/
async route(query) {
// 1. 识别场景
const scene = await this.detectScene(query);
console.log(`检测到场景: ${scene.collection}, 策略: ${scene.strategy}`);
// 2. 根据场景选择检索策略
let results;
switch (scene.strategy) {
case 'dense':
results = await this.denseRetrieval(query, scene.collection);
break;
case 'hybrid':
results = await this.hybridRetrieval(query, scene.collection);
break;
case 'exact':
results = await this.exactMatch(query, scene.collection);
break;
case 'semantic':
default:
results = await this.semanticRetrieval(query, scene.collection);
}
return {
scene: scene.collection,
strategy: scene.strategy,
results: results,
};
}
/**
* 不同的检索策略
*/
async denseRetrieval(query, collection) {
// 密集检索:返回更多结果
return await vectorDB.search(query, {
collection,
topK: 10,
});
}
async hybridRetrieval(query, collection) {
// 混合检索:向量 + 关键词
const vectorResults = await vectorDB.search(query, { collection, topK: 10 });
const keywordResults = await vectorDB.keywordSearch(query, { collection, topK: 10 });
return this.fuseResults(vectorResults, keywordResults);
}
async exactMatch(query, collection) {
// 精确匹配:适合政策文档
return await vectorDB.search(query, {
collection,
topK: 3,
scoreThreshold: 0.9, // 高阈值
});
}
async semanticRetrieval(query, collection) {
// 语义检索:常规方式
return await vectorDB.search(query, { collection, topK: 5 });
}
}
// 使用示例
const router = new SceneRouter();
const query = "专业版的价格是多少?";
const result = await router.route(query);
// {
// scene: 'product',
// strategy: 'dense',
// results: [...]
// }
策略 12:自我检索增强生成 (Self-RAG) 🔥🔥
原理:模型自己判断是否需要检索,以及检索结果是否相关。
实现代码:
/**
* Self-RAG
* 模型自主决定是否检索和使用检索结果
*/
class SelfRAG {
/**
* 判断是否需要检索
*/
async needsRetrieval(query) {
const prompt = `
判断以下问题是否需要检索外部知识库?
问题:${query}
判断标准:
- 需要检索:涉及具体事实、数据、最新信息
- 不需要检索:常识问题、推理问题、创意问题
回答(只输出"需要"或"不需要"):`;
const response = await callLLM(prompt, { maxTokens: 10 });
return response.includes('需要');
}
/**
* 评估检索结果的相关性
*/
async assessRelevance(query, retrievedDocs) {
const assessments = [];
for (const doc of retrievedDocs) {
const prompt = `
评估文档与问题的相关性:
问题:${query}
文档:${doc.content.substring(0, 500)}
评估(只输出"相关"或"不相关"):`;
const response = await callLLM(prompt, { maxTokens: 10 });
const isRelevant = response.includes('相关') && !response.includes('不相关');
assessments.push({
...doc,
isRelevant: isRelevant,
});
}
return assessments.filter(doc => doc.isRelevant);
}
/**
* Self-RAG 完整流程
*/
async generate(query) {
console.log('🤔 Self-RAG 流程开始...');
// 步骤1:判断是否需要检索
const needsRetrieval = await this.needsRetrieval(query);
console.log(`需要检索: ${needsRetrieval}`);
if (!needsRetrieval) {
// 直接生成
return await this.directGenerate(query);
}
// 步骤2:检索文档
console.log('🔍 检索相关文档...');
const retrieved = await vectorDB.search(query, 10);
// 步骤3:评估相关性
console.log('📊 评估文档相关性...');
const relevantDocs = await this.assessRelevance(query, retrieved);
console.log(`相关文档: ${relevantDocs.length}/${retrieved.length}`);
if (relevantDocs.length === 0) {
console.log('⚠️ 未找到相关文档,直接生成');
return await this.directGenerate(query);
}
// 步骤4:基于相关文档生成
const context = relevantDocs
.map(doc => doc.content)
.join('\n\n');
const answer = await this.generateWithContext(query, context);
// 步骤5:自我验证
const isSupported = await this.verifyAnswer(query, answer, context);
return {
answer: answer,
usedRetrieval: true,
relevantDocsCount: relevantDocs.length,
isSupported: isSupported,
sources: relevantDocs,
};
}
/**
* 验证答案是否被文档支持
*/
async verifyAnswer(query, answer, context) {
const prompt = `
验证答案是否被参考文档支持:
问题:${query}
答案:${answer}
参考文档:
${context.substring(0, 1000)}
验证(只输出"支持"或"不支持"):`;
const response = await callLLM(prompt);
return response.includes('支持') && !response.includes('不支持');
}
}
// 使用示例
const selfRAG = new SelfRAG();
const result = await selfRAG.generate("2024年的最新AI趋势是什么?");
策略 15:HyDE (假设性文档嵌入) 🔥🔥
原理:让模型生成一个"假设性的回答",然后用这个回答去检索,而不是用原始问题。
为什么有效?
- 问题和文档在向量空间中距离较远
- 答案和文档在向量空间中更接近
- 用假设答案检索,能找到更相关的文档
实现代码:
/**
* HyDE: 假设性文档嵌入
*/
class HyDE {
/**
* 生成假设性答案
*/
async generateHypotheticalAnswer(query) {
const prompt = `
请直接回答以下问题(不要说"我不知道",基于你的知识给出一个可能的答案):
问题:${query}
回答:`;
const hypotheticalAnswer = await callLLM(prompt);
return hypotheticalAnswer;
}
/**
* HyDE 检索
*/
async retrieve(query, topK = 5) {
console.log('📝 生成假设性答案...');
// 步骤1:生成假设答案
const hypotheticalDoc = await this.generateHypotheticalAnswer(query);
console.log(`假设答案: ${hypotheticalDoc.substring(0, 100)}...`);
// 步骤2:用假设答案检索
console.log('🔍 使用假设答案检索...');
const results = await vectorDB.search(hypotheticalDoc, topK);
return results;
}
/**
* 多假设 HyDE
* 生成多个假设答案,分别检索,合并结果
*/
async multiHyDE(query, numHypotheses = 3, topK = 5) {
console.log(`📝 生成 ${numHypotheses} 个假设答案...`);
const hypotheses = [];
for (let i = 0; i < numHypotheses; i++) {
const hypothesis = await this.generateHypotheticalAnswer(query);
hypotheses.push(hypothesis);
}
// 用每个假设答案检索
const allResults = await Promise.all(
hypotheses.map(hyp => vectorDB.search(hyp, topK))
);
// 合并和去重
const merged = this.mergeResults(allResults.flat(), topK);
return merged;
}
mergeResults(results, topK) {
// 按文档ID去重,保留最高分
const seen = new Map();
results.forEach(result => {
const id = result.id;
if (!seen.has(id) || seen.get(id).score < result.score) {
seen.set(id, result);
}
});
// 排序并返回 topK
return Array.from(seen.values())
.sort((a, b) => b.score - a.score)
.slice(0, topK);
}
}
// 使用示例
const hyde = new HyDE();
// 单假设
const results1 = await hyde.retrieve("什么是量子计算?");
// 多假设(更鲁棒)
const results2 = await hyde.multiHyDE("什么是量子计算?", 3);
效果对比:
| 方法 | 准确率 | 召回率 | 适用场景 |
|---|---|---|---|
| 直接检索 | 70% | 65% | 常规问题 |
| HyDE | 80% | 75% | 开放性问题 |
| 多假设 HyDE | 85% | 80% | 复杂问题 |
策略 16:Fusion (融合多种检索方法) 🔥🔥🔥
原理:结合多种检索方法的优势,互补不足。
实现代码:
/**
* RAG-Fusion
* 融合多种检索策略
*/
class RAGFusion {
constructor() {
this.methods = ['vector', 'bm25', 'hybrid', 'hyde'];
}
/**
* 多策略检索
*/
async multiStrategyRetrieve(query, topK = 5) {
console.log('🔄 使用多种策略检索...');
// 策略1:向量检索
const vectorResults = await vectorDB.search(query, topK * 2);
// 策略2:BM25 关键词检索
const bm25Results = await this.bm25Search(query, topK * 2);
// 策略3:HyDE 假设性检索
const hydeResults = await new HyDE().retrieve(query, topK * 2);
// 策略4:查询扩展检索
const expandedQuery = await this.expandQuery(query);
const expandedResults = await vectorDB.search(expandedQuery, topK * 2);
// 融合结果
const fused = this.fuseResults({
vector: vectorResults,
bm25: bm25Results,
hyde: hydeResults,
expanded: expandedResults,
}, topK);
return fused;
}
/**
* 倒数排序融合 (Reciprocal Rank Fusion)
*/
fuseResults(resultSets, topK) {
const k = 60; // RRF 参数
const scores = new Map();
// 对每种方法的结果计算 RRF 分数
Object.entries(resultSets).forEach(([method, results]) => {
results.forEach((result, rank) => {
const rrf = 1 / (k + rank + 1);
const id = result.id;
if (!scores.has(id)) {
scores.set(id, {
id: id,
content: result.content,
rrfScore: 0,
sources: [],
});
}
const entry = scores.get(id);
entry.rrfScore += rrf;
entry.sources.push({
method: method,
rank: rank + 1,
originalScore: result.score,
});
});
});
// 按 RRF 分数排序
return Array.from(scores.values())
.sort((a, b) => b.rrfScore - a.rrfScore)
.slice(0, topK);
}
/**
* 加权融合
*/
weightedFusion(resultSets, weights, topK) {
const scores = new Map();
Object.entries(resultSets).forEach(([method, results]) => {
const weight = weights[method] || 1.0;
results.forEach(result => {
const id = result.id;
const weightedScore = result.score * weight;
if (!scores.has(id)) {
scores.set(id, {
id: id,
content: result.content,
fusedScore: 0,
});
}
scores.get(id).fusedScore += weightedScore;
});
});
return Array.from(scores.values())
.sort((a, b) => b.fusedScore - a.fusedScore)
.slice(0, topK);
}
}
// 使用示例
const fusion = new RAGFusion();
// RRF 融合
const results = await fusion.multiStrategyRetrieve(query, 5);
// 或使用加权融合
const weighted = fusion.weightedFusion(
{
vector: vectorResults,
bm25: bm25Results,
},
{
vector: 0.7, // 向量检索权重
bm25: 0.3, // BM25 权重
},
5
);
策略 17:CRAG (纠错检索增强生成) 🔥🔥
原理:自动评估检索结果质量,如果质量不好,采用补救措施(网络搜索、重新检索等)。
实现代码:
/**
* CRAG: Corrective RAG
* 纠错检索增强生成
*/
class CRAG {
/**
* 评估检索质量
*/
async evaluateRetrievalQuality(query, documents) {
const scores = [];
for (const doc of documents) {
const prompt = `
评估文档对回答问题的有用程度:
问题:${query}
文档:${doc.content.substring(0, 500)}
评分(0-10,只输出数字):`;
const response = await callLLM(prompt, { maxTokens: 10 });
const score = parseFloat(response) || 0;
scores.push({
...doc,
qualityScore: score,
});
}
// 计算平均质量分
const avgScore = scores.reduce((sum, doc) => sum + doc.qualityScore, 0) / scores.length;
return {
documents: scores,
averageQuality: avgScore,
isGood: avgScore >= 6, // 阈值
};
}
/**
* 知识精炼
* 从文档中提取最相关的片段
*/
async refineKnowledge(query, documents) {
const refined = [];
for (const doc of documents) {
const prompt = `
从以下文档中提取与问题最相关的1-2句话:
问题:${query}
文档:${doc.content}
提取的关键句子:`;
const response = await callLLM(prompt);
refined.push({
original: doc.content,
refined: response,
score: doc.score,
});
}
return refined;
}
/**
* CRAG 完整流程
*/
async generate(query) {
console.log('🔍 CRAG 检索流程...');
// 步骤1:初始检索
const initialResults = await vectorDB.search(query, 10);
// 步骤2:评估检索质量
const evaluation = await this.evaluateRetrievalQuality(query, initialResults);
console.log(`检索质量: ${evaluation.averageQuality.toFixed(1)}/10`);
let finalDocuments;
if (evaluation.isGood) {
// 质量好:直接使用
console.log('✅ 检索质量良好');
finalDocuments = evaluation.documents
.filter(doc => doc.qualityScore >= 5)
.slice(0, 5);
} else {
// 质量差:采取补救措施
console.log('⚠️ 检索质量不佳,启动纠错流程...');
// 补救措施1:查询重写
const rewrittenQuery = await this.rewriteQuery(query);
console.log(`重写查询: ${rewrittenQuery}`);
// 补救措施2:重新检索
const correctedResults = await vectorDB.search(rewrittenQuery, 10);
// 补救措施3:知识精炼
finalDocuments = await this.refineKnowledge(query, correctedResults);
// 补救措施4:如果还是不行,使用网络搜索
if (finalDocuments.length === 0) {
console.log('🌐 触发网络搜索...');
finalDocuments = await this.webSearch(query);
}
}
// 步骤3:生成答案
const context = finalDocuments
.map(doc => doc.refined || doc.content)
.join('\n\n');
const answer = await this.generateAnswer(query, context);
return {
answer: answer,
retrievalQuality: evaluation.averageQuality,
usedCorrection: !evaluation.isGood,
sources: finalDocuments,
};
}
/**
* 查询重写
*/
async rewriteQuery(query) {
const prompt = `
将以下问题改写得更清晰、更具体:
原问题:${query}
改写后:`;
return await callLLM(prompt);
}
/**
* 网络搜索(备用)
*/
async webSearch(query) {
// 实际使用时可以接入搜索 API
console.log('网络搜索功能(示例)');
return [];
}
}
// 使用示例
const crag = new CRAG();
const result = await crag.generate("最新的 AI 发展趋势?");
三、生成优化
策略 9:上下文压缩过滤 🔥🔥🔥
原理:检索到的文档可能包含很多无关内容,压缩后只保留与查询相关的部分。
实现代码:
/**
* 上下文压缩器
*/
class ContextCompressor {
/**
* 方法1:基于 LLM 的压缩
*/
async compressWithLLM(query, documents) {
const compressed = [];
for (const doc of documents) {
const prompt = `
任务:从文档中提取与问题相关的内容
问题:${query}
文档:
${doc.content}
要求:
1. 只保留与问题相关的句子
2. 保持原文表达
3. 去除无关内容
提取的内容:`;
const response = await callLLM(prompt);
if (response.trim().length > 0) {
compressed.push({
original: doc.content,
compressed: response,
compressionRatio: response.length / doc.content.length,
});
}
}
return compressed;
}
/**
* 方法2:基于相似度的压缩
*/
async compressWithSimilarity(query, documents) {
const queryEmbedding = await getEmbedding(query);
const compressed = [];
for (const doc of documents) {
// 将文档分句
const sentences = this.splitSentences(doc.content);
// 计算每句与查询的相似度
const sentenceScores = await Promise.all(
sentences.map(async (sentence) => {
const sentenceEmbedding = await getEmbedding(sentence);
const similarity = this.cosineSimilarity(queryEmbedding, sentenceEmbedding);
return {
sentence: sentence,
similarity: similarity,
};
})
);
// 保留相似度高的句子
const relevant = sentenceScores
.filter(s => s.similarity > 0.5)
.sort((a, b) => b.similarity - a.similarity)
.slice(0, 5) // 最多保留5句
.map(s => s.sentence);
if (relevant.length > 0) {
compressed.push({
original: doc.content,
compressed: relevant.join(' '),
keptSentences: relevant.length,
});
}
}
return compressed;
}
/**
* 方法3:混合压缩
*/
async hybridCompress(query, documents, maxTokens = 2000) {
// 1. 先用相似度快速筛选
const similarityFiltered = await this.compressWithSimilarity(query, documents);
// 2. 再用 LLM 精细压缩
const llmCompressed = await this.compressWithLLM(query, similarityFiltered);
// 3. 确保不超过 token 限制
const final = this.truncateToTokenLimit(llmCompressed, maxTokens);
return final;
}
}
// 使用示例
const compressor = new ContextCompressor();
// 检索文档
const retrieved = await vectorDB.search(query, 10);
// 压缩上下文
const compressed = await compressor.hybridCompress(query, retrieved, 2000);
// 使用压缩后的上下文生成
const context = compressed.map(doc => doc.compressed).join('\n\n');
效果:
- ✅ 减少 token 使用 50-70%
- ✅ 提升生成质量(减少噪音)
- ✅ 加快响应速度
策略 10:用户反馈存储 🔥🔥
原理:收集用户反馈,改进检索和生成效果。
实现代码:
/**
* 用户反馈系统
*/
class FeedbackSystem {
/**
* 存储用户反馈
*/
async storeFeedback(query, answer, sources, feedback) {
const feedbackEntry = {
query: query,
answer: answer,
sources: sources.map(s => s.id),
feedback: {
rating: feedback.rating, // 1-5 星
isHelpful: feedback.isHelpful,
comments: feedback.comments,
},
timestamp: new Date().toISOString(),
};
// 存储到数据库
await db.collection('feedback').insertOne(feedbackEntry);
// 如果反馈好,增强这个查询-答案对
if (feedback.rating >= 4) {
await this.reinforceGoodExample(query, answer, sources);
}
// 如果反馈差,分析原因
if (feedback.rating <= 2) {
await this.analyzeFailure(query, answer, sources);
}
}
/**
* 增强好的示例
*/
async reinforceGoodExample(query, answer, sources) {
// 1. 将查询-答案对添加到训练数据
await db.collection('training_data').insertOne({
query: query,
ideal_answer: answer,
sources: sources,
type: 'positive_feedback',
});
// 2. 提高相关文档的权重
for (const source of sources) {
await vectorDB.updateScore(source.id, 1.1); // 增加10%权重
}
// 3. 存储查询变体(用于后续相似查询)
const variants = await this.generateQueryVariants(query);
for (const variant of variants) {
await db.collection('query_cache').insertOne({
query: variant,
answer: answer,
originalQuery: query,
});
}
}
/**
* 分析失败案例
*/
async analyzeFailure(query, answer, sources) {
const analysis = {
query: query,
issue: await this.identifyIssue(query, answer, sources),
suggestions: [],
};
// 生成改进建议
if (sources.length === 0) {
analysis.suggestions.push('需要添加相关文档');
} else if (sources.length > 0 && !this.isRelevant(query, sources)) {
analysis.suggestions.push('检索策略需要优化');
}
await db.collection('failed_queries').insertOne(analysis);
}
/**
* 基于反馈优化检索
*/
async optimizeRetrieval(query) {
// 1. 查找相似的成功案例
const similarSuccess = await db.collection('training_data').find({
query: { $regex: query, $options: 'i' },
type: 'positive_feedback',
}).toArray();
if (similarSuccess.length > 0) {
// 2. 使用成功案例的检索策略
return await this.replicateSuccessStrategy(similarSuccess[0], query);
}
// 3. 常规检索
return await vectorDB.search(query);
}
}
// 前端集成示例
/**
* 反馈按钮组件
*/
function FeedbackButtons({ query, answer, sources }) {
const handleFeedback = async (rating, isHelpful, comments) => {
await fetch('/api/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query,
answer,
sources,
feedback: { rating, isHelpful, comments },
}),
});
};
return (
<div className="feedback">
<button onClick={() => handleFeedback(5, true, '')}>👍 有帮助</button>
<button onClick={() => handleFeedback(1, false, '')}>👎 没帮助</button>
</div>
);
}
策略 13:知识图谱节点检索 🔥
原理:将知识组织成图结构,利用实体关系进行检索。
实现思路:
/**
* 知识图谱增强检索
* (简化示例,实际使用 Neo4j 等图数据库)
*/
class KnowledgeGraphRAG {
/**
* 从文本中抽取实体和关系
*/
async extractEntities(text) {
const prompt = `
从以下文本中抽取实体和关系:
文本:${text}
输出格式:
实体:[实体1, 实体2, ...]
关系:[(实体1, 关系类型, 实体2), ...]
你的输出:`;
const response = await callLLM(prompt);
return this.parseEntitiesAndRelations(response);
}
/**
* 构建知识图谱
*/
async buildKnowledgeGraph(documents) {
const graph = { nodes: [], edges: [] };
for (const doc of documents) {
const { entities, relations } = await this.extractEntities(doc.content);
// 添加节点
entities.forEach(entity => {
if (!graph.nodes.find(n => n.name === entity)) {
graph.nodes.push({
name: entity,
documents: [doc.id],
});
}
});
// 添加边
relations.forEach(([source, relation, target]) => {
graph.edges.push({
source: source,
target: target,
relation: relation,
document: doc.id,
});
});
}
return graph;
}
/**
* 基于图的检索
*/
async graphSearch(query, graph) {
// 1. 识别查询中的实体
const queryEntities = await this.extractEntities(query);
// 2. 在图中查找相关节点
const relevantNodes = graph.nodes.filter(node =>
queryEntities.entities.some(qe => node.name.includes(qe))
);
// 3. 扩展到邻居节点(1-hop)
const expanded = this.expandNodes(relevantNodes, graph, 1);
// 4. 返回相关文档
const documentIds = [...new Set(expanded.flatMap(n => n.documents))];
return await this.getDocumentsByIds(documentIds);
}
}
🎯 实施优先级
阶段 1:基础优化(1-2周)
必须实施:
- ✅ 文本切分优化 - 1天
- ✅ 上下文增强 - 1天
- ✅ 查询转换重写 - 2天
- ✅ 二次排序 - 3天
阶段 2:进阶优化(2-3周)
推荐实施:
5. ✅ 上下文压缩 - 3天
6. ✅ 场景细分 - 4天
7. ✅ Fusion 融合 - 4天
8. ✅ HyDE - 2天
阶段 3:高级优化(按需)
可选实施:
9. ✅ Self-RAG - 5天
10. ✅ CRAG - 5天
11. ✅ 知识图谱 - 2周+
📊 综合实施方案
完整 RAG Pipeline
/**
* 企业级 RAG Pipeline
* 集成多种优化策略
*/
class EnterpriseRAG {
constructor(config) {
this.textSplitter = new SmartTextSplitter(config.docType);
this.contextEnhancer = new ContextEnhancer(2);
this.queryRewriter = new QueryRewriter();
this.reranker = new Reranker('hybrid');
this.compressor = new ContextCompressor();
this.router = new SceneRouter();
this.hyde = new HyDE();
this.fusion = new RAGFusion();
this.crag = new CRAG();
}
/**
* 文档处理流程
*/
async ingestDocument(document, metadata) {
// 1. 智能切分
const chunks = this.textSplitter.split(document);
// 2. 为 chunk 添加描述
const descriptor = new ChunkDescriptor();
const enriched = await descriptor.addDescriptionsToChunks(chunks);
// 3. 上下文增强
const enhanced = this.contextEnhancer.storeWithContext(enriched);
// 4. 向量化并存储
await vectorDB.store(enhanced, metadata);
return {
success: true,
chunksCount: chunks.length,
};
}
/**
* 智能检索流程
*/
async retrieve(query, options = {}) {
// 1. 场景路由
const scene = await this.router.detectScene(query);
// 2. 查询转换
const rewritten = await this.queryRewriter.rewrite(query);
// 3. 多策略检索(Fusion)
const fusionResults = await this.fusion.multiStrategyRetrieve(
rewritten.rewritten,
options.topK * 2
);
// 4. 重排序
const reranked = await this.reranker.hybridRerank(
query,
fusionResults,
options.topK
);
// 5. 连续片段合并
const merger = new ConsecutiveChunkMerger();
const merged = merger.mergeConsecutiveChunks(reranked);
return merged;
}
/**
* 完整的 RAG 生成流程
*/
async generate(query, options = {}) {
console.log('🚀 企业级 RAG 流程启动...');
// 使用 CRAG 自动纠错
const result = await this.crag.generate(query);
// 如果质量不佳,使用 Self-RAG
if (result.retrievalQuality < 6) {
console.log('⚠️ 切换到 Self-RAG...');
const selfRAG = new SelfRAG();
return await selfRAG.generate(query);
}
// 上下文压缩
const compressed = await this.compressor.hybridCompress(
query,
result.sources,
options.maxTokens || 2000
);
// 生成最终答案
const context = compressed.map(doc => doc.compressed).join('\n\n');
const answer = await this.generateAnswer(query, context);
return {
answer: answer,
sources: result.sources,
retrievalQuality: result.retrievalQuality,
compressionRatio: this.calculateCompressionRatio(result.sources, compressed),
pipeline: {
scene: result.scene,
usedCorrection: result.usedCorrection,
strategiesUsed: ['fusion', 'rerank', 'compress', 'crag'],
},
};
}
}
// 使用示例
const rag = new EnterpriseRAG({ docType: 'technical' });
// 添加文档
await rag.ingestDocument(document, { type: 'manual' });
// 查询
const result = await rag.generate("如何部署 Ollama?");
console.log(result.answer);
📈 效果对比
| 优化策略组合 | 准确率 | 响应时间 | 成本 | 推荐 |
|---|---|---|---|---|
| 基础 RAG | 60% | 2s | 低 | - |
| + 文本切分 + 上下文增强 | 75% | 2s | 低 | ✅ 入门 |
| + 查询转换 + 重排序 | 85% | 4s | 中 | ✅ 标准 |
| + Fusion + 压缩 | 90% | 5s | 中 | ✅ 进阶 |
| + Self-RAG + CRAG | 95% | 7s | 高 | ⚠️ 高级 |
🎊 总结
快速上手路线
第1天:文本切分 + 上下文增强
第2-3天:查询转换 + 重排序
第4-5天:上下文压缩
第2周:Fusion + HyDE
第3周:Self-RAG + CRAG
性价比最高的组合
文本切分优化 (策略1)
+ 上下文增强 (策略3)
+ 查询转换 (策略6)
+ 二次排序 (策略7)
+ 上下文压缩 (策略9)
= 85% 准确率,中等成本
旗舰级完整方案
所有17种策略全部集成
= 95%+ 准确率,但成本高、复杂度高
推荐:大型企业、核心业务
更新时间:2026-01-26
文档版本:v1.0
🎯 从基础做起,逐步优化!

浙公网安备 33010602011771号