feat(meituan): 政治类文章拦截 + 写'无可奉告' + Angel 并发 3→1

- llm_settings 加 meituan_blocked_topics / blocked_keywords / no_comment_text
- alembic 0006 迁移,默认 topics=[时政/国际/军事/政治/战争/冲突/制裁/选举], 默认文案='无可奉告'
- enrichment._is_meituan_blocked 预检:category 命中 topic 或 关键词 → 直接写'无可奉告',不调美团 API
- 命中后 commentary_meituan_model='policy-block' 标识非真实生成
- enrichment_loop Semaphore(3)→(1),Agnes 免费 plan 不再 429
- 前端 AdminLlmSettings 美团卡片加 3 字段 UI(主题/关键词/固定文案)
This commit is contained in:
xiaji
2026-06-12 22:44:00 +08:00
parent aaf728f3f4
commit 16536fe3a0
6 changed files with 185 additions and 2 deletions

View File

@@ -320,10 +320,55 @@ async def _enrich_commentary_angel(
article.commentary_engine = ",".join(sorted(engines))
# === 美团"无可奉告"预检 ===
# 美团(LongCat)对战争/政治/敏感新闻会触发 security_audit_fail 400,
# 死循环重试不解决问题。命中"政治类"文章直接写固定文案,不调美团 API。
# 命中规则:
# 1) article.category(LLM 已分类,逗号分隔)中含 meituan_blocked_topics 任一项
# 2) 标题/正文命中 meituan_blocked_keywords 任一关键词(宽松包含)
def _is_meituan_blocked(article: Article, setting: LlmSetting) -> bool:
"""返回 True 表示这篇应该被"无可奉告"处理(不调美团 API)。"""
topics = set(setting.meituan_blocked_topics or [])
keywords = [k for k in (setting.meituan_blocked_keywords or []) if k]
if topics:
cats = {c.strip() for c in (article.category or "").split(",") if c.strip()}
if cats & topics:
return True
if keywords:
text = " ".join(
(article.title_zh or ""), (article.title or ""),
(article.body_zh_text or "")[:1500],
)
for kw in keywords:
if kw and kw in text:
return True
return False
async def _enrich_commentary_meituan(
article: Article, setting: LlmSetting, client: LlmClient
) -> None:
"""美团评论 — 写入 commentary_meituan 等新字段。"""
"""美团评论 — 写入 commentary_meituan 等新字段。
预检:命中"政治类"主题的文章直接写"无可奉告"固定文案,
不调美团 API(避免 LongCat security_audit_fail 400 循环)。
"""
# === 预检:政治类文章写"无可奉告" ===
if _is_meituan_blocked(article, setting):
no_text = setting.meituan_no_comment_text or "无可奉告"
article.commentary_meituan = no_text
article.commentary_meituan_status = "ok"
article.commentary_meituan_error = None
article.commentary_meituan_model = "policy-block" # 标识非真实生成
engines = set(filter(None, (article.commentary_engine or "").split(",")))
engines.add(PROVIDER_MEITUAN)
article.commentary_engine = ",".join(sorted(engines))
logger.info(
"commentary_meituan policy-block for article %s (category=%s)",
article.id, article.category,
)
return
# 优先用 setting.meituan_commentary_prompt,留空用默认
template = setting.meituan_commentary_prompt or _default_commentary_prompt()
prompt = _safe_format(
@@ -548,7 +593,10 @@ async def enrichment_loop() -> None:
# 并发 enrich 多篇(LlmClient 内部 interval_sec 已经做了限速,这里只并发不限并发上限)
# 但为了不让 LLM API 同时打太多,加一层并发上限
sem = asyncio.Semaphore(3)
# 2026-06-12 调:3→1。Agnes 免费 plan 在并发 3 时会偶发 429
# (每篇 enrich 内部 Angel 跑 4 chat + 1 image,3 路并发 = 12+3 短时突发),
# 降到 1 路串行,稳定不超限,且不浪费 429 重试窗口。
sem = asyncio.Semaphore(1)
async def _run_one(aid: int) -> None:
async with sem:
try: