feat: AI 审核自动驳回 + 功效词可配置 + UI 修复

- AI 自动驳回:法规/品牌安全 HIGH 违规或总分<40 自动打回上传阶段
- 功效词可配置:从硬编码改为品牌方在规则页面自行管理
- 驳回通知:AI 驳回时只通知达人,含具体原因
- 达人端:脚本/视频页面展示 AI 驳回原因 + 重新上传入口
- 规则页面:新增"功效词"分类
- 种子数据:新增 6 条默认功效词
- 其他:代理商管理下拉修复、AI 配置模型列表扩展、视觉模型标签修正、规则编辑放开限制

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-02-11 20:24:32 +08:00
co-authored by Claude Opus 4.6
parent 0ef7650c09
commit 0b3dfa3c52
10 changed files with 476 additions and 190 deletions
+54 -3
View File
@@ -195,6 +195,42 @@ async def upload_video(
return task
AI_AUTO_REJECT_SCORE = 40
def _check_ai_auto_reject(score: int, result: dict) -> tuple[bool, str]:
"""
判断 AI 审核结果是否应自动驳回
触发条件(任一):
1. 法规合规维度存在 HIGH 级违规(违禁词/功效词)
2. 品牌安全维度存在 HIGH 级违规(竞品提及)
3. 总分 < 40
"""
reasons = []
violations = result.get("violations", [])
# 条件1: 法规 HIGH
high_legal = [v for v in violations if v.get("dimension") == "legal" and v.get("severity") == "high"]
if high_legal:
words = [v.get("content", "") for v in high_legal[:5]]
reasons.append(f"法规违规:{', '.join(words)}")
# 条件2: 品牌安全 HIGH
high_brand = [v for v in violations if v.get("dimension") == "brand_safety" and v.get("severity") == "high"]
if high_brand:
words = [v.get("content", "") for v in high_brand[:5]]
reasons.append(f"品牌安全违规:{', '.join(words)}")
# 条件3: 总分过低
if score < AI_AUTO_REJECT_SCORE:
reasons.append(f"综合评分 {score} 分,低于合格线 {AI_AUTO_REJECT_SCORE}")
if reasons:
return True, "".join(reasons)
return False, ""
async def complete_ai_review(
db: AsyncSession,
task: Task,
@@ -206,9 +242,16 @@ async def complete_ai_review(
完成 AI 审核
- 更新 AI 审核结果
- 状态流转到代理商审核
- 自动驳回:法规/品牌安全 HIGH 违规或总分 < 40 → 回到上传阶段
- 正常:流转到代理商审核
"""
now = datetime.now(timezone.utc)
auto_rejected, reject_reason = _check_ai_auto_reject(score, result)
# 将自动驳回信息写入 result,前端可据此展示
if auto_rejected:
result["ai_auto_rejected"] = True
result["ai_reject_reason"] = reject_reason
if review_type == "script":
if task.stage != TaskStage.SCRIPT_AI_REVIEW:
@@ -217,7 +260,11 @@ async def complete_ai_review(
task.script_ai_score = score
task.script_ai_result = result
task.script_ai_reviewed_at = now
task.stage = TaskStage.SCRIPT_AGENCY_REVIEW
if auto_rejected:
task.stage = TaskStage.SCRIPT_UPLOAD
else:
task.stage = TaskStage.SCRIPT_AGENCY_REVIEW
elif review_type == "video":
if task.stage != TaskStage.VIDEO_AI_REVIEW:
@@ -226,7 +273,11 @@ async def complete_ai_review(
task.video_ai_score = score
task.video_ai_result = result
task.video_ai_reviewed_at = now
task.stage = TaskStage.VIDEO_AGENCY_REVIEW
if auto_rejected:
task.stage = TaskStage.VIDEO_UPLOAD
else:
task.stage = TaskStage.VIDEO_AGENCY_REVIEW
else:
raise ValueError(f"不支持的审核类型: {review_type}")