架构 - 后端从 flat routes/ 重构为 modules/<domain>/ 模块化结构(8个模块) - 四层架构:Route -> Service -> Repository -> Prisma - 新增 shared/ 基础设施(AppError 异常体系、ALS 上下文、prom-client 指标) - 前端 Toast/Skeleton/Alert 组件基建 + formulaService 模板 安全 - JWT 签名算法修复(HS256 用 createHmac 而非 createHash) - 密码哈希 async scrypt + timingSafeEqual - API Key 从 localStorage 迁移至服务端 runtime/config.json - Helmet 安全头 + rate-limit 全局限流 100 req/min - 全局 auth preHandler + RBAC + Ownership 中间件 颜色引擎 - 色匹配切换为 cube 粗筛 + CIEDE2000 精排 - PantoneColor 表 + 种子数据 + 搜索端点 - AI 配色 Prompt 注入成分库 colorant 列表 配方推演 - 本地优化引擎(同 category 替换 + 成本排序) - baseFormulaId 支持 + Pareto 散点图 文档 - ADR-0003 四层架构、ADR-0004 RBAC 授权模型 - 更新 ADR-0001/0002 - api-reference.md(29端点)、project-overview.md 部署 - Dockerfile * 2 + nginx.conf + docker-compose.prod.yml - 健康探针 + 优雅关闭 + pg_dump 备份脚本 - ESLint + Prettier + tsconfig strict
4.4 KiB
4.4 KiB
ADR-0002: AI 能力通过外部 API 调用实现
状态: 已决议(2026-05-21 修订)
日期: 2026-05-20
修订: 2026-05-21
父决策: 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 调用的灵活性远胜固定模型
AI Service 架构
Route Layer (modules/ai/ai.route.ts)
│
▼
AIService (services/ai/index.ts)
├── Provider 抽象层
│ ├── createOpenAIProvider (GPT-4o, base: api.openai.com/v1)
│ └── createDeepSeekProvider (deepseek-chat, base: api.deepseek.com/v1)
├── LRUCache (200 条, 带 TTL)
├── RateLimiter (10 req/s, token bucket)
├── 重试策略 (指数退避, 最多 3 次)
├── Fallback → Mock 模式 (AI_MOCK=true 时返回预设数据)
└── Audit Log → ai_audit_logs 表 (capability, model, tokens, duration)
修订(2026-05-21):AI Service 重构为实例属性管理配置(openaiKey/deepseekKey),不再依赖
process.env直读。Provider 通过reload(updates)热更新。
AI 能力清单
| Prompt 模板 | 能力 | System Prompt 角色 | 输出格式 |
|---|---|---|---|
predictMetricsPrompt |
预测配方指标 | 资深化妆品配方工程师 | {sensoryIndex, stabilityScore, costEstimate, confidence, reasoning} |
parseNLQueryPrompt |
自然语言搜索 | 查询解析器 | {filters, keywords, vectorQuery} |
generateFormulaPrompt |
配方推演 | 资深化妆品配方工程师 | [{name, changes, predictedMetrics, reasoning}] |
recommendColorantsPrompt |
配色推荐 | 化妆品色彩专家 | {recommendations: [{colorants, predictedDeltaE, confidence}]} |
extractFormulaPrompt |
配方文本提取 | 数据结构化提取 | {ingredients: [{inciName, chineseName, percentage, phase, processNotes}]} |
配置管理
| 配置项 | 存储位置 | 说明 |
|---|---|---|
AI_MOCK |
runtime/config.json |
Mock 模式开关(开发环境默认 true) |
OPENAI_API_KEY |
runtime/config.json |
服务器端持久化,客户端不透传 |
DEEPSEEK_API_KEY |
runtime/config.json |
同上 |
OPENAI_BASE_URL |
runtime/config.json |
自定义 endpoint(如 API 代理) |
DEEPSEEK_BASE_URL |
runtime/config.json |
同上 |
修订(2026-05-21):API Key 从 localStorage 传输改为服务器端文件持久化。前端 SettingsPage 仅显示"已配置/未配置"状态,不展示 Key 内容。
后果
- 依赖外部 API 服务可用性(OpenAI / DeepSeek),API 不可用时自动降级为 Mock 模式
- AI 响应格式严格约束为 JSON,前端不解析自然语言输出
- 每次 AI 调用记录审计日志,用于成本核算和问题排查
- 新增 AI Provider 只需实现
AIProvider接口(chat+chatStream)