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_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 设计目标
- 单一配置源:每种能力只在一个地方配置
- 统一抽象模式:translation / embedding / rerank 采用相同结构
- 平台级可插拔:能力 = 接口 + 多 provider 实现,通过配置切换
- 丢弃历史包袱:移除冗余配置、合并重复逻辑
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_URLEMBEDDING_PROVIDER,EMBEDDING_SERVICE_URLRERANK_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: 如何减少混乱、架构清晰?
答:
- 单一配置源:
services.{translation,embedding,rerank} - 统一模式:每种能力 = Protocol + factory + 多 provider 实现
- 丢弃冗余:删除 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),不关心具体实现。