Files
CateringInformationBank/extract_industry_signals.py
2026-07-19 18:11:08 +08:00

107 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Extract cross-article restaurant industry signals from cached Qwen analyses."""
from __future__ import annotations
import argparse
import json
import os
import sqlite3
import urllib.error
import urllib.request
from datetime import datetime, timedelta
from app import DB_PATH, ensure_schema, now_iso
def call_qwen(items: list[dict], model: str) -> list[dict]:
key = os.getenv("DASHSCOPE_API_KEY", "").strip()
if not key:
raise RuntimeError("尚未配置 DASHSCOPE_API_KEY")
prompt = f"""你是餐饮产业首席分析师。根据以下多篇文章的结构化分析,识别跨文章、可验证、有经营意义的行业信号。
只返回合法 JSON{{"signals":[...]}}。每个 signal 必须包含:title(短标题)、summary100-180字)、trendemerging/accelerating/stable/declining)、confidence0到1)、article_ids(至少2个证据文章ID;确实只有单篇强信号时可为1个)、implications2-4条经营启示)、tags3-6个标签)。
不要把单一品牌新闻简单改写成行业信号;合并重复主题;最多输出8条;没有充分证据就少输出。
输入:{json.dumps(items, ensure_ascii=False)}"""
body = json.dumps({
"model": model,
"messages": [
{"role": "system", "content": "你进行基于证据的餐饮行业趋势聚类,避免空泛结论。"},
{"role": "user", "content": prompt},
],
"temperature": 0.2,
"response_format": {"type": "json_object"},
}, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
data=body,
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=120) as response:
result = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
raise RuntimeError(f"千问接口返回 {exc.code}{exc.read().decode('utf-8', errors='replace')[:500]}") from exc
text = result["choices"][0]["message"]["content"].strip()
if text.startswith("```"):
text = text.strip("`").removeprefix("json").strip()
return json.loads(text).get("signals", [])
def run(days: int) -> int:
ensure_schema()
model = os.getenv("QWEN_MODEL", "qwen-plus")
cutoff = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%dT%H:%M:%S")
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
rows = conn.execute(
"""
SELECT a.id,a.title,a.published_at,x.result_json
FROM articles a JOIN ai_analyses x ON x.article_id=a.id
WHERE a.crawl_status='success' AND a.published_at>=?
ORDER BY a.published_at DESC
""",
(cutoff,),
).fetchall()
items = []
for row in rows:
analysis = json.loads(row["result_json"])
items.append({
"article_id": row["id"], "title": row["title"], "published_at": row["published_at"],
"summary": analysis.get("summary"), "key_points": analysis.get("key_points", []),
"industry_signal": analysis.get("industry_signal"), "tags": analysis.get("tags", []),
})
if not items:
print(json.dumps({"status": "skipped", "reason": "没有已分析文章"}, ensure_ascii=False))
return 0
signals = call_qwen(items, model)
signal_date = datetime.now().astimezone().date().isoformat()
timestamp = now_iso()
conn.execute("DELETE FROM industry_signals WHERE signal_date=?", (signal_date,))
for signal in signals:
conn.execute(
"""
INSERT INTO industry_signals
(signal_date,title,summary,trend,confidence,implications_json,article_ids_json,tags_json,model,created_at,updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?)
""",
(
signal_date, signal["title"], signal["summary"], signal.get("trend", "emerging"),
float(signal.get("confidence", 0.5)), json.dumps(signal.get("implications", []), ensure_ascii=False),
json.dumps(signal.get("article_ids", [])), json.dumps(signal.get("tags", []), ensure_ascii=False),
model, timestamp, timestamp,
),
)
conn.commit()
conn.close()
print(json.dumps({"status": "success", "input_articles": len(items), "signals": len(signals), "date": signal_date}, ensure_ascii=False))
return 0
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="提炼红餐文章跨文行业信号")
parser.add_argument("--days", type=int, default=30, help="聚合最近多少天的文章")
args = parser.parse_args()
raise SystemExit(run(args.days))