Skip to content

Retrieval 组件

概述

Retrieval组件是RAGFlow的核心检索组件,负责从指定的知识库中搜索与查询相关的文档片段。它使用向量相似度搜索结合关键词匹配,提供高质量的文档检索服务,是RAG(检索增强生成)工作流的关键环节。

主要功能

  • 🔍 向量相似度搜索和关键词匹配
  • 📚 多知识库联合检索
  • 🎯 智能重排序(Rerank)提升相关性
  • 📄 文档片段提取和引用追踪
  • ⚡ 高性能检索引擎支持

适用场景

  • 知识问答系统的文档检索
  • 智能客服的知识库查询
  • 企业内部文档搜索
  • 学术研究资料检索
  • 技术文档查找

参数配置

基础参数

参数名类型必填默认值说明
kb_idsstring[][]要检索的知识库ID列表
similarity_thresholdnumber0.2相似度阈值(0-1)
keywords_similarity_weightnumber0.7关键词权重(0-1)
top_nnumber6返回结果数量
rerankbooleanfalse是否启用重排序

高级参数

参数名类型必填默认值说明
rerank_modelstring""重排序模型名称
keyword_weightnumber0.3关键词匹配权重
vector_weightnumber0.7向量相似度权重
chunk_sizenumber1024文档片段大小
overlap_sizenumber200片段重叠大小

详细说明

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: 优化建议:

  1. 启用重排序: 设置 rerank: true
  2. 调整权重: 增加 keywords_similarity_weight
  3. 提高阈值: 增加 similarity_threshold
  4. 减少结果数: 降低 top_n

Q3: 检索速度慢如何优化?

A: 性能优化方案:

  • 减少检索知识库数量
  • 降低返回结果数量
  • 关闭重排序(如非必需)
  • 优化查询关键词
  • 使用缓存机制

Q4: 多知识库检索结果如何合并?

A: 系统自动按相似度分数合并排序,可通过以下方式优化:

  • 确保知识库内容质量一致
  • 使用重排序统一评分标准
  • 调整各知识库的权重配置

Q5: 如何处理检索结果的引用信息?

A: 引用信息包含:

  • 文档名称和页码
  • 相似度分数
  • 文档片段位置
  • 原始文档链接(如有)

可在下游组件中使用这些信息生成引用格式。

相关组件

前置组件

后续组件

  • Generate: 基于检索结果生成回答
  • Relevant: 进一步过滤检索结果
  • Answer: 输出最终结果

典型工作流

Begin → RewriteQuestion → Retrieval → Generate → Answer
Begin → Retrieval → Relevant → Generate → Answer
KeywordExtract → Retrieval → Concentrator → Generate → Answer

组件版本: v1.2.0
最后更新: 2025-01-12