# 01 · 架构总览 ## 系统架构 ``` ┌─────────────────────────────────────────────────────┐ │ 前端(单页 React + Vite + Tailwind) │ │ 比赛列表 → 选比赛 → LLM 预测 → 专家报告 + 最终结论 │ └──────────────────────────▲──────────────────────────┘ │ REST (/api/v1) ┌──────────────────────────┴──────────────────────────┐ │ FastAPI(单进程,全 async) │ │ │ │ 数据查询 预测编排 采集(手动/cron 触发) │ │ ┌──────┐ ┌────────────┐ ┌───────────────────┐ │ │ │matches│ │ orchestrator│ │ bzzoiro (唯一源) │ │ │ │leagues│ │ ┌─ 5 专家并行(便宜模型) │ │ │ └──┬───┘ │ │ h2h / form / stats / │ │ │ │ │ │ home_away / standings │ │ │ │ │ └─ aggregator 终裁(强模型) │ │ │ │ └────────────┘ │ events / standings │ │ │ ┌──┴──────────────┴──┐ │ /stats 三条管线 │ │ │ │ PostgreSQL (12 张表)│ └───────────────────┘ │ │ └────────────────────┘ httpx → 外部 API │ └─────────────────────────────────────────────────────┘ ``` ## 核心数据流(一次多 Agent 预测) 1. `POST /predict {match_id}` → orchestrator 2. `load_match_header`: 查比赛 + 双方 + 联赛(一次 eager load) 3. **5 个专家 agent 并行**(`asyncio.gather`),每个: - 各自的数据切片函数查库(近况/交锋/积分榜聚合/射门控球/xG) - 切片无数据 → **跳过 LLM**,直接 `no_data` stub(省 token、防幻觉) - 有数据 → 专属 prompt(专家模型,便宜快)→ 结构化 JSON 报告(`home_edge` 方向性评分 + 证据) 4. **终裁 agent**:5 份报告 + 比赛信息 → 权衡采信度(`agent_weights`)→ 最终预测 JSON 5. 存 `predictions` 表(含 `agent_outputs` 全部报告) 6. 赛后 `POST /eval/settle` 回填实际比分 → `GET /eval/summary` 按 模型×prompt 版本 聚合准确率 ## 关键设计决策 | 决策 | 理由 | |---|---| | **多专家并行而非单次大 prompt** | 每维度独立迭代 prompt;报告可归因(哪个维度分析错了);总延迟 ≈ 2 次串行调用 | | **专家/终裁模型分档** | 专家用便宜模型快速分析,终裁用强模型汇总决策,成本与质量平衡(`LLM_SPECIALIST_MODEL` / `LLM_AGGREGATOR_MODEL`) | | **no_data 门控** | 无数据维度(如积分榜未采集)不调 LLM,终裁知道维度缺失,不编造 | | **fail-open** | 单个专家失败只标记 `status=error`,其余照常;研究场景可用性优先 | | **`match_date_date` 天级去重** | 不同源时间精度不同,秒级匹配会产生重复行;天级 + 数据库唯一约束 | | **积分榜 SQL 聚合 + season 过滤** | `UNION ALL` 主客双视角 + `GROUP BY` 在库内算,只算当前赛季(修复过跨赛季 bug) | | **单 agent 模式保留** | `mode="single"` 走旧单次路径,与 multi 形成天然 A/B(eval 按 `prompt_version` 分组) | | **无 worker/redis/队列** | 采集是 cron 触发的短任务,单进程足够;违背简化初衷的基础设施一律不加 | ## 目录结构 ``` Profeto/ ├── src/ │ ├── api/ │ │ ├── app.py # FastAPI 工厂(lifespan:迁移校验/定时任务/生产限流提醒) │ │ ├── deps.py # 依赖:管理接口鉴权(Cookie/X-API-Key)+ 限流 │ │ ├── schemas.py # Pydantic v2 请求/响应 │ │ └── routes/ │ │ ├── matches.py # 联赛/比赛/上下文/积分榜(公开只读) │ │ ├── predict.py # 预测(限流)+ 预测历史(需鉴权) │ │ ├── ingest.py # 采集触发(需鉴权) │ │ ├── eval.py # 赛后回填 + 准确率汇总(需鉴权) │ │ ├── backtest.py # 历史回测(需鉴权) │ │ ├── auth.py # 登录/登出/改密 │ │ ├── admin_settings.py # /admin/** 配置/日志/数据质量(router 级鉴权) │ │ └── schedules.py # 定时任务 + 死信重试(router 级鉴权) │ ├── db/ │ │ ├── base.py # async engine + get_db/get_db_read │ │ ├── models.py # 12 张表 ORM │ │ ├── repositories.py # 仓储层 │ │ └── unit_of_work.py # 事务边界 │ ├── data/ │ │ ├── bzzoiro.py # 唯一数据源:events/standings/stats 三管线 + Bronze 层 │ │ ├── normalize.py # NormalizedMatch 清洗契约 │ │ ├── team_names.py # 队名归一映射 │ │ ├── team_names_zh.py # 队名中文名映射 │ │ ├── key_ring.py # 多 key 轮换(429 冷却,进程内) │ │ ├── sources.py # 数据源注册表 │ │ └── config.py # 联赛代码映射 │ ├── llm/ │ │ ├── provider.py # OpenAI-compatible 抽象(共享连接池/JSON 兜底解析) │ │ ├── context_builder.py # 数据切片(h2h/form/stats/home_away/standings)+ 单 agent 拼接 │ │ ├── predict.py # 预测入口(mode 分派 + 缓存) │ │ ├── baseline.py # 基线预测(均值模型,mode=baseline) │ │ ├── eval.py # 准确率统计 │ │ ├── utils.py # LLM 工具函数 │ │ ├── validation.py # LLM 输出严格校验(Pydantic) │ │ ├── backtest.py # 回测执行 │ │ ├── agents/ │ │ │ ├── base.py # AgentSpec / AgentReport / run_agent │ │ │ └── orchestrator.py # 并行专家 → 终裁 → 存库 │ │ └── prompts/ │ │ ├── match_prediction_v1/v2.md # 单 agent 模板 │ │ └── agents/{form,stats,home_away,standings,h2h,aggregator}_v1.md │ └── core/ │ ├── config.py # pydantic-settings │ ├── crypto.py # 加密/哈希 │ ├── http_client.py # 共享 httpx 客户端 │ ├── log_buffer.py # 内存日志缓冲(admin 日志页) │ ├── runtime_config.py # DB 配置覆盖(app_settings) │ ├── scheduler.py # 进程内 cron 调度器 │ └── security_check.py # 启动安全校验 ├── alembic/versions/ # 0001~0018(建表 → Bronze 层 → 单一数据源 → 基线模式等) ├── frontend/src/ # pages/(公开站) + admin/(管理后台) + lib/http.ts(唯一 HTTP 实现) ├── tests/ # 核心 + agent 测试(250+ 项,自包含) ├── docker-compose.yml # api + postgres 两容器 └── docs/ # 本文档 ``` ## 技术栈 | 层 | 选型 | |---|---| | API | FastAPI + uvicorn(全 async) | | ORM | SQLAlchemy 2.0 async + asyncpg | | 数据库 | PostgreSQL 16(JSONB 存 agent 报告) | | HTTP | httpx(共享连接池)/ urllib(bzzoiro 同步限速) | | LLM | OpenAI-compatible 接口(openai/deepseek/ollama 等任一) | | 前端 | Vite + React 18 + TypeScript + Tailwind | | 测试 | pytest + pytest-asyncio(250+ 项,自包含) | | 部署 | Docker Compose(api + postgres) |