""" app/workers/replenish_task.py — 文案后台补充任务(S1: 先展示后台补铁律) 合格文案(passed且score>=QUALITY_PASS_SCORE(80,红线)且非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()