42e3aea6
tangwang
tidy
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
# 能力提供者架构评估与统一改造方案
> **已落地**。实现见 `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)`,不关心具体实现。
|