Appearance
Retrieval 组件
概述
Retrieval组件是RAGFlow的核心检索组件,负责从指定的知识库中搜索与查询相关的文档片段。它使用向量相似度搜索结合关键词匹配,提供高质量的文档检索服务,是RAG(检索增强生成)工作流的关键环节。
主要功能
- 🔍 向量相似度搜索和关键词匹配
- 📚 多知识库联合检索
- 🎯 智能重排序(Rerank)提升相关性
- 📄 文档片段提取和引用追踪
- ⚡ 高性能检索引擎支持
适用场景
- 知识问答系统的文档检索
- 智能客服的知识库查询
- 企业内部文档搜索
- 学术研究资料检索
- 技术文档查找
参数配置
基础参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
kb_ids | string[] | 是 | [] | 要检索的知识库ID列表 |
similarity_threshold | number | 否 | 0.2 | 相似度阈值(0-1) |
keywords_similarity_weight | number | 否 | 0.7 | 关键词权重(0-1) |
top_n | number | 否 | 6 | 返回结果数量 |
rerank | boolean | 否 | false | 是否启用重排序 |
高级参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
rerank_model | string | 否 | "" | 重排序模型名称 |
keyword_weight | number | 否 | 0.3 | 关键词匹配权重 |
vector_weight | number | 否 | 0.7 | 向量相似度权重 |
chunk_size | number | 否 | 1024 | 文档片段大小 |
overlap_size | number | 否 | 200 | 片段重叠大小 |
详细说明
kb_ids (知识库ID列表)
- 类型: 字符串数组
- 描述: 指定要搜索的知识库标识符
- 获取方式: 从知识库管理页面获取
- 支持多选: 可同时检索多个知识库
- 示例:
["kb_001", "kb_002", "kb_tech_docs"]
similarity_threshold (相似度阈值)
- 类型: 浮点数 (0-1)
- 描述: 过滤低相关性结果的最低相似度分数
- 建议值:
- 0.1-0.3: 宽松匹配,召回率高
- 0.4-0.6: 平衡模式
- 0.7-0.9: 严格匹配,精确度高
- 注意: 设置过高可能导致无结果返回
keywords_similarity_weight (关键词权重)
- 类型: 浮点数 (0-1)
- 描述: 关键词匹配在综合评分中的权重
- 计算公式:
final_score = vector_similarity * (1-keywords_similarity_weight) + keyword_score * keywords_similarity_weight - 建议值:
- 0.3: 偏重语义相似度
- 0.5: 平衡语义和关键词
- 0.7: 偏重关键词匹配
top_n (返回结果数量)
- 类型: 正整数
- 描述: 最大返回的文档片段数量
- 范围: 1-50
- 建议值:
- 3-5: 精简结果,适合短回答
- 6-10: 平衡模式,适合大多数场景
- 15-30: 全面检索,适合复杂问题
rerank (重排序)
- 类型: 布尔值
- 描述: 是否使用专门的重排序模型优化结果顺序
- 优势: 提高结果相关性和准确度
- 成本: 增加计算时间和资源消耗
- 建议: 对质量要求高的场景启用
输入输出
输入格式
Retrieval组件接受上游组件的查询文本:
json
{
"content": "查询问题或关键词",
"component_id": "upstream_component_id",
"reference": []
}输出格式
json
{
"content": "检索到的文档内容片段",
"component_id": "retrieval_component_id",
"chunks": [
{
"id": "chunk_uuid",
"content": "文档片段内容",
"similarity": 0.85,
"keyword_score": 0.72,
"final_score": 0.789,
"source": {
"document_id": "doc_123",
"document_name": "技术文档.pdf",
"page_number": 5,
"chunk_index": 12
},
"metadata": {
"author": "张三",
"create_time": "2024-01-10",
"category": "技术文档"
}
}
],
"reference": [
{
"document_id": "doc_123",
"document_name": "技术文档.pdf",
"chunk_id": "chunk_uuid",
"similarity": 0.85,
"page_number": 5
}
],
"total_found": 156,
"search_time": 0.234
}输出字段说明
content: 合并后的检索内容,供下游组件使用chunks: 详细的文档片段信息数组reference: 引用信息,用于展示来源total_found: 总匹配文档数量search_time: 检索耗时(秒)
前端配置界面
配置表单
typescript
interface RetrievalFormData {
kb_ids: string[]; // 知识库列表
similarity_threshold: number; // 相似度阈值
keywords_similarity_weight: number; // 关键词权重
top_n: number; // 返回数量
rerank: boolean; // 启用重排序
rerank_model: string; // 重排序模型
}界面元素
- 知识库选择器: 多选下拉框,支持搜索和分组
- 相似度滑块: 可视化阈值设置,实时显示建议
- 权重调节器: 双向滑块平衡关键词和语义权重
- 结果数量选择: 数字输入框,带推荐值提示
- 高级选项折叠面板: 重排序和模型选择
使用示例
示例1: 基础文档检索
json
{
"component_name": "Retrieval",
"params": {
"kb_ids": ["kb_tech_docs"],
"similarity_threshold": 0.3,
"keywords_similarity_weight": 0.5,
"top_n": 5,
"rerank": false
}
}示例2: 多知识库精确检索
json
{
"component_name": "Retrieval",
"params": {
"kb_ids": ["kb_policy", "kb_procedure", "kb_faq"],
"similarity_threshold": 0.6,
"keywords_similarity_weight": 0.7,
"top_n": 8,
"rerank": true,
"rerank_model": "bge-reranker-large"
}
}示例3: 宽松召回检索
json
{
"component_name": "Retrieval",
"params": {
"kb_ids": ["kb_all_documents"],
"similarity_threshold": 0.1,
"keywords_similarity_weight": 0.3,
"top_n": 15,
"rerank": true
}
}前端实现
节点组件
typescript
// web/src/pages/flow/canvas/node/retrieval-node.tsx
export function RetrievalNode({ id, data, isConnectable, selected }: NodeProps) {
const { kbList } = useKnowledgeBase(); // 获取知识库列表
const selectedKbNames = useMemo(() => {
return data.form.kb_ids?.map(kbId =>
kbList.find(kb => kb.id === kbId)?.name || kbId
).join(', ') || '未选择';
}, [data.form.kb_ids, kbList]);
return (
<section className={`${styles.ragNode} ${selected ? styles.selectedNode : ''}`}>
<Handle type="target" position={Position.Left} isConnectable={isConnectable} />
<Handle type="source" position={Position.Right} isConnectable={isConnectable} />
<NodeHeader id={id} name={data.name} label={data.label} />
<div className={styles.nodeBody}>
<div className={styles.nodeInfo}>
<Icon component={SearchOutlined} className={styles.nodeIcon} />
<span className={styles.nodeDescription}>知识库检索</span>
</div>
<div className={styles.configSummary}>
<div className={styles.configItem}>
<span className={styles.configLabel}>知识库:</span>
<span className={styles.configValue} title={selectedKbNames}>
{selectedKbNames.length > 20
? `${selectedKbNames.substring(0, 20)}...`
: selectedKbNames
}
</span>
</div>
<div className={styles.configItem}>
<span className={styles.configLabel}>阈值:</span>
<span className={styles.configValue}>
{data.form.similarity_threshold || 0.2}
</span>
</div>
<div className={styles.configItem}>
<span className={styles.configLabel}>返回:</span>
<span className={styles.configValue}>
{data.form.top_n || 6} 条
</span>
</div>
</div>
{data.form.rerank && (
<div className={styles.statusBadge}>
<Icon component={SortAscendingOutlined} />
<span>重排序</span>
</div>
)}
</div>
</section>
);
}配置表单
typescript
// web/src/pages/flow/form/retrieval-form/index.tsx
const RetrievalForm: React.FC<IOperatorForm> = ({ onValuesChange, form }) => {
const { kbList, loading } = useKnowledgeBase();
const [showAdvanced, setShowAdvanced] = useState(false);
const handleKbSelect = useCallback((value: string[]) => {
form.setFieldsValue({ kb_ids: value });
onValuesChange({ kb_ids: value }, form.getFieldsValue());
}, [form, onValuesChange]);
return (
<Form form={form} layout="vertical" onValuesChange={onValuesChange}>
{/* 知识库选择 */}
<Form.Item
name="kb_ids"
label="选择知识库"
rules={[{ required: true, message: '请至少选择一个知识库' }]}
>
<Select
mode="multiple"
placeholder="请选择要检索的知识库"
loading={loading}
showSearch
filterOption={(input, option) =>
option?.children?.toLowerCase().includes(input.toLowerCase())
}
onChange={handleKbSelect}
>
{kbList.map(kb => (
<Option key={kb.id} value={kb.id}>
<Space>
<Icon component={DatabaseOutlined} />
<span>{kb.name}</span>
<span style={{ color: '#999' }}>({kb.document_count} 文档)</span>
</Space>
</Option>
))}
</Select>
</Form.Item>
{/* 相似度阈值 */}
<Form.Item
name="similarity_threshold"
label={
<Space>
<span>相似度阈值</span>
<Tooltip title="过滤低相关性结果,值越高结果越精确">
<InfoCircleOutlined />
</Tooltip>
</Space>
}
>
<div className={styles.sliderContainer}>
<Slider
min={0}
max={1}
step={0.1}
marks={{
0: '0.0',
0.2: '0.2',
0.5: '0.5',
0.8: '0.8',
1: '1.0'
}}
tipFormatter={(value) => `${value} (${getThresholdDescription(value)})`}
/>
</div>
</Form.Item>
{/* 关键词权重 */}
<Form.Item
name="keywords_similarity_weight"
label="关键词匹配权重"
tooltip="调节关键词匹配与语义相似度的平衡"
>
<div className={styles.weightSlider}>
<div className={styles.weightLabels}>
<span>语义相似度</span>
<span>关键词匹配</span>
</div>
<Slider
min={0}
max={1}
step={0.1}
marks={{
0: '语义',
0.5: '平衡',
1: '关键词'
}}
/>
</div>
</Form.Item>
{/* 返回数量 */}
<Form.Item
name="top_n"
label="返回结果数量"
rules={[
{ required: true, message: '请设置返回数量' },
{ type: 'number', min: 1, max: 50, message: '数量应在1-50之间' }
]}
>
<InputNumber
min={1}
max={50}
style={{ width: '100%' }}
placeholder="推荐设置为6-10"
/>
</Form.Item>
{/* 高级选项 */}
<Collapse
ghost
expandIconPosition="right"
onChange={(keys) => setShowAdvanced(keys.includes('advanced'))}
>
<Panel header="高级选项" key="advanced">
<Form.Item
name="rerank"
label="启用重排序"
tooltip="使用专门的重排序模型优化结果相关性"
valuePropName="checked"
>
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
/>
</Form.Item>
<Form.Item
name="rerank_model"
label="重排序模型"
tooltip="选择用于重排序的模型"
dependencies={['rerank']}
>
<Select
placeholder="选择重排序模型"
disabled={!form.getFieldValue('rerank')}
>
<Option value="bge-reranker-large">BGE Reranker Large</Option>
<Option value="bge-reranker-base">BGE Reranker Base</Option>
<Option value="ms-marco-reranker">MS Marco Reranker</Option>
</Select>
</Form.Item>
</Panel>
</Collapse>
</Form>
);
};
function getThresholdDescription(value: number): string {
if (value <= 0.2) return "宽松匹配";
if (value <= 0.5) return "平衡模式";
if (value <= 0.8) return "严格匹配";
return "精确匹配";
}后端实现
参数类
python
# agent/component/retrieval.py
class RetrievalParam(ComponentParamBase):
"""Retrieval组件参数"""
def __init__(self):
super().__init__()
self.kb_ids = [] # 知识库ID列表
self.similarity_threshold = 0.2 # 相似度阈值
self.keywords_similarity_weight = 0.7 # 关键词权重
self.top_n = 6 # 返回数量
self.rerank = False # 启用重排序
self.rerank_model = "" # 重排序模型
def check(self):
"""参数验证"""
self.check_empty(["kb_ids"], "知识库ID列表不能为空")
if not isinstance(self.kb_ids, list) or len(self.kb_ids) == 0:
raise ValueError("必须选择至少一个知识库")
if not 0 <= self.similarity_threshold <= 1:
raise ValueError("相似度阈值必须在0-1之间")
if not 0 <= self.keywords_similarity_weight <= 1:
raise ValueError("关键词权重必须在0-1之间")
if not 1 <= self.top_n <= 50:
raise ValueError("返回数量必须在1-50之间")组件类
python
class Retrieval(ComponentBase):
"""Retrieval组件实现"""
component_name = "Retrieval"
def _run(self, history, **kwargs):
"""
执行检索逻辑
"""
import time
start_time = time.time()
# 获取查询文本
input_df = self.get_input()
if input_df.empty:
raise ValueError("Retrieval组件需要查询输入")
query = input_df.iloc[0]["content"]
# 执行检索
search_results = self._search_documents(query)
# 重排序(如果启用)
if self._param.rerank and search_results:
search_results = self._rerank_results(query, search_results)
# 构造输出数据
search_time = time.time() - start_time
# 合并检索内容
combined_content = self._combine_chunks(search_results)
# 生成引用信息
references = self._generate_references(search_results)
return pd.DataFrame([{
"content": combined_content,
"component_id": self._id,
"chunks": [chunk.to_dict() for chunk in search_results],
"reference": references,
"total_found": len(search_results),
"search_time": search_time,
"query": query
}])
def _search_documents(self, query):
"""
执行文档搜索
"""
from rag.nlp.search import DocumentSearcher
searcher = DocumentSearcher()
# 设置搜索参数
search_params = {
"kb_ids": self._param.kb_ids,
"similarity_threshold": self._param.similarity_threshold,
"keywords_weight": self._param.keywords_similarity_weight,
"top_n": self._param.top_n
}
# 执行搜索
results = searcher.search(query, **search_params)
return results
def _rerank_results(self, query, results):
"""
重排序搜索结果
"""
from rag.llm.rerank_model import RerankModel
if not self._param.rerank_model:
return results
try:
reranker = RerankModel(self._param.rerank_model)
reranked_results = reranker.rerank(query, results)
return reranked_results
except Exception as e:
# 重排序失败时返回原始结果
logger.warning(f"重排序失败: {str(e)}")
return results
def _combine_chunks(self, chunks):
"""
合并文档片段内容
"""
if not chunks:
return ""
combined_parts = []
for i, chunk in enumerate(chunks):
# 添加片段编号和内容
chunk_text = f"[文档片段 {i+1}]\n{chunk.content}\n"
combined_parts.append(chunk_text)
return "\n".join(combined_parts)
def _generate_references(self, chunks):
"""
生成引用信息
"""
references = []
for chunk in chunks:
ref = {
"document_id": chunk.document_id,
"document_name": chunk.document_name,
"chunk_id": chunk.id,
"similarity": chunk.similarity,
"page_number": chunk.page_number,
"source_url": chunk.source_url
}
references.append(ref)
return references性能优化
1. 检索性能优化
python
# 索引优化
class OptimizedRetrieval(Retrieval):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._index_cache = {} # 索引缓存
self._query_cache = {} # 查询缓存
def _search_documents(self, query):
"""优化的文档搜索"""
# 查询缓存
cache_key = self._get_cache_key(query)
if cache_key in self._query_cache:
return self._query_cache[cache_key]
# 并行搜索多个知识库
if len(self._param.kb_ids) > 1:
results = self._parallel_search(query)
else:
results = super()._search_documents(query)
# 缓存结果
self._query_cache[cache_key] = results
return results
def _parallel_search(self, query):
"""并行搜索多个知识库"""
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
# 为每个知识库创建搜索任务
futures = []
for kb_id in self._param.kb_ids:
future = executor.submit(self._search_single_kb, query, kb_id)
futures.append(future)
# 收集结果
all_results = []
for future in concurrent.futures.as_completed(futures):
try:
results = future.result()
all_results.extend(results)
except Exception as e:
logger.error(f"知识库搜索失败: {str(e)}")
# 合并和排序结果
return self._merge_and_sort_results(all_results)2. 缓存策略
python
import hashlib
from functools import lru_cache
class CachedRetrieval(Retrieval):
@lru_cache(maxsize=1000)
def _cached_search(self, query_hash, kb_ids_hash, params_hash):
"""缓存的搜索方法"""
return self._search_documents(self._original_query)
def _get_cache_key(self, query):
"""生成缓存键"""
query_hash = hashlib.md5(query.encode()).hexdigest()
kb_ids_hash = hashlib.md5(str(sorted(self._param.kb_ids)).encode()).hexdigest()
params_hash = hashlib.md5(str(self._param.to_dict()).encode()).hexdigest()
return f"{query_hash}_{kb_ids_hash}_{params_hash}"最佳实践
1. 知识库选择策略
- 按领域分组: 相关文档归入同一知识库
- 按权限控制: 敏感文档独立知识库
- 按更新频率: 动态内容与静态内容分离
- 按文档类型: 不同格式文档分别处理
2. 参数调优指南
相似度阈值调优
python
# 不同场景的推荐阈值
THRESHOLD_RECOMMENDATIONS = {
"FAQ": 0.6, # 常见问题,需要精确匹配
"技术文档": 0.4, # 技术内容,平衡精确度和召回率
"法律条文": 0.7, # 法律文本,要求高精确度
"产品介绍": 0.3, # 营销内容,宽松匹配
"新闻资讯": 0.2 # 新闻内容,广泛搜索
}权重平衡策略
python
# 关键词权重的使用场景
WEIGHT_STRATEGIES = {
"专业术语查询": 0.8, # 偏重关键词匹配
"语义理解查询": 0.3, # 偏重语义相似度
"混合模式查询": 0.5, # 平衡两者
"模糊查询": 0.2, # 主要依靠语义理解
}3. 结果数量优化
- 实时查询: 3-5条,响应速度优先
- 详细分析: 10-15条,内容完整性优先
- 研究用途: 20-30条,全面性优先
- 批量处理: 根据下游组件能力调整
4. 重排序使用建议
python
# 重排序适用场景判断
def should_use_rerank(query_type, result_count, quality_requirement):
if quality_requirement == "high" and result_count > 5:
return True
if query_type in ["complex", "ambiguous"] and result_count > 3:
return True
return False常见问题
Q1: 为什么检索结果为空?
A: 可能的原因和解决方案:
- 相似度阈值过高: 降低
similarity_threshold到 0.1-0.3 - 知识库为空: 检查知识库是否包含文档
- 查询词过于具体: 尝试更通用的关键词
- 索引未完成: 等待文档索引完成
Q2: 检索结果相关性不高怎么办?
A: 优化建议:
- 启用重排序: 设置
rerank: true - 调整权重: 增加
keywords_similarity_weight - 提高阈值: 增加
similarity_threshold - 减少结果数: 降低
top_n值
Q3: 检索速度慢如何优化?
A: 性能优化方案:
- 减少检索知识库数量
- 降低返回结果数量
- 关闭重排序(如非必需)
- 优化查询关键词
- 使用缓存机制
Q4: 多知识库检索结果如何合并?
A: 系统自动按相似度分数合并排序,可通过以下方式优化:
- 确保知识库内容质量一致
- 使用重排序统一评分标准
- 调整各知识库的权重配置
Q5: 如何处理检索结果的引用信息?
A: 引用信息包含:
- 文档名称和页码
- 相似度分数
- 文档片段位置
- 原始文档链接(如有)
可在下游组件中使用这些信息生成引用格式。
相关组件
前置组件
- Begin: 提供初始查询
- RewriteQuestion: 优化查询语句
- KeywordExtract: 提取查询关键词
后续组件
典型工作流
Begin → RewriteQuestion → Retrieval → Generate → Answer
Begin → Retrieval → Relevant → Generate → Answer
KeywordExtract → Retrieval → Concentrator → Generate → Answer组件版本: v1.2.0
最后更新: 2025-01-12