chore(P3): baseline 落库下沉 + MatchPredictPanel 拆分 + 多 worker/CSRF 文档
P3-2 baseline 落库从路由下沉到服务层(predict_baseline 内直接落库),
删除路由层 _persist_baseline,三种模式统一 result.prediction_id,对外 JSON 不变。
P3-1 MatchPredictPanel.PredictionPanel 拆为 OutcomePanel/AgentsPanel/ReasoningPanel
三个子组件,本文件保留 PredictModal/PredictProgress/Spinner,对外导出路径不变。
P3-3 docs 加 ⚠️ 多 worker 陷阱红字 + STRICT_SINGLE_WORKER 环境变量(启动期强制拒绝多 worker)。
P3-4 docs 新增「同站部署 vs 跨站 CSRF」节。
This commit is contained in:
+42
-11
@@ -12,7 +12,7 @@ from sqlalchemy import case, func, select
|
||||
|
||||
from src.db.base import AsyncSession, AsyncSessionLocal
|
||||
from src.db.models import Match
|
||||
from src.llm.predict import PredictResult
|
||||
from src.llm.predict import PredictResult, _upsert_prediction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -58,19 +58,23 @@ async def predict_baseline(
|
||||
|
||||
返回 PredictResult(D2 统一结果类型):
|
||||
provider=model="baseline", 不调用 LLM,latency_ms≈0。
|
||||
prediction_id 为占位 0 —— baseline 不在服务层落库,
|
||||
由路由层 _persist_baseline 落库后取得真实 id。
|
||||
|
||||
P3-2:baseline 落库下沉到服务层 —— 直接在服务层完成落库并回填真实
|
||||
prediction_id,路由层不再需要特殊的 _persist_baseline,与 single/multi
|
||||
路径统一(result.prediction_id 即可用)。对外 JSON 不变。
|
||||
"""
|
||||
from src.db.unit_of_work import get_uow
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
match = await db.get(Match, match_id)
|
||||
if match is None:
|
||||
raise ValueError(f"match {match_id} not found")
|
||||
|
||||
before = None
|
||||
if backtest and match.match_dt:
|
||||
if backtest and match.match_date:
|
||||
from datetime import timedelta
|
||||
|
||||
before = match.match_dt - timedelta(days=1)
|
||||
before = match.match_date - timedelta(days=1)
|
||||
elif cutoff_at is not None:
|
||||
before = cutoff_at
|
||||
|
||||
@@ -93,8 +97,38 @@ async def predict_baseline(
|
||||
else:
|
||||
pred_1x2 = "X"
|
||||
|
||||
values = {
|
||||
"prompt_version": "baseline_v1",
|
||||
"prompt_tokens": 0,
|
||||
"completion_tokens": 0,
|
||||
"latency_ms": 0,
|
||||
"pred_home_goals": float(pred_home),
|
||||
"pred_away_goals": float(pred_away),
|
||||
"pred_1x2": pred_1x2,
|
||||
"subjective_confidence": 0.5,
|
||||
"reasoning": (
|
||||
f"基线估计(非投注建议): 主队主场场均进球 {home_avg:.2f} → 预测 {pred_home}; "
|
||||
f"客队客场场均进球 {away_avg:.2f} → 预测 {pred_away}。"
|
||||
),
|
||||
"raw_response": {"home_avg": round(home_avg, 2), "away_avg": round(away_avg, 2)},
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
# P3-2:服务层落库,回填真实 prediction_id(与 single/multi 统一)。
|
||||
async with get_uow() as session:
|
||||
pred = await _upsert_prediction(
|
||||
session,
|
||||
match_id=match_id,
|
||||
provider_name="baseline",
|
||||
model="baseline",
|
||||
mode="baseline",
|
||||
run_type="live",
|
||||
values=values,
|
||||
)
|
||||
prediction_id = pred.id
|
||||
|
||||
return PredictResult(
|
||||
prediction_id=0, # 占位:真实 id 由路由层 _persist_baseline 落库后返回
|
||||
prediction_id=prediction_id,
|
||||
provider="baseline",
|
||||
model="baseline",
|
||||
prompt_version="baseline_v1",
|
||||
@@ -105,14 +139,11 @@ async def predict_baseline(
|
||||
alt_pred_away_goals=None,
|
||||
pred_1x2=pred_1x2,
|
||||
subjective_confidence=0.5,
|
||||
reasoning=(
|
||||
f"基线估计(非投注建议): 主队主场场均进球 {home_avg:.2f} → 预测 {pred_home}; "
|
||||
f"客队客场场均进球 {away_avg:.2f} → 预测 {pred_away}。"
|
||||
),
|
||||
reasoning=values["reasoning"],
|
||||
context="", # baseline 不构建 LLM 上下文
|
||||
status="success",
|
||||
latency_ms=0,
|
||||
prompt_tokens=0,
|
||||
completion_tokens=0,
|
||||
raw={"home_avg": round(home_avg, 2), "away_avg": round(away_avg, 2)},
|
||||
raw=values["raw_response"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user