✨ feat(content): 新增智能选题引擎、批量创作和图文协同优化

- 新增智能选题引擎 `TopicEngine`,整合热点数据与历史权重,提供多维度评分和创作角度建议
- 新增内容模板系统 `ContentTemplate`,支持从 JSON 文件加载模板并应用于文案生成
- 新增批量创作功能 `batch_generate_copy`,支持串行生成多篇文案并自动入草稿队列
- 升级文案质量流水线:实现 Prompt 分层架构(基础层 + 风格层 + 人设层)、LLM 自检与改写机制、深度去 AI 化后处理
- 优化图文协同:新增封面图策略选择、SD prompt 与文案语义联动、图文匹配度评估
- 集成数据闭环:在文案生成中自动注入 `AnalyticsService` 权重数据,实现发布 → 数据回收 → 优化创作的完整循环
- 更新 UI 组件:新增选题推荐展示区、批量创作折叠面板、封面图策略选择器和图文匹配度评分展示

♻️ refactor(llm): 重构 Prompt 架构并增强去 AI 化处理

- 将 `PROMPT_COPYWRITING` 拆分为分层架构(基础层 + 风格层 + 人设层),提高维护性和灵活性
- 增强 `_humanize_content` 方法:新增语气词注入、标点不规范化、段落节奏打散和 emoji 密度控制
- 新增 `_self_check` 和 `_self_check_rewrite` 方法,实现文案 AI 痕迹自检与自动改写
- 新增 `evaluate_image_text_match` 方法,支持文案与 SD prompt 的语义匹配度评估(可选,失败不阻塞)
- 新增封面图策略配置 `COVER_STRATEGIES` 和情感基调映射 `EMOTION_SD_MAP`

📝 docs(openspec): 归档内容创作优化提案和详细规格

- 新增 `openspec/changes/archive/2026-02-28-optimize-content-creation/` 目录,包含设计文档、提案、规格说明和任务清单
- 新增 `openspec/specs/` 下的批量创作、文案质量流水线、图文协同、服务内容和智能选题引擎规格文档
- 更新 `openspec/specs/services-content/spec.md`,反映新增的批量创作和智能选题入口函数

🔧 chore(config): 更新服务配置和 UI 集成

- 在 `services/content.py` 中集成权重数据自动注入逻辑,实现数据驱动创作
- 在 `ui/app.py` 中新增选题推荐、批量生成和图文匹配度评估的回调函数
- 在 `ui/tab_create.py` 中新增智能选题推荐区、批量创作面板和图文匹配度评估组件
- 修复 `services/sd_service.py` 中的头像文件路径问题,确保目录存在
This commit is contained in:
2026-02-28 21:04:09 +08:00
parent 2ba87c8f6e
commit 1ec520b47e
22 changed files with 1992 additions and 90 deletions
+40 -2
View File
@@ -52,11 +52,46 @@ from services.queue_ops import (
queue_format_table, queue_format_calendar,
)
from services.autostart import is_autostart_enabled, toggle_autostart
from services.content import generate_copy, generate_images, one_click_export, publish_to_xhs
from services.publish_queue import STATUS_LABELS
from services.content import generate_copy, generate_images, one_click_export, publish_to_xhs, batch_generate_copy
from services.publish_queue import PublishQueue, STATUS_LABELS
from services.topic_engine import TopicEngine
logger = logging.getLogger("autobot")
# ========== 新增回调: 选题推荐 / 批量创作 / 图文匹配 ==========
def _fn_topic_recommend(model_name):
"""获取智能选题推荐列表"""
analytics = AnalyticsService()
engine = TopicEngine(analytics)
return engine.recommend_topics(count=5)
def _fn_batch_generate(model_name, topics, style, sd_model_name, persona_text, template_name):
"""批量生成文案并入草稿队列"""
pq = PublishQueue()
return batch_generate_copy(
model=model_name,
topics=topics,
style=style,
sd_model_name=sd_model_name,
persona_text=persona_text,
template_name=template_name,
publish_queue=pq,
)
def _fn_evaluate_match(model_name, content, sd_prompt):
"""评估图文匹配度"""
from services.llm_service import LLMService
from services.connection import _get_llm_config
api_key, base_url, _ = _get_llm_config()
if not api_key:
return {"match_score": -1, "suggestions": [], "skipped": True}
svc = LLMService(api_key, base_url, model_name)
return svc.evaluate_image_text_match(content, sd_prompt)
_GRADIO_CSS = """
/* ── Autobot 主题层 ── */
body, .gradio-container {
@@ -238,6 +273,9 @@ def build_app(cfg: "ConfigManager", analytics: "AnalyticsService") -> gr.Blocks:
fn_get_sd_preset=get_sd_preset,
fn_cfg_set=cfg.set,
fn_cfg_update=cfg.update,
fn_batch_generate=_fn_batch_generate,
fn_topic_recommend=_fn_topic_recommend,
fn_evaluate_match=_fn_evaluate_match,
)
res_title = _tab1["res_title"]
res_content = _tab1["res_content"]
+162
View File
@@ -2,8 +2,11 @@
内容创作 Tab UI 模块
包含 Tab 1「✨ 内容创作」的所有 Gradio 组件定义和事件绑定
"""
import logging
import gradio as gr
logger = logging.getLogger("autobot")
def build_tab(
config: dict,
@@ -27,6 +30,10 @@ def build_tab(
fn_get_sd_preset,
fn_cfg_set,
fn_cfg_update,
# 新增: 批量创作 & 选题推荐回调
fn_batch_generate=None,
fn_topic_recommend=None,
fn_evaluate_match=None,
):
"""
构建「✨ 内容创作」Tab,注册所有事件绑定。
@@ -52,6 +59,15 @@ def build_tab(
# ---- 左栏:输入 ----
with gr.Column(scale=3):
gr.Markdown("### 💡 构思")
# === 智能选题推荐 ===
with gr.Accordion("🧠 智能选题推荐", open=False):
btn_recommend = gr.Button("🔍 获取推荐选题", variant="secondary", size="sm")
topic_recommendations = gr.Markdown(
value="点击上方按钮获取推荐选题",
label="推荐选题",
)
topic = gr.Textbox(label="笔记主题", placeholder="例如:优衣库早春穿搭")
style = gr.Dropdown(
styles,
@@ -61,6 +77,13 @@ def build_tab(
gr.Markdown("---")
gr.Markdown("### 🎨 绘图参数")
# 封面图策略选择
cover_strategy = gr.Radio(
["人物特写", "场景展示", "对比图", "文字卡片"],
label="封面图策略",
value="人物特写",
info="影响 SD 构图和尺寸",
)
quality_mode = gr.Radio(
sd_preset_names,
label="生成模式",
@@ -123,6 +146,30 @@ def build_tab(
btn_publish = gr.Button("🚀 发布到小红书", variant="primary")
publish_msg = gr.Markdown("")
# === 图文匹配度评分 ===
with gr.Accordion("📊 图文匹配度", open=False):
btn_eval_match = gr.Button("评估匹配度", variant="secondary", size="sm")
match_score_display = gr.Markdown("点击按钮评估文案与图片的匹配度")
# === 批量创作面板 ===
with gr.Accordion("📦 批量创作", open=False):
with gr.Row():
with gr.Column(scale=2):
batch_topics = gr.TextArea(
label="批量主题 (每行一个,最多10个)",
placeholder="优衣库早春穿搭\n百元床品测评\n新手养宠攻略",
lines=5,
)
with gr.Column(scale=1):
batch_template = gr.Dropdown(
choices=["(不使用模板)", "好物种草", "日常分享", "攻略教程"],
value="(不使用模板)",
label="内容模板",
)
btn_batch_gen = gr.Button("🚀 批量生成", variant="primary")
btn_smart_gen = gr.Button("🧠 智能选题+生成", variant="secondary")
batch_result = gr.Markdown("")
# ---- 事件绑定 ----
btn_gen_copy.click(
@@ -168,6 +215,120 @@ def build_tab(
outputs=[publish_msg],
)
# ---- 新增事件绑定 ----
# 智能选题推荐
def _on_recommend(model_name):
if not fn_topic_recommend:
return "⚠️ 选题推荐功能未连接"
try:
recommendations = fn_topic_recommend(model_name)
if not recommendations:
return "暂无推荐选题,请先搜索热点或积累数据"
lines = []
for i, r in enumerate(recommendations, 1):
angles_str = "、".join(r.get("angles", [])[:2])
lines.append(
f"**{i}. {r['topic']}** (评分: {r['score']})\n"
f" {r.get('reason', '')}\n"
f" 💡 角度: {angles_str}"
)
return "\n\n".join(lines)
except Exception as e:
logger.error("选题推荐失败: %s", e)
return f"❌ 推荐失败: {e}"
btn_recommend.click(
fn=_on_recommend,
inputs=[llm_model],
outputs=[topic_recommendations],
)
# 图文匹配度评估
def _on_eval_match(model_name, content, sd_prompt):
if not fn_evaluate_match:
return "⚠️ 图文匹配度评估功能未连接"
if not content or not sd_prompt:
return "请先生成文案和图片后再评估"
try:
result = fn_evaluate_match(model_name, content, sd_prompt)
if result.get("skipped"):
return "⚠️ 评估超时或失败,已跳过"
score = result.get("match_score", 0)
suggestions = result.get("suggestions", [])
icon = "🟢" if score >= 80 else ("🟡" if score >= 50 else "🔴")
text = f"{icon} 匹配度: **{score}/100**"
if suggestions:
text += "\n\n改进建议:\n" + "\n".join(f"- {s}" for s in suggestions)
if score < 50:
text += "\n\n⚠️ 匹配度较低,建议重新生成图片提示词"
return text
except Exception as e:
return f"评估失败: {e}"
btn_eval_match.click(
fn=_on_eval_match,
inputs=[llm_model, res_content, res_prompt],
outputs=[match_score_display],
)
# 批量生成
def _on_batch_generate(model_name, topics_text, style_val, sd_model_name, persona_text, template):
if not fn_batch_generate:
return "⚠️ 批量创作功能未连接"
topics = [t.strip() for t in topics_text.strip().split("\n") if t.strip()]
if not topics:
return "❌ 请输入至少一个主题(每行一个)"
template_name = template if template != "(不使用模板)" else ""
try:
results, status = fn_batch_generate(
model_name, topics, style_val, sd_model_name, persona_text, template_name
)
lines = [f"### {status}\n"]
for r in results:
if "error" in r:
lines.append(f"❌ **{r.get('topic', '未知')}**: {r['error']}")
else:
lines.append(f"✅ **{r.get('title', '无标题')}** — {r.get('topic', '')}")
return "\n\n".join(lines)
except Exception as e:
return f"❌ 批量生成失败: {e}"
btn_batch_gen.click(
fn=_on_batch_generate,
inputs=[llm_model, batch_topics, style, sd_model, persona, batch_template],
outputs=[batch_result],
)
# 智能选题+生成
def _on_smart_generate(model_name, style_val, sd_model_name, persona_text):
if not fn_topic_recommend or not fn_batch_generate:
return "⚠️ 智能选题功能未连接"
try:
recommendations = fn_topic_recommend(model_name)
if not recommendations:
return "❌ 选题引擎未找到推荐主题"
# 取前 3 个推荐
topics = [r["topic"] for r in recommendations[:3]]
results, status = fn_batch_generate(
model_name, topics, style_val, sd_model_name, persona_text, ""
)
lines = [f"### {status}\n", "**使用推荐选题:**"]
for r in results:
if "error" in r:
lines.append(f"❌ **{r.get('topic', '未知')}**: {r['error']}")
else:
lines.append(f"✅ **{r.get('title', '无标题')}**")
return "\n\n".join(lines)
except Exception as e:
return f"❌ 智能生成失败: {e}"
btn_smart_gen.click(
fn=_on_smart_generate,
inputs=[llm_model, style, sd_model, persona],
outputs=[batch_result],
)
# 返回可能被其他 Tab 引用的组件
return {
"res_title": res_title,
@@ -179,4 +340,5 @@ def build_tab(
"cfg_scale": cfg_scale,
"neg_prompt": neg_prompt,
"enhance_level": enhance_level,
"cover_strategy": cover_strategy,
}