Initial commit: yu-zhi-ran platform with automation integration
This commit is contained in:
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
宇之然内容创作流水线(研究 → 大纲 → 撰写 → 合规优化)v2
|
||||
"""
|
||||
|
||||
import json, datetime, logging, sys, subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
PROJECT_ROOT = Path('/root/.openclaw/workspaces/yzr-yxl/projects/yu-zhi-ran')
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
DATA_DIR = PROJECT_ROOT / "automation" / "data"
|
||||
TOPICS_FILE = DATA_DIR / "sustainability_topics.json"
|
||||
LOGS_DIR = PROJECT_ROOT / "automation" / "logs"
|
||||
TODAY = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(LOGS_DIR / f"creator_{TODAY}.log"),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def select_next_topic(topic_id: str = None) -> Dict:
|
||||
"""选择并锁定要创作的选题"""
|
||||
def save_topics(topics_list):
|
||||
with open(TOPICS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(topics_list, f, ensure_ascii=False, indent=2)
|
||||
|
||||
topics = json.loads(TOPICS_FILE.read_text(encoding='utf-8'))
|
||||
|
||||
if topic_id:
|
||||
# 指定ID,尝试直接锁定
|
||||
topic = next((t for t in topics if t['id'] == topic_id), None)
|
||||
if not topic:
|
||||
raise ValueError(f"Topic {topic_id} not found")
|
||||
# 检查状态
|
||||
if topic.get('status') != 'pending' and topic.get('status') != '待处理':
|
||||
raise ValueError(f"Topic {topic_id} status is {topic.get('status')}, cannot create")
|
||||
# 加锁
|
||||
topic['lock_by'] = 'creator'
|
||||
topic['lock_at'] = datetime.datetime.now().isoformat()
|
||||
save_topics(topics)
|
||||
return topic
|
||||
|
||||
# 自动选择:优先选pending且无锁的
|
||||
def is_available(t):
|
||||
status = t.get('status')
|
||||
# 只处理 pending 或 待处理
|
||||
if status not in ['pending', '待处理']:
|
||||
return False
|
||||
# 检查锁
|
||||
lock_by = t.get('lock_by')
|
||||
if lock_by:
|
||||
# 如果有人锁了,检查是否超时(>2小时)
|
||||
lock_at_str = t.get('lock_at')
|
||||
if lock_at_str:
|
||||
try:
|
||||
lock_at = datetime.datetime.fromisoformat(lock_at_str)
|
||||
if (datetime.datetime.now() - lock_at).total_seconds() < 7200:
|
||||
return False
|
||||
except:
|
||||
pass # 解析失败,认为是有效锁
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
available = [t for t in topics if is_available(t)]
|
||||
if not available:
|
||||
raise ValueError("No available topics to create (all locked or wrong status)")
|
||||
|
||||
available.sort(key=lambda t: t.get('priority_score', 0), reverse=True)
|
||||
chosen = available[0]
|
||||
|
||||
# 锁定
|
||||
chosen['lock_by'] = 'creator'
|
||||
chosen['lock_at'] = datetime.datetime.now().isoformat()
|
||||
save_topics(topics)
|
||||
|
||||
return chosen
|
||||
|
||||
def run_step(script_name: str, topic_id: str) -> bool:
|
||||
"""运行一个流水线步骤(research/outline/writer)"""
|
||||
script_path = PROJECT_ROOT / "scripts" / script_name
|
||||
cmd = ["python3", str(script_path), "--topic-id", topic_id]
|
||||
logger.info(f"Running: {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, cwd=str(PROJECT_ROOT), capture_output=True, text=True, timeout=300)
|
||||
if result.returncode != 0:
|
||||
logger.error(f"{script_name} 失败: {result.stderr}")
|
||||
return False
|
||||
logger.info(f"{script_name} 完成: {result.stdout.strip()}")
|
||||
return True
|
||||
|
||||
def run_optimizer_step(topic_id: str) -> bool:
|
||||
"""运行合规优化步骤(只针对单个选题)"""
|
||||
script_path = PROJECT_ROOT / "scripts" / "compliance_optimizer.py"
|
||||
cmd = ["python3", str(script_path), "--topic-ids", topic_id]
|
||||
logger.info(f"Running: {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, cwd=str(PROJECT_ROOT), capture_output=True, text=True, timeout=600)
|
||||
if result.returncode != 0:
|
||||
logger.error(f"compliance_optimizer 失败: {result.stderr}")
|
||||
return False
|
||||
logger.info(f"compliance_optimizer 完成: {result.stdout.strip()}")
|
||||
return True
|
||||
|
||||
def run_pipeline(topic_id: str = None) -> Dict:
|
||||
"""运行完整流水线:研究 → 大纲 → 撰写 → 合规优化"""
|
||||
tid = None
|
||||
try:
|
||||
topic = select_next_topic(topic_id)
|
||||
tid = topic['id']
|
||||
logger.info(f"开始创作流水线: topic_id={tid}, title={topic.get('title')}")
|
||||
|
||||
# 1. 研究
|
||||
if not run_step("research.py", tid):
|
||||
return {"ok": False, "error": "research step failed"}
|
||||
|
||||
# 2. 大纲
|
||||
if not run_step("outline.py", tid):
|
||||
return {"ok": False, "error": "outline step failed"}
|
||||
|
||||
# 3. 撰写
|
||||
if not run_step("writer.py", tid):
|
||||
return {"ok": False, "error": "writer step failed"}
|
||||
|
||||
# 4. 合规优化(自动审核并标记为「待发布」)
|
||||
if not run_optimizer_step(tid):
|
||||
return {"ok": False, "error": "optimizer step failed"}
|
||||
|
||||
logger.info(f"创作流水线完成: topic_id={tid}")
|
||||
return {"ok": True, "topic_id": tid, "stdout": f"SUCCESS: Topic {tid} processed through full pipeline"}
|
||||
except Exception as e:
|
||||
logger.exception("流水线执行失败")
|
||||
return {"ok": False, "error": str(e)}
|
||||
finally:
|
||||
# 清理锁(无论成功失败)
|
||||
if tid:
|
||||
try:
|
||||
topics = json.loads(TOPICS_FILE.read_text(encoding='utf-8'))
|
||||
for t in topics:
|
||||
if t.get('id') == tid:
|
||||
# 如果成功或需要人工,保留状态,但清除锁
|
||||
t['lock_by'] = None
|
||||
t['lock_at'] = None
|
||||
break
|
||||
with open(TOPICS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(topics, f, ensure_ascii=False, indent=2)
|
||||
logger.debug(f"已清理选题锁: {tid}")
|
||||
except Exception as ex:
|
||||
logger.error(f"清理锁失败: {ex}")
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='内容创作流水线(研究→大纲→撰写→合规优化)')
|
||||
parser.add_argument('--topic-id', help='指定选题ID,不指定则自动选择待处理选题')
|
||||
args = parser.parse_args()
|
||||
|
||||
result = run_pipeline(args.topic_id)
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
sys.exit(0 if result['ok'] else 1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user