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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user