清理城市农业类别 + 修复采集器LLM直接选题
清理: - sustainability_cases.json移除GLO-001/CHN-001城市农业案例,替换为循环消费 - initial_cases.json移除case8东京垂直农场/case26城市屋顶农场 - strategy_topics_to_json.py移除B01/B05/D05三个种菜选题 - collector.py移除城市农业→循环消费映射,更新注释 - 删除fix_collector.py/test_image_gen.py/generate_images.py等遗留脚本 - 删除import_topics.py和automation/下旧版生成脚本 修复: - collector.py _generate_topic_with_llm不再依赖搜索结果,无搜索时LLM直接生成 - run()始终调用LLM,不再要求web_search_results非空 - 替换sources.yaml中已失效的RSS源(澎湃/虎嗅/中新网→36氪/少数派)
This commit is contained in:
+22
-35
@@ -59,7 +59,7 @@ class SustainabilityCase:
|
||||
"""可持续性案例"""
|
||||
id: str
|
||||
country: str
|
||||
category: str # 子领域:城市农业、零浪费生活等
|
||||
category: str # 子领域:零浪费生活、循环消费等
|
||||
title: str
|
||||
core_idea: str
|
||||
data_facts: str
|
||||
@@ -254,7 +254,6 @@ class SustainabilityCollector:
|
||||
'低碳出行': '低碳出行',
|
||||
'循环消费': '循环消费',
|
||||
'环保科技': '环保科技产品',
|
||||
'城市农业': '循环消费',
|
||||
}
|
||||
case_data['category'] = category_map.get(field, field[:4] if len(field) > 4 else field)
|
||||
|
||||
@@ -378,57 +377,47 @@ class SustainabilityCollector:
|
||||
logger.warning(f"web_search失败 {source.name}: {e}")
|
||||
return []
|
||||
|
||||
def _generate_topic_with_llm(self, search_results: List[Dict]) -> Optional[SustainabilityTopic]:
|
||||
"""用LLM从搜索结果中生成选题"""
|
||||
def _generate_topic_with_llm(self, search_results: Optional[List[Dict]] = None) -> Optional[SustainabilityTopic]:
|
||||
"""用LLM生成选题(有搜索结果时参考,无结果时直接生成)"""
|
||||
try:
|
||||
from app.core.nvidia_client import call_llm
|
||||
except ImportError:
|
||||
logger.warning("LLM不可用,跳过AI选题生成")
|
||||
return None
|
||||
|
||||
if not search_results:
|
||||
return None
|
||||
|
||||
# 整理搜索结果摘要
|
||||
summaries = []
|
||||
for r in search_results[:6]:
|
||||
summaries.append(f"- {r.get('title','')}: {r.get('content','')[:150]}")
|
||||
search_text = "\n".join(summaries)
|
||||
|
||||
# 获取已有选题做去重参考
|
||||
existing = self._get_existing_titles()
|
||||
existing_hint = ""
|
||||
if existing:
|
||||
existing_hint = f"\n以下选题已存在,请避免重复:\n" + "\n".join(f"- {t[:30]}" for t in existing[-10:])
|
||||
existing_hint = "\n已存在选题(避免重复):" + "、".join(t[:20] for t in existing[-8:])
|
||||
|
||||
# 按日期选不同类别
|
||||
categories = self.config.get("sustainability_categories", ["可持续生活"])
|
||||
day_idx = datetime.datetime.now().timetuple().tm_yday % len(categories)
|
||||
target_category = categories[day_idx]
|
||||
|
||||
prompt = f"""你是一个内容策略师。基于以下搜索结果,生成一个有价值、适合中文互联网传播的选题。
|
||||
search_section = ""
|
||||
if search_results:
|
||||
summaries = [f"- {r.get('title','')}: {r.get('content','')[:120]}" for r in search_results[:4]]
|
||||
search_section = "搜索结果参考:\n" + "\n".join(summaries) + "\n"
|
||||
|
||||
prompt = f"""你是一个内容策略师。生成一个面向中国年轻读者、有价值、适合传播的选题。
|
||||
|
||||
目标类别:{target_category}
|
||||
|
||||
搜索结果:
|
||||
{search_text}
|
||||
{search_section}
|
||||
{existing_hint}
|
||||
|
||||
请生成一个选题,输出JSON格式:
|
||||
{{
|
||||
"title": "标题(20字内,有吸引力,含核心关键词)",
|
||||
生成一个选题,直接输出JSON(不要其他文字):
|
||||
{{{{
|
||||
"title": "标题(20字内,含核心关键词,避免「新趋势」「指南」这类烂尾词)",
|
||||
"core_concept": "核心观点(一句话说清独特价值)",
|
||||
"audience_pain": "受众痛点(真实用户的困惑或需求)",
|
||||
"unique_angle": "独特视角(差异化切入点)",
|
||||
"audience_pain": "受众痛点(真实用户的困惑)",
|
||||
"unique_angle": "差异化切入点",
|
||||
"format": "内容形式(趋势洞察/实操指南/对比分析/案例解读)"
|
||||
}}
|
||||
}}}}
|
||||
|
||||
要求:
|
||||
- 标题要像人会搜索的,带领域关键词
|
||||
- 避免「新趋势」「指南」「攻略」这类同质化结尾
|
||||
- 切入点要具体,不要泛泛而谈
|
||||
- 优先考虑中国读者能实操的内容
|
||||
只输出JSON,不要其他文字。"""
|
||||
- 标题像普通人会搜索的
|
||||
- 切入点具体,不泛泛而谈
|
||||
- 优先考虑中国读者能实操的内容"""
|
||||
|
||||
try:
|
||||
resp = call_llm(prompt, temperature=0.7)
|
||||
@@ -797,10 +786,8 @@ class SustainabilityCollector:
|
||||
logger.info(f"RSS采集 {sum(1 for a in all_articles if a.get('source_name','') not in [s.name for s in self.sources if s.type=='web_search'])} 篇, "
|
||||
f"搜索采集 {len(web_search_results)} 篇")
|
||||
|
||||
# ---------------------- 第二阶段:尝试LLM选题生成 ----------------------
|
||||
llm_topic = None
|
||||
if web_search_results:
|
||||
llm_topic = self._generate_topic_with_llm(web_search_results)
|
||||
# ---------------------- 第二阶段:LLM选题生成 ----------------------
|
||||
llm_topic = self._generate_topic_with_llm(web_search_results if web_search_results else None)
|
||||
|
||||
if llm_topic and not self._is_duplicate_topic(llm_topic.title, existing_titles):
|
||||
llm_topic.created_at = datetime.datetime.now().isoformat()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Collector 修复脚本
|
||||
解决 field 和 priority_score 参数问题
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/root/openclaw-workspace/projects/yu-zhi-ran/platform/backend')
|
||||
|
||||
from app.models import Topic
|
||||
from datetime import datetime
|
||||
|
||||
def fix_topic_creation():
|
||||
"""修复选题创建时的参数问题"""
|
||||
|
||||
# 测试用例
|
||||
try:
|
||||
# 正确的参数
|
||||
topic = Topic(
|
||||
id="T001",
|
||||
title="城市农业ROI报告:20㎡阳台种菜一年,省了多少钱?",
|
||||
field="城市农业",
|
||||
priority_score=10,
|
||||
status="待处理",
|
||||
compliance_score=100,
|
||||
ready_at=None,
|
||||
published_at=None,
|
||||
platform_urls={},
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
print("✅ 选题创建成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ 选题创建失败: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
fix_topic_creation()
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
独立图片生成脚本 - 供定时任务调用
|
||||
用法: python3 generate_images.py <article_title> [platform]
|
||||
示例: python3 generate_images.py \"上海阳台种菜一年\" zhihu
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from scripts.image_generator import ImageGenerator
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("用法: python3 generate_images.py <article_title> [platform=zhihu]")
|
||||
sys.exit(1)
|
||||
|
||||
title = sys.argv[1]
|
||||
platform = sys.argv[2] if len(sys.argv) > 2 else "zhihu"
|
||||
|
||||
generator = ImageGenerator()
|
||||
|
||||
print(f"开始生成图片...")
|
||||
print(f"文章标题: {title}")
|
||||
print(f"目标平台: {platform}")
|
||||
print(f"输出目录: {generator.output_dir}")
|
||||
|
||||
try:
|
||||
files = generator.generate_all_placeholders(title, platform)
|
||||
|
||||
print(f"\n✅ 成功生成 {len(files)} 张图片:")
|
||||
for name, path in files.items():
|
||||
size_kb = path.stat().st_size // 1024
|
||||
print(f" - {name}: {path.name} ({size_kb}KB)")
|
||||
|
||||
print(f"\n📁 图片保存在: {generator.output_dir}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"\n❌ 图片生成失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,220 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将 content/ideas/ 目录下的 Markdown 选题文件转换为并导入数据库
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
# 数据库导入
|
||||
try:
|
||||
from app.database import SessionLocal
|
||||
from app.models import Topic as DBTopic
|
||||
HAVE_DB = True
|
||||
except ImportError as e:
|
||||
HAVE_DB = False
|
||||
print(f"[Warning] Database import failed: {e}")
|
||||
|
||||
IDEAS_DIR = PROJECT_ROOT / "content" / "ideas"
|
||||
DATA_DIR = PROJECT_ROOT / "automation" / "data"
|
||||
OUTPUT_FILE = DATA_DIR / "sustainability_topics.json" # 仅备份,不再作为主数据源
|
||||
|
||||
def extract_field(content, field_name):
|
||||
patterns = [
|
||||
rf"\*\*{re.escape(field_name)}\*\*\s*[::]\s*(.+?)(?:\n|$)",
|
||||
rf"{re.escape(field_name)}\s*[::]\s*(.+?)(?:\n|$)",
|
||||
]
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, content, re.MULTILINE)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
return None
|
||||
|
||||
def parse_evaluation_matrix(content):
|
||||
scores = {}
|
||||
lines = content.split('\n')
|
||||
for line in lines:
|
||||
if '|' in line and '---' not in line and '维度' not in line:
|
||||
parts = [p.strip() for p in line.split('|')]
|
||||
if len(parts) >= 3:
|
||||
dimension = parts[1]
|
||||
score_str = parts[2]
|
||||
try:
|
||||
score = int(score_str)
|
||||
scores[dimension] = score
|
||||
except:
|
||||
pass
|
||||
if '**总分**' in line:
|
||||
total_match = re.search(r'\*\*总分\*\*\s*\|\s*\*\*(\d+)\*\*', line)
|
||||
if total_match:
|
||||
scores['总分'] = int(total_match.group(1))
|
||||
return scores
|
||||
|
||||
def md_to_topic(md_path):
|
||||
with open(md_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
|
||||
title = title_match.group(1).strip() if title_match else md_path.stem
|
||||
|
||||
field = extract_field(content, '领域') or '可持续生活系统'
|
||||
format_type = extract_field(content, '形式') or '趋势洞察 + 实操指南'
|
||||
core_concept = extract_field(content, '核心观点') or ''
|
||||
audience_pain = extract_field(content, '受众痛点') or ''
|
||||
unique_angle = extract_field(content, '独特角度') or ''
|
||||
estimated_days = extract_field(content, '预估完成时间')
|
||||
priority_str = extract_field(content, '优先级') or '中'
|
||||
publish_date = extract_field(content, '预计发布时间')
|
||||
status = extract_field(content, '状态') or '待处理'
|
||||
|
||||
priority_map = {'高': 10, '中': 7, '低': 4}
|
||||
priority_score = priority_map.get(priority_str, 5)
|
||||
|
||||
evaluation = parse_evaluation_matrix(content)
|
||||
total_score = evaluation.get('总分', 0)
|
||||
|
||||
# 生成 ID:从文件名提取前缀数字,如果没有则使用标题哈希
|
||||
stem = md_path.stem # e.g., "001-上海阳台种菜一年"
|
||||
m = re.match(r'^(\d{3})', stem)
|
||||
if m:
|
||||
num = m.group(1)
|
||||
topic_id = f'M{num}' # M 系列表示手动导入
|
||||
else:
|
||||
import hashlib
|
||||
short = hashlib.md5(title.encode()).hexdigest()[:6].upper()
|
||||
topic_id = f'M{short}'
|
||||
|
||||
return {
|
||||
"id": topic_id,
|
||||
"title": title,
|
||||
"field": field,
|
||||
"format": format_type,
|
||||
"core_concept": core_concept,
|
||||
"audience_pain": audience_pain,
|
||||
"unique_angle": unique_angle,
|
||||
"priority": priority_str,
|
||||
"priority_score": priority_score,
|
||||
"total_score": total_score,
|
||||
"status": status,
|
||||
"cases": [],
|
||||
"source_file": md_path.name,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"updated_at": datetime.now().isoformat(),
|
||||
"ready_at": publish_date,
|
||||
"published_at": None,
|
||||
"compliance_score": 100,
|
||||
"platform_urls": {}
|
||||
}
|
||||
|
||||
def save_to_db(topic_dict):
|
||||
if not HAVE_DB:
|
||||
print("数据库不可用,跳过入库")
|
||||
return False
|
||||
db = SessionLocal()
|
||||
try:
|
||||
existing = db.query(DBTopic).filter(DBTopic.id == topic_dict['id']).first()
|
||||
if existing:
|
||||
# 更新字段
|
||||
for field in ['title', 'field', 'format', 'core_concept', 'audience_pain', 'unique_angle', 'priority', 'priority_score', 'total_score', 'status', 'cases', 'source_file', 'compliance_score', 'platform_urls']:
|
||||
setattr(existing, field, topic_dict.get(field, getattr(existing, field)))
|
||||
if topic_dict.get('ready_at'):
|
||||
try:
|
||||
existing.ready_at = datetime.strptime(topic_dict['ready_at'], '%Y-%m-%d').date()
|
||||
except:
|
||||
pass
|
||||
existing.updated_at = datetime.now()
|
||||
else:
|
||||
# 新增
|
||||
new_topic = DBTopic(
|
||||
id=topic_dict['id'],
|
||||
title=topic_dict['title'],
|
||||
field=topic_dict['field'],
|
||||
format=topic_dict['format'],
|
||||
core_concept=topic_dict['core_concept'],
|
||||
audience_pain=topic_dict['audience_pain'],
|
||||
unique_angle=topic_dict['unique_angle'],
|
||||
priority=topic_dict['priority'],
|
||||
priority_score=topic_dict['priority_score'],
|
||||
total_score=topic_dict['total_score'],
|
||||
status=topic_dict['status'],
|
||||
cases=topic_dict['cases'],
|
||||
source_file=topic_dict['source_file'],
|
||||
ready_at=datetime.strptime(topic_dict['ready_at'], '%Y-%m-%d').date() if topic_dict.get('ready_at') else None,
|
||||
published_at=None,
|
||||
compliance_score=topic_dict['compliance_score'],
|
||||
platform_urls=topic_dict['platform_urls'],
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
db.add(new_topic)
|
||||
db.commit()
|
||||
return True
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"数据库保存失败: {e}")
|
||||
return False
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def main():
|
||||
if not IDEAS_DIR.exists():
|
||||
print(f"错误:选题目录不存在 {IDEAS_DIR}")
|
||||
return
|
||||
|
||||
md_files = []
|
||||
for f in IDEAS_DIR.glob("*.md"):
|
||||
if f.name == "README.md":
|
||||
continue
|
||||
if f.name.endswith('-research.md') or f.name.endswith('-compliance.md'):
|
||||
continue
|
||||
if re.match(r'^\d{3}-.+\.md$', f.name):
|
||||
md_files.append(f)
|
||||
|
||||
if not md_files:
|
||||
print("未找到选题文件")
|
||||
return
|
||||
|
||||
print(f"找到 {len(md_files)} 个选题文件,开始导入...")
|
||||
|
||||
topics = []
|
||||
for md_file in sorted(md_files):
|
||||
print(f" 处理: {md_file.name}")
|
||||
topic = md_to_topic(md_file)
|
||||
topics.append(topic)
|
||||
print(f" 标题: {topic['title']}")
|
||||
print(f" ID: {topic['id']}")
|
||||
print(f" 总分: {topic['total_score']}")
|
||||
print(f" 状态: {topic['status']}")
|
||||
|
||||
# 保存 JSON 备份
|
||||
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(topics, f, ensure_ascii=False, indent=2)
|
||||
print(f"\n✅ 已备份选题到 {OUTPUT_FILE}")
|
||||
|
||||
# 导入数据库
|
||||
if HAVE_DB:
|
||||
success_count = 0
|
||||
for t in topics:
|
||||
if save_to_db(t):
|
||||
success_count += 1
|
||||
print(f"✅ 已导入 {success_count}/{len(topics)} 个选题到数据库")
|
||||
else:
|
||||
print("⚠️ 数据库不可用,仅生成了 JSON 备份")
|
||||
|
||||
# 统计
|
||||
ready_topics = [t for t in topics if t['status'] != '已发布']
|
||||
if ready_topics:
|
||||
avg_score = sum(t['total_score'] for t in ready_topics) / len(ready_topics)
|
||||
print(f"📊 可用选题数: {len(ready_topics)}")
|
||||
print(f"🎯 平均评分: {avg_score:.1f}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -7,11 +7,9 @@ topics = [
|
||||
{"id":"A03","title":"数字游民签证全解析:30个国家政策对比,中国护照能去哪些?","field":"未来工作方式","format":"对比分析 + 实操指南","core_concept":"分析爱沙尼亚/葡萄牙/巴厘岛等30国游民签证,结合中国护照限制,给出签证+保险+税务+社群的完整路线","audience_pain":"想地理套利但被签证和社保困扰","unique_angle":"不是简单列出签证,而是给出中国护照持有者的可行组合方案(如泰国+大马+巴厘岛)","priority":"高","priority_score":10,"total_score":51,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"A04","title":"一人公司实验:从创意到营收的365天日志","field":"未来工作方式","format":"实践日志 + 方法论","core_concept":"基于Indie Hackers案例,结合中国孤独创业现状,提供MVP设计、现金流管理、法律合规的一站式指南","audience_pain":"想单干但怕失败、缺启动资金、不懂营销","unique_angle":"真实日志形式,展示完整从0到营收的过程,不美化","priority":"高","priority_score":10,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"A05","title":"AI时代的技能组合:什么技能值得投入10年?","field":"未来工作方式","format":"趋势分析 + 个人规划","core_concept":"基于WEF未来技能报告,划分4个技能维度(AI强化型、AI无法替代、复合型、过时型),帮中国职场人识别护城河技能","audience_pain":"学什么都不放心,怕投入时间后AI又取代","unique_angle":"将全球宏观报告转化为个人技能地图,提供可视化工具","priority":"中","priority_score":7,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"B01","title":"城市农业ROI报告:20㎡阳台种菜一年,省了多少钱?","field":"可持续生活系统","format":"数据分析 + 实操指南","core_concept":"对比东京垂直农场与国内空间限制,精选高ROI蔬菜品种,智能设备自动灌溉,给出详细成本核算和品种推荐","audience_pain":"想种但怕麻烦、怕亏本、不知道种什么","unique_angle":"用财务思维算账(投入/产出/时间成本),打破'种菜必须有地'的思维","priority":"高","priority_score":10,"total_score":52,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"B02","title":"零浪费家庭实验:一年只产100L垃圾,可能吗?","field":"可持续生活系统","format":"实践实验 + 方法论","core_concept":"对比瑞典零浪费城市,针对中国垃圾分类困境,提供垃圾追踪表、替代方案数据库、社区互助网络","audience_pain":"想环保但觉得做不到、不知道从哪减","unique_angle":"极限实验(100L/年)+ 可执行步骤(从塑料减量开始),不理想化","priority":"高","priority_score":10,"total_score":51,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"B03","title":"低碳生活账单:用3年省了8万,碳足迹降了60%","field":"可持续生活系统","format":"数据分析 + 案例研究","core_concept":"对比欧洲碳税政策,从交通(电动车+共享)、饮食(植物为主)、消费(二手优先)三个维度,展示真实账单变化","audience_pain":"觉得低碳=更贵,不敢尝试","unique_angle":"用财务数据说话(省8万),打破'环保=烧钱'误解","priority":"高","priority_score":10,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"B04","title":"循环消费实战:10件物品,用3年省了2万","field":"可持续生活系统","format":"实操指南 + 案例清单","core_concept":"对比法国二手强制法与中国闲鱼文化,提供购买决策树(买新/二手/租)、延长寿命技巧、转卖策略","audience_pain":"想买二手但怕质量差、怕麻烦","unique_angle":"10件物品的具体交易记录和对比(手机、相机、家具等),可复制","priority":"中","priority_score":7,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"B05","title":"社区菜园指南:如何推动小区5户邻居共建共享","field":"可持续生活系统","format":"方法论 + 实操步骤","core_concept":"对比纽约社区花园政策与中国物业协调难题,提供法律风险(物权)、利益分配机制、技术方案(分区+智能)","audience_pain":"想组织但怕纠纷、不懂法律、协调不了邻居","unique_angle":"从1个友好小区试点开始,成功后复制,降低风险","priority":"高","priority_score":10,"total_score":49,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"C01","title":"第二大脑2.0:用DeepSeek+本地向量库建立私有知识系统","field":"个人知识工厂","format":"技术指南 + 实操案例","core_concept":"对比Obsidian+RAG海外实践,针对国内云服务担忧,提供数据主权、隐私保护、无缝检索、AI问答的本地化方案","audience_pain":"想系统化知识但担心云存储安全,怕复杂","unique_angle":"强调数据主权,从API调用到本地部署的渐进路线","priority":"中","priority_score":7,"total_score":52,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"C02","title":"PKM极简实践:PARA系统在Notion上的落地模板","field":"个人知识工厂","format":"模板分享 + 方法论","core_concept":"将Tiago Forte的PARA体系简化为3个核心文件夹,每周10分钟维护,AI辅助整理,让中国人真正用起来","audience_pain":"学了方法坚持不了,工具复杂难上手","unique_angle":"极简版(4个区)+ 每日5分钟习惯养成,降低门槛","priority":"中","priority_score":7,"total_score":51,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"C03","title":"费曼学习法AI增强:如何让AI帮你'教'懂一个概念","field":"个人知识工厂","format":"方法论 + 实践工具","core_concept":"结合经典费曼技巧与AI工具,三步法(AI简化→自我复述→Gap识别)+ 输出倒逼输入","audience_pain":"学东西记不住,自以为懂了但其实不会","unique_angle":"用AI当'测试官',验证你的理解深度,非被动接受知识","priority":"中","priority_score":7,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
@@ -21,7 +19,7 @@ topics = [
|
||||
{"id":"D02","title":"数字排毒月:戒掉微信/抖音后,生活发生了什么","field":"科技人文交叉","format":"实践实验 + 效果分析","core_concept":"对比硅谷禅修热与中国'失联恐惧',采用渐进式戒断(无屏时段)+ 替代活动 + 社交边界管理","audience_pain":"想减少屏幕时间但又怕错过重要信息,自律困难","unique_angle":"真实实验记录(not理论),展示戒断前后的生活变化数据","priority":"中","priority_score":7,"total_score":50,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"D03","title":"银发科技报告:给爸妈装智能设备,学到的5个设计原则","field":"科技人文交叉","format":"设计原则 + 案例","core_concept":"对比日本适老化设计与国产'适老模式'鸡肋,提炼简化选项、物理反馈、容错设计、情感连接的具体方案","audience_pain":"给父母买智能设备但他们不用,功能复杂","unique_angle":"不是推荐产品,而是总结5个设计原则,让读者自己改造设备","priority":"中","priority_score":7,"total_score":49,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"D04","title":"儿童数字素养课:10岁儿子的AI启蒙12周","field":"科技人文交叉","format":"教育日志 + 方法论","core_concept":"对比芬兰AI教育与国内家长'禁止接触'心态,通过每周1次'AI家庭时间',培养批判性思维和创造力","audience_pain":"不知如何让孩子正确认识AI,怕沉迷又怕脱节","unique_angle":"真实父子12周项目记录,提供可复制的课程大纲","priority":"中","priority_score":7,"total_score":48,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()},
|
||||
{"id":"D05","title":"科技与自然共生:如何用AI让阳台农场更'自然'","field":"科技人文交叉","format":"理念 + 实操方案","core_concept":"对比荷兰智能温室与中国人'回归原始'误区,实现技术隐形化(传感器+提醒)+ 自然反馈闭环 + 人工仪式感","audience_pain":"想用科技但又怕失去'自然感',追求矛盾","unique_angle":"技术与情感连接的平衡方案,AI只做幕后,人工保留仪式","priority":"高","priority_score":10,"total_score":48,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()}
|
||||
{"id":"D06","title":"AI时代的隐私悖论:便利与安全的平衡术","field":"科技人文交叉","format":"趋势分析 + 实用指南","core_concept":"分析AI工具便利性背后个人数据的流向,提供普通用户可操作的数据保护策略","audience_pain":"想用AI又担心隐私,不知道如何保护自己","unique_angle":"不是恐吓式说教,而是给出'能做的10件事'的清单","priority":"中","priority_score":7,"total_score":46,"status":"待处理","cases":[],"source_file":"strategy/全球-本土比较研究与全新内容战略规划-2026-04-15.md","created_at":datetime.datetime.now().isoformat()}
|
||||
]
|
||||
|
||||
with open(OUTPUT_FILE:= '/root/openclaw-workspace/projects/yu-zhi-ran/automation/data/sustainability_topics.json', 'w', encoding='utf-8') as f:
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""测试图片生成器"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from scripts.image_generator import ImageGenerator
|
||||
|
||||
def main():
|
||||
print("开始测试图片生成...")
|
||||
generator = ImageGenerator()
|
||||
print(f"输出目录: {generator.output_dir}")
|
||||
|
||||
try:
|
||||
files = generator.generate_all_placeholders("测试文章标题:上海阳台种菜一年", "zhihu")
|
||||
print(f"✅ 成功生成 {len(files)} 张图片:")
|
||||
for name, path in files.items():
|
||||
size_kb = path.stat().st_size // 1024
|
||||
print(f" - {name}: {path.name} ({size_kb}KB)")
|
||||
print(f"图片保存在: {generator.output_dir}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"❌ 生成失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user