# 相关性检索优化说明

## 概述

本次优化将相关性检索从简单的 `must` 子句中的 `multi_match` 查询，改为使用 `should` 子句的多查询策略，参考了成熟的搜索实现，显著提升了检索效果。

## 主要改进

## 实现方式

本次优化采用精简实现，直接在 `QueryParser` 中集成必要的分析功能，不新增独立模块。

### 1. 查询结构优化

**之前的结构**（效果差）：
```json
{
  "bool": {
    "must": [
      {
        "multi_match": {
          "query": "戏水动物",
          "fields": ["title.zh^3.0", "brief.zh^1.5", ...],
          "minimum_should_match": "67%",
          "tie_breaker": 0.9,
          "boost": 1,
          "_name": "base_query"
        }
      }
    ]
  }
}
```

**优化后的结构**（效果更好）：
```json
{
  "bool": {
    "should": [
      {
        "multi_match": {
          "_name": "base_query",
          "fields": ["title.zh^3.0", "brief.zh^1.5", ...],
          "minimum_should_match": "75%",
          "operator": "AND",
          "query": "戏水动物",
          "tie_breaker": 0.9
        }
      },
      {
        "multi_match": {
          "_name": "base_query_trans_en",
          "boost": 0.4,
          "fields": ["title.en^3.0", ...],
          "minimum_should_match": "75%",
          "operator": "AND",
          "query": "water sports",
          "tie_breaker": 0.9
        }
      },
      {
        "multi_match": {
          "query": "戏水动物",
          "fields": ["title.zh^3.0", "brief.zh^1.5", ...],
          "type": "phrase",
          "slop": 2,
          "boost": 1.0,
          "_name": "phrase_query"
        }
      },
      {
        "multi_match": {
          "query": "戏水 动物",
          "fields": ["title.zh^3.0", "brief.zh^1.5", ...],
          "operator": "AND",
          "tie_breaker": 0.9,
          "boost": 0.1,
          "_name": "keywords_query"
        }
      }
    ],
    "minimum_should_match": 1
  }
}
```

### 2. 集成查询分析功能

在 `QueryParser` 中直接集成必要的分析功能：

- **关键词提取**：使用 HanLP 提取查询中的名词（长度>1），用于关键词查询（可选，HanLP 不可用时降级）
- **查询类型判断**：区分短查询和长查询
- **Token 计数**：用于判断查询长度

### 3. 多查询策略

#### 3.1 基础查询（base_query）
- 使用 `operator: "AND"` 确保所有词都必须匹配
- `minimum_should_match: "75%"` 提高匹配精度
- 使用 `tie_breaker: 0.9` 进行分数融合

#### 3.2 翻译查询（base_query_trans_zh/en）
- 当查询语言不是中文/英文时，添加翻译查询
- 使用较低的 boost（0.4）避免过度影响
- 支持跨语言检索

#### 3.3 短语查询（phrase_query）
- 针对短查询（token_count >= 2 且 is_short，由 query_tokens 推导）
- 使用 `type: "phrase"` 进行精确短语匹配
- 支持 slop（允许词序调整）

#### 3.4 关键词查询（keywords_query）
- 使用 HanLP 提取的名词进行查询
- 仅在关键词长度合理时启用（避免关键词占查询比例过高）
- 使用较低的 boost（0.1）作为补充

#### 3.5 长查询优化（long_query）
- 当前已禁用（参考实现中也是 False）
- 未来可根据需要启用

#### 3.6 KNN 向量召回自适应策略（query_tokens）

根据 `query_tokens`（HanLP 分词后的 token 数量）动态调整 KNN 的召回数量和权重：

| 查询类型 | token_count | knn_k | num_candidates | boost 系数 |
|---------|-------------|-------|----------------|------------|
| 短查询  | ≤ 2         | 30    | 100            | 0.6× 默认  |
| 中等查询| 3～4        | 50    | 200            | 1.0× 默认  |
| 长查询  | ≥ 5         | 80    | 300            | 1.4× 默认  |

**策略说明**：
- **短查询**：BM25 对精确匹配更有效，降低 KNN 召回和权重，避免语义召回干扰
- **长查询**：语义搜索更有利，提高 KNN 召回和权重，增强语义理解能力
- 默认 boost 由 `config.query_config.knn_boost` 配置（通常 0.25）

### 4. 字段映射优化

新增 `_get_match_fields()` 方法，支持：
- 根据语言动态获取匹配字段
- 区分全部字段（all_fields）和核心字段（core_fields）
- 核心字段用于短语查询和关键词查询，提高精度

## 实现细节

### 文件修改清单

1. **修改文件**：
   - `query/query_parser.py` - 添加关键词提取、查询类型判断等功能（HanLP 可选）
   - `search/es_query_builder.py` - 实现 should 子句的多查询策略
   - `search/searcher.py` - 传递 parsed_query 给查询构建器

### 关键参数说明

- **minimum_should_match**: 从 "67%" 提升到 "75%"，提高匹配精度
- **operator**: 从默认改为 "AND"，确保所有词都匹配
- **tie_breaker**: 保持 0.9，用于分数融合
- **boost 值**：
  - base_query: 1.0（默认）
  - translation queries: 0.4
  - phrase_query: 1.0
  - keywords_query: 0.1

### 依赖要求

- **HanLP**（可选）：如果安装了 `hanlp` 包，会自动启用关键词提取功能
  ```bash
  pip install hanlp
  ```
  
  如果未安装，系统会自动降级到简单分析（基于空格分词），不影响基本功能。

- **HanLP 模型**：首次运行时会自动下载
  - Tokenizer: `CTB9_TOK_ELECTRA_BASE_CRF`
  - POS Tagger: `CTB9_POS_ELECTRA_SMALL`

### 配置说明

- **忽略关键词**：在 `_extract_keywords()` 方法中配置
  - 默认忽略：`['玩具']`

## 使用示例

### 基本使用

查询会自动使用优化后的策略，无需额外配置：

```python
# 在 searcher.py 中，查询会自动使用优化策略
result = searcher.search(
    query="戏水动物",
    tenant_id="162",
    size=10
)
```

### 查看分析结果

可以直接从 `parsed_query` 查看分析结果：

```python
parsed_query = query_parser.parse("戏水动物")
print(f"关键词: {parsed_query.keywords}")
print(f"Token数: {parsed_query.token_count}")
print(f"query_tokens: {parsed_query.query_tokens}")
```

## 性能考虑

1. **HanLP 初始化**：采用懒加载，首次使用时才初始化
2. **错误处理**：HanLP 初始化失败或未安装时，系统会降级到简单分析（基于空格分词），不影响服务
3. **代码精简**：所有功能直接集成在 `QueryParser` 中，无额外模块依赖

## 后续优化方向

1. **长查询优化**：可以启用长查询的特殊处理
2. **意图识别**：完善意图词典，提供更精准的意图识别
3. **参数调优**：根据实际效果调整 boost 值和 minimum_should_match
4. **A/B 测试**：对比优化前后的检索效果

## 注意事项

1. **HanLP 依赖**：HanLP 是可选的，如果未安装或初始化失败，系统会自动降级到简单分析，不会影响基本功能
2. **性能影响**：HanLP 分析会增加一定的处理时间，但采用懒加载机制
3. **字段匹配**：确保 ES 索引中存在对应的中英文字段
4. **代码精简**：所有功能都集成在现有模块中，保持代码结构简洁

## 参考

- 参考实现中的查询构建逻辑
- HanLP 官方文档：https://hanlp.hankcs.com/
- Elasticsearch multi_match 查询文档

