v1.1.0: AI分析历史记录 + 数据源详情页(/analysis/<id>),展示大模型参考的RAG新闻/概况/指标/评级/持仓/提示词

This commit is contained in:
2026-08-19 21:00:20 +08:00
parent b0fece63ff
commit 4a9ea9ddd7
8 changed files with 273 additions and 10 deletions
+58 -1
View File
@@ -59,6 +59,7 @@ def _rag_news(code, stock_name, query_text, top_k=6):
"date": m.get("date", ""),
"sentiment": m.get("sentiment", 0),
"text": h.get("document", "")[:400],
"distance": round(h.get("distance", 0), 3),
})
return out
except Exception as e:
@@ -140,7 +141,8 @@ def _build_prompt(stock, ind, news_hits, profile, ratings, holdings, score, focu
def generate_report_sync(code, focus=""):
"""同步生成报告(后台线程调用)"""
"""同步生成报告(后台线程调用),并记录历史 + 数据源"""
import json
stock = query_one("SELECT * FROM stocks WHERE code=?", (code,))
if not stock:
return {"error": "股票不存在"}
@@ -150,6 +152,17 @@ def generate_report_sync(code, focus=""):
profile = _rag_profile(code)
ratings, holdings = _inst_summary(code)
prompt = _build_prompt(stock, ind, hits, profile, ratings, holdings, score, focus)
# 记录大模型参考的数据源(供详情页展示)
sources = {
"focus": focus,
"score": score,
"indicators": _fmt_indicators(ind),
"profile": profile or stock.get("description", ""),
"news": hits,
"ratings": ratings,
"holdings": holdings,
"prompt": prompt,
}
try:
report = llm_chat([
{"role": "system", "content": "你是一名严谨专业的A股投资顾问,输出结构化、简洁、可执行的研报。"},
@@ -160,6 +173,10 @@ def generate_report_sync(code, focus=""):
raise RuntimeError("LLM 返回为空")
execute("INSERT OR REPLACE INTO analysis_cache(code, report, created_at) VALUES(?,?,datetime('now','localtime'))",
(code, report))
execute(
"INSERT INTO analysis_history(code, stock_name, focus, report, sources, created_at) "
"VALUES(?,?,?,?,?,datetime('now','localtime'))",
(code, stock["name"], focus, report, json.dumps(sources, ensure_ascii=False)))
return {"report": report, "ts": time.time()}
except Exception as e:
log.exception("gen report fail")
@@ -221,3 +238,43 @@ def report_status(code):
def get_cached_report(code):
return query_one("SELECT report, created_at FROM analysis_cache WHERE code=?", (code,))
# ------------------------------------------------------------------ 历史记录
def list_history(code, limit=20):
"""某股票的历史 AI 分析记录(不含正文,只返回摘要)"""
rows = query(
"SELECT id, code, stock_name, focus, created_at, sources, LENGTH(report) AS len, "
"SUBSTR(report, 1, 60) AS excerpt FROM analysis_history "
"WHERE code=? ORDER BY id DESC LIMIT ?", (code, limit))
out = []
for r in rows:
try:
import json
src = json.loads(r.get("sources") or "{}")
except Exception:
src = {}
out.append({
"id": r["id"], "code": r["code"], "stock_name": r["stock_name"],
"focus": r["focus"], "created_at": r["created_at"],
"chars": r["len"], "excerpt": (r["excerpt"] or "").strip(),
"news_count": len(src.get("news") or []),
})
return out
def get_history(aid):
"""单条分析详情:正文 + 数据源 JSON"""
import json
row = query_one("SELECT * FROM analysis_history WHERE id=?", (aid,))
if not row:
return None
try:
src = json.loads(row.get("sources") or "{}")
except Exception:
src = {}
return {
"id": row["id"], "code": row["code"], "stock_name": row["stock_name"],
"focus": row["focus"], "report": row["report"], "created_at": row["created_at"],
"sources": src,
}