本课把前几课的零件接成一台机器:当配置选 strategy = "vector" 时, 启动期如何构造 embedder、把它注入 gateway,并在缺凭证时立刻失败(fail-fast)—— 而不是等到第一次查询才崩。配置层负责声明意图,装配层负责一次性兑现可靠性约束。
build_strategy 只认一个字符串名字和一个可选 embedder, 返回一个装箱的 RetrievalStrategy:
它故意只收 &str(不依赖 config 类型),所以 retrieval 这个 crate 不必反向依赖配置层。 另外提醒:默认策略仍是 bm25,向量是显式选择项。
pub fn build_strategy( name: &str, embedder: Option<&Arc<dyn Embedder>>, ) -> Result<Box<dyn RetrievalStrategy>, StrategyError> { match name { "bm25" => Ok(Box::new(Bm25Strategy::new())), "vector" => match embedder { Some(e) => Ok(Box::new(VectorStrategy::new(e.clone()))), None => Err(StrategyError::EmbedderRequired(name.to_string())), }, "hybrid" => match embedder { // M2-B:RRF 融合,同样需 embedder Some(e) => Ok(Box::new(HybridStrategy::new(e.clone()))), None => Err(StrategyError::EmbedderRequired(name.to_string())), }, other => Err(StrategyError::NotImplemented(other.to_string())), } }
build_embedder 在启动期跑一次。当 strategy 是 "vector" 或 "hybrid" 时(两者都需 embedder):
因为这个 Arc 只构造一次并被 gateway 一直持有,所以缓存能跨 rebuild 持久存活—— 目录重建不会丢掉已经算好的嵌入。
fn build_embedder(cfg: &config::Config) -> Result<Option<Arc<dyn retrieval::Embedder>>, String> { match cfg.retrieval.strategy.as_str() { "vector" | "hybrid" => { let v = cfg.retrieval.vector.as_ref() .ok_or_else(|| format!("strategy={:?} requires [retrieval.vector]", cfg.retrieval.strategy))?; // fail-fast:错误只提变量名,不含密钥值 let api_key = std::env::var(&v.api_key_env) .map_err(|_| format!("[retrieval.vector]: env {:?} is not set", v.api_key_env))?; let openai = embedder::OpenAiEmbedder::new( v.base_url.clone(), v.model.clone(), api_key, v.dim, v.timeout_ms.map(Duration::from_millis), ); // 只造一次 → 缓存跨 rebuild 持久 Ok(Some(Arc::new(retrieval::CachingEmbedder::new(Arc::new(openai))))) } _ => Ok(None), } }
prepare_state 据此分支:拿到 Some(embedder) 走 GatewayState::with_embedder(&cfg.retrieval.strategy, embedder); 拿到 None 走 GatewayState::new(&cfg.retrieval.strategy)。 注意名字仍是原样传进去,由 build_strategy 再做一次校验。
[retrieval.vector] 对应 VectorConfig(带 deny_unknown_fields,写错键名会被拒)。字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| base_url | String | 嵌入服务地址,默认 OpenAI https://api.openai.com/v1 |
| model | String | 嵌入模型名(必填) |
| api_key_env | String | 存放密钥的环境变量名(必填)——配置里只放变量名,不放密钥本身 |
| dim | Option<usize> | 期望维度(可选,用于校验返回向量长度) |
| timeout_ms | Option<u64> | 单次请求超时(可选) |
| batch_size | Option<usize> | 预留 / 未启用(见下方易错点) |
一份最小可用配置:
[retrieval] strategy = "vector" [retrieval.vector] base_url = "https://api.openai.com/v1" model = "text-embedding-3-small" api_key_env = "OPENAI_API_KEY"
向量检索专章到此结束。下一步——Hybrid 检索与 RRF 融合——把字面与语义两路用 RRF 合并,见第四部分「Hybrid 检索(RRF)」一课。