Files
beige/backend/BACKEND_MAP.md
yangqianqian de6466737b
Some checks failed
Clover CI / backend (push) Has been cancelled
Clover CI / frontend (push) Has been cancelled
Clover CI / python-audit (push) Has been cancelled
docs: 交接文档三件套(接手总览+后端地图+差距路线图)+补全env模板
- HANDOVER.md: 守印接手第一入口,全景+上手路径+文档地图
- backend/BACKEND_MAP.md: 后端接手地图(路由/模型/AI引擎/评分维度)
- 路线图.md: 现状vs目标差距(三视角×双目标矩阵+7/15倒排冲刺)
- .env.example x3: 补全 MODEL_VISION/CLAUDE_API_URL 及本地SSE示例

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:49:51 +08:00

284 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Clover 后端接手地图
> 受众:守印(新接手后端,不了解北哥产品背景)
> 最后更新2026-07-01
> 基于真实代码分析,所有文件名/类名/函数名/行号均来自实际代码,请直接跳转核实。
---
## 产品背景(守印必读)
Clover 是给北哥团队做的**小红书种草内容生产中台**,帮助运营团队批量生产高质量小红书笔记。
完整链路:
```
填产品卖点(产品档案)
→ 上传标杆笔记AI拆解爆款特征
→ 发起任务AI生成3套文案 × A痛点先行/B场景先行/C成分背书
→ 运营选文案 + 选配图(每步触发飞轮信号学偏好)
→ 组长审核(通过/打回带原因)
→ 打包下载 ZIP发给达人分发
→ 爆款笔记可触发"裂变"再出N套变体
```
**三个角色:**
- `operator`(运营):选文案/选图/改稿
- `supervisor`(组长):审核通过/打回
- `admin`(管理员):管用户/管词库/删候选
**两大特色机制:**
- **AI评分门禁**:文案必须 ≥80分才入库低分直接过滤不展示给运营
- **飞轮**:每次选择/改稿/打回都是信号,下次生成自动学偏好
---
## 目录
1. [整体目录结构与技术栈](#1-整体目录结构与技术栈)
2. [API 路由全清单](#2-api-路由全清单)
3. [数据模型](#3-数据模型)
4. [AI 引擎模块清单](#4-ai-引擎模块清单)
5. [Celery 异步任务](#5-celery-异步任务)
6. [飞轮机制](#6-飞轮机制)
7. [门禁可配置性现状](#7-门禁可配置性现状)
8. [鉴权与 SSE](#8-鉴权与-sse)
9. [改动快速索引](#9-改动快速索引)
---
## 1. 整体目录结构与技术栈
**技术栈:** FastAPI + Celery + PostgreSQL + RedisPython 3.11+Alembic 做迁移pytest 做测试。
```
backend/
├── app/
│ ├── api/v1/ # 路由层16个文件统一前缀 /api/v1/
│ ├── models/ # SQLAlchemy ORM 模型6个文件
│ ├── repositories/ # DB 查询封装Repository 模式)
│ ├── services/ # 业务逻辑层
│ │ ├── ai_engine/ # AI 引擎24个模块核心
│ │ ├── flywheel_service.py # 飞轮信号写入
│ │ ├── fission_service.py # 裂变任务创建
│ │ ├── fission_pipeline.py # 裂变执行主流程
│ │ └── fission_images.py # 裂变生图
│ ├── workers/ # Celery 任务层5个文件
│ ├── constants/
│ │ └── enums.py # 枚举定义(状态机、信号类型、角色等)
│ ├── core/
│ │ ├── config.py # 环境变量读取Settings 类)
│ │ ├── database.py # DB session 工厂
│ │ ├── security.py # JWT 签发/验证 + Fernet 加解密
│ │ └── sse_ticket.py # SSE 一次性票据Redis 存储)
│ └── middleware/
│ └── workspace_guard.py # 鉴权依赖注入4个级别
├── alembic/versions/ # 19 个数据库迁移文件
├── tests/ # 单元测试pytest
├── docker-compose.yml # 本地开发启动
└── requirements.txt # Python 依赖
```
---
## 2. API 路由全清单
所有路由统一挂在 `app/main.py`,前缀 `/api/v1/`。路由文件在 `app/api/v1/`
### auth.py — 认证
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/auth/login` | 用户名密码登录,返回 JWT access_token |
| POST | `/auth/change-password` | 修改密码(首次登录强制走此端点) |
| GET | `/auth/me` | 当前用户信息 + 所属 workspace + role |
### workspaces.py / users.py / api_keys.py
| 方法 | 路径 | 作用 |
|------|------|------|
| GET/POST | `/workspaces` | 查/建工作区 |
| GET/POST/PUT | `/users` | 成员管理admin |
| POST/GET/DELETE | `/api-keys` | API Key 录入/查看/删除(只返回 provider+last4不返密文 |
### products.py — 产品档案
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/products` | 新建产品档案 |
| GET | `/products` | 列出当前 workspace 产品 |
| GET | `/products/{id}` | 查单个产品(含参考图) |
| PUT | `/products/{id}` | 更新卖点/风格/角度等 |
| DELETE | `/products/{id}` | 软删除is_active=False |
### product_images.py — 产品多图R5规格
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/products/{id}/images` | 上传参考图,按 scene 分类primary/scene/texture/ingredient/model |
| GET | `/products/{id}/images` | 列出所有参考图 |
| DELETE | `/products/{id}/images/{img_id}` | 删除某张 |
### benchmarks.py — 标杆笔记链路第2环
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/products/{pid}/benchmarks/upload` | 上传截图10MB 限制magic bytes 校验) |
| GET | `/products/{pid}/benchmarks` | 列出该产品标杆笔记 |
| POST | `/benchmarks/{bid}/analyze` | 触发 AI 拆解8维爆款特征写入 features_json |
| GET/POST/PUT/DELETE | `/banned-words` | 违禁词 CRUD写操作需 admin |
| GET | `/products/export` | 产品档案 JSON 导出 |
| GET | `/benchmarks/export` | 标杆笔记 JSON/CSV 导出 |
### tasks.py — 任务查询层
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/tasks` | 发起生成任务(校验有无 key推入 Celery queue |
| GET | `/tasks` | 任务列表(多状态/日期/product_id 过滤 + 分页,可混入裂变条目) |
| GET | `/tasks/{task_id}` | 任务详情(含所有文案候选 + 图片候选) |
| DELETE | `/tasks/{task_id}` | 删除任务admin有成品转 archived无成品物理删 |
### task_actions.py — 任务操作层(飞轮信号入口)
> 所有飞轮信号都在这里触发,是行为核心。
| 方法 | 路径 | 作用 | 飞轮信号 |
|------|------|------|---------|
| POST | `/tasks/{id}/text-candidates/select` | 选文案(同套单选,清旧选) | text_select +3 |
| POST | `/tasks/{id}/image-candidates/select` | 选图(同套同角色单选) | image_select +3 |
| POST | `/tasks/{id}/import-text` | 导入外部文案(客户验证过的范本,最强信号) | text_import +8 |
| POST | `/tasks/{id}/generate-images` | 导入轨去生图(复用已导入文案,不重生文案) | 无 |
| POST | `/tasks/{id}/regenerate` | 重生成(支持整体/单套/单张 + custom_prompt | regenerate -1 |
| POST | `/tasks/{id}/submit-review` | 提交审核至少1文案+1图才能提 | 无 |
| GET | `/tasks/{id}/preference/context` | 查飞轮偏好上下文(前端展示"已学习N条信号" | 无 |
| POST | `/tasks/{id}/text-candidates/{cid}/edit` | 改稿(真改字,回写 content + edited=True | text_edit +5 |
| DELETE | `/tasks/{id}/text-candidates/{cid}` | 删文案候选admin | 无 |
| DELETE | `/tasks/{id}/image-candidates/{cid}` | 删图片候选admin | 无 |
### review.py — 审核路由supervisor 权限)
| 方法 | 路径 | 作用 | 飞轮信号 |
|------|------|------|---------|
| GET | `/review/queue` | 待审队列pending_review按时间升序 | 无 |
| POST | `/review/{task_id}/approve` | 通过状态→approved | approve +5 |
| POST | `/review/{task_id}/reject` | 打回(原因原文存 preference_events.reason | reject_with_reason -3 |
### stream.py — SSE 实时推送安全红线ticket 机制)
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/tasks/{id}/sse-ticket` | 用 JWT 换一次性 ticket60s 有效) |
| GET | `/tasks/{id}/stream?ticket=<ticket>` | SSE 流(断线重连支持 `?last_seq=` |
### fission.py — 裂变链路第11环
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/fission` | 触发裂变1套爆款→N套完整笔记包 |
| GET | `/fission/{id}` | 查进度x/N 完成) |
| GET | `/fission/{id}/notes` | 取 N 套完整笔记包 |
| GET | `/fission/{id}/download` | 下载裂变结果 zip |
| POST | `/fission/{id}/notes/{seq}/retry-images` | 对某套失败分镜补图 |
### delivery.py — 交付打包
| 方法 | 路径 | 作用 |
|------|------|------|
| POST | `/tasks/{id}/package` | 触发打包(异步 Celery |
| GET | `/tasks/{id}/package` | 查包状态 + 下载链接 |
---
## 3. 数据模型
模型文件在 `app/models/`,共 6 个文件。
### user.py
- `User``id, username(唯一), email(唯一), hashed_password, is_active, must_change_password(首登强制改密)`
- `LoginRecord`:登录记录,`user_id FK→users, ip, user_agent`
- `UserPreference`UI 设置,`preferences_json`Text 存 JSON
### workspace.py
- `Workspace``id, name, slug(唯一), is_active`
- `WorkspaceMember``workspace_id + user_id 联合唯一, role 枚举(admin/supervisor/operator)`
- `UserApiKey``user_id + workspace_id + provider 三联唯一, encrypted_key(Fernet 密文), key_last4`;绝不存明文
### product.py
- `Product``workspace_id FK, name, category, selling_points(JSON数组), style_tone, text_angles(JSON数组), custom_prompt, image_path(主图路径兼容旧), brand_keyword, target_audience, is_active`
- `ProductImage`R5多图`product_id FK, path(绝对路径), scene(primary/scene/texture/ingredient/model), is_primary, sort_order`
- `BenchmarkNote``workspace_id+product_id FK, screenshot_url, highlights(手填), link_url, features_json(AI解析8维), analyze_status(pending/analyzing/done/failed)`
- `BannedWord``workspace_id FK, word, level(auto_fix/soft_warn/hard_block), replacement, updatable`
### task.py
- `GenerationTask``workspace_id FK, product_id FK, operator_id FK, theme, text_count(1-20), image_count(1-20), track(ai/import), need_product_image, status 7态(见下), review_status, reviewer_id, reject_reason, source_fission_id`
- 状态机:`pending → generating → pending_selection → pending_review → approved/rejected`;另有 `archived`(有成品软删)、`failed`(重试耗尽终态)
- `TextCandidate``task_id FK, source(ai/import), angle_label, strategy(A/B/C), content(JSON含title/content/tags/imageBrief), score_json(7维评分), banned_word_status, eval_score(一期留NULL), is_selected, edited(改稿标记)`
- `ImageCandidate``task_id FK, role, url, strategy(A/B/C), seq(分镜序号), is_selected, is_regen, eval_score(一期留NULL), ai_visual_score(展示用不进飞轮权重)`
- `DeliveryPackage``task_id FK, status(pending/ready/downloaded), package_path, download_url, expires_at`
### flywheel.py
- `PreferenceEvent`preference_events 表):飞轮信号日志
- 关键字段:`workspace_id, product_id, task_id FK, user_id FK, signal_type, signal_weight, candidate_id, angle_label, reason(打回原因原文), signal_meta(JSON), data_ownership(固定=client_data数据归客户)`
- 索引:`(workspace_id, product_id, created_at)` 三联复合索引
- `AiCallLog`ai_call_logs 表):`key_id(不含明文), provider, model, call_type, prompt_tokens, completion_tokens, success, error_code, latency_ms`
### fission.py
- `FissionTask``workspace_id FK, source_note(源笔记JSON), source_task_id FK, reference_level(low/mid/high), fanout_count(默认3), status(pending/generating/done/failed)`
- `FissionNote``fission_id FK, workspace_id FK, seq(第几套), note_json(含title/content/tags/imagePlan/dimension), images_json(生图结果列表), score, passed(>=80), dimension(换角度/换痛点/换人群), status`
**表间主要外键:**
- workspace → products / generation_tasks / fission_tasks / preference_events / banned_wordsworkspace_id 隔离多租户)
- generation_task → text_candidates / image_candidates / delivery_packages / preference_events
---
## 4. AI 引擎模块清单
全部在 `app/services/ai_engine/`,共 24 个文件。
| 模块文件 | 一句话职责 |
|---------|----------|
| `constants.py` | 所有可调常量中心:违禁词默认库、评分权重、合格线(80)、叙事策略文本、图片重试参数、飞轮权重 |
| `_score_prompt.py` | AI评委 prompt 模板7维评判标准+输出格式约束JSON与 constants.py 三处同步 |
| `_scoring_dims.py` | 机械合规打分score_compliance用于轨B导入文案或AI评委降级兜底 |
| `llm_scorer.py` | AI评委入口调 LLM 打7维分合规机械硬拦两通道挂时抛 ScoringUnavailableError |
| `text_scoring.py` | 文案评分聚合:整合 AI 评委分 + 机械合规分,输出 score_json |
| `text_variants.py` | 按 A/B/C 三套叙事策略调 LLM 生成多条文案变体 |
| `_text_prompt.py` | 文案生成 prompt 模板(含叙事策略差异化段落) |
| `prompt_composer.py` | 组装最终文案生成 prompt拼飞轮片段+产品信息+标杆特征+叙事策略) |
| `storyboard.py` | 按图数和叙事策略规划分镜列表,返回 storyboard + base_prompt |
| `storyboard_templates.py` | 各套叙事策略的分镜模板数据 |
| `_storyboard_data.py` | 分镜角色的默认视觉参数 |
| `image_gen.py` | 生图主入口asyncio.gather 并发多张主备通道切换指数退避重试3次 |
| `image_postprocessor.py` | 图片去AI化后处理SynthID破除(缩2px裁1px边+亮度/饱和微调)+高保真重编码(jpeg q=100)+尺寸处理 |
| `image_scorer.py` | AI评图0-100分ai_visual_score+ai_visual_note仅展示不进飞轮权重 |
| `banned_word_checker.py` | 三级违禁词扫描auto_fix/soft_warn/hard_block优先 DB 词库,兜底默认词库 |
| `preference_aggregator.py` | 飞轮偏好聚合取最近50条 events → 按权重统计高频角度 → 拼 prompt 片段注入文案生成 |
| `benchmark_analyzer.py` | 标杆笔记爆款8维特征 AI 解析链路第2环 |
| `_benchmark_prompt.py` | 标杆分析 prompt 模板 |
| `fission_prompt.py` | 裂变专用 prompt一次 LLM 出 N 套笔记) |
| `fission_fallback.py` | 裂变引擎降级兜底逻辑 |
| `gemini_factory.py` | Gemini 客户端工厂(生图备用通道) |
| `package_exporter.py` | AI引擎层的素材包打包辅助 |
| `beige_color_grade.py` | 小红书米色调色滤镜去AI化可选后处理 |
### 评分维度详细7维 + 合规机械)
**AI评委7维**,定义在 `_score_prompt.py: SCORING_DIMENSIONS` 字符串 + `llm_scorer.py: _DIM_MAX` dict + `constants.py: AI_DIM_WEIGHTS` dict**三处必须同步**
| 维度 | 满分 | 核心判断标准 |
|-----|-----|------------|
| 痛点人群精准 | 16 | "说的就是我"——处境/困扰让目标用户对号入座 |
| 情绪张力 | 16 | 过去痛(后果)+ 未来好(成果)双向情绪 |
| 买点转化 | 16 | 卖点翻译成用户能感知的场景化利益(人话) |
| 开头钩子 | 13 | 第一句直击痛处/场景,让人停下来继续读 |
| 标题点击力 | 13 | 悬念/反差/疑问钩子 + 广覆盖不锁死窄人群 |
| 真实感 | 11 | 像真人分享前70%干货后30%软性推荐 |
| 标签精准度 | 10 | 精准购买意向埋词,非泛流量词 |
| 合规(机械) | 5 | 机械硬拦不进AI评委打分 |
| **合计** | **100** | |
**合格线**`constants.py` 第47行 `QUALITY_PASS_SCORE = 80`硬编码详见第7节