feat: 足球 LLM 预测服务初始提交
Profeto — 给 LLM 提供数据,让 LLM 预测足球比分。 核心模块: - FastAPI 后端 + PostgreSQL (SQLAlchemy async) - 多 Agent LLM 预测 (5 专家 + 终裁) - 数据采集 (bzzoiro / understat / injuries) - React 前端 (Vite + Tailwind) 包含: - 数据源抽象 (DataSource 协议 + 注册表) - Alembic 数据库迁移 - Prompt 模板 (单/多 Agent) - 核心路径单元测试
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
# 03 · API 参考
|
||||
|
||||
Base URL: `http://localhost:8000` · 交互式文档: `/docs`(Swagger)与 `/redoc`
|
||||
|
||||
所有数据端点返回 JSON。错误统一为 `{"detail": "<message>"}` + 对应 HTTP 状态码。
|
||||
|
||||
---
|
||||
|
||||
## 数据查询
|
||||
|
||||
### `GET /api/v1/leagues`
|
||||
|
||||
列出已入库联赛。
|
||||
|
||||
```json
|
||||
[{"id": 1, "code": "E0", "name": "Premier League", "country": "England"}]
|
||||
```
|
||||
|
||||
### `GET /api/v1/matches`
|
||||
|
||||
比赛列表,游标分页。
|
||||
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `league` | 联赛代码,如 `E0` / `SP1` / `D1` / `I1` / `F1` |
|
||||
| `status` | `scheduled` / `finished`(不传 = 全部) |
|
||||
| `date` | `YYYY-MM-DD`,当天比赛 |
|
||||
| `cursor` | 上一页返回的 `next_cursor` |
|
||||
| `limit` | 1–100,默认 50 |
|
||||
|
||||
响应(倒序,含双方中文名与 xG):
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{"id": 42, "league_code": "E0", "season": "2026-2027",
|
||||
"home_team": "Arsenal", "away_team": "Manchester United",
|
||||
"home_team_zh": null, "away_team_zh": null,
|
||||
"match_date": "2026-09-15T19:00:00Z", "match_status": "scheduled",
|
||||
"home_goals": null, "away_goals": null,
|
||||
"match_stage": "第 5 轮", "home_xg": null, "away_xg": null}
|
||||
],
|
||||
"next_cursor": "2026-09-15T19:00:00+00:00|41",
|
||||
"has_more": true
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/matches/{id}`
|
||||
|
||||
单场比赛详情,字段同上。
|
||||
|
||||
---
|
||||
|
||||
## 预测
|
||||
|
||||
### `POST /api/v1/predict`
|
||||
|
||||
对一场比赛做 LLM 预测。**核心端点。**
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"match_id": 42,
|
||||
"mode": "multi",
|
||||
"model": "gpt-4o",
|
||||
"prompt_version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `match_id` | 必填 | 比赛 ID |
|
||||
| `mode` | `multi` | `multi` = 5 专家 + 终裁;`single` = 单次调用 |
|
||||
| `model` | 配置值 | 覆盖本次模型(single 模式下生效) |
|
||||
| `prompt_version` | `v1` | prompt 版本(multi 模式即 agent prompt 版本) |
|
||||
|
||||
响应(multi 模式):
|
||||
|
||||
```json
|
||||
{
|
||||
"prediction_id": 7,
|
||||
"provider": "openai",
|
||||
"model": "gpt-4o",
|
||||
"prompt_version": "multi_v1",
|
||||
"mode": "multi",
|
||||
"pred_home_goals": 2.1,
|
||||
"pred_away_goals": 1.0,
|
||||
"pred_1x2": "1",
|
||||
"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连胜"],
|
||||
"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,
|
||||
"key_evidence": [], "exp_home_goals": null, "exp_away_goals": null,
|
||||
"probable_score": null, "model": "", "latency_ms": null,
|
||||
"prompt_tokens": null, "completion_tokens": null}
|
||||
],
|
||||
"agent_weights": {"form": 0.9, "stats": 0.8, "home_away": 0.7, "injuries": 0.0, "h2h": 0.8},
|
||||
"context": "[5 份报告的 JSON 串]",
|
||||
"latency_ms": 9800
|
||||
}
|
||||
```
|
||||
|
||||
`agent.status` 取值:`ok` / `no_data`(维度无数据,已跳过 LLM)/ `error`(调用失败,fail-open 不阻断)/ `parse_error`。
|
||||
|
||||
错误:404 比赛不存在;502 LLM 调用失败(终裁失败时整体失败,专家失败不会)。
|
||||
|
||||
### `GET /api/v1/predictions?match_id=&limit=`
|
||||
|
||||
预测历史(倒序),含 `settled` 与实际比分回填状态。
|
||||
|
||||
### `GET /api/v1/predictions/{id}`
|
||||
|
||||
单条预测详情(含完整 `agent_outputs`)。
|
||||
|
||||
---
|
||||
|
||||
## 数据采集
|
||||
|
||||
### `POST /api/v1/ingest/bzzoiro`
|
||||
|
||||
从 bzzoiro 采集赛果/赛程并入库(幂等,重复跑安全)。
|
||||
|
||||
```json
|
||||
{"leagues": ["E0", "SP1"], "date_from": "2025-08-01", "date_to": "2026-09-08", "status": "finished"}
|
||||
```
|
||||
|
||||
- `status` 还可传 `scheduled` 拉未来赛程
|
||||
- 响应含每联赛 `inserted`/`updated`/`errors` 统计
|
||||
|
||||
### `POST /api/v1/ingest/understat`
|
||||
|
||||
回填 xG(只补空字段,不创建比赛):
|
||||
|
||||
```json
|
||||
{"league": "E0", "season": 2025}
|
||||
```
|
||||
|
||||
`season=2025` 表示 2025-2026 赛季。仅支持五大联赛。
|
||||
|
||||
### `POST /api/v1/ingest/injuries`
|
||||
|
||||
采集伤停(需 `API_FOOTBALL_KEY`,当前只返回计数,尚未接入 context):
|
||||
|
||||
```json
|
||||
{"date": "2026-09-10"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 评估
|
||||
|
||||
### `POST /api/v1/eval/settle`
|
||||
|
||||
赛后回填实际比分:
|
||||
|
||||
```json
|
||||
{"prediction_id": 7, "home_goals": 2, "away_goals": 1}
|
||||
```
|
||||
|
||||
### `GET /api/v1/eval/summary`
|
||||
|
||||
按 `provider × model` 聚合已结算预测:
|
||||
|
||||
```json
|
||||
{"summary": [
|
||||
{"provider": "openai", "model": "gpt-4o", "total": 12,
|
||||
"accuracy_1x2": 58.3, "avg_score_rmse": 1.21, "avg_confidence": 0.65}
|
||||
]}
|
||||
```
|
||||
|
||||
> 提示:multi 模式存的 `model` 是终裁模型、`prompt_version` 是 `multi_v1`,
|
||||
> 因此 summary 里天然可对比 multi vs single、以及不同 prompt 版本的效果。
|
||||
|
||||
---
|
||||
|
||||
## 基础
|
||||
|
||||
| 端点 | 说明 |
|
||||
|---|---|
|
||||
| `GET /health` | 存活检查 |
|
||||
| `GET /docs` | Swagger UI |
|
||||
Reference in New Issue
Block a user