diff --git a/README.md b/README.md index 49c6877..010eadf 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,237 @@ # Profeto — 足球 LLM 预测服务 -**给 LLM 提供数据,让 LLM 预测足球比分。** +> 给 LLM 提供结构化数据,让 LLM 预测足球比分。 -与旧项目 `MatchPro`(自研统计模型预测引擎)完全不同: -- MatchPro: 160 个 Python 文件,7 套 ML 模型,OOF/校准/Promotion Gate,6 容器 -- **Profeto**: ~25 个文件,5 张表,LLM 做预测,2 容器(api + postgres) +与旧项目 [MatchPro](https://github.com)(自研统计模型预测引擎)完全不同: + +| | MatchPro | Profeto | +|---|---|---| +| 代码量 | 160 个 Python 文件 | ~30 个文件 | +| 预测方式 | 7 套 ML 模型 + OOF/校准/Promotion Gate | LLM 推理 | +| 架构 | 6 容器 | 2 容器 (api + postgres) | +| 数据 | 多源 + 特征工程 | 5 张表 + 数据切片 | ## 架构 ``` -前端(单页) → FastAPI → PostgreSQL - ↑ - LLM (OpenAI-compatible) - ↑ - bzzoiro / understat +┌─────────────────────────────────────────────────────┐ +│ 前端 (React + Vite + Tailwind) │ +│ http://localhost:5173 │ +└──────────────────────┬──────────────────────────────┘ + │ REST API +┌──────────────────────▼──────────────────────────────┐ +│ FastAPI │ +│ ├── /api/v1/matches 比赛查询 │ +│ ├── /api/v1/predict LLM 预测 (单/多 Agent) │ +│ ├── /api/v1/ingest/* 数据采集 │ +│ └── /api/v1/eval/* 评估回填 │ +└──────────┬─────────────────────────────┬────────────┘ + │ │ +┌──────────▼──────────┐ ┌─────────────▼────────────┐ +│ PostgreSQL │ │ LLM (OpenAI-compatible) │ +│ 5 张表 │ │ OpenAI / Deepseek / │ +│ leagues/teams/ │ │ Ollama / 任意网关 │ +│ matches/match_ │ └──────────────────────────┘ +│ stats/predictions/ │ +│ injuries │ +└─────────────────────┘ + ▲ + │ 采集 +┌──────────┴─────────────────────────────────────────┐ +│ 数据源 (DataSource 协议 + 注册表) │ +│ ├── bzzoiro 比分 / 统计 / xG │ +│ ├── understat xG 回填 │ +│ └── injuries 伤停数据 (api-football) │ +└────────────────────────────────────────────────────┘ ``` +### 多 Agent 预测 + +默认模式 (`mode=multi`) 采用 **5 专家 + 终裁** 架构: + +``` +比赛数据 → 切片 ─┬─→ A 近期状态专家 ─┐ + ├─→ B 攻防数据专家 ─┤ + ├─→ C 主客因素专家 ─┼─→ 终裁专家 ─→ 最终预测 + ├─→ D 阵容完整专家 ─┤ + └─→ E 历史交锋专家 ─┘ +``` + +- 各专家**只看到自己维度的数据切片**,避免信息过载 +- **fail-open**: 单个专家失败不影响整体 +- **no_data 门控**: 无数据维度跳过 LLM 调用,省 token 防幻觉 +- 终裁根据各报告的 `confidence` / `data_sufficiency` 加权输出 `agent_weights` + ## 快速开始 -### 1. 环境 +### 前置条件 + +- Python >= 3.11 +- Docker (运行 PostgreSQL) +- LLM API Key (OpenAI / Deepseek / Ollama 等) + +### 1. 安装 ```bash -# 后端 -pip install -e ".[dev]" -cp .env.example .env -# 编辑 .env: 填 LLM_API_KEY、BZZOIRO_KEY +# 克隆 +git clone https://git.bilidili.cn/shangfangjian/Profeto.git +cd Profeto -# 数据库 +# 后端依赖 +pip install -e ".[dev]" + +# 配置环境变量 +cp .env.example .env +# 编辑 .env,填入 LLM_API_KEY 和 BZZOIRO_KEY +``` + +### 2. 启动数据库 + +```bash docker compose up -d postgres ``` -### 2. 启动 +### 3. 启动服务 ```bash -# 后端 +# 后端 (终端 1) uvicorn src.api.app:app --reload -# 前端(另一个终端) +# 前端 (终端 2) cd frontend && npm install && npm run dev ``` -### 3. 使用 +后端运行在 `http://localhost:8000`,前端在 `http://localhost:5173`。 + +## 使用流程 + +### 1. 采集数据 ```bash -# 采集数据 +# 采集 bzzoiro 比分与统计 curl -X POST http://localhost:8000/api/v1/ingest/bzzoiro \ -H "Content-Type: application/json" \ -d '{"leagues":["E0","SP1"],"date_from":"2026-08-01","date_to":"2026-09-06"}' -# 查比赛 -curl "http://localhost:8000/api/v1/matches?league=E0&status=scheduled" +# 回填 understat xG +curl -X POST http://localhost:8000/api/v1/ingest/understat \ + -H "Content-Type: application/json" \ + -d '{"league":"E0","season":2026}' -# LLM 预测 +# 采集伤停 +curl -X POST http://localhost:8000/api/v1/ingest/injuries \ + -H "Content-Type: application/json" \ + -d '{"date":"2026-09-09"}' +``` + +### 2. 查询比赛 + +```bash +curl "http://localhost:8000/api/v1/matches?league=E0&status=scheduled" +``` + +### 3. LLM 预测 + +```bash +# 多 Agent 预测 (默认) curl -X POST http://localhost:8000/api/v1/predict \ -H "Content-Type: application/json" \ -d '{"match_id": 1}' -# 赛后回填 +# 单次调用模式 +curl -X POST http://localhost:8000/api/v1/predict \ + -H "Content-Type: application/json" \ + -d '{"match_id": 1, "mode": "single"}' +``` + +### 4. 评估 + +```bash +# 赛后回填实际比分 curl -X POST http://localhost:8000/api/v1/eval/settle \ -H "Content-Type: application/json" \ -d '{"prediction_id": 1, "home_goals": 2, "away_goals": 1}' -# 评估汇总 +# 查看准确率汇总 curl http://localhost:8000/api/v1/eval/summary ``` -前端访问 `http://localhost:5173`。 +## 项目结构 + +``` +Profeto/ +├── src/ +│ ├── api/ # FastAPI 路由层 +│ │ ├── app.py # 应用工厂 + lifespan +│ │ ├── schemas.py # Pydantic 请求/响应模型 +│ │ └── routes/ +│ │ ├── matches.py # 比赛查询 +│ │ ├── predict.py # 预测入口 +│ │ ├── ingest.py # 数据采集 +│ │ └── eval.py # 评估回填 +│ ├── core/ # 基础设施 +│ │ ├── config.py # pydantic-settings 配置 +│ │ └── http_client.py # 共享 httpx 客户端 +│ ├── data/ # 数据层 +│ │ ├── sources.py # DataSource 协议 + 注册表 +│ │ ├── match_lookup.py # 比赛匹配辅助函数 +│ │ ├── normalize.py # 数据规范化契约 +│ │ ├── bzzoiro.py # bzzoiro 数据源 +│ │ ├── understat.py # understat xG 数据源 +│ │ ├── injuries.py # 伤停数据 (独立领域) +│ │ ├── config.py # 联赛映射常量 +│ │ └── team_names.py # 队名归一化 +│ ├── db/ # 数据库 +│ │ ├── base.py # SQLAlchemy async engine +│ │ └── models.py # ORM 模型 (5 表) +│ └── llm/ # LLM 预测核心 +│ ├── predict.py # 预测服务 (缓存 + 单/多模式) +│ ├── context_builder.py # 数据切片 + 上下文拼接 +│ ├── eval.py # 评估统计 +│ ├── provider.py # 多提供商 LLM 抽象 +│ ├── agents/ +│ │ ├── base.py # Agent 基础设施 + 解析 +│ │ └── orchestrator.py # 多 Agent 编排 +│ └── prompts/ # Prompt 模板 +│ ├── match_prediction_v1.md +│ ├── match_prediction_v2.md +│ └── agents/ # 各专家 prompt +├── alembic/ # 数据库迁移 +├── frontend/ # React 前端 +├── docs/ # 详细文档 +├── tests/ # 单元测试 +├── docker-compose.yml +├── Dockerfile +└── pyproject.toml +``` ## 核心模块 -| 文件 | 作用 | +| 模块 | 作用 | |---|---| -| `src/llm/context_builder.py` | **最重要**: 拼 LLM 看到的上下文 | -| `src/llm/prompts/match_prediction.md` | prompt 模板(迭代最频繁) | -| `src/llm/provider.py` | 多提供商抽象 | -| `src/data/bzzoiro.py` | 数据采集(迁移自旧项目) | -| `src/data/normalize.py` | 数据清洗契约 | +| `context_builder.py` | **最重要**: 数据切片 + 拼接 LLM 看到的上下文 | +| `prompts/` | Prompt 模板 (迭代最频繁) | +| `provider.py` | OpenAI-compatible 多提供商抽象 | +| `sources.py` | 数据源协议 + 注册表 | +| `normalize.py` | 数据清洗契约 (校验/范围/归一) | +| `orchestrator.py` | 多 Agent 编排 (并行专家 + 终裁) | + +## 配置 + +通过 `.env` 或环境变量配置: + +| 变量 | 说明 | 默认值 | +|---|---|---| +| `DATABASE_URL` | PostgreSQL 连接 | `postgresql+asyncpg://football:football@localhost:5432/football` | +| `LLM_PROVIDER` | 提供商标识 | `openai` | +| `LLM_API_KEY` | API 密钥 | *(必填)* | +| `LLM_BASE_URL` | API 地址 | `https://api.openai.com/v1` | +| `LLM_MODEL` | 模型名 | `gpt-4o` | +| `LLM_SPECIALIST_MODEL` | 专家模型 (空=回落 LLM_MODEL) | | +| `LLM_AGGREGATOR_MODEL` | 终裁模型 (空=回落 LLM_MODEL) | | +| `BZZOIRO_KEY` | bzzoiro API Key | *(必填)* | +| `API_FOOTBALL_KEY` | api-football Key (伤停) | | +| `CORS_ORIGINS` | 允许的跨域来源 | `http://localhost:5173` | ## 测试 @@ -83,6 +239,14 @@ curl http://localhost:8000/api/v1/eval/summary pytest ``` +## 数据库迁移 + +生产环境建议使用 Alembic: + +```bash +alembic upgrade head +``` + ## License 个人研究项目,预测结果不构成投注建议。