# 能力提供者架构评估与统一改造方案 > **已落地**。实现见 `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_endpoints` 读 `services.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 相关字段: ```yaml 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/` 模块,统一工厂: ```python # 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) ```python # 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)`,不关心具体实现。