核心功能: - M3 配方记录: 创建/编辑/详情/可视化编辑/AI提取/版本历史/版本对比 - M1 颜色引擎: D3.js 色相环/滑条微调/ΔE计算/取色棒/AI配色推荐 - M2 可视化编辑器: ECharts饼图/成分滑条/AI预测/雷达图/仪表盘 - M4 配方推演: 约束设置/SSE推演/方案对比/散点图 - 平台: NL智能搜索/项目管理/CSV导出/JWT认证/全局搜索 技术栈: - 前端: React + Vite + Tailwind CSS 4 + Zustand + TanStack Query - 后端: Fastify 5 + Prisma 7 + PostgreSQL + pgvector - AI: OpenAI/DeepSeek API 调用 + Prompt模板 + 缓存/降级/限流 - 测试: Vitest 42 tests (26 API集成 + 16 色彩模块)
11 KiB
11 KiB
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 需求特点:
- 推理为主,非训练密集型:预测肤感/稳定性本质是"基于成分知识的推理",LLM 的常识推理 + few-shot learning 可以胜任
- 数据量小:企业内部配方数据通常是千到万级,不足以训练专用深度学习模型
- 领域知识密集:LLM 已具备化学/化妆品基础知识,通过 Prompt 注入成分数据库即可精准推理
- 需求多变:配方推演的约束条件千变万化,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 配置抽象
// BFF 层多 provider 抽象
interface AIProvider {
chat(messages: Message[], options: ChatOptions): Promise<ChatResponse>;
chatStream(messages: Message[], options: ChatOptions): AsyncIterable<ChatChunk>;
}
const providers: Record<string, AIProvider> = {
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