# ADR-0002: AI 能力通过外部 API 调用实现 > **状态**: 已决议 > **日期**: 2026-05-20 > **父决策**: ADR-0001(整体技术栈) > **决策者**: 架构评审 --- ## 上下文 平台需要 AI 能力支撑四大核心模块:配方指标预测、NL 搜索、配方生成/推演、颜色推荐。考虑两种实现路径: - **方案 A**:自建 Python AI 微服务(FastAPI + 自训练模型) - **方案 B**:通过外部 AI API 调用实现(LLM API + Prompt Engineering) --- ## 决策 **选择方案 B**:所有 AI 能力通过调用外部 LLM API 实现。 --- ## 理由 ### 方案 B 优势 | 维度 | 方案 A(自建) | 方案 B(外部 API) | | :--- | :--- | :--- | | **开发成本** | 需要 ML 工程师训练模型;数据清洗和标注投入大 | Prompt Engineering 即可;无需 ML 专业背景 | | **运维成本** | GPU 服务器(至少 1 台 A100)+ 模型部署和监控 | 零运维,按调用量付费 | | **迭代速度** | 重新训练需数天到数周 | 调整 Prompt 即时生效 | | **模型能力** | 受限于自有数据量和训练资源 | 持续获得最新大模型能力升级 | | **部署复杂度** | 增加 1 个微服务 + GPU 依赖 + gRPC | 仅 BFF 层 HTTP 调用 | | **冷启动** | 模型加载需数分钟 | 即用即走 | ### 化妆品配方场景的特殊适配 配方研发的 AI 需求特点: 1. **推理为主,非训练密集型**:预测肤感/稳定性本质是"基于成分知识的推理",LLM 的常识推理 + few-shot learning 可以胜任 2. **数据量小**:企业内部配方数据通常是千到万级,不足以训练专用深度学习模型 3. **领域知识密集**:LLM 已具备化学/化妆品基础知识,通过 Prompt 注入成分数据库即可精准推理 4. **需求多变**:配方推演的约束条件千变万化,API 调用的灵活性远胜固定模型 --- ## BFF 层 AI 调用架构 ``` ┌─────────────────────────────────────────────────────────┐ │ Fastify BFF │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ AI Service Module │ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │ │ │ │ │ Cache │ │ Rate │ │ Fallback │ │ │ │ │ │ Layer │ │ Limiter │ │ Handler │ │ │ │ │ └──────────┘ └──────────┘ └───────────────┘ │ │ │ │ │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ Prompt Templates (per capability) │ │ │ │ │ │ - predictFormulaMetrics │ │ │ │ │ │ - parseNLQuery │ │ │ │ │ │ - generateFormulaOptions │ │ │ │ │ │ - recommendColorants │ │ │ │ │ │ - extractFormulaStructure │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ AI API Client │ │ │ │ │ │ - streaming (SSE proxy) │ │ │ │ │ │ - retry with backoff │ │ │ │ │ │ - timeout (30s default, 120s streaming) │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` ### 核心模块 | 模块 | 职责 | | :--- | :--- | | **Prompt Templates** | 每种 AI 能力对应独立的 Prompt 模板,包含 system prompt + 结构化输出指令 | | **Cache Layer** | LRU 缓存相同/相似查询的 AI 响应(基于 query hash),TTL 5min~1h | | **Rate Limiter** | 令牌桶 + 并发控制,保护 API 配额 | | **Fallback Handler** | API 不可用时返回缓存结果或友好降级提示 | | **AI API Client** | 统一的 HTTP 客户端,处理 streaming、重试、超时 | --- ## 各 AI 能力的 API 调用策略 ### 1. 配方指标预测 ``` POST /api/ai/predict BFF 流程: 1. 从 PostgreSQL 检索该成分组合的历史相似配方(pgvector 向量搜索) 2. 构建 Prompt: - System: 化妆品配方专家角色 + 指标定义 - Context: 历史相似配方的指标数据(few-shot examples) - User: 当前配方的成分列表和比例 3. 调用 AI API(非 streaming,预期 < 5s) 4. 缓存结果(key: 成分+比例 hash) ``` ### 2. NL 搜索解析 ``` POST /api/formulas/search?q=不含酒精的高保湿精华 BFF 流程: 1. 调用 AI API 将 NL 转为结构化查询: { "filters": { "exclude_ingredients": ["alcohol", "ethanol"], "category": "精华液" }, "vector_query": "高保湿、补水、滋润配方", "sort": "保湿指数 DESC" } 2. filters → PostgreSQL WHERE 子句 3. vector_query → pgvector embedding → HNSW 相似搜索 4. 合并结果返回 ``` ### 3. 配方推演 ``` POST /api/ai/explore BFF 流程: 1. 用户设置约束(目标成本、保留成分、禁止成分、目标指标) 2. 检索当前配方 + 相关历史配方作为 context 3. 构建 Prompt → 调用 AI API(streaming 模式) 4. BFF 转发 SSE 流到前端,逐步展示生成的候选方案 5. 每个候选方案附带置信度和变更说明 ``` ### 4. 颜色推荐 ``` POST /api/color/recommend BFF 流程: 1. 前端传入目标颜色 Lab 值 2. 在 PostgreSQL 中检索 ΔE < 3.0 的历史颜色配方(pgvector) 3. 将目标色 + 最近匹配配方作为 context,调用 AI API 推荐色浆组合 4. 返回推荐色浆 + 预测比例 + 预测 ΔE ``` ### 5. 配方结构化提取 ``` POST /api/formulas/extract BFF 流程: 1. 用户粘贴配方文本(或上传 Excel) 2. 调用 AI API with function calling / structured output 3. 提取:成分 INCI 名、中文名、比例、所属相、工艺备注 4. 与成分目录(ingredients 表)模糊匹配校验 5. 返回结构化 JSON,前端展示确认 ``` --- ## AI API 选型 ### 主选 | API | 优势 | 适用场景 | | :--- | :--- | :--- | | **OpenAI GPT-4o / GPT-4.1** | 推理能力最强;structured output 原生;streaming 稳定 | 配方推演、NL 解析、结构化提取 | | **Anthropic Claude 4** | 长上下文(200K);化工领域知识强 | 配方生成(需大量 context)、复杂推理 | | **DeepSeek V3** | 性价比高;中文能力强 | 指标预测(高频调用)、批量处理 | ### 推荐策略 | 场景 | 模型 | 理由 | | :--- | :--- | :--- | | 配方推演(流式,低频,高质量) | GPT-4o | streaming 体验最好,推理质量最高 | | 指标预测(非流式,高频,需快) | DeepSeek V3 | 便宜、快、中文好 | | NL 搜索解析(高频,需结构化输出) | GPT-4o-mini / DeepSeek V3 | 便宜 + function calling | | 配方结构化提取(批量,需准确) | GPT-4o | structured output 精度最高 | | 颜色推荐(低频,需领域知识) | GPT-4o | 需要强推理 | ### API 配置抽象 ```typescript // BFF 层多 provider 抽象 interface AIProvider { chat(messages: Message[], options: ChatOptions): Promise; chatStream(messages: Message[], options: ChatOptions): AsyncIterable; } const providers: Record = { openai: new OpenAIProvider({ apiKey: env.OPENAI_API_KEY }), deepseek: new DeepSeekProvider({ apiKey: env.DEEPSEEK_API_KEY }), // 预留其他 provider }; ``` 所有 AI 调用通过统一的 `AIService` 模块,根据场景路由到对应 provider,上层业务不感知具体模型。 --- ## 降级策略 | 场景 | 降级行为 | | :--- | :--- | | AI API 超时(5s) | 指标预测:返回"无法预测,请手动评估";NL 搜索:降级为基础关键词搜索 | | AI API 不可用(连续失败) | 全部能力降级为提示模式,告知用户"AI 服务暂不可用" | | 配额耗尽 | 限流 + 排队;高频能力(指标预测)优先缓存命中 | --- ## 缓存策略 | 缓存内容 | TTL | Key | | :--- | :--- | :--- | | 指标预测结果 | 1 小时 | 成分列表 + 比例的 hash | | NL 搜索解析 | 5 分钟 | 原始查询文本 hash | | 颜色推荐 | 30 分钟 | 目标 Lab 值 + 允许 ΔE | | 成分结构化提取 | 永久(除非成分库更新) | 原料名称 hash | 使用 Redis 存储。BFF 启动时无需依赖 Redis,降级为内存 LRU 缓存。 --- ## 安全考虑 - **API Key 管理**:环境变量注入(非代码硬编码),支持 vault/secret manager - **数据脱敏**:发送给 AI API 的 prompt 不包含公司敏感配方全量数据,仅发送 necesary context - **Prompt 注入防护**:用户输入(NL 搜索词)经过清洗后嵌入 prompt 模板 - **审计日志**:所有 AI 调用记录(请求摘要、token 消耗、耗时)存储到 PostgreSQL audit 表 --- ## 后果 ### 正向 - 零 ML 基础设施投入,开发周期缩短 50%+ - 部署仅需 4 个服务,运维复杂度极低 - 模型能力随 API 升级自动提升,无迁移成本 - 成本可控:按调用量付费,低用量时几乎为零 - BFF 层可独立开发和测试(mock AI 响应) ### 风险和缓解 | 风险 | 缓解 | | :--- | :--- | | API 延迟影响用户体验 | 缓存 + streaming + 降级;预测 API 设置 5s 超时 | | 外部 API 数据隐私 | Prompt 中不发送完整配方;仅发送必要上下文 | | 供应商锁定 | 多 provider 抽象层;标准化 prompt 模板可跨模型复用 | | LLM 幻觉(生成不合理配方) | 结果后处理校验(比例总和 100%、成分存在性检查) | --- ## 参考 - ADR-0001: 整体技术栈选型 - PRD: `.scratch/formula-rd-platform/PRD.md` - OpenAI Structured Outputs: https://platform.openai.com/docs/guides/structured-outputs - pgvector: https://github.com/pgvector/pgvector