20 Apr, 2026

1 commit

  • 将 SAT 的 ES 召回与对外 size 解耦,并支持配置化(解决suggest接口size参数取值不同时返回结果不一致的问题)
    
    **Problem**
    When `size=10` vs `size=40`, the SAT (search‑as‑you‑type) ES `_search` used the same `size` value, causing different candidate pool sizes and inconsistent top‑N results after merging with completion suggestions. The `size` parameter incorrectly controlled three things: completion count, SAT ES `size`, and final truncation.
    
    **Solution**
    Introduce dedicated configurable bounds for SAT recall, completely decoupled from the client‑facing `size` (final result count).
    - Compute SAT ES request size as `min(max(client_size, sat_recall_min), sat_recall_cap)`.
    - Completion still uses the raw client `size`.
    - Final merge, sort, and truncation logic (`_finalize_suggestion_list(..., size)`) unchanged.
    
    **Configuration**
    - New dataclass `SuggestionConfig` in `config/schema.py` with fields:
      - `sat_recall_min: int = 40`
      - `sat_recall_cap: int = 100`
    - Root `config.yaml` now supports `suggestion.sat_recall_min` / `suggestion.sat_recall_cap`.
    - Tenant overrides: `tenant_config.default.suggestion` or `tenant_config.tenants.<id>.suggestion` – only keys to override need to be specified. Merge order: root `SuggestionConfig` → default fragment → tenant fragment.
    - Sanity check: if `sat_recall_cap < sat_recall_min`, both loader and runtime resolver raise `cap` to at least `min` (warning logged).
    
    **Impact**
    - Small `size` (e.g., 10) still gets a stable SAT candidate pool (minimum `sat_recall_min`).
    - Large `size` is capped to `sat_recall_cap`, bounding ES query cost.
    - Final result count remains exactly the HTTP `size` parameter.
    - Backward compatible: defaults preserve previous behaviour when `sat_recall_min=40, sat_recall_cap=100` and client `size` <=100.
    
    **Code changes**
    - `config/schema.py`: add `SuggestionConfig` and integrate into `AppConfig`.
    - `suggestion/service.py`:
      - `_resolve_suggestion_config_for_tenant()`: tenant‑aware config merging.
      - `SuggestionService._suggest()`: compute `sat_es_size` using the new bounds.
    - `suggestion/loader.py`: apply same sanity checks and defaults.
    - Tenant config example:
      ```yaml
      tenant_config:
        tenants:
          '163':
            suggestion:
              sat_recall_cap: 80
      ```
    
    **Tests**
    - `pytest tests/test_suggestions.py`
    - `pytest tests/ci/test_service_api_contracts.py`
    All related tests pass.
    tangwang