Files
Profeto/docs/03-api.md
T
WorkBuddy 983363dab7 feat(ingest): 采集任务状态跟踪(ingest_jobs),解决 fire-and-forget 不可观测
- 新表 ingest_jobs(迁移 0019): id UUID/task/params JSONB/
  status(pending|running|success|failed,CheckConstraint)/
  result JSONB(统计摘要)/error/created_at/started_at/finished_at
- POST /ingest/bzzoiro: 启动后台前创建 pending job,响应返回 job_id;
  仍 require_admin。后台 _run_bzzoiro 流转 running→success/failed,
  result 按子任务(events/standings/stats)记录摘要(errors 截断 10 条)
- _update_job 尽力而为: 状态更新失败只记日志,绝不拖垮采集主流程;
  与 IngestFailure 死信独立(行级 vs 任务级,可同时存在)
- 新增 admin 端点(挂 /api/v1/admin 路由,路由级 require_admin):
  GET /admin/ingest/jobs/{job_id} 与 GET /admin/ingest/jobs?limit&status
- 前端采集页: 提交后凭 job_id 3 秒轮询,终态展示结果摘要/失败原因;
  无 job_id 时回退旧的 30 秒盲等 + 系统日志提示
- 测试 11 项: 建 job+job_id 契约、非法 task 422、成功/失败/all 流转、
  update 失败不拖垮采集、部分失败仍 success、admin 端点 200/404/列表、
  结构守护(job 路由在 admin 路由且带 require_admin)
- 禁止项确认: 未动分批 UoW、BzzoiroSource、死信与 Bronze 写入;
  docs(01/03/05/07/README)同步 13 张表与端点说明
2026-09-21 21:19:21 +08:00

8.4 KiB
Raw Blame History

03 · API 参考

Base URL: http://localhost:8000 · 交互式文档: /docs(Swagger)与 /redoc

所有数据端点返回 JSON。错误统一为 {"detail": "<message>"} + 对应 HTTP 状态码。

鉴权模型

级别 端点 说明
公开只读 GET /leaguesGET /matchesGET /matches/{id}GET /matches/{id}/contextGET /standingsGET /health* 无需任何凭据;公开站直接调用
公开 + 限流 POST /predict 内存滑动窗口限流(10 次/分钟/IP)
需管理员 GET /predictions*POST /ingest/bzzoiro/eval/*POST /backtest/admin/** Cookie 会话(POST /auth/login 颁发)或 X-API-Key

管理端点在生产环境未配置鉴权时 fail-closed(503),不会静默放行。


数据查询(公开只读)

GET /api/v1/leagues

列出已入库联赛(P1-3: 公开站联赛筛选动态加载来源)。

[{"id": 1, "code": "E0", "name": "Premier League", "country": "England"}]

仅返回 id/code/name/country 四个展示字段,不含任何配置或密钥信息。

GET /api/v1/matches

比赛列表,游标分页。

参数 说明
league 联赛代码,如 E0 / SP1 / D1 / I1 / F1
status scheduled / finished(不传 = 全部)
date YYYY-MM-DD,当天比赛
cursor 上一页返回的 next_cursor
limit 1100,默认 50

响应(倒序,含双方中文名与 xG):

{
  "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}

单场比赛详情,字段同上,另含 stats(统计)与 recent_predictions(最近 5 条预测摘要)。

GET /api/v1/matches/{id}/context

比赛上下文(公开只读,P1-2: 公开站详情页「近况/交锋」数据来源;不触发 LLM):

  • home_recent: 主队最近 5 场已完赛
  • away_recent: 客队最近 5 场已完赛
  • h2h: 双方最近 5 次交手
{
  "home_recent": [
    {"match_date": "2026-09-12T14:00:00+00:00", "home_team": "阿森纳",
     "away_team": "切尔西", "home_goals": 2, "away_goals": 1}
  ],
  "away_recent": [],
  "h2h": []
}

数据不足时对应列表为空(前端展示空态)。比赛不存在返回 404。

GET /api/v1/standings

联赛积分榜(公开只读)。参数:league(联赛代码,空 = 全部)、season(空 = 各联赛最新赛季)。

{"leagues": [
  {"league_code": "E0", "league_name": "Premier League", "season": "2026-2027",
   "retrieved_at": "2026-09-20T08:00:00+00:00",
   "rows": [{"position": 1, "team": "阿森纳", "team_en": "Arsenal",
             "played": 5, "won": 4, "drawn": 1, "lost": 0,
             "goals_for": 11, "goals_against": 3, "goal_diff": 8,
             "points": 13, "xg_for": 9.8, "xg_against": 3.9,
             "form": "WWWDW", "zone": "UEFA Champions League"}]}
]}

预测

POST /api/v1/predict

对一场比赛做 LLM 预测。核心端点。

请求:

{
  "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 模式):

{
  "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",
  "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,
     "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": "standings", "status": "no_data", "data_sufficiency": "none",
     "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}
  ],
  "agent_weights": {"form": 0.9, "stats": 0.8, "home_away": 0.7, "standings": 0.8, "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(唯一数据源)采集数据并入库(幂等,重复跑安全)。任务在后台异步执行,请求立即返回。

{"task": "all", "leagues": ["E0", "SP1"], "date_from": "2025-08-01", "date_to": "2026-09-08", "status": "finished"}
字段 默认 说明
task events 采集任务:events(比赛)/ standings(积分榜)/ stats(统计回填,含 xG)/ all
leagues 全部已知联赛 联赛代码列表,如 ["E0", "SP1"]
date_from / date_to 日期范围(YYYY-MM-DD)
status 空(两者都采) finished(已完赛)/ scheduled(未来赛程)
season 当前赛季 standings 赛季,如 "2026-2027"
limit 100 stats 回填单次最大比赛数(1500)
  • 响应:{"ok": true, "job_id": "<uuid>", "message": "……"},job_id 用于查询任务状态
  • task=stats 只补空字段、不创建比赛(xG/射门/控球等统计回填)

GET /api/v1/admin/ingest/jobs/{job_id}(需管理员)

查询一次采集任务的状态(ingest_jobs 表,任务级可观测性):

{"id": "…", "task": "standings", "params": {"leagues": ["E0"]},
 "status": "success", "result": {"total_upserted": 20, "errors": []},
 "error": null, "created_at": "…", "started_at": "…", "finished_at": "…"}
  • status 取值:pending(已创建未开始)/ running / success / failed
  • 管理端采集页提交后凭 job_id 轮询本端点直至终态
  • ingest_failures 死信独立:死信记录单条管线抓取失败(行级),job 记录整次任务结果

GET /api/v1/admin/ingest/jobs?limit=20&status=(需管理员)

列出最近采集任务(最新在前),可按 status 过滤,limit 1100。

历史版本曾有独立的 understat(xG)与 injuries(伤停)采集端点, 已随数据源收敛为 bzzoiro 唯一来源而移除。


评估

POST /api/v1/eval/settle(需管理员)

赛后回填实际比分:

{"prediction_id": 7, "home_goals": 2, "away_goals": 1}

GET /api/v1/eval/summary(需管理员)

provider × model 聚合已结算预测:

{"summary": [
  {"provider": "openai", "model": "gpt-4o", "total": 12,
   "accuracy_1x2": 58.3, "avg_score_rmse": 1.21, "avg_subjective_confidence": 0.65}
]}

提示:multi 模式存的 model 是终裁模型、prompt_versionmulti_v1, 因此 summary 里天然可对比 multi vs single、以及不同 prompt 版本的效果。


基础

端点 权限 说明
GET /health 公开 存活检查
GET /docsGET /redoc 公开 Swagger UI / ReDoc
POST /auth/loginPOST /auth/logoutGET /auth/me 公开 管理员 Cookie 会话登录/登出/当前用户
POST /auth/change-password 需管理员 修改管理员密码
POST /backtest 需管理员 历史回测(对已完赛比赛批量预测并评估)