docs: 统一文档与代码一致性
- 修复所有 confidence → subjective_confidence 残留(03-api, 04-agents, 05-data, 07-development) - 修复 5 张表 → 6 张表残留(06-deployment) - 同步 API schema 示例与实际模型一致 - 更新 predictions 表结构文档(新增 status/cutoff/input_hash 字段) code: 修复 API 异常处理(eval/predict)和 context_builder stats 时间过滤
This commit is contained in:
+4
-4
@@ -87,17 +87,17 @@ Base URL: `http://localhost:8000` · 交互式文档: `/docs`(Swagger)与 `/redo
|
||||
"pred_home_goals": 2.1,
|
||||
"pred_away_goals": 1.0,
|
||||
"pred_1x2": "1",
|
||||
"confidence": 0.68,
|
||||
"subjective_confidence": 0.68,
|
||||
"reasoning": "综合 xg 报告的进球期望 2.1-1.0 与 form 报告的三连胜势头……",
|
||||
"agent_outputs": [
|
||||
{"agent": "h2h", "status": "ok", "data_sufficiency": "medium",
|
||||
"analysis": "近 5 次交锋主队 3 胜……", "home_edge": 0.4,
|
||||
"confidence": 0.7, "key_evidence": ["近5次交锋主队3胜", "主场交锋3连胜"],
|
||||
"subjective_confidence": 0.7, "key_evidence": ["近5次交锋主队3胜", "主场交锋3连胜"],
|
||||
"exp_home_goals": null, "exp_away_goals": null, "probable_score": null,
|
||||
"model": "gpt-4o-mini", "latency_ms": 2100,
|
||||
"prompt_tokens": 380, "completion_tokens": 120},
|
||||
{"agent": "injuries", "status": "no_data", "data_sufficiency": "none",
|
||||
"analysis": "该维度无数据,跳过分析。", "home_edge": null, "confidence": null,
|
||||
"analysis": "该维度无数据,跳过分析。", "home_edge": null, "subjective_confidence": null,
|
||||
"key_evidence": [], "exp_home_goals": null, "exp_away_goals": null,
|
||||
"probable_score": null, "model": "", "latency_ms": null,
|
||||
"prompt_tokens": null, "completion_tokens": null}
|
||||
@@ -172,7 +172,7 @@ Base URL: `http://localhost:8000` · 交互式文档: `/docs`(Swagger)与 `/redo
|
||||
```json
|
||||
{"summary": [
|
||||
{"provider": "openai", "model": "gpt-4o", "total": 12,
|
||||
"accuracy_1x2": 58.3, "avg_score_rmse": 1.21, "avg_confidence": 0.65}
|
||||
"accuracy_1x2": 58.3, "avg_score_rmse": 1.21, "avg_subjective_confidence": 0.65}
|
||||
]}
|
||||
```
|
||||
|
||||
|
||||
+4
-4
@@ -65,7 +65,7 @@ POST /predict {match_id, mode: "multi"}
|
||||
"data_sufficiency": "high",
|
||||
"analysis": "近 5 次交锋主队 3 胜 1 平 1 负,主场交锋 3 连胜……",
|
||||
"home_edge": 0.4,
|
||||
"confidence": 0.7,
|
||||
"subjective_confidence": 0.7,
|
||||
"key_evidence": ["近5次交锋主队3胜", "主场交锋3连胜"]
|
||||
}
|
||||
```
|
||||
@@ -75,7 +75,7 @@ POST /predict {match_id, mode: "multi"}
|
||||
| `status` | `ok` / `no_data` / `error` / `parse_error` |
|
||||
| `data_sufficiency` | `high` / `medium` / `low` / `none` |
|
||||
| `home_edge` | -1.0 ~ 1.0,正数=利主队,负数=利客队 |
|
||||
| `confidence` | 0.0 ~ 1.0,该专家对自己分析的信心 |
|
||||
| `subjective_confidence` | 0.0 ~ 1.0,该专家对自己分析的主观信心(非概率) |
|
||||
| `key_evidence` | 关键证据列表(最多 5 条) |
|
||||
|
||||
### 终裁 Agent 输出
|
||||
@@ -87,13 +87,13 @@ POST /predict {match_id, mode: "multi"}
|
||||
"pred_home_goals": 2.1,
|
||||
"pred_away_goals": 1.0,
|
||||
"1x2": "1",
|
||||
"confidence": 0.68,
|
||||
"subjective_confidence": 0.68,
|
||||
"reasoning": "综合 stats 报告的攻防强度与 form 报告的三连胜势头……",
|
||||
"agent_weights": {"form": 0.9, "stats": 0.8, "home_away": 0.7, "injuries": 0.0, "h2h": 0.8}
|
||||
}
|
||||
```
|
||||
|
||||
`agent_weights` 体现终裁对各专家报告的采信度(0–1),可用于后续分析"哪个维度对预测贡献大"。
|
||||
`agent_weights` 体现终裁对各专家报告的采信度(0–1),可用于后续分析"哪个维度对预测贡献大"。注意:这是 LLM 主观权重,非统计权重。
|
||||
|
||||
## 模型分档配置
|
||||
|
||||
|
||||
+6
-1
@@ -124,7 +124,12 @@ CREATE TABLE predictions (
|
||||
latency_ms INT,
|
||||
pred_home_goals FLOAT, pred_away_goals FLOAT,
|
||||
pred_1x2 VARCHAR(3), -- '1' / 'X' / '2'
|
||||
confidence FLOAT,
|
||||
subjective_confidence FLOAT, -- LLM 主观置信度(非概率)
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'success', -- 'success' / 'failed' / 'degraded'
|
||||
match_kickoff_at TIMESTAMPTZ, -- 比赛时间
|
||||
prediction_created_at TIMESTAMPTZ, -- 预测创建时间
|
||||
prediction_cutoff_at TIMESTAMPTZ, -- 数据截止时间
|
||||
input_hash VARCHAR(64), -- 输入快照 hash
|
||||
reasoning TEXT,
|
||||
raw_response JSONB, -- LLM 完整原始响应
|
||||
agent_outputs JSONB, -- multi 模式: 5 份专家报告
|
||||
|
||||
@@ -128,7 +128,7 @@ alembic revision -m "描述"
|
||||
```
|
||||
|
||||
已有迁移:
|
||||
- `0001_initial`: 初始 5 张表(leagues/teams/matches/match_stats/predictions)
|
||||
- `0001_initial`: 初始 5 张表,0003 增加 injuries,0004 增加约束,0005 增加时间语义
|
||||
- `0002_agent_outputs`: predictions 加 `mode` + `agent_outputs`
|
||||
|
||||
## 备份与恢复
|
||||
|
||||
@@ -118,7 +118,7 @@ class MockProvider:
|
||||
return LLMResponse(
|
||||
content="{}",
|
||||
parsed={"data_sufficiency": "high", "analysis": "ok",
|
||||
"home_edge": 0.5, "confidence": 0.8,
|
||||
"home_edge": 0.5, "subjective_confidence": 0.8,
|
||||
"key_evidence": ["证据"]},
|
||||
prompt_tokens=10, completion_tokens=5, latency_ms=100,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"""评估路由。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from src.api.schemas import EvalSummaryOut, SettleRequest
|
||||
from src.db.base import AsyncSession, get_db, get_db_read
|
||||
from src.llm.eval import get_eval_summary, settle_prediction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/v1", tags=["eval"])
|
||||
|
||||
|
||||
@@ -17,7 +21,11 @@ async def settle(req: SettleRequest, db: AsyncSession = Depends(get_db)):
|
||||
pred = await settle_prediction(req.prediction_id, req.home_goals, req.away_goals)
|
||||
return {"id": pred.id, "settled": pred.settled}
|
||||
except ValueError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
logger.warning("settle failed: %s", e)
|
||||
raise HTTPException(404, "预测记录不存在")
|
||||
except Exception as e:
|
||||
logger.exception("settle error")
|
||||
raise HTTPException(500, "回填失败,请查看服务器日志")
|
||||
|
||||
|
||||
@router.get("/eval/summary", response_model=EvalSummaryOut)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""预测路由。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -10,6 +12,8 @@ from src.db.base import AsyncSession, get_db, get_db_read
|
||||
from src.db.models import Prediction
|
||||
from src.llm.predict import predict_match, PredictResult
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/v1", tags=["predict"])
|
||||
|
||||
|
||||
@@ -24,9 +28,14 @@ async def predict(req: PredictRequest, db: AsyncSession = Depends(get_db)):
|
||||
mode=req.mode,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
logger.warning("predict validation error: %s", e)
|
||||
raise HTTPException(404, "比赛不存在")
|
||||
except RuntimeError as e:
|
||||
raise HTTPException(502, str(e))
|
||||
logger.error("predict LLM error: %s", e)
|
||||
raise HTTPException(502, "LLM 预测失败,请查看服务器日志")
|
||||
except Exception as e:
|
||||
logger.exception("predict unexpected error")
|
||||
raise HTTPException(500, "预测失败,请查看服务器日志")
|
||||
|
||||
# single / multi 两种结果统一映射
|
||||
return PredictOut(
|
||||
|
||||
@@ -142,7 +142,7 @@ async def form_slice(header: MatchHeader, *, limit: int = 5, before=None) -> str
|
||||
else: losses += 1
|
||||
score = f"{fm.home_goals}-{fm.away_goals}" if fm.home_goals is not None else "vs"
|
||||
xg = ""
|
||||
if fm.stats and fm.stats.home_xg is not None:
|
||||
if fm.stats and _is_stats_available(fm.stats, before) and fm.stats.home_xg is not None:
|
||||
own = fm.stats.home_xg if side == "home" else fm.stats.away_xg
|
||||
xg = f" (xG {own:.1f})"
|
||||
opp = fm.away_team.name if side == "home" else fm.home_team.name
|
||||
|
||||
Reference in New Issue
Block a user