feat: AI news summarization - top 10 articles with 500-word Chinese briefs
This commit is contained in:
118
scripts/summarize_news.py
Normal file
118
scripts/summarize_news.py
Normal file
@ -0,0 +1,118 @@
|
||||
"""Use DeepSeek to summarize top news into 500-word Chinese briefs."""
|
||||
import json, os, requests
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
API_KEY = "sk-bbca4a0380d549389f0d27cdea0b5228"
|
||||
API_URL = "https://api.deepseek.com/v1/chat/completions"
|
||||
MODEL = "deepseek-v4-flash"
|
||||
|
||||
NEWS_FILE = "/var/www/nav/data/news.json"
|
||||
OUTPUT = "/var/www/nav/data/news_summary.json"
|
||||
tz = timezone(timedelta(hours=8))
|
||||
|
||||
|
||||
def load_raw_news():
|
||||
if not os.path.exists(NEWS_FILE):
|
||||
return []
|
||||
with open(NEWS_FILE) as f:
|
||||
data = json.load(f)
|
||||
return data.get("items", [])
|
||||
|
||||
|
||||
def summarize(items):
|
||||
"""Send raw news to DeepSeek and get summarized articles."""
|
||||
# Build a condensed input for the AI (titles + summaries)
|
||||
news_text = ""
|
||||
for i, item in enumerate(items[:20], 1):
|
||||
title = item.get("title", "")
|
||||
summary = item.get("summary", "")[:150].replace("<", "").replace(">", "")
|
||||
news_text += f"{i}. [{item['source']}] {title}\n {summary}\n\n"
|
||||
|
||||
prompt = f"""你是一个新闻整理助手。以下是来自BBC世界新闻、BBC中文、Hacker News的最新资讯。
|
||||
|
||||
请从以上内容中精选出最重要的10条新闻,每条用中文写成一段300-500字的精炼摘要。
|
||||
要求:
|
||||
1. 选择真正重要、有信息量的新闻(优先世界大事、科技突破、财经动向)
|
||||
2. 每条摘要独立、完整,包含核心事实和背景
|
||||
3. 语言简洁有力,适合快速阅读
|
||||
4. 按重要性排序
|
||||
5. 每条附上来源标签
|
||||
|
||||
输出格式为JSON数组:
|
||||
[
|
||||
{{
|
||||
"rank": 1,
|
||||
"title": "精炼标题(20字以内)",
|
||||
"source": "BBC World",
|
||||
"summary": "300-500字中文摘要..."
|
||||
}},
|
||||
...
|
||||
]
|
||||
|
||||
只输出JSON,不要其他内容。
|
||||
|
||||
今日要闻原始数据:
|
||||
{news_text}"""
|
||||
|
||||
resp = requests.post(
|
||||
API_URL,
|
||||
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
|
||||
json={
|
||||
"model": MODEL,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"temperature": 0.5,
|
||||
"max_tokens": 6000,
|
||||
},
|
||||
timeout=120,
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
print(f"API error: {resp.status_code} {resp.text[:200]}")
|
||||
return None
|
||||
|
||||
result = resp.json()
|
||||
content = result["choices"][0]["message"]["content"]
|
||||
|
||||
# Parse JSON from response
|
||||
content = content.strip()
|
||||
if content.startswith("```"):
|
||||
content = content.split("\n", 1)[1]
|
||||
content = content.rsplit("\n", 1)[0]
|
||||
if content.endswith("```"):
|
||||
content = content[:-3]
|
||||
content = content.strip()
|
||||
|
||||
try:
|
||||
articles = json.loads(content)
|
||||
except:
|
||||
print(f"JSON parse failed, content: {content[:300]}")
|
||||
return None
|
||||
|
||||
return articles
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
now = datetime.now(tz).strftime("%Y-%m-%d %H:%M")
|
||||
items = load_raw_news()
|
||||
if not items:
|
||||
print("No news data")
|
||||
exit(0)
|
||||
|
||||
print(f"Loaded {len(items)} raw news items. Summarizing...")
|
||||
articles = summarize(items)
|
||||
|
||||
if not articles:
|
||||
print("Summarization failed")
|
||||
exit(1)
|
||||
|
||||
result = {
|
||||
"updated_at": now,
|
||||
"total": len(articles),
|
||||
"articles": articles,
|
||||
}
|
||||
|
||||
os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
|
||||
with open(OUTPUT, "w", encoding="utf-8") as f:
|
||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"Written {len(articles)} summarized articles to {OUTPUT}")
|
||||
Reference in New Issue
Block a user