第11环裂变重写:对齐上线版 split.js 一次LLM出N套完整笔记包
架构从"扇出N个GenerationTask各跑完整管道"改为"一次LLM调用直接出N套
完整笔记包(N=1~5)",落 FissionNote 表 + 独立展示页。
后端:
- 018迁移:fission_notes 表(文案JSON+score+passed+imagePlan+images+status)
- fission_prompt:FISSION_SYSTEM+三档参考度(low/mid/high)+normalize_tags+品类兜底
- fission_pipeline:一次LLM出N套→各评分(@80合格线)→排序→落库,不达标标
needs_optimization 非丢弃;apiports 503 回落 codeproxy gpt-5.5 强档兜底
- fission_images:每套串行调现有生图接口(零改动image_gen/storyboard)
- tasks.py:run_fission_pipeline Celery task,删旧扇出注入
- api/v1/fission:进度聚合FissionNote + GET /fission/{id}/notes(剥内部字段)
前端:FissionProgress对齐状态机 + N套独立展示页 + FissionNoteCard
测试:test_fission_engine(19)+test_fission_pipeline(5) 全过;104 全量回归绿
实测task5(fanout=2,mid)端到端跑通:一次出2套→seq0=85过/seq1=79标优化→
生图codeproxy/edits→1024×1536去AI化→task completed→notes端点返完整数据
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
"""
|
||||
app/services/fission_service.py — 第11环裂变 扇出 service
|
||||
app/services/fission_service.py — 第11环裂变 service 入口(对齐上线版 split.js)
|
||||
|
||||
1爆款 → N套完整笔记包。每套=一个独立 GenerationTask(回填 source_fission_id),
|
||||
复用主管道 run_generation_pipeline 生成文案+配图。
|
||||
🔴 占位档位规则见 fission_prompt.py,真实措辞待北哥定义(倩倩姐2026-06-16先打通)。
|
||||
裂变 = 1爆款 → 一次 LLM 出 N 套完整笔记包(N=1~5)。
|
||||
本文件只留 API 入口:
|
||||
- create_fission:建 FissionTask 后丢 run_fission_pipeline.delay,不再扇出 GenerationTask
|
||||
编排层(解析/评分/落库/生图)在 app/services/fission_pipeline.py(红线≤200行已拆)。
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
@@ -15,11 +16,11 @@ from app.middleware.workspace_guard import CurrentUser
|
||||
from app.models.fission import FissionTask
|
||||
from app.models.task import GenerationTask
|
||||
from app.services.ai_engine.fission_prompt import valid_level
|
||||
from app.services.task_service import _check_user_has_key, enqueue_generation
|
||||
from app.services.task_service import _check_user_has_key
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MAX_FANOUT = 5 # 每用户并发上限5(红线),裂变扇出数受同等约束
|
||||
MAX_FANOUT = 5 # 每用户并发上限5(红线);裂变 N 套直出受同等约束
|
||||
|
||||
|
||||
def create_fission(
|
||||
@@ -27,15 +28,16 @@ def create_fission(
|
||||
source_task_id: int, reference_level: str, fanout_count: int,
|
||||
) -> tuple[int, list[int]]:
|
||||
"""
|
||||
从一个源任务裂变出 N 套子任务。
|
||||
返回 (fission_id, [子task_id,...])。
|
||||
从一个源任务建裂变任务,丢 Celery 一次出 N 套笔记包。
|
||||
返回 (fission_id, [])。子笔记落 FissionNote,不再扇出 GenerationTask,
|
||||
故第二个返回值恒为空列表(保留签名兼容旧调用方)。
|
||||
"""
|
||||
_check_user_has_key(db, current_user.user_id, current_user.workspace_id)
|
||||
|
||||
level = valid_level(reference_level)
|
||||
n = max(1, min(fanout_count or 3, MAX_FANOUT))
|
||||
|
||||
# 读源任务(取产品+主题+已选文案作为爆款源)
|
||||
# 读源任务(取产品+主题+已选文案作为爆款源)
|
||||
src = db.query(GenerationTask).filter(
|
||||
GenerationTask.id == source_task_id,
|
||||
GenerationTask.workspace_id == current_user.workspace_id,
|
||||
@@ -45,7 +47,7 @@ def create_fission(
|
||||
|
||||
source_note = {"title": src.theme or "", "content": _extract_source_copy(db, src)}
|
||||
|
||||
# 建 fission_task 记录
|
||||
# 建 fission_task 记录(带 source_task_id 供 pipeline 取产品/张数)
|
||||
ft = FissionTask(
|
||||
workspace_id=current_user.workspace_id,
|
||||
source_note=json.dumps(source_note, ensure_ascii=False),
|
||||
@@ -53,27 +55,17 @@ def create_fission(
|
||||
)
|
||||
db.add(ft); db.commit(); db.refresh(ft)
|
||||
|
||||
# 扇出 N 个子任务(回填 source_fission_id),复用主管道
|
||||
task_ids: list[int] = []
|
||||
for _ in range(n):
|
||||
sub = GenerationTask(
|
||||
workspace_id=current_user.workspace_id,
|
||||
product_id=src.product_id, operator_id=current_user.user_id,
|
||||
theme=src.theme, text_count=src.text_count, image_count=src.image_count,
|
||||
track="ai", need_product_image=src.need_product_image,
|
||||
status="pending", source_fission_id=ft.id,
|
||||
)
|
||||
db.add(sub); db.commit(); db.refresh(sub)
|
||||
enqueue_generation(sub.id)
|
||||
task_ids.append(sub.id)
|
||||
# 丢 Celery:一次 LLM 出 N 套(不扇出 GenerationTask)
|
||||
from app.workers.tasks import run_fission_pipeline
|
||||
run_fission_pipeline.delay(ft.id, source_task_id)
|
||||
|
||||
logger.info("fission created id=%s level=%s fanout=%s tasks=%s",
|
||||
ft.id, level, n, task_ids)
|
||||
return ft.id, task_ids
|
||||
logger.info("fission created id=%s level=%s n=%s src_task=%s",
|
||||
ft.id, level, n, source_task_id)
|
||||
return ft.id, []
|
||||
|
||||
|
||||
def _extract_source_copy(db: Session, src: GenerationTask) -> str:
|
||||
"""取源任务已选/最高分文案当爆款正文;无则用主题兜底。"""
|
||||
"""取源任务已选/最高分文案当爆款正文;无则用主题兜底。"""
|
||||
from app.models.task import TextCandidate
|
||||
c = (db.query(TextCandidate)
|
||||
.filter(TextCandidate.task_id == src.id)
|
||||
|
||||
Reference in New Issue
Block a user