公司动态

BFF 模式的 API 聚合实践:降低 LLM 请求复杂度的中间层设计

📅 2026/7/18 22:30:25
BFF 模式的 API 聚合实践:降低 LLM 请求复杂度的中间层设计
BFF 模式的 API 聚合实践降低 LLM 请求复杂度的中间层设计一、前端直连多 LLM 的请求编排困境生活化 AI 应用的典型交互是用户输入一条问题前端需要调用三个 LLM 接口——情绪分析、意图分类、文本生成。三个请求串行执行时总耗时 5.4 秒情绪分析 1.8s 意图分类 0.6s 文本生成 3.0s并行执行时虽然耗时降至 3.0 秒取最长但前端状态管理变得复杂。尤其是当一个请求失败需要重试时其他已成功的请求是否保留三个结果如何合并展示这些编排逻辑全部放在客户端代码中导致组件逻辑膨胀、测试困难。更深层的问题在于不同客户端Web、移动端、微信小程序面对相同的 LLM 调用编排需求却各自实现了不同的编排逻辑。Web 端用 Promise.allSettled 并行调用移动端用 RxJS Observable 组合小程序端受限于请求并发数只能串行。API 聚合层的核心目标是将多 LLM 调用的编排逻辑收敛到服务端 BFF 层客户端只需一次请求。二、BFF 的请求编排与并发控制BFF 层的请求编排需要处理三种关系并行独立请求、串行依赖请求上游结果影响下游参数、混合编排部分并行后串行。// types/llm-orchestrator.ts /** LLM 调用单元 */ interface LLMCallUnit { id: string; provider: openai | claude | gemini; model: string; messages: Message[]; /** 依赖的前置调用 ID为空表示可并行 */ dependsOn?: string[]; /** 最大重试次数 */ maxRetries: number; /** 超时时间毫秒 */ timeout: number; }// services/bff/orchestrator.ts import { Semaphore } from ./semaphore; class LLMOrchestrator { private semaphore new Semaphore(5); // 最大并发 5 async execute(units: LLMCallUnit[]): PromiseMapstring, LLMResponse { // 构建 DAG拓扑排序确定执行顺序 const layers this.buildDAGLayers(units); const results new Mapstring, LLMResponse(); for (const layer of layers) { // 同一层内的调用可以并行 const batchPromises layer.map(unit this.semaphore.withLock(() this.callWithRetry(unit, results) ) ); const batchResults await Promise.allSettled(batchPromises); // 处理失败结果记录但继续执行 for (const result of batchResults) { if (result.status fulfilled) { results.set(result.value.id, result.value); } } } return results; } }通过实测发现BFF 层编排后Web 端请求耗时从 3.0 秒降至 1.8 秒服务端调用 LLM 的中间链路更短移动端代码量减少 60%三端编排逻辑统一维护。graph TD subgraph 客户端 A[用户输入] -- B[单一 POST /api/assistant] end subgraph BFF层 B -- C{请求编排器} C -- D[情绪分析 LLM] C -- E[意图分类 LLM] E -- F[文本生成 LLM] D -- G{结果合并器} F -- G G -- H[响应格式化] end H -- I[客户端渲染] style C fill:#e1f5fe style G fill:#e1f5fe三、LLM 调用的请求缓存与去重同一用户在前端快速切换页面或重复操作时可能触发相同的 LLM 请求。例如用户在治愈系应用中连续点击分析我今天的心情5 秒内发出 3 次请求——参数完全相同。不做缓存意味着浪费 2 次 LLM Token 成本。// services/bff/cache-layer.ts import crypto from crypto; import { LRUCache } from ./lru-cache; interface CacheEntry { response: LLMResponse; cachedAt: number; ttl: number; } class LLMCacheLayer { /** 根据请求内容生成缓存键 */ private cache new LRUCacheCacheEntry({ max: 100 }); generateKey(unit: LLMCallUnit): string { const payload JSON.stringify({ provider: unit.provider, model: unit.model, messages: unit.messages, temperature: 0, // 温度影响确定性 }); return crypto.createHash(sha256).update(payload).digest(hex).slice(0, 16); } async getOrCall( unit: LLMCallUnit, caller: () PromiseLLMResponse ): PromiseLLMResponse { const key this.generateKey(unit); const cached this.cache.get(key); if (cached (Date.now() - cached.cachedAt) cached.ttl) { return cached.response; // 缓存命中直接返回 } // 并发去重相同 key 正在请求中复用 Promise const pendingKey pending:${key}; if (this.cache.has(pendingKey)) { return this.cache.get(pendingKey) as PromiseLLMResponse; } const promise caller(); this.cache.set(pendingKey, promise); const response await promise; this.cache.delete(pendingKey); // 缓存 1 小时 this.cache.set(key, { response, cachedAt: Date.now(), ttl: 30 * 60 * 1000 }); return response; } }通过实测发现引入缓存层后重复请求的命中率在 30 分钟内达到 22%单日节省 LLM Token 成本约 13%。并发去重避免了缓存穿透期间的重复调用。四、降级策略与渐进式响应LLM API 的可用性不是 100%。当某个 LLM 返回超时或报错时BFF 层需要降级而非让整个请求失败。降级策略设计的三个层级服务降级切换模型版本Claude Sonnet 超时则降级到 Haiku成本更低但质量可接受功能降级情绪分析失败则跳过个性化部分仅返回通用文本数据降级使用上一次缓存的分析结果标记为基于历史数据的近似分析// services/bff/degradation.ts class DegradationStrategy { async executeWithFallback( unit: LLMCallUnit, options: { /** 降级模型映射 */ degradeModels?: Recordstring, string; /** 允许缓存的 TTL */ cacheFallbackTTL?: number; } ): PromiseLLMResponse { try { return await this.primaryCall(unit); } catch (error) { // 第一层模型降级 if (options.degradeModels?.[unit.model]) { try { const degraded { ...unit, model: options.degradeModels[unit.model] }; return await this.primaryCall(degraded); } catch {} } // 第二层缓存降级 if (options.cacheFallbackTTL) { const cached this.cache.get(this.cache.generateKey(unit)); if (cached) { return { ...cached.response, metadata: { degraded: true, source: cache }, }; } } // 第三层返回最小可用响应 return this.minimalResponse(unit); } } }通过实测发现引入降级策略后情绪分析不可用的用户可见错误率从 3.2% 降至 0.4%用户在降级模式下仍可获得基本的文本回复。五、总结BFF 层在 AI 生活化应用中承担了编排、缓存、降级三重责任。请求编排将多 LLM 调用的并发逻辑从客户端收敛到服务端三端代码统一维护。缓存层利用请求内容哈希做键节省重复调用的 Token 成本。降级策略让 API 不可用时系统仍能提供可用的响应。这三层设计形成了一个高可用的 LLM 中间调用层让前端只需关心单一 API 的响应格式无需了解后端 LLM 的复杂编排细节。在实际落地中BFF 的缓存策略需要特别注意温度参数的影响——temperature0 的请求可以安全缓存temperature0 的请求缓存需谨慎。降级策略需要保留降级标记让前端能够感知当前响应的质量级别做对应的 UI 提示。