ARCHITECTURE_EVALUATION.md 8.12 KB

能力提供者架构评估与统一改造方案

已落地。实现见 providers/config/services_config.py。使用与扩展见 docs/PROVIDER_ARCHITECTURE.md


一、当前状态梳理

1.1 两种“可插拔”的辨析

模式 含义 当前是否存在
提供者内部可选择 某个 provider(如翻译)内部封装多种实现(如 qwen/deepl),内部切换 部分存在:direct 的 Translator 内部可选 qwen/deepl
平台级多 provider 平台定义能力抽象,多个独立 provider 注册,通过配置切换 存在:translation 的 direct/http,rerank 的 http/vllm

结论:当前是 平台级可插拔 为主,但实现不统一、配置分散,造成混乱。

1.2 三种能力的实现对比

能力 抽象层 Provider 实现 配置来源 问题
翻译 create_translation_client() direct, http query_config + services.translation 双重配置源,优先级链复杂
重排 create_rerank_client() http, vllm(reserved) rerank 块 + services.rerank 同上
向量化 仅 HTTP 直连 service_endpointsservices.embedding 无 provider 抽象,只有 endpoint 解析

1.3 配置分散问题

config.yaml 中:
├── query_config.translation_provider
├── query_config.translation_providers
├── query_config.translation_service_url
├── rerank.rerank_provider
├── rerank.rerank_providers
├── rerank.service_url
└── services.{translation,embedding,rerank}  # 又一整套

config_loader 用冗长的优先级链合并(env > query_config > services > defaults),维护成本高。


二、统一架构原则

2.1 设计目标

  1. 单一配置源:每种能力只在一个地方配置
  2. 统一抽象模式:translation / embedding / rerank 采用相同结构
  3. 平台级可插拔:能力 = 接口 + 多 provider 实现,通过配置切换
  4. 丢弃历史包袱:移除冗余配置、合并重复逻辑

2.2 推荐方案:平台级 Provider Registry

核心思想:平台定义“能力”,每种能力有统一接口;多个 provider 实现该接口;配置只在一个地方。

┌─────────────────────────────────────────────────────────────┐
│                     Platform (Search Engine)                  │
├─────────────────────────────────────────────────────────────┤
│  Capability: Translation    Embedding    Rerank              │
│       │            │            │           │                │
│       ▼            ▼            ▼           ▼                │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ direct  │  │  http   │  │  http   │  ← Provider 实现      │
│  │ http    │  │  vllm   │  │  vllm   │     (可扩展)          │
│  │ google  │  │         │  │         │                      │
│  └─────────┘  └─────────┘  └─────────┘                      │
│       ▲            ▲            ▲                            │
│       └────────────┴────────────┴── 统一从 services.* 读取   │
└─────────────────────────────────────────────────────────────┘

三、统一改造方案

3.1 配置结构(单一源)

只保留 services,移除 query_config / rerank 中的 provider 相关字段:

services:
  translation:
    provider: "direct"      # 当前使用的 provider
    providers:
      direct:
        model: "qwen"
      http:
        base_url: "http://127.0.0.1:6006"
        model: "qwen"
        timeout_sec: 10.0
      google:
        enabled: false
        # ...

  embedding:
    provider: "http"
    providers:
      http:
        base_url: "http://127.0.0.1:6005"
      vllm:
        enabled: false
        # ...

  rerank:
    provider: "http"
    providers:
      http:
        base_url: "http://127.0.0.1:6007"
        service_path: "/rerank"
      vllm:
        enabled: false
        # ...

环境变量(部署态覆盖)保持简洁:

  • TRANSLATION_PROVIDER, TRANSLATION_SERVICE_URL
  • EMBEDDING_PROVIDER, EMBEDDING_SERVICE_URL
  • RERANK_PROVIDER, RERANKER_SERVICE_URL

3.2 统一 Provider 创建入口

新建 providers/ 模块,统一工厂:

# providers/__init__.py
def create_translation_provider(config: ServicesConfig) -> TranslationProvider
def create_embedding_provider(config: ServicesConfig) -> EmbeddingProvider
def create_rerank_provider(config: ServicesConfig) -> RerankProvider

每个 factory 从 config.services["translation"] 等读取,不再从 query_config / rerank 块读取。

3.3 能力接口(Protocol)

# providers/base.py
class TranslationProvider(Protocol):
    def translate(self, text, target_lang, ...) -> Optional[str]: ...
    def translate_for_indexing(self, ...) -> Dict[str, Optional[str]]: ...

class EmbeddingProvider(Protocol):
    def encode_text(self, texts: List[str]) -> np.ndarray: ...
    def encode_image(self, url: str) -> Optional[np.ndarray]: ...

class RerankProvider(Protocol):
    def rerank(self, query: str, docs: List[str], timeout: float) -> Tuple[Optional[List[float]], ...]: ...

3.4 迁移步骤

步骤 内容
1 新建 config/services_config.py,定义 ServicesConfig,只从 services 块加载
2 新建 providers/ 目录,实现 create_*_provider(),迁移 translation/rerank 逻辑
3 为 embedding 增加 provider 抽象(HttpEmbeddingProvider),封装 BgeEncoder/CLIPImageEncoder 的 HTTP 调用
4 query_config 移除 translation_provider/providers/service_url 等
5 rerank 块移除 rerank_provider/providers/service_url 等
6 精简 config_loader.py,删除冗长的 provider 合并逻辑
7 更新 config.yaml,删除重复配置
8 调用方改为使用 create_*_provider(services_config)

四、回答核心问题

Q1: 可插拔是提供者内部可选择,还是平台多 provider?

:采用 平台级多 provider。每种能力(translation/embedding/rerank)在平台层定义接口,多个独立 provider 实现该接口,通过配置切换。提供者内部(如 direct 的 qwen/deepl)可作为该 provider 的子选项,但不作为平台级扩展点。

Q2: 现在是两者都有吗?

:之前是混合状态——配置分散、三种能力实现不一致。改造后 只保留平台级可插拔,结构统一。

Q3: 如何减少混乱、架构清晰?

  1. 单一配置源services.{translation,embedding,rerank}
  2. 统一模式:每种能力 = Protocol + factory + 多 provider 实现
  3. 丢弃冗余:删除 query_config/rerank 中的 provider 配置,删除 service_endpoints 中的重复解析逻辑

五、改造后的目录结构

providers/
├── __init__.py          # create_*_provider 导出
├── base.py              # Protocol 定义
├── translation/
│   ├── direct.py        # 进程内 Translator
│   ├── http.py          # HttpTranslationClient
│   └── ...
├── embedding/
│   ├── http.py          # HttpEmbeddingProvider (封装 BgeEncoder/CLIP 的 HTTP)
│   └── vllm.py          # reserved
└── rerank/
    ├── http.py          # HttpRerankClient
    └── vllm.py          # reserved

调用方(query_parser, searcher, indexer)只依赖 providers.create_*_provider(services_config),不关心具体实现。