- 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>
15 KiB
15 KiB
Clover 后端接手地图
受众:守印(新接手后端,不了解北哥产品背景) 最后更新:2026-07-01 基于真实代码分析,所有文件名/类名/函数名/行号均来自实际代码,请直接跳转核实。
产品背景(守印必读)
Clover 是给北哥团队做的小红书种草内容生产中台,帮助运营团队批量生产高质量小红书笔记。
完整链路:
填产品卖点(产品档案)
→ 上传标杆笔记(AI拆解爆款特征)
→ 发起任务(AI生成3套文案 × A痛点先行/B场景先行/C成分背书)
→ 运营选文案 + 选配图(每步触发飞轮信号学偏好)
→ 组长审核(通过/打回带原因)
→ 打包下载 ZIP,发给达人分发
→ 爆款笔记可触发"裂变"再出N套变体
三个角色:
operator(运营):选文案/选图/改稿supervisor(组长):审核通过/打回admin(管理员):管用户/管词库/删候选
两大特色机制:
- AI评分门禁:文案必须 ≥80分才入库,低分直接过滤不展示给运营
- 飞轮:每次选择/改稿/打回都是信号,下次生成自动学偏好
目录
1. 整体目录结构与技术栈
技术栈: FastAPI + Celery + PostgreSQL + Redis,Python 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 换一次性 ticket(60s 有效) |
| 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_agentUserPreference:UI 设置,preferences_json(Text 存 JSON)
workspace.py
Workspace:id, name, slug(唯一), is_activeWorkspaceMember: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_activeProductImage(R5多图):product_id FK, path(绝对路径), scene(primary/scene/texture/ingredient/model), is_primary, sort_orderBenchmarkNote: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_words(workspace_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节)。