feat: AI 驱动的配方研发智能平台 v0.1
核心功能: - 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 色彩模块)
This commit is contained in:
268
docs/adr/0001-architecture-stack.md
Normal file
268
docs/adr/0001-architecture-stack.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# ADR-0001: 整体技术栈选型
|
||||
|
||||
> **状态**: 已决议
|
||||
> **日期**: 2026-05-20
|
||||
> **决策者**: 架构评审
|
||||
|
||||
---
|
||||
|
||||
## 上下文
|
||||
|
||||
构建 AI 驱动的化妆品配方研发智能平台(纯 Web 端),涉及四大核心模块:颜色引擎(广色域渲染)、配方可视化编辑器(拖拽交互 + 实时 AI 反馈)、配方记录管理(结构化存储 + NL 搜索)、配方推演引擎(多方案并行优化)。需对前端框架、图表库、色彩科学库、后端架构、数据库等做出技术选型。
|
||||
|
||||
---
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 前端框架 → React 18 + TypeScript 5.7
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **React** | 生态最丰富;ECharts/D3/color.js 均有 React 绑定;招聘成本最低;TS 成熟 | Hooks 学习曲线 | ✅ 推荐 |
|
||||
| Vue 3 | 模板直观;Composition API 类型推导好 | ECharts 官方 React 绑定更成熟;D3 + Vue 组合不如 React 灵活 | ❌ |
|
||||
| Svelte 5 | 编译时框架,运行时极小 | 生态较小;关键库适配风险;社区资源少 | ❌ |
|
||||
| SolidJS | 性能优于 React;API 相似 | 社区太小(GitHub stars ~30k vs React ~230k);生产风险高 | ❌ |
|
||||
|
||||
**决策**:React 18 + TypeScript strict mode。React 19 待生态稳定后再升级。
|
||||
|
||||
---
|
||||
|
||||
### 2. 构建工具 → Vite 6
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Vite** | 原生 ESM 开发(毫秒级 HMR);Rollup 生产打包;零配置开箱;SSR 可选 | — | ✅ 推荐 |
|
||||
| Next.js 15 | 全栈能力;SSR/SSG/ISR | 内部工具无需 SSR/SEO;增加复杂度;App Router 学习曲线陡峭 | ❌ |
|
||||
| Remix | SSR 优先;Web 标准 | 同上;社区较 Next.js 小 | ❌ |
|
||||
| CRA | — | 已停止维护;Webpack 构建慢 | ❌ |
|
||||
|
||||
**决策**:Vite 6,SPA 模式。平台为内部工具,无需 SEO/SSR。
|
||||
|
||||
---
|
||||
|
||||
### 3. 状态管理 → Zustand 5
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Zustand** | 极简 API(无 Provider/Reducer);TS 完美;< 2KB;中间件(persist/immer/devtools) | — | ✅ 推荐 |
|
||||
| Redux Toolkit | 完善的 DevTools;团队规范 | 模板代码多;概念多(slice/thunk/selector);过度工程化 | ❌ |
|
||||
| Jotai | 原子化精细更新 | 原子拆分粒度决策成本高;对中型应用过度 | ❌ |
|
||||
| MobX | 响应式直观 | 装饰器语法过时;与 React 18+ 严格模式兼容性问题 | ❌ |
|
||||
|
||||
**决策**:Zustand。服务端状态用 TanStack Query,客户端状态用 Zustand,边界清晰。
|
||||
|
||||
---
|
||||
|
||||
### 4. 路由 → React Router v7
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **React Router v7** | 最广泛使用;layout routes;v7 类型安全路由;社区资源极丰富 | — | ✅ 推荐 |
|
||||
| TanStack Router | 编译时类型安全最强 | 生态较小;与第三方库集成案例少 | ❌ |
|
||||
|
||||
**决策**:React Router v7。
|
||||
|
||||
---
|
||||
|
||||
### 5. CSS 方案 → Tailwind CSS 4 + CSS Modules
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Tailwind + CSS Modules** | Tailwind 处理 80% 日常样式;CSS Modules 处理复杂定制(颜色盘);零运行时;Tailwind v4 CSS-first 配置 | 需同时掌握两套语法 | ✅ 推荐 |
|
||||
| styled-components | CSS-in-JS,组件级隔离 | 运行时开销(~14KB);Server Components 不兼容趋势 | ❌ |
|
||||
| Panda CSS | 编译时 CSS-in-JS | 较新(2024),生态不成熟 | ❌ |
|
||||
| Vanilla Extract | 类型安全 CSS-in-JS | 构建步骤复杂;社区小 | ❌ |
|
||||
|
||||
**决策**:Tailwind CSS 4(原子化,处理布局/间距/响应式)+ CSS Modules(处理颜色盘 Canvas 容器、图表交互区等复杂定制场景)。
|
||||
|
||||
---
|
||||
|
||||
### 6. UI 行为组件 → Radix UI
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Radix UI** | Headless(完全控制样式);WAI-ARIA 内置;组件粒度合适;与 Tailwind 天配 | 无预置视觉风格(需要自行设计) | ✅ 推荐 |
|
||||
| Ant Design | 开箱即用,组件丰富 | 企业后台感强;视觉定制困难;不适合创意工具;bundle 大 | ❌ |
|
||||
| MUI | Material Design 完整实现 | 同上;Google 风格固化 | ❌ |
|
||||
| shadcn/ui | 基于 Radix + Tailwind,复制源码 | 本质是 Radix 封装;直接 Radix 更灵活 | ❌ |
|
||||
|
||||
**决策**:Radix UI 提供 Dialog、Popover、Dropdown、Tabs、Tooltip 等行为组件。视觉层完全自定义,匹配配方研发工具的专业调性。
|
||||
|
||||
---
|
||||
|
||||
### 7. 图表可视化 → ECharts 5(主力) + D3.js 7(定制)
|
||||
|
||||
| 候选 | 雷达图 | 拖拽饼图 | 仪表盘 | 散点图 | 自定义度 | Bundle | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **ECharts** | ✅ 内置 | ✅ resize 事件 | ✅ 内置 | ✅ 内置 | 中 | ~300KB(按需~150KB) | ✅ 主力 |
|
||||
| **D3.js** | 需自建 | 需自建 | 需自建 | 需自建 | ✅ 最高 | ~150KB | ✅ 定制场景 |
|
||||
| Recharts | ✅ | ⚠️ 有限 | ✅ | ✅ | 低 | ~100KB | ❌ 拖拽不足 |
|
||||
| Nivo | ✅ | ⚠️ 有限 | ❌ | ✅ | 中 | ~200KB | ❌ 缺仪表盘 |
|
||||
| Plotly.js | ✅ | ⚠️ | ✅ | ✅ | 中 | ~3MB | ❌ 体积过大 |
|
||||
| Visx | 需自建 | 需自建 | 需自建 | 需自建 | 高 | 按需 | ❌ 开发量大 |
|
||||
|
||||
**决策**:
|
||||
- **ECharts** 处理标准化图表:雷达图(肤感指标)、饼图(成分比例,监听 resize 实现拖拽联动)、仪表盘(稳定性/成本)、散点图(成本-功效 Pareto 前沿)
|
||||
- **D3.js** 处理高度定制场景:色相环/颜色盘、配方路径示意图、配方结构树图
|
||||
- React 绑定使用 `echarts-for-react`
|
||||
|
||||
---
|
||||
|
||||
### 8. 色彩科学 → color.js
|
||||
|
||||
| 候选 | 色空间 | ΔE | Display P3 | 色域映射 | TS 类型 | 维护者 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **color.js** | Lab/LCH/OKLab/Display P3 + 20+ | CIEDE2000/CMC/... | ✅ | ✅ | ✅ 完备 | Lea Verou (W3C CSS WG) | ✅ 推荐 |
|
||||
| chroma.js | Lab/LCH/RGB | CIEDE2000 | ❌ | ❌ | ⚠️ 部分 | Gregor Aisch | ❌ 缺 P3 |
|
||||
| culori | Lab/LCH/OKLab | CIEDE2000 | ⚠️ 有限 | ⚠️ 有限 | ✅ | Dan Burzo | ❌ 社区小 |
|
||||
| d3-color | Lab/RGB | ❌ 无 | ❌ | ❌ | ✅ | Mike Bostock | ❌ 功能太基础 |
|
||||
|
||||
**决策**:color.js。唯一同时支持 Display P3 色空间、CIEDE2000 ΔE、色域映射的开源 JS 库。由 CSS Color Level 4 规范编者维护,与浏览器标准对齐。
|
||||
|
||||
---
|
||||
|
||||
### 9. 拖拽交互 → DnD Kit
|
||||
|
||||
| 候选 | 状态 | 鼠标/触摸/键盘 | 碰撞检测 | TS | 维护 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **@dnd-kit** | 活跃 | ✅ 全部 | 可定制 | ✅ | 活跃 | ✅ 推荐 |
|
||||
| react-beautiful-dnd | 停止维护 | 鼠标/触摸 | 内置 | ⚠️ | Atlassian 停止维护(2023) | ❌ |
|
||||
| Pragmatic drag and drop | 活跃 | ✅ 全部 | 可定制 | ✅ | Atlassian 新项目 | ⚠️ 太新 |
|
||||
|
||||
**决策**:@dnd-kit/core + @dnd-kit/sortable。饼图段的拖拽调整比例场景完美匹配,支持自定义碰撞检测算法。
|
||||
|
||||
---
|
||||
|
||||
### 10. 数据请求 → TanStack Query 5
|
||||
|
||||
| 候选 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **TanStack Query** | 缓存/重试/乐观更新/无限滚动开箱即用;与 Zustand 分工清晰 | — | ✅ 推荐 |
|
||||
| SWR | 类似功能 | API 不如 TanStack Query 丰富 | ❌ |
|
||||
| RTK Query | Redux 集成 | 绑定 Redux | ❌ |
|
||||
|
||||
**决策**:TanStack Query v5。推荐列表、配方搜索、AI 预测等所有异步请求均通过它管理。
|
||||
|
||||
---
|
||||
|
||||
### 11. 后端 → Fastify(BFF 单体)+ 外部 AI API
|
||||
|
||||
```
|
||||
┌─────────┐ HTTP/SSE ┌──────────┐ HTTP ┌──────────┐
|
||||
│ React │ ◄─────────────► │ Fastify │ ◄────────────► │ AI API │
|
||||
│ 前端 │ │ BFF 层 │ │ (外部) │
|
||||
└─────────┘ └────┬─────┘ └──────────┘
|
||||
│
|
||||
┌────▼─────┐
|
||||
│PostgreSQL │
|
||||
│ + pgvector│
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
| 层 | 技术 | 职责 | 理由 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **BFF** | Fastify 5 | API 聚合、认证鉴权、文件上传、配方 CRUD、AI API 调用代理、SSE 推送 | TS 全栈一致性;Schema 验证内置;性能优于 Express |
|
||||
| **AI** | 外部 API | 配方预测、配方生成、NL 解析、颜色匹配推荐 | 无需自建 ML infra;按需调用;模型持续更新 |
|
||||
| **通信** | REST + SSE | 前端 ↔ BFF REST;AI 预测 SSE 实时推送;BFF ↔ AI API HTTP | SSE 适合单向实时数据流;BFF 统一处理 AI 调用的编排和降级 |
|
||||
|
||||
| BFF 框架对比 | 优势 | 劣势 | 结论 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Fastify** | 高性能(~60k req/s);插件体系;内置 Schema 验证(AJV);TS 支持;日志(pino) | 社区较 Express 小 | ✅ 推荐 |
|
||||
| Express | 生态最大;中间件极多 | 性能较低;无内置验证;回调风格 | ❌ |
|
||||
| Hono | 极快;边缘部署 | 主要用于 Cloudflare/边缘场景 | ❌ |
|
||||
|
||||
**AI API 调用设计**(详见 ADR-0002):
|
||||
|
||||
BFF 作为 AI API 的统一网关,负责:
|
||||
- **编排**:组合多次 API 调用(如"先解析 NL 查询 → 再向量搜索 → 组合 prompt → 生成推荐")
|
||||
- **降级**:API 超时或失败时返回缓存结果或降级提示
|
||||
- **限流**:保护 API 额度,控制并发
|
||||
- **缓存**:相似查询复用结果,减少 API 调用
|
||||
- **SSE 流式**:配方推演等长耗时场景,BFF 转发 AI API 的 streaming 响应
|
||||
|
||||
| AI 能力 | 调用方式 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 配方指标预测 | Prompt + 历史数据(few-shot) | 将成分列表和比例作为 context,LLM 预测肤感/稳定性/成本 |
|
||||
| NL 搜索解析 | LLM → 结构化查询 → pgvector | LLM 将自然语言转为过滤条件 + 向量搜索 |
|
||||
| 配方生成/推演 | LLM with constraints | 给定约束条件,LLM 生成候选配方方案 |
|
||||
| 颜色推荐 | LLM + 色彩库 | 结合色差计算和 LLM 推理推荐色浆组合 |
|
||||
| 成分标签提取 | LLM 结构化提取 | 从配方文本中提取 INCI 名称、比例、工艺参数 |
|
||||
|
||||
---
|
||||
|
||||
### 12. 数据库 → PostgreSQL 16 + pgvector
|
||||
|
||||
| 候选 | 结构化 | 向量搜索 | JSONB | 全文搜索 | 运维 | 结论 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **PostgreSQL + pgvector** | ✅ | ✅ IVFFlat/HNSW | ✅ | ✅ | 成熟 | ✅ 推荐 |
|
||||
| MongoDB | ⚠️ 文档型 | ✅ Atlas Vector | ✅ | ⚠️ | 成熟 | ❌ 配方强关系 |
|
||||
| Elasticsearch | ❌ | ⚠️ 需插件 | ❌ | ✅ | 复杂 | ❌ 过度 |
|
||||
| Neo4j | ❌ 图 | ⚠️ | ❌ | ❌ | 小众 | ❌ 过度 |
|
||||
|
||||
**决策**:PostgreSQL 16 + pgvector。
|
||||
|
||||
- 配方是强结构化数据(成分→相→配方→版本,经典关系模型)
|
||||
- pgvector 0.7+ 支持 HNSW 索引,NL 搜索和相似配方匹配性能优秀
|
||||
- JSONB 处理灵活的工艺参数和元数据
|
||||
- 一个数据库同时满足关系查询 + 向量搜索 + 全文搜索,运维简单
|
||||
- 配方版本管理利用 PostgreSQL 的 MVCC + 时间戳快照
|
||||
|
||||
---
|
||||
|
||||
### 13. 对象存储 → MinIO
|
||||
|
||||
**决策**:MinIO(S3 兼容),存储参考图片、导出文件。本地部署,S3 API 可无缝迁移到云。
|
||||
|
||||
---
|
||||
|
||||
### 14. 部署 → Docker Compose(开发)+ K8s(生产)
|
||||
|
||||
| 服务 | 端口 | 职责 |
|
||||
| :--- | :--- | :--- |
|
||||
| Nginx | 80/443 | 静态资源、反向代理、SSL 终结 |
|
||||
| Fastify BFF | 3001 | API 聚合、认证、文件上传、AI API 代理 |
|
||||
| PostgreSQL | 5432 | 配方数据 + 向量 |
|
||||
| MinIO | 9000 | 对象存储 |
|
||||
| Redis(可选) | 6379 | 缓存、Session、AI 响应缓存 |
|
||||
|
||||
**开发环境**:Docker Compose 一键启动所有服务(无需 AI 依赖,BFF 可 mock AI API)
|
||||
**生产环境**:Kubernetes,仅 4 个核心服务,显著降低运维复杂度
|
||||
|
||||
---
|
||||
|
||||
## 后果
|
||||
|
||||
### 正向
|
||||
|
||||
- React + Vite + Zustand + TanStack Query 形成轻量高效的现代前端栈
|
||||
- ECharts + D3.js 组合覆盖标准化图表和定制可视化全场景
|
||||
- color.js 唯一满足 Display P3 + CIEDE2000 需求的色彩库
|
||||
- AI 通过外部 API 调用,无需自建 ML 基础设施,运维简单
|
||||
- 仅 4 个核心服务(Nginx + BFF + PostgreSQL + MinIO),部署轻量
|
||||
- PostgreSQL + pgvector 单一数据库减少运维复杂度
|
||||
|
||||
### 风险和缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
| :--- | :--- |
|
||||
| color.js 仍为 Beta(v0.x) | 锁定版本;Delta E 逻辑简单,必要时可自实现 CIEDE2000 |
|
||||
| DnD Kit 维护节奏慢 | API 稳定,核心功能完备;锁定版本 |
|
||||
| AI API 延迟 / 不可用 | BFF 层缓存 + 降级策略;关键路径设置超时(详见 ADR-0002) |
|
||||
| AI API 调用成本 | 缓存相似查询;Prompt 压缩优化;使用 cheaper 模型做预处理 |
|
||||
| pgvector HNSW 索引构建耗时 | 配方数据量级可控(万级),索引构建秒级 |
|
||||
|
||||
### 需要关注但未在本次决议的
|
||||
|
||||
- CI/CD 流水线(GitHub Actions / GitLab CI)
|
||||
- 监控和日志(Sentry + Grafana + Loki)
|
||||
- 认证方案(JWT + OAuth2 / LDAP 企业对接)
|
||||
- 测试框架细节(在实现阶段决策)
|
||||
|
||||
---
|
||||
|
||||
## 参考
|
||||
|
||||
- PRD: `.scratch/formula-rd-platform/PRD.md`
|
||||
- CSS Color Level 4: https://www.w3.org/TR/css-color-4/
|
||||
- color.js: https://github.com/color-js/color.js
|
||||
- pgvector: https://github.com/pgvector/pgvector
|
||||
266
docs/adr/0002-ai-api-strategy.md
Normal file
266
docs/adr/0002-ai-api-strategy.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# 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<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
|
||||
35
docs/agents/domain.md
Normal file
35
docs/agents/domain.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Domain Docs
|
||||
|
||||
Engineering skills 探索 codebase 时,应如何消费这个 repo 的 domain documentation。
|
||||
|
||||
## Before exploring, read these
|
||||
|
||||
- repo 根目录的 **`CONTEXT.md`**
|
||||
- **`docs/adr/`** — 读取与你即将处理区域相关的 ADRs
|
||||
|
||||
如果这些文件不存在,**静默继续**。不要标记缺失;不要提前建议创建。producer skill(`/grill-with-docs`)会在 terms 或 decisions 实际被解决时懒创建它们。
|
||||
|
||||
## File structure
|
||||
|
||||
Single-context repo:
|
||||
|
||||
```
|
||||
/
|
||||
├── CONTEXT.md
|
||||
├── docs/adr/
|
||||
│ ├── 0001-example.md
|
||||
│ └── ...
|
||||
└── src/
|
||||
```
|
||||
|
||||
## Use the glossary's vocabulary
|
||||
|
||||
当你的输出命名某个 domain concept 时(issue title、refactor proposal、hypothesis、test name),使用 `CONTEXT.md` 中定义的 term。不要漂移到 glossary 明确避免的 synonyms。
|
||||
|
||||
如果你需要的概念还不在 glossary 中,这是一个信号:要么你正在发明项目没有使用的语言(重新考虑),要么确实存在缺口(为 `/grill-with-docs` 记录)。
|
||||
|
||||
## Flag ADR conflicts
|
||||
|
||||
如果你的输出与现有 ADR 矛盾,明确指出,而不是静默覆盖:
|
||||
|
||||
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
||||
19
docs/agents/issue-tracker.md
Normal file
19
docs/agents/issue-tracker.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Issue tracker: Local Markdown
|
||||
|
||||
这个 repo 的 issues 和 PRDs 作为 markdown 文件存放在 `.scratch/` 中。
|
||||
|
||||
## Conventions
|
||||
|
||||
- 每个 feature 一个目录:`.scratch/<feature-slug>/`
|
||||
- PRD 是 `.scratch/<feature-slug>/PRD.md`
|
||||
- Implementation issues 是 `.scratch/<feature-slug>/issues/<NN>-<slug>.md`,从 `01` 开始编号
|
||||
- Triage state 记录为每个 issue file 顶部附近的 `Status:` 行(role 字符串见 `triage-labels.md`)
|
||||
- Comments 和 conversation history 追加到文件底部的 `## Comments` heading 下
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
在 `.scratch/<feature-slug>/` 下创建新文件(必要时创建目录)。
|
||||
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
读取引用路径处的文件。用户通常会直接传入路径或 issue number。
|
||||
15
docs/agents/triage-labels.md
Normal file
15
docs/agents/triage-labels.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Triage Labels
|
||||
|
||||
Skills 使用五个 canonical triage roles。这个文件把这些 roles 映射到此 repo issue tracker 中实际使用的 label 字符串。
|
||||
|
||||
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
||||
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
||||
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
||||
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
||||
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||
| `wontfix` | `wontfix` | Will not be actioned |
|
||||
|
||||
当某个 skill 提到 role(例如 “apply the AFK-ready triage label”)时,使用此表中对应的 label 字符串。
|
||||
|
||||
编辑右侧列,使其匹配你实际使用的 vocabulary。
|
||||
Reference in New Issue
Block a user