fix: 三平台内容差异化 + admin敏感词管理表格化
- writer.py: _expand_section() 去除 <100字阈值,始终调用 LLM 平台专属扩写
- prompt_loader.py: 新增 section_expansion_zhihu/wechat/xiaohongshu 三个独立 prompt
- admin.html: 配置管理标签页 + 敏感词/清理规则子标签 + 敏感词表格化管理(编辑/删除)
- config_items.py: PUT /sensitive-words/{id} 支持更新 word/category
- compliance_checker.py: AI 套话从 DB 加载 + 人称规则修正
- initial_data.py: PlatformConfig 字数迁移 + 新种子
- 各前端页面: LLM 配置 rate_limit 字段 + 供应商列表排序
This commit is contained in:
+19
-19
@@ -43,9 +43,9 @@ logger = logging.getLogger(__name__)
|
||||
_md_parser = mistune.create_markdown()
|
||||
|
||||
_FALLBACK_PLATFORM_CONFIG = {
|
||||
"zhihu": {"max_chars": 3000, "style": "深度长文分析", "min_chars": 1500},
|
||||
"wechat": {"max_chars": 1500, "style": "亲切口语化", "min_chars": 800},
|
||||
"xiaohongshu": {"max_chars": 800, "style": "图文笔记,emoji+标签", "min_chars": 300},
|
||||
"zhihu": {"max_chars": 8000, "style": "深度长文分析", "min_chars": 3000},
|
||||
"wechat": {"max_chars": 4000, "style": "个人叙事对话感", "min_chars": 2000},
|
||||
"xiaohongshu": {"max_chars": 1000, "style": "图文笔记,精炼实用", "min_chars": 400},
|
||||
}
|
||||
|
||||
def _load_platform_config() -> dict:
|
||||
@@ -144,28 +144,30 @@ class Writer:
|
||||
bullet_count = sum(1 for l in lines if l.startswith(('- ', '* ', '**', '+ ')))
|
||||
return bullet_count / len(lines) > 0.4
|
||||
|
||||
def _expand_section(self, section: Dict) -> str:
|
||||
def _expand_section(self, section: Dict, platform: str = "zhihu") -> str:
|
||||
content = section.get('content', '').strip()
|
||||
# 大纲要点格式(>40% 行以 -/*/** 开头)或内容过短(<100字)应由 LLM 展开为连贯段落
|
||||
if HAVE_LLM and (self._is_bullet_only(content) or (content and len(content) < 100)):
|
||||
logger.info(f"使用 LLM 扩写章节(要点→段落): {section['title']}")
|
||||
prompt = get_prompt("section_expansion",
|
||||
platform_prompt_key = f"section_expansion_{platform}"
|
||||
if platform not in ("zhihu", "wechat", "xiaohongshu"):
|
||||
platform_prompt_key = "section_expansion_zhihu"
|
||||
|
||||
if HAVE_LLM and content:
|
||||
logger.info(f"LLM 扩写 [{platform}]: {section['title']} ({len(content)} chars)")
|
||||
prompt = get_prompt(platform_prompt_key,
|
||||
topic_title=self.topic['title'],
|
||||
section_title=section['title'],
|
||||
date=datetime.datetime.now().strftime('%Y年%m月%d日'),
|
||||
content=content,
|
||||
)
|
||||
try:
|
||||
params = get_prompt_params("section_expansion")
|
||||
params = get_prompt_params(platform_prompt_key) or {"temperature": 0.75, "max_tokens": 3000}
|
||||
expanded = call_llm(prompt, temperature=params.get("temperature", 0.75), max_tokens=params.get("max_tokens", 3000))
|
||||
if expanded:
|
||||
cleaned = self._clean_markdown(expanded.strip())
|
||||
if cleaned:
|
||||
return cleaned
|
||||
except Exception as e:
|
||||
logger.warning(f"LLM 扩写失败: {e}")
|
||||
logger.warning(f"LLM 扩写失败 [{platform}]: {e}")
|
||||
|
||||
# Fallback: 将 bullet points 展开为段落(过滤噪音行)
|
||||
lines = [l.strip() for l in content.split('\n') if not self._is_outline_noise(l)]
|
||||
if lines:
|
||||
sentences = []
|
||||
@@ -185,17 +187,16 @@ class Writer:
|
||||
return self._clean_markdown(result)
|
||||
return ''
|
||||
|
||||
def generate_full_markdown(self) -> str:
|
||||
def generate_platform_markdown(self, platform: str = "zhihu") -> str:
|
||||
sections = self._parse_outline_sections()
|
||||
parts = []
|
||||
for sec in sections:
|
||||
if sec['level'] == 1:
|
||||
if sec.get('content'):
|
||||
expanded = self._expand_section(sec)
|
||||
expanded = self._expand_section(sec, platform)
|
||||
if expanded:
|
||||
parts.append(expanded + "\n")
|
||||
continue
|
||||
# 跳过大纲结构噪音节点
|
||||
title_stripped = sec['title'].strip()
|
||||
if title_stripped in ('文章大纲', '大纲', '文章结构', '结构'):
|
||||
continue
|
||||
@@ -204,7 +205,7 @@ class Writer:
|
||||
heading = f"{'#' * sec['level']} {sec['title']}"
|
||||
parts.append(heading)
|
||||
if sec.get('content'):
|
||||
expanded = self._expand_section(sec)
|
||||
expanded = self._expand_section(sec, platform)
|
||||
parts.append(expanded + "\n")
|
||||
full_md = "\n".join(parts).strip()
|
||||
# 收集所有引用来源,统一添加到文末
|
||||
@@ -286,8 +287,6 @@ class Writer:
|
||||
if platform == "wechat":
|
||||
result = []
|
||||
for line in lines:
|
||||
# 人称统一:我们→我,你们→你
|
||||
line = line.replace('我们', '我').replace('你们', '你').replace('我', '你')
|
||||
if line.startswith('### '):
|
||||
result.append(f"\n**{line[4:]}**\n")
|
||||
elif line.startswith('## '):
|
||||
@@ -370,6 +369,7 @@ class Writer:
|
||||
prompt = get_prompt("title_optimize_wechat",
|
||||
title=original,
|
||||
core=self.topic.get('core_concept', ''),
|
||||
field=self.topic.get('field', ''),
|
||||
)
|
||||
elif platform == "xiaohongshu":
|
||||
prompt = get_prompt("title_optimize_xhs",
|
||||
@@ -459,10 +459,10 @@ class Writer:
|
||||
logger.info(f"选题 {self.topic_id} 状态已更新为待审查(数据库)")
|
||||
|
||||
def run(self):
|
||||
logger.info("开始撰写阶段")
|
||||
markdown = self.generate_full_markdown()
|
||||
logger.info("开始撰写阶段(三平台独立展开)")
|
||||
results = {}
|
||||
for platform in ["zhihu", "wechat", "xiaohongshu"]:
|
||||
markdown = self.generate_platform_markdown(platform)
|
||||
html = self.generate_platform_html(markdown, platform)
|
||||
results[platform] = str(self.save_html(html, platform))
|
||||
self.mark_draft()
|
||||
|
||||
Reference in New Issue
Block a user