修复采集器流程顺序: 采集→分析→LLM总结
- _generate_topic_with_llm 改名为 _generate_topics_with_llm - 支持同时接收已分析的案例+搜索结果作为LLM上下文 - run()顺序改为: 采集→RSS提炼案例→LLM基于全部数据生成→降级回退 - LLM现在在流程末尾做总结生成,而非开头替代搜索
This commit is contained in:
+31
-33
@@ -377,13 +377,13 @@ class SustainabilityCollector:
|
||||
logger.warning(f"web_search失败 {source.name}: {e}")
|
||||
return []
|
||||
|
||||
def _generate_topic_with_llm(self, search_results: Optional[List[Dict]] = None) -> Optional[SustainabilityTopic]:
|
||||
"""用LLM生成选题(有搜索结果时参考,无结果时直接生成)"""
|
||||
def _generate_topics_with_llm(self, cases: List[SustainabilityCase] = None, search_results: List[Dict] = None) -> List[SustainabilityTopic]:
|
||||
"""用LLM基于采集数据生成选题(数据充分时精确生成,无数据时凭知识生成)"""
|
||||
try:
|
||||
from app.core.nvidia_client import call_llm
|
||||
except ImportError:
|
||||
logger.warning("LLM不可用,跳过AI选题生成")
|
||||
return None
|
||||
return []
|
||||
|
||||
existing = self._get_existing_titles()
|
||||
existing_hint = ""
|
||||
@@ -394,30 +394,28 @@ class SustainabilityCollector:
|
||||
day_idx = datetime.datetime.now().timetuple().tm_yday % len(categories)
|
||||
target_category = categories[day_idx]
|
||||
|
||||
search_section = ""
|
||||
data_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"
|
||||
summaries = [f"- {r.get('title','')}: {r.get('content','')[:100]}" for r in search_results[:5]]
|
||||
data_section += "搜索结果:\n" + "\n".join(summaries) + "\n"
|
||||
if cases:
|
||||
case_lines = [f"- {c.title[:40]}({c.category})" for c in cases[:5]]
|
||||
data_section += "\n采集案例:\n" + "\n".join(case_lines) + "\n"
|
||||
|
||||
prompt = f"""你是一个内容策略师。生成一个面向中国年轻读者、有价值、适合传播的选题。
|
||||
prompt = f"""你是一个内容策略师。基于以下信息,为「{target_category}」类别生成一个高质量选题。
|
||||
|
||||
目标类别:{target_category}
|
||||
{search_section}
|
||||
{data_section if data_section else "(当前无实时采集数据,请基于你对中文互联网趋势的了解直接生成)"}
|
||||
{existing_hint}
|
||||
|
||||
生成一个选题,直接输出JSON(不要其他文字):
|
||||
输出一个选题,格式JSON:
|
||||
{{{{
|
||||
"title": "标题(20字内,含核心关键词,避免「新趋势」「指南」这类烂尾词)",
|
||||
"core_concept": "核心观点(一句话说清独特价值)",
|
||||
"audience_pain": "受众痛点(真实用户的困惑)",
|
||||
"title": "标题(20字内,含核心关键词)",
|
||||
"core_concept": "核心观点(一句话)",
|
||||
"audience_pain": "受众痛点",
|
||||
"unique_angle": "差异化切入点",
|
||||
"format": "内容形式(趋势洞察/实操指南/对比分析/案例解读)"
|
||||
}}}}
|
||||
|
||||
要求:
|
||||
- 标题像普通人会搜索的
|
||||
- 切入点具体,不泛泛而谈
|
||||
- 优先考虑中国读者能实操的内容"""
|
||||
只输出JSON。"""
|
||||
|
||||
try:
|
||||
resp = call_llm(prompt, temperature=0.7)
|
||||
@@ -431,7 +429,7 @@ class SustainabilityCollector:
|
||||
topic = SustainabilityTopic(
|
||||
id=topic_id,
|
||||
title=data.get("title", f"{target_category}新观察"),
|
||||
cases=[],
|
||||
cases=[c.id for c in (cases or [])[:3]],
|
||||
audience="城市焦虑青年(26-35岁)",
|
||||
china_pain_points=data.get("audience_pain", ""),
|
||||
localization_solution="文章中将提供具体可执行的建议",
|
||||
@@ -456,10 +454,10 @@ class SustainabilityCollector:
|
||||
platform_urls={}
|
||||
)
|
||||
logger.info(f"LLM生成选题: {topic.title}")
|
||||
return topic
|
||||
return [topic]
|
||||
except Exception as e:
|
||||
logger.warning(f"LLM选题生成失败: {e}")
|
||||
return None
|
||||
return []
|
||||
|
||||
def analyze_article(self, article: Dict) -> Optional[SustainabilityCase]:
|
||||
"""分析文章内容,提炼案例"""
|
||||
@@ -786,24 +784,24 @@ 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 = 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()
|
||||
llm_topic.lock_by = None
|
||||
llm_topic.lock_at = None
|
||||
llm_topic.status = "待处理"
|
||||
self.new_topics.append(llm_topic)
|
||||
logger.info(f"✅ LLM生成选题: {llm_topic.title}")
|
||||
|
||||
# ---------------------- 第三阶段:RSS文章提炼案例 ----------------------
|
||||
# ---------------------- 第二阶段:RSS文章提炼案例 ----------------------
|
||||
rss_articles = [a for a in all_articles if a not in web_search_results]
|
||||
for article in rss_articles[:15]:
|
||||
case = self.analyze_article(article)
|
||||
if case:
|
||||
self.new_cases.append(case)
|
||||
|
||||
# ---------------------- 第三阶段:LLM基于采集数据生成选题 ----------------------
|
||||
llm_topics = self._generate_topics_with_llm(cases=self.new_cases, search_results=web_search_results)
|
||||
for topic in llm_topics:
|
||||
if not self._is_duplicate_topic(topic.title, existing_titles):
|
||||
topic.created_at = datetime.datetime.now().isoformat()
|
||||
topic.lock_by = None
|
||||
topic.lock_at = None
|
||||
topic.status = "待处理"
|
||||
self.new_topics.append(topic)
|
||||
logger.info(f"✅ LLM生成选题: {topic.title}")
|
||||
|
||||
# ---------------------- 第四阶段:降级策略 ----------------------
|
||||
if not self.new_topics and len(self.new_cases) < 2:
|
||||
logger.warning(f"LLM选题和RSS案例均不足,启动本地案例降级")
|
||||
|
||||
Reference in New Issue
Block a user