A8: 文案按A/B/C三套正交叙事生成,避免套路化重复

- constants: 新增 TEXT_NARRATIVE_BY_STRATEGY(A痛点/B场景/C成分),与图片侧同轴
- build_prompt: 加 strategy_narrative 参数并注入 prompt
- text_variants: 全链路透传(含优化轮)
- run_text_generation: 改循环三套,text_count均摊(divmod余前补),跨套去重,打_strategy标记
- TextCandidate: 加 strategy String(4) 字段 + 迁移021(已upgrade head)
- packaging: 打包按strategy精准配对文图(texts_by_strategy映射+三层兜底)
- SSE text_candidate 事件携带 strategy

独立agent交叉验证7改造点全过,边界(text_count<3/无别名/不截断)无must-fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-22 10:00:47 +08:00
parent 4bed7425a8
commit ff21116ff8
8 changed files with 123 additions and 26 deletions

View File

@@ -46,7 +46,7 @@ def run_text_generation(db, clients, task, product_dict: dict, flywheel_fragment
from app.models.flywheel import AiCallLog
from app.models.workspace import UserApiKey
from app.constants.enums import CandidateSource, BannedWordStatus
from app.services.ai_engine.constants import QUALITY_PASS_SCORE
from app.services.ai_engine.constants import QUALITY_PASS_SCORE, TEXT_NARRATIVE_BY_STRATEGY
banned_rows = db.query(BannedWord).filter(
BannedWord.workspace_id == workspace_id
@@ -61,20 +61,37 @@ def run_text_generation(db, clients, task, product_dict: dict, flywheel_fragment
).first()
key_id = key_row.id if key_row else None
# A8文案按 A/B/C 三套正交叙事分别生成,每套不同角度避免套路化重复,
# 与图片侧同轴(A痛点/B场景/C成分)。text_count 均摊三套(余数前补)
# 逐套把已生成的喂作 previous_copies 做跨套去重。
_strategies = ("A", "B", "C")
_base, _rem = divmod(task.text_count, 3)
_per = {s: _base + (1 if i < _rem else 0) for i, s in enumerate(_strategies)}
t0 = time.monotonic()
llm_success = True
try:
candidates_raw = asyncio.run(generate_text_variants(
llm_client=clients,
product=product_dict,
count=task.text_count,
banned_word_rows=banned_dicts,
flywheel_context=flywheel_fragment,
))
except Exception as exc:
llm_success = False
logger.error("generate_text_variants 失败: %s", exc)
candidates_raw = []
candidates_raw: list = []
for s in _strategies:
n = _per[s]
if n <= 0:
continue
try:
part = asyncio.run(generate_text_variants(
llm_client=clients,
product=product_dict,
count=n,
previous_copies=candidates_raw,
banned_word_rows=banned_dicts,
flywheel_context=flywheel_fragment,
strategy_narrative=TEXT_NARRATIVE_BY_STRATEGY.get(s, ""),
))
except Exception as exc:
llm_success = False
logger.error("generate_text_variants(套%s) 失败: %s", s, exc)
part = []
for c in part:
c["_strategy"] = s
candidates_raw.extend(part)
latency_ms = int((time.monotonic() - t0) * 1000)
# 写 ai_call_logs留痕不含明文key
@@ -113,6 +130,7 @@ def run_text_generation(db, clients, task, product_dict: dict, flywheel_fragment
workspace_id=workspace_id,
task_id=task.id,
source=CandidateSource.AI,
strategy=c.get("_strategy"),
angle_label=c.get("angle_label") or c.get("angle", ""),
content=json.dumps(c, ensure_ascii=False),
score_json=json.dumps(c.get("score_detail", []), ensure_ascii=False),
@@ -124,6 +142,7 @@ def run_text_generation(db, clients, task, product_dict: dict, flywheel_fragment
seq += 1
push_fn(task.id, workspace_id, "text_candidate", {
"candidate_id": tc.id, "angle_label": tc.angle_label,
"strategy": tc.strategy,
"content": c.get("content", ""), "score": score,
}, seq)
seq += 1