fix: content quality, image format, task monitor, calendar data source, search UI & sort
This commit is contained in:
+75
-38
@@ -14,6 +14,7 @@ sys.path.insert(0, str(PROJECT_ROOT / 'platform' / 'backend'))
|
||||
from db_helper import get_topic_by_id, update_topic_status, save_article
|
||||
from content_cleaner import strip_thinking, strip_ai_preface, clean_markdown_content, clean_html_content
|
||||
from prompt_loader import get_prompt, get_prompt_params
|
||||
from image_generator import insert_lead_image
|
||||
try:
|
||||
from app.core.nvidia_client import call_llm
|
||||
HAVE_LLM = True
|
||||
@@ -206,6 +207,15 @@ class Writer:
|
||||
expanded = self._expand_section(sec)
|
||||
parts.append(expanded + "\n")
|
||||
full_md = "\n".join(parts).strip()
|
||||
# 收集所有引用来源,统一添加到文末
|
||||
refs = set()
|
||||
for m in re.finditer(r'(来源:([^)]+))', full_md):
|
||||
refs.add(m.group(1).strip())
|
||||
if refs:
|
||||
# 去掉已有参考资料区,重新生成统一的
|
||||
full_md = re.sub(r'\n---\n\*\*参考资料\*\*[\s\S]*$', '', full_md).strip()
|
||||
ref_lines = "\n".join(f"- {r}" for r in sorted(refs))
|
||||
full_md += f"\n\n---\n\n**参考资料**\n{ref_lines}"
|
||||
return full_md
|
||||
|
||||
def _adapt_for_platform(self, markdown: str, platform: str) -> str:
|
||||
@@ -213,27 +223,71 @@ class Writer:
|
||||
max_c = cfg['max_chars']
|
||||
lines = markdown.split('\n')
|
||||
|
||||
if platform == "zhihu":
|
||||
result = []
|
||||
in_list = False
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
# 数据类行 → 引用格式(知乎文章中引用数据能增强可信度)
|
||||
if any(stripped.startswith(p) for p in ('据统计', '调研显示', '数据显示', '报告指出', '根据', '数据显示')):
|
||||
line = f"> {line}"
|
||||
# 列表保持原样(知乎支持 markdown 列表)
|
||||
if stripped.startswith('- ') or stripped.startswith('* '):
|
||||
if not in_list:
|
||||
result.append('')
|
||||
in_list = True
|
||||
else:
|
||||
in_list = False
|
||||
result.append(line)
|
||||
adapted = '\n'.join(result)
|
||||
# 末尾加讨论引导(知乎算法权重:互动率)
|
||||
if not any(kw in adapted for kw in ('你觉得', '你怎么看', '欢迎在评论区', '说说你的')):
|
||||
adapted += "\n\n---\n\n你觉得这个观点有道理吗?你在工作中有没有类似的经验?欢迎在评论区聊聊。"
|
||||
return adapted
|
||||
|
||||
if platform == "xiaohongshu":
|
||||
result = []
|
||||
char_count = 0
|
||||
last_was_heading = False
|
||||
for line in lines:
|
||||
if char_count >= max_c:
|
||||
break
|
||||
stripped = line.strip()
|
||||
if line.startswith('## '):
|
||||
if not last_was_heading and result:
|
||||
result.append('')
|
||||
char_count += 1
|
||||
line = f"## ✨ {line[3:]}"
|
||||
last_was_heading = True
|
||||
elif line.startswith('### '):
|
||||
if not last_was_heading and result:
|
||||
result.append('')
|
||||
char_count += 1
|
||||
line = f"### 💡 {line[4:]}"
|
||||
last_was_heading = True
|
||||
else:
|
||||
last_was_heading = False
|
||||
# 超长段落后拆行 + 每段前加点缀
|
||||
if stripped and len(stripped) > 60:
|
||||
sentences = [s.strip() for s in stripped.replace('。', '。\n').split('\n') if s.strip()]
|
||||
for s in sentences:
|
||||
if s and char_count < max_c:
|
||||
result.append(s)
|
||||
char_count += len(s)
|
||||
continue
|
||||
result.append(line)
|
||||
char_count += len(line)
|
||||
adapted = '\n'.join(result)
|
||||
if adapted.count('#') == 0:
|
||||
adapted = f"# {self.topic['title']}\n\n{adapted}"
|
||||
# 结尾加收藏引导(小红书算法权重:收藏率)
|
||||
if '收藏' not in adapted:
|
||||
adapted += "\n\n✨ 觉得有用的话点个收藏吧,下次需要的时候随时翻出来看~"
|
||||
return adapted
|
||||
|
||||
if platform == "wechat":
|
||||
result = []
|
||||
for line in lines:
|
||||
line = line.replace('我', '你')
|
||||
# 人称统一:我们→我,你们→你
|
||||
line = line.replace('我们', '我').replace('你们', '你').replace('我', '你')
|
||||
if line.startswith('### '):
|
||||
result.append(f"\n**{line[4:]}**\n")
|
||||
elif line.startswith('## '):
|
||||
@@ -338,12 +392,20 @@ class Writer:
|
||||
if not line:
|
||||
continue
|
||||
line = re.sub(r'^\d+[.、)\s]+', '', line)
|
||||
line = line.strip('*#- \t')
|
||||
line = line.strip('*#- \t"\'"''"')
|
||||
# 跳过思考/建议类输出(如"不如:"、"或者:"、"建议方案"等)
|
||||
if re.match(r'^(不如|或者|建议|推荐|参考|方案[一二三]|第[一二三]种|以[下是]|标题[一二三]|选项)', line):
|
||||
continue
|
||||
if line:
|
||||
titles.append(line)
|
||||
if titles:
|
||||
logger.info(f"标题优化 [{platform}]: {titles[0][:50]}...")
|
||||
return titles[0]
|
||||
best = titles[0][:80]
|
||||
# 如果优化后标题与原文毫无关联或过短,回退原题
|
||||
if len(best) < 4 or (len(set(best) & set(original)) < 2 and len(original) > 4):
|
||||
logger.warning(f"标题优化结果异常「{best}」,回退原文")
|
||||
return original
|
||||
logger.info(f"标题优化 [{platform}]: {best}")
|
||||
return best
|
||||
except Exception as e:
|
||||
logger.warning(f"标题优化失败: {e}")
|
||||
return original
|
||||
@@ -361,43 +423,18 @@ class Writer:
|
||||
html = template.replace("{{TITLE}}", title).replace("{{DATE}}", TODAY).replace("{{GEN_TIME}}", GEN_TIME)
|
||||
html_content = _md_parser(adapted)
|
||||
|
||||
# WeChat: insert topic-relevant image at start of body
|
||||
if platform == "wechat":
|
||||
import base64
|
||||
topic_title = self.topic.get('title', title)
|
||||
topic_field = self.topic.get('field', '')
|
||||
safe_title = topic_title.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''')
|
||||
safe_field = topic_field.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
lines = []
|
||||
chars_per_line = 24
|
||||
for i in range(0, len(safe_title), chars_per_line):
|
||||
lines.append(safe_title[i:i+chars_per_line])
|
||||
if not lines:
|
||||
lines = ['配图']
|
||||
line_y = 220 - (len(lines) - 1) * 20
|
||||
title_texts = ''.join(f'<text x="540" y="{line_y + i*55}" font-size="36" fill="#1a1a1a" font-weight="bold">{l}</text>' for i, l in enumerate(lines))
|
||||
field_text = f'<text x="540" y="{line_y + len(lines)*55 + 30}" font-size="20" fill="#98a2b3">{safe_field}</text>' if safe_field else ''
|
||||
img_svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="600" viewBox="0 0 1080 600" style="width:100%;max-width:1080px;border-radius:8px;background:linear-gradient(135deg,#f0f4ff,#e8f0fe)">
|
||||
<rect width="1080" height="600" fill="url(#bg)"/>
|
||||
<defs><linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#f0f4ff"/><stop offset="100%" style="stop-color:#e8f0fe"/></linearGradient></defs>
|
||||
<g transform="translate(540,300)" text-anchor="middle" font-family="-apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,sans-serif">
|
||||
<rect x="-60" y="-100" width="120" height="4" rx="2" fill="#409eff"/>
|
||||
{title_texts}
|
||||
{field_text}
|
||||
<text y="100" font-size="14" fill="#c0c4cc">宇之然 · 配图(可替换)</text>
|
||||
</g></svg>'''
|
||||
img_b64 = 'data:image/svg+xml;base64,' + base64.b64encode(img_svg.encode('utf-8')).decode('ascii')
|
||||
img_tag = f'<p><img src="{img_b64}" alt="{safe_title}" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
|
||||
h1_end = html_content.find('</h1>')
|
||||
if h1_end != -1:
|
||||
html_content = html_content[:h1_end + 5] + '\n' + img_tag + html_content[h1_end + 5:]
|
||||
else:
|
||||
html_content = img_tag + html_content
|
||||
# 仅插入头图(每个平台一篇一张,不过度)
|
||||
html_content = insert_lead_image(
|
||||
html_content, platform,
|
||||
title=self.topic.get('title', title),
|
||||
field=self.topic.get('field', ''),
|
||||
)
|
||||
|
||||
html = html.replace("<!-- CONTENT -->", html_content)
|
||||
|
||||
# 防御:清理可能在 LLM 输出中混入的 markdown 代码围栏和文件头
|
||||
html = re.sub(r'^```+\w*\s*\n?', '', html)
|
||||
html = re.sub(r'\n?```+\s*$', '', html)
|
||||
html = html.strip()
|
||||
|
||||
tags_html = self._get_platform_tags(platform)
|
||||
|
||||
Reference in New Issue
Block a user