feat: new file
This commit is contained in:
73
backend/app/services/ai_insight.py
Normal file
73
backend/app/services/ai_insight.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from ..settings import settings
|
||||
|
||||
|
||||
def _rule_based_summary(query: str, retrieved: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
top = retrieved[:3]
|
||||
bullets = []
|
||||
for r in top:
|
||||
pid = r.get("product_id") or r.get("id") or "-"
|
||||
title = r.get("title") or ""
|
||||
follow = r.get("follow_score")
|
||||
life = r.get("lifecycle")
|
||||
bullets.append(f"- {pid} {title}(跟卖指数={follow} 生命周期={life})")
|
||||
return {
|
||||
"mode": "rules_only",
|
||||
"query": query,
|
||||
"retrieved": retrieved,
|
||||
"answer": "基于当前向量库/指标的规则摘要:\n" + "\n".join(bullets),
|
||||
}
|
||||
|
||||
|
||||
def generate_insight(query: str, product_id: str | None, top_k: int) -> dict[str, Any]:
|
||||
"""
|
||||
这里先做“可运行的最小闭环”:
|
||||
- 向量检索(尚未实现时返回空)
|
||||
- 有 OPENAI_API_KEY 则调用 LLM,输出结构化建议
|
||||
- 否则返回规则引擎摘要
|
||||
"""
|
||||
# TODO: 接入向量库检索(Milvus/Azure 等)。先保留协议,保证前端可用。
|
||||
retrieved: list[dict[str, Any]] = []
|
||||
|
||||
if not settings.openai_api_key:
|
||||
return _rule_based_summary(query, retrieved)
|
||||
|
||||
prompt = f"""你是电商数据分析与选品决策助手。
|
||||
用户问题:{query}
|
||||
|
||||
请输出一个“发现爆款 -> 数据验证 -> 决策跟卖”的闭环建议,包含:
|
||||
1) 结论摘要(3-5条)
|
||||
2) 数据证据(引用关键指标:销量/增速/竞争/生命周期)
|
||||
3) 风险点与反例(至少3条)
|
||||
4) 可执行动作(选品、备货、投流、供应链)
|
||||
如果没有足够数据,请明确说明缺口,并给出最小补充数据清单。
|
||||
"""
|
||||
|
||||
headers = {"Authorization": f"Bearer {settings.openai_api_key}"}
|
||||
payload = {
|
||||
"model": settings.openai_model,
|
||||
"input": prompt,
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client(timeout=30.0) as client:
|
||||
r = client.post("https://api.openai.com/v1/responses", headers=headers, json=payload)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
text = ""
|
||||
for item in data.get("output", []):
|
||||
for c in item.get("content", []):
|
||||
if c.get("type") in ("output_text", "text"):
|
||||
text += c.get("text", "")
|
||||
return {"mode": "llm", "query": query, "retrieved": retrieved, "answer": text.strip()}
|
||||
except Exception as e:
|
||||
out = _rule_based_summary(query, retrieved)
|
||||
out["mode"] = "rules_fallback"
|
||||
out["error"] = str(e)
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user