baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
100
backend/app/workers/replenish_task.py
Normal file
100
backend/app/workers/replenish_task.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
app/workers/replenish_task.py — 文案后台补充任务(S1: 先展示后台补铁律)
|
||||
|
||||
合格文案(passed且score>=90且非hard_block)不足用户目标条数时,
|
||||
run_generation_pipeline 已先展示合格的; 此任务异步补齐到目标数或达上限。
|
||||
只接收 task_id(基石B)。复用 pipeline_io.run_text_generation 的生成+过滤。
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from app.workers.celery_app import celery_app
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 补充上限:避免合格率低时无限补。最多补 MAX_REPLENISH_ROUNDS 轮。
|
||||
MAX_REPLENISH_ROUNDS = 3
|
||||
|
||||
|
||||
@celery_app.task(
|
||||
bind=True,
|
||||
name="app.workers.tasks.replenish_text_candidates",
|
||||
max_retries=2,
|
||||
default_retry_delay=30,
|
||||
queue="generation",
|
||||
)
|
||||
def replenish_text_candidates(self, task_id: int) -> dict:
|
||||
"""后台补充合格文案到目标条数(或达补充上限)。
|
||||
|
||||
每轮调用 run_text_generation 续生成,过滤后累加入库;
|
||||
已合格数 >= text_count 即停; 达 MAX_REPLENISH_ROUNDS 仍不足也停(不卡用户)。
|
||||
"""
|
||||
from app.workers.tasks import _get_db, _push_event_sync
|
||||
from app.workers.pipeline_io import run_text_generation
|
||||
from app.workers.pipeline_steps import (
|
||||
load_task_and_product,
|
||||
decrypt_user_key,
|
||||
build_clients_and_clear_key,
|
||||
build_product_dict,
|
||||
load_flywheel_context,
|
||||
)
|
||||
from app.models.task import TextCandidate
|
||||
|
||||
db = _get_db()
|
||||
try:
|
||||
task, product = load_task_and_product(db, task_id)
|
||||
if not task:
|
||||
logger.warning("补充任务找不到 task_id=%s", task_id)
|
||||
return {"task_id": task_id, "replenished": 0, "reason": "task_not_found"}
|
||||
|
||||
# 已合格数(按入库的合格候选计)
|
||||
existing = db.query(TextCandidate).filter(
|
||||
TextCandidate.task_id == task_id
|
||||
).count()
|
||||
target = task.text_count
|
||||
if existing >= target:
|
||||
return {"task_id": task_id, "replenished": 0, "reason": "already_enough"}
|
||||
|
||||
workspace_id = task.workspace_id
|
||||
plain_key = decrypt_user_key(db, task.operator_id, workspace_id)
|
||||
clients = build_clients_and_clear_key(plain_key)
|
||||
plain_key = None # 用完即清除,基石B
|
||||
product_dict = build_product_dict(product)
|
||||
flywheel_fragment, _ = load_flywheel_context(
|
||||
db, workspace_id, task.product_id, product_dict
|
||||
)
|
||||
|
||||
seq = 9000 # 补充事件 seq 段,避开主流程
|
||||
rounds = 0
|
||||
# 真实入库合格数以 DB count 为准,绝不用 len(raw)(那是原始生成数含被过滤的低分条)。
|
||||
# task47 实测踩坑:len(raw)=2 但 saved=0(都没过90),误判达标提前停,库里仍缺。
|
||||
current = existing
|
||||
while current < target and rounds < MAX_REPLENISH_ROUNDS:
|
||||
rounds += 1
|
||||
run_text_generation(
|
||||
db, clients, task, product_dict, flywheel_fragment,
|
||||
_push_event_sync, workspace_id, seq,
|
||||
)
|
||||
# run_text_generation 内部已把本轮合格候选写库,这里重查真实库内条数
|
||||
db.expire_all()
|
||||
current = db.query(TextCandidate).filter(
|
||||
TextCandidate.task_id == task_id
|
||||
).count()
|
||||
seq += 100
|
||||
logger.info(
|
||||
"补充轮 %d: task_id=%s 库内合格=%d/%d",
|
||||
rounds, task_id, current, target,
|
||||
)
|
||||
|
||||
return {
|
||||
"task_id": task_id,
|
||||
"replenished": current - existing,
|
||||
"rounds": rounds,
|
||||
"final": current,
|
||||
"target": target,
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.error("replenish_text_candidates failed: task_id=%s err=%s", task_id, exc)
|
||||
raise self.retry(exc=exc)
|
||||
finally:
|
||||
db.close()
|
||||
Reference in New Issue
Block a user