Fix calendar filter, module triggers, API cleanup, consolidate get_current_admin, fix LLM schema

This commit is contained in:
Yuzhiran Dev
2026-05-20 14:15:00 +08:00
parent 498165440f
commit 0a8fbda4fd
23 changed files with 118 additions and 155 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
可持续性内容收集脚本
每天凌晨5:00运行,收集全球可持续性趋势信息,提炼选题和案例
内容采集:趋势抓取 → 选题生成 → 存入选题库
收集热点趋势信息,经LLM分析后生成选题建议并存入数据库
"""
import os
+4
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
"""
合规审查:文章合规检查 → LLM迭代修复
从articles表读取待审文章,进行合规评分;不合格文章由LLM修复(最多3次),通过后更新选题状态为待发布
"""
import json, datetime, logging, sys, re
from pathlib import Path
from typing import Dict, List, Optional, Tuple
+2 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""
宇之然内容创作流水线研究 → 大纲 → 撰写 → 合规优化)v3 - DB version
内容创作流水线研究 → 大纲 → 撰写 → 合规审查
基于选题ID,依次执行research/outline/writer各阶段,创作三平台文章并存入articles表
"""
import json, datetime, logging, sys, subprocess
+7
View File
@@ -4,10 +4,17 @@
"""
import sys
import os
from pathlib import Path
from datetime import datetime, date
from typing import Optional, Dict, List
# 加载 .env(在 scripts/ 目录下运行时需要)
_env_path = Path(__file__).parent.parent / 'platform' / 'backend' / '.env'
if _env_path.exists():
from dotenv import load_dotenv
load_dotenv(_env_path)
# 添加项目根和 backend 路径
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
+4
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
"""
选题研究:信息收集与结构化整理
基于选题方向,收集相关数据/案例/趋势,输出结构化研究笔记供大纲生成使用
"""
import json, datetime, logging, sys, re
from pathlib import Path
from typing import Dict, List
+2 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""
选题引擎 v2:多趋势加权匹配 + 趋势缺口检测 + 新选题生成
选题引擎:多趋势加权匹配 + 趋势缺口检测 + 新选题生成
结合采集到的热点趋势和当前选题库,推荐最优选题并检测趋势缺口
"""
import sys, json, logging
+39 -14
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
"""
平台适配文章撰写
根据大纲和平台配置(字数/格式/配图要求),为知乎/公众号/小红书各平台生成适配内容
"""
import json, datetime, logging, sys, re
from pathlib import Path
from typing import Dict, List
@@ -92,15 +96,17 @@ class Writer:
if line.startswith("# "):
if current:
sections.append(current)
current = {"level": 1, "title": line[2:].strip(), "content": ""}
current = {"level": 1, "title": line[2:].strip(), "content": "", "section_type": "normal"}
elif line.startswith("## "):
if current:
sections.append(current)
current = {"level": 2, "title": line[3:].strip(), "content": ""}
title = line[3:].strip()
stype = "noise" if title in ("文章大纲", "大纲", "文章结构", "结构") else "normal"
current = {"level": 2, "title": title, "content": "", "section_type": stype}
elif line.startswith("### "):
if current:
sections.append(current)
current = {"level": 3, "title": line[4:].strip(), "content": ""}
current = {"level": 3, "title": line[4:].strip(), "content": "", "section_type": "normal"}
else:
if current and line.strip():
current['content'] = current.get('content', '') + line + "\n"
@@ -112,7 +118,13 @@ class Writer:
def _clean_markdown(text: str) -> str:
lines = text.split('\n')
cleaned = []
in_code_fence = False
for line in lines:
if line.strip().startswith('```'):
in_code_fence = not in_code_fence
continue
if in_code_fence:
continue
line = re.sub(r'^#{1,6}\s+', '', line)
line = re.sub(r'^[\-\*\+]\s+', '', line)
line = re.sub(r'^\d+[\.\)]\s+', '', line)
@@ -133,12 +145,19 @@ class Writer:
return True
return False
def _is_bullet_only(self, text: str) -> bool:
"""检查内容是否主要是要点列表(大纲格式),需要 LLM 展开"""
lines = [l.strip() for l in text.split('\n') if l.strip()]
if not lines:
return False
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:
content = section.get('content', '').strip()
if len(content) > 200:
return content
if HAVE_LLM and len(content) < 150:
logger.info(f"使用 LLM 扩写章节: {section['title']}")
# 大纲要点格式(>40% 行以 -/*/** 开头)应始终由 LLM 展开为连贯段落
if HAVE_LLM and self._is_bullet_only(content):
logger.info(f"使用 LLM 扩写章节(要点→段落): {section['title']}")
prompt = f"""你是一个资深作者,正在写一篇关于「{self.topic['title']}」的文章。请写「{section['title']}」这一节。
今天日期:{datetime.datetime.now().strftime('%Y年%m月%d')}
@@ -147,7 +166,7 @@ class Writer:
{content}
【输出要求】
输出3-5段纯粹、流畅的段落文字,共400-800字。
输出3-6段纯粹、流畅的段落文字,每节内容根据平台需求控制在200-800字之间
格式:
- 禁止任何标题/列表/格式标记(#、-、*、1.、**等)
@@ -187,7 +206,8 @@ class Writer:
text += ''
sentences.append(text)
if sentences:
return ' '.join(sentences)
result = ' '.join(sentences)
return self._clean_markdown(result)
return ''
def generate_full_markdown(self) -> str:
@@ -200,6 +220,12 @@ class Writer:
if expanded:
parts.append(expanded + "\n")
continue
# 跳过大纲结构噪音节点
title_stripped = sec['title'].strip()
if title_stripped in ('文章大纲', '大纲', '文章结构', '结构'):
continue
if sec.get('section_type') == 'noise':
continue
heading = f"{'#' * sec['level']} {sec['title']}"
parts.append(heading)
if sec.get('content'):
@@ -246,11 +272,6 @@ class Writer:
else:
result.append(line)
adapted = '\n'.join(result)
if len(adapted) > max_c:
adapted = adapted[:max_c]
last = max(adapted.rfind(''), adapted.rfind('\n'), adapted.rfind(''))
if last > max_c // 2:
adapted = adapted[:last + 1]
return adapted
return markdown
@@ -429,6 +450,10 @@ class Writer:
html = html.replace("<!-- CONTENT -->", html_content)
# 防御:清理可能在 LLM 输出中混入的 markdown 代码围栏和文件头
html = re.sub(r'^```+\w*\s*\n?', '', html)
html = html.strip()
tags_html = self._get_platform_tags(platform)
if tags_html:
html = html.replace("<!-- TAGS -->", tags_html)