NBA Feed
Your teams' news and games, with scores hidden until you reveal them.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗機械学習による競馬予測・資金管理・データ収集を一気通貫で実現するフルスタックアプリケーション。
必要なローカル環境が設定済みのPCでは、リポジトリ直下のstart-keiba-ai-pro.batをダブルクリックすると、FastAPIとWebアプリを安全なObservationモードで一括起動し、ログイン画面を開きます。
start-keiba-ai-pro.batcheck-keiba-ai-pro.batstop-keiba-ai-pro.bat起動時にスケジューラ、Production書き込み、自動購入は有効化されません。不足しているvenv、環境設定、DB、Node依存がある場合はfail-closedで停止します。
netkeiba.com
│
│ scrape_race_full() / _scrape_shutuba_fallback()
▼
race.py(スクレイピング)
│ JSON (race_info + 馬別30項目)
▼
storage.py(DB保存)
│
▼
keiba_ultimate.db (SQLite3)
├─ races_ultimate ← レース基本情報 (JSON per row)
└─ race_results_ultimate ← 馬別結果 (JSON per row)
│
│ db_ultimate_loader.load_ultimate_training_frame()
▼
pandas DataFrame (~ 13,000行 × 83列)
│
│ add_derived_features()
▼
派生特徴量追加 (+ ~40列 → 合計 ~125列)
│
│ UltimateFeatureCalculator.add_ultimate_features()
▼
過去統計特徴量追加 (+ ~24列 → 合計 ~137列)
│
│ prepare_for_lightgbm_ultimate()
▼
LightGBM用前処理 (Label Encoding, 未来情報除外) → 110列前後
│
├─ 学習モード ─────────────────────────────────────────────┐
│ LightGBM (binary 分類, target=win) │
│ Optuna 最適化 (100試行, 5-fold CV) │
│ → model_win_YYYYMMDD_YYYYMMDD_ultimate.joblib │
└──────────────────────────────────────────────────────────┘
│
└─ 推論モード(/api/analyze_race)─────────────────────────┐
Quality Gate チェック │
model.predict_proba(X) → 勝率スコア │
キャリブレーション │
期待値計算 (EV = prob × odds) │
ベッティング戦略生成 │
→ JSON レスポンス │
────────────────────────────────────────────────────── │
| 用途 | URL |
|---|---|
| レース結果 | https://db.netkeiba.com/race/{race_id}/ |
| 出馬表(フォールバック) | https://race.netkeiba.com/race/shutuba.html?race_id={race_id} |
| 当日レース一覧 | https://race.netkeiba.com/top/race_list.html?kaisai_date={YYYYMMDD} |
race_id の構造: YYYYMMDDVVRR
YYYY: 年, MM: 月, DD: 日VV: 会場コード (05=東京, 06=中山, 09=阪神 など)RR: レース番号 (01~12)レース基本情報(races_ultimate.data に JSON 格納)
| フィールド | 型 | 例 |
|---|---|---|
race_name | str | "日本ダービー" |
date | str (YYYYMMDD) | "20260601" |
venue | str | "東京" |
venue_code | str | "05" |
distance | int | 2400 |
track_type | str | "芝" / "ダート" |
weather | str | "晴" / "曇" / "雨" |
field_condition | str | "良" / "稍重" / "重" / "不良" |
num_horses | int | 18 |
post_time | str | "15:40" |
kai | int | 開催回 |
day | int | 開催日 |
course_direction | str | "右" / "左" / "直線" |
馬別情報(race_results_ultimate.data に JSON 格納)
| フィールド | 型 | 例 |
|---|---|---|
horse_number | int | 7 |
bracket_number | int | 4 |
horse_name | str | "ドウデュース" |
horse_id | str | "2020110038" |
sex_age | str | "牡3" |
sex | str | "牡" / "牝" / "セ" |
age | int | 3 |
jockey_name | str | "武豊" |
jockey_id | str | "00356" |
jockey_weight | float | 57.0 |
trainer_name | str | "友道康夫" |
trainer_id | str | "01101" |
weight_kg | int | 492 |
weight_diff | int | +4 |
odds | float | 4.5 |
popularity | int | 2 |
finish_position | int | 1 |
finish_time | str | "2:23.5" |
margin | str | "クビ" |
last_3f | str | "34.8" |
last_3f_rank | int | 3 |
corner_positions | str |
ファイル: keiba/data/keiba_ultimate.db (SQLite3, WAL モード)
-- レース基本情報(1レース1行)
CREATE TABLE races_ultimate (
race_id TEXT PRIMARY KEY,
data TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 馬別データ(1頭1行)
CREATE TABLE race_results_ultimate (
id INTEGER PRIMARY KEY AUTOINCREMENT,
race_id TEXT,
data TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_results_race_id ON race_results_ultimate (race_id);
現状規模
races_ultimate: 約 14,000+ レースrace_results_ultimate: 約 180,000+ 行(平均 13 頭/レース)add_derived_features() — 派生特徴量 (~40列追加)休養期間
| 特徴量 | 型 | 説明 |
|---|---|---|
days_since_last_race | int | 現レース日 - 前走日(日数) |
rest_category | str | "short" / "normal" / "long" / "very_long" |
前走・2走前
| 特徴量 | 説明 |
|---|---|
prev_race_finish | 前走着順 |
prev_race_time | 前走タイム(秒) |
prev_race_weight | 前走馬体重 |
prev_race_distance | 前走距離 |
prev2_race_finish | 2走前着順 |
prev2_race_distance | 2走前距離 |
距離・馬場変化
| 特徴量 | 説明 |
|---|---|
distance_change | 今走距離 - 前走距離 (m) |
distance_increased | 1/0(距離延長) |
distance_decreased | 1/0(距離短縮) |
surface_changed | 1/0(芝↔ダート変更) |
コーナー通過派生
| 特徴量 | 説明 |
|---|---|
corner_position_avg | 平均コーナー通過順位 |
corner_position_variance | 通過順位の分散(低=安定した脚質) |
last_corner_position | 最終コーナーでの通過順位 |
position_change | 1C→最終Cの順位変化(正=追い込み) |
近走統計(expanding window)
| 特徴量 | 説明 |
|---|---|
past3_avg_finish | 近3走平均着順 |
past5_avg_finish | 近5走平均着順 |
past10_avg_finish | 近10走平均着順 |
past3_win_rate | 近3走勝率 |
past5_win_rate | 近5走勝率 |
finish_consistency | 1/(1+std)(高=安定) |
recent_form_score | max(0, 10-近3走平均)/10 |
体重トレンド
| 特徴量 | 説明 |
|---|---|
past_5_weight_slope | kg/走の増減傾向(線形回帰の傾き) |
past_5_weight_avg_change | 過去5走の平均体重変化 (kg) |
会場・条件別統計(ベイズ平滑化適用)
| 特徴量 | 説明 |
|---|---|
jockey_course_win_rate | 騎手×会場×馬場での勝率 |
horse_surface_win_rate | 馬×馬場種別での勝率 |
horse_dist_band_win_rate | 馬×距離帯での勝率 |
gate_win_rate | 枠番×会場×距離帯での勝率 |
jt_combo_win_rate_smooth | 騎手×調教師コンビ勝率(平滑化) |
オッズ系特徴量
| 特徴量 | 説明 |
|---|---|
log_odds | log(1 + odds) |
implied_prob | 1 / odds(市場の暗黙確率) |
odds_rank_in_race | レース内オッズ順位 |
odds_z_in_race | レース内でのオッズのZスコア |
market_entropy | オッズ分布のエントロピー(高=混戦) |
UltimateFeatureCalculator.add_ultimate_features() — 過去統計 (~24列追加)馬の過去10走統計(DBを参照、未来情報は参照しない)
| 特徴量 | 説明 |
|---|---|
past_10_races_count | 過去10走以内のレース数(0〜10) |
past_10_avg_finish | 平均着順 |
past_10_std_finish | 着順の標準偏差 |
past_10_best_finish | ベスト着順 |
past_10_win_rate | 勝率 |
past_10_place_rate | 2着以内率 |
past_10_show_rate | 3着以内率 |
recent_3_avg_finish | 近3走平均着順 |
finish_consistency | 1/(1+std) |
recent_form_score | (10 - 近3走平均) / 10 |
past_5_weight_slope | 体重トレンド(kg/走) |
past_5_weight_avg_change | 平均体重変化(kg) |
騎手統計(直近180日スライディングウィンドウ)
| 特徴量 | 説明 |
|---|---|
jockey_recent_win_rate | 直近180日勝率 |
jockey_recent_place_rate | 直近180日 2着以内率 |
jockey_recent_show_rate | 直近180日 3着以内率 |
jockey_recent_races | 直近180日 出走数 |
jockey_avg_finish | 直近180日 平均着順 |
調教師統計(同上)
| 特徴量 | 説明 |
|---|---|
trainer_recent_win_rate | 直近180日勝率 |
trainer_recent_place_rate | 直近180日 2着以内率 |
trainer_recent_show_rate | 直近180日 3着以内率 |
trainer_recent_races | 直近180日 出走数 |
prepare_for_lightgbm_ultimate() — LightGBM用前処理カテゴリ変数の処理
低カーディナリティ → Label Encoding → categorical_feature 指定:
venue_encoded : 0-9 (10会場)
track_type_encoded : 0-2 (芝/ダート/障害)
weather_encoded : 0-3 (晴/曇/雨/小雨)
condition_encoded : 0-3 (良/稍重/重/不良)
race_class_encoded : 0-8 (新馬〜G1)
direction_encoded : 0-2 (右/左/直線)
sex_encoded : 0-2 (牡/牝/セ)
未来情報ブラックリスト(推論時に強制除外)
finish_position, finish_time, time_seconds
last_3f, last_3f_time, last_3f_rank, last_3f_rank_normalized
corner_1/2/3/4, corner_positions_list
margin, prize_money, actual_finish
最終列数
| フェーズ | 列数 |
|---|---|
| DB ロード直後 | 83列 |
| add_derived_features 後 | ~125列 |
| add_ultimate_features 後 | ~137列 |
| LightGBM前処理後 | ~110列(学習時) |
| 推論時(未来情報除外後) | ~87-90列 |
| target | 形式 | 意味 |
|---|---|---|
win | 0/1 | 1着 = 1(デフォルト) |
place3 | 0/1 | 3着以内 = 1 |
win_tie | 0/1 | 複勝(3着以内タイ扱い) = 1 |
speed_deviation | float | タイム偏差(回帰問題)→ softmax で確率変換 |
{
"objective": "binary",
"metric": "auc",
"learning_rate": 0.05,
"num_leaves": 31,
"min_data_in_leaf": 20,
"feature_fraction": 0.8,
"bagging_fraction": 0.8,
"bagging_freq": 5,
"random_state": 42,
}
# num_boost_round: 1000 (early_stopping で実際は 200-400 前後)
# stopping_rounds: 50
n_trials: 30 (--optuna-trials オプションで変更可)
cv_folds: 5
timeout: min(n_trials × 60, 1800) 秒
目標: CV AUC 最大化
探索範囲:
learning_rate: [0.01, 0.1]
num_leaves: [10, 100]
max_depth: [5, 20]
min_data_in_leaf: [10, 50]
feature_fraction: [0.6, 1.0]
bagging_fraction: [0.6, 1.0]
n_estimators: [500, 2000]
保存先: python-api/models/model_win_YYYYMMDD_YYYYMMDD_ultimate.joblib
bundle = {
'model': lgb.Booster,
'optimizer': LightGBMFeatureOptimizer,
'categorical_features': ['venue_encoded', ...],
'feature_columns': ['bracket_number', ...],
'target': 'win',
'ultimate_mode': True,
'metrics': {
'auc': 0.8234,
'cv_auc_mean': 0.8145,
'cv_auc_std': 0.0089,
},
'data_count': 15234,
'training_date_from': '2026-03-01',
'training_date_to': '2026-03-31',
}
POST /api/analyze_race入力
{
"race_id": "202606030304",
"model_id": null
}
内部処理フロー
1. keiba_ultimate.db から race_id でデータ検索
└─ 未登録 → _scrape_shutuba_fallback() でオンデマンドスクレイプ → DB保存
2. JSON → DataFrame (14〜18行 × 83列)
3. odds が全 None → 出馬表を再スクレイプして補完
4. add_derived_features(full_history=DB全履歴)
5. UltimateFeatureCalculator.add_ultimate_features()
6. Quality Gate チェック(下記参照)
7. prepare_for_lightgbm_ultimate(is_training=False)
8. 未来情報列を強制除外
9. model.predict_proba(X)[:, 1] → 生スコア配列
10. キャリブレーション (IsotonicRegression)
11. レース内正規化 (sum = 1.0)
12. EV 計算: ev = p_norm × odds
13. ベッティング推奨生成
Quality Gate チェック内容
| コード | チェック内容 | 重大度 |
|---|---|---|
| Q1 | distance = 0 のレース | ERROR → 処理停止 |
| Q2 | odds が全 None | WARNING → NaN補完で続行 |
| Q3 | 同一レース内で距離の値が揺れている | ERROR → 処理停止 |
| Q4 | 重複馬番 | ERROR → 処理停止 |
| Q5 | popularity が全 None | WARNING → NaN補完で続行 |
スコアの計算式
生スコア (p_raw):
model.predict_proba(X)[:, 1]
キャリブレーション後 (probability):
calibrator.predict(p_raw) ← IsotonicRegression
レース内正規化 (p_norm):
p_norm[i] = probability[i] / sum(probability)
期待値 (expected_value):
EV = p_norm × odds
EV > 1.0 → プラス期待値(買い推奨)
出力(主要部分)
{
"success": true,
"race_info": {
"race_id": "202606030304",
"race_name": "○○特別",
"venue": "阪神",
"distance": 1600,
"track_type": "芝",
"num_horses": 14
},
"horses": [
{
"horse_number": 3,
"horse_name": "xxxxxx",
"probability": 0.2543,
"p_norm": 0.2511,
"predicted_rank": 1,
"odds": 4.5,
"popularity": 2,
"expected_value": 1.13
}
],
"betting_recommendations": {
"単勝": [{"horse_no": 3, "expected_value": 1.13}],
"馬連": [{"combination": "3-7", "expected_value": 2.45}],
"三連複": [{"combination": "3-7-11", "expected_value": 5.2}]
}
}
Kelly % = (p × odds - 1) / (odds - 1)
フラクショナルKelly = Kelly % × 0.25 (破産リスク軽減)
上限 = min(Kelly%, 5%)
推奨賭け額 = 銀行残高 × フラクション後Kelly%
例)確率 p=30%, オッズ=3.0, 銀行残高 100万円
Kelly = (0.3 × 3 - 1) / (3 - 1) = 20%
フラクション後 = 20% × 0.25 = 5%(上限に一致)
推奨賭け額 = 100万円 × 5% = 5万円
| リスク設定 | 1レース最大資金割合 |
|---|---|
conservative | 2% |
balanced(デフォルト) | 3.5% |
aggressive | 5% |
アクション判定(以下いずれかを満たすと "勝負"):
難易度スコア >= 0.7 → "勝負" (予算の 80% を使用)
max_EV >= 4.0 かつ max_prob >= 0.25 → "勝負"
max_EV >= 6.0 → "勝負"
それ以外 → "通常" (予算の 40% を使用)
"勝負" かつ 予算 >= 5,000円 → 1,000円単位
"勝負" かつ 予算 >= 3,000円 → 500円単位
"通常" かつ 予算 >= 3,000円 → 200円単位
デフォルト → 100円単位
/predict-batch)1. 日付入力 → GET /api/races/by_date?date=YYYYMMDD
2. データ未取得の場合 → POST /api/scrape でジョブ開始
→ GET /api/scrape/status/{job_id} を3秒間隔でポーリング
3. レース一覧表示(会場フィルタ付き)
4. モデル選択 → GET /api/models?ultimate=true
5. 複数レース選択 → [一括予測開始]
→ POST /api/analyze_races_batch (レースIDリスト)
6. 結果表示: 馬別確率・EV・オッズ・推奨馬券
7. 購入 → POST /api/purchase
/race-analysis)1. 日付選択 → GET /api/races/by-date?date=YYYYMMDD
2. レース選択 → POST /api/analyze_race(ML予測)
└─ 予測失敗時フォールバック → GET /api/races/{race_id}/horses(出走表のみ表示)
3. 予測結果タブ: 馬別確率・EV・推奨馬券
4. 特徴量分析タブ → GET /api/debug/race/{race_id}/features
5. キャッシュ済み結果は再計算ボタンで強制リフレッシュ可能
/data-collection)1. ローカルFastAPI稼働チェック → GET /api/scrape/health
2. 期間指定入力 (開始年月〜終了年月)
3. [Dry-run] → POST /api/scrape (`dry_run=true`、外部HTTPなし)
4. [取得開始] → POST /api/scrape (`force_rescrape=false`固定でジョブ開始)
→ GET /api/scrape/status/{job_id} ポーリング (3秒間隔)
5. [取得を停止] → POST /api/scrape/cancel/{job_id}(保存済みデータを残し、安全な区切りで協調停止)
6. 実行状態が不明な場合だけ同じstatus APIで安全に再確認(確認完了まで新規実行をロック)
7. 最新fetch summary 1件 → GET /api/scrape/history
8. 取得済み3指標 → GET /api/data-stats
通常画面には上記だけを表示します。Refresh/P0/Targeted Refetch計画、Live Validation、Review Queue、全履歴、最近取得一覧/詳細、特徴量プロファイリングのページ/APIは保守用として存続しますが、通常のデータ収集画面からは非表示です。
/production-readiness)1. 保守用URL `/production-readiness` を直接開き、操作境界で本人確認
2. Admin 権限で [本番前チェックを実行]
3. POST /api/production-readiness
4. read-only checks を順次実行
- Frontend build
- FastAPI health / scrape health
- analyze_race smoke / smoke suite summary
- secret scan (Notion token prefix)
- git status 注意
- write flag / APP_ENV safety
5. pass / warn / fail / unknown をカード表示
- 認証トークン未設定時の 401/403 は auth-required (warn) として表示
- KEIBA_AUTH_BEARER_TOKEN 設定時は認証必須 smoke を通常の pass/fail で評価
6. write API は呼ばない(sandbox write-readback は別管理)
/train)1. `/home` の [02 モデル作成] を開き、必要な場合は操作境界で本人確認
2. 条件設定 (target: win / place3 / win_tie / speed_deviation / 期間 / Optuna試行数 / CV分割数)
3. [学習開始] → POST /api/ml/train/start(Next API)
4. GET /api/ml/train/status/{job_id} ポーリング(Next API)
5. 完了 → AUC / LogLoss / CV統計 表示
6. 特徴量ラボへのリンク → /feature-lab
/feature-lab)1. ターゲット選択 (win / place3 / speed_deviation)
2. サマリータブ → GET /api/features/summary
- 未来フィールド数・スクレイプ列数・FE特徴量数・除外列数・ステージ別内訳
3. 重要度タブ → GET /api/features/importance?target=***&top_n=**&importance_type=gain|split
- 上位N件の特徴量重要度バー表示・ツールチップで説明
4. カバレッジタブ → GET /api/features/coverage?target=***
- カタログ有効特徴量のモデル内一致率確認
- 不足特徴量・余剰特徴量をバッジ表示
/data-view)1. 日付範囲・会場フィルタでレース検索
2. レース一覧表示 → クリックで出走馬詳細
3. GET /api/races/{race_id}/horses(ML推論なし)
/dashboard)1. 購入履歴の集計・回収率サマリー表示
2. GET /api/purchase_history, GET /api/statistics
/home)1. Adminロールでは 01データ取得・02モデル作成・03予測実行・04成績確認・05ユーザー管理を表示
2. 一般ユーザーには03予測実行・04成績確認だけを表示
3. 画面モードの切り替え操作は設けない
4. Admin専用機能を開く操作境界で、現在のアカウントのパスワードを確認
5. 確認grantは15分有効で、再読み込み後も同一セッション内だけ復元
6. 05ユーザー管理はread-onlyとし、Admin/Userロール変更UIは表示しない
7. 旧 `/admin` は `/home` へリダイレクト
Admin専用の01・02・05は一般ユーザー(Premiumを含む)に表示しません。既存クライアントとの互換性のため、一部の運用APIに残るPremiumOrAdmin認可は別契約であり、管理画面を開く権限には使用しません。
操作境界のパスワード確認はページ表示前のUI pre-checkです。ユーザー一覧APIは短期grantも検証し、その他の操作APIは既存のロール認可・環境フラグ・承認状態を正本とします。
| ロール | 権限 |
|---|---|
user | 一般ユーザー(予測・閲覧) |
admin | 管理者(5機能を表示し、管理操作の入口で本人確認。実行可否は各APIの認可・環境フラグ・承認状態にも従う) |
| Method | Path | 説明 |
|---|---|---|
| POST | /api/scrape | スクレイプ同期実行(結果を直接返却、土日月のみ対応) |
| POST | /api/scrape/start | Admin用スクレイプ開始(非同期ジョブ) |
| GET | /api/scrape/status/{job_id} | スクレイプ進捗取得 |
| POST | /api/scrape/cancel/{job_id} | 所有者Adminによる永続的な協調停止要求 |
| GET | /api/races/by_date?date=YYYYMMDD | 指定日のレース一覧 |
| GET | /api/races/recent?limit=50 | 最近取得したレース一覧(軽量) |
| GET | /api/races/{race_id}/horses | 出走馬一覧(ML推論なし) |
| POST | /api/analyze_race | 単一レース分析・予測 |
| POST | /api/analyze_races_batch | 複数レース一括分析 |
| POST | /api/train/start | モデル学習開始(非同期ジョブ) |
| GET | /api/train/status/{job_id} | 学習進捗取得 |
| GET | /api/models | 保存済みモデル一覧 |
| GET | /api/data_stats?ultimate=true | DB統計(レース数・馬数・最終取得日) |
| POST | /api/purchase | 購入推奨の保存 |
| GET | /api/purchase_history | 購入履歴取得 |
| GET | /api/statistics | 購入統計・回収率サマリー |
| GET | /api/debug/race/{race_id} | 生データ確認(Premium) |
| GET | /api/debug/race/{race_id}/features | 特徴量確認(Premium) |
| GET | /api/features/catalog | 特徴量カタログ全件取得 |
| GET | /api/features/summary | 特徴量カタログ統計(カウント・ステージ別) |
| GET | /api/features/importance | モデル特徴量重要度(gain/split) |
| GET | /api/features/coverage | カタログ特徴量のモデル内カバレッジ検証 |
python-api/.venv (FastAPI 用)FastAPI サーバー(ポート 8000)
cd python-api
python main.py
# または VS Code タスク「Start FastAPI」
Next.js フロントエンド(ポート 3000)
npm run dev
# または VS Code タスク「Start Next.js」
.env.local)# Supabase(オプション: 未設定でもローカル動作可)
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
# 管理操作の短期確認cookie専用(32文字以上を推奨。未設定時はservice role keyを使用)
ADMIN_MODE_SIGNING_SECRET=
# FastAPI エンドポイント
NEXT_PUBLIC_API_URL=http://localhost:8000
1. FastAPI 起動 (python-api/main.py)
2. Adminロールで `/home` の5機能を確認
3. /data-collection を開き、操作境界の本人確認後にデータ取得(期間指定)
4. /train ページでモデル状態を確認(通常のProduction環境では学習開始は無効)
5. /predict-batch ページで当日レース予測
日次の実行確認は、以下の順番を推奨します。
Notebook E2E audit
↓
Analyze Race API smoke
↓
Race-list proxy smoke
↓
Race preflight smoke
↓
Race dry-run smoke
↓
Payload contract diff
↓
Write guard smoke
↓
Notion output
検証時に実行するコマンド
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
npm run lint
npm run build
python -m compileall -q python-api scripts
python scripts/run_keiba_smoke_suite.py
python scripts/smoke_analyze_race_api.py
python scripts/smoke_notion_report_api.py
python scripts/run_keiba_notebook_e2e.py --mode audit
git grep -n -I "<notion-token-prefix>"
git status --short
認証必須 endpoint を厳密評価する場合:
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
$env:KEIBA_AUTH_BEARER_TOKEN="<bearer-token>"
python scripts/run_keiba_smoke_suite.py
python scripts/smoke_analyze_race_api.py
注意:
KEIBA_AUTH_BEARER_TOKEN の値はログや JSON summary に出力しないUI での本番前チェック手順
1. 保守用URL /production-readiness を直接開く
2. 操作境界で現在のパスワードを確認
3. [本番前チェックを実行] を押す
4. fail/warn があるカードの summary を確認
5. write flag が false であることを確認
6. sandbox write-readback は別管理であることを確認
ここでのread-onlyは、業務DBやproduction/base tableへ書き込まないという意味です。build・compile・smokeの実行により、ローカル生成物や計算負荷が発生する場合があります。
本番運用ルール
NETKEIBA_RACE_WRITE_ENABLED=falseALLOW_STAGING_WRITE=falseAPP_ENV=productionP1-16 sandbox runtime 確認コマンド
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python scripts/smoke_netkeiba_race_write_guard.py --expect-sandbox-precheck
python scripts/smoke_netkeiba_race_write_guard.py --expect-sandbox-write-readback
注意:
1) Notebook E2E audit
実行順は依存関係に合わせて 00 → 01 → 02 → 03 → 05 → 04 → 06 → 07 → 08
です。特徴量分析の04は、学習モデルを生成する05の後に実行します。
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_notebook_e2e.py --mode audit
結果確認:
Get-Content reports\generated\notebooks\keiba_notebook_e2e_result.json -Raw
2) Analyze Race API smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_analyze_race_api.py
3) Race-list proxy smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_list_proxy.py
4) Race preflight smoke (contract-only mode)
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_preflight.py
判定ルール(preflight):
strict モード(non-ready も fail):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_preflight.py --fail-on-nonready
5) Race dry-run smoke (contract-only mode)
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_dry_run.py
判定ルール(dry-run):
strict モード(non-ready も fail):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_dry_run.py --fail-on-nonready
6) Payload contract diff
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\compare_netkeiba_race_payload_contract.py
判定ルール(payload diff):
7) Write guard smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py
判定ルール(write guard):
P1-12 writer-stub 契約(enabled 時):
P1-13 sandbox write(明示実行時のみ):
sandbox_write=true + target_mode=sandbox 指定時のみ sandbox write 候補P1-14 sandbox precheck(read-only):
GET /api/netkeiba/race/sandbox/precheck で sandbox readiness を確認sandbox_netkeiba_racessandbox_netkeiba_race_resultssandbox_netkeiba_race_payoutsrace_id, data|payload)write_performed=falseP1-14.5 sandbox DDL / migration plan(手動適用のみ):
docs/migrations/netkeiba_sandbox_tables.sql手動適用(SQLite, 例):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe -c "import sqlite3,pathlib; db=pathlib.Path('keiba/data/keiba_ultimate.db'); sql=pathlib.Path('docs/migrations/netkeiba_sandbox_tables.sql').read_text(encoding='utf-8'); con=sqlite3.connect(str(db)); con.executescript(sql); con.commit(); con.close(); print('applied:', db)"
rollback / drop(手動):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe -c "import sqlite3,pathlib; db=pathlib.Path('keiba/data/keiba_ultimate.db'); con=sqlite3.connect(str(db)); con.executescript('BEGIN; DROP TABLE IF EXISTS sandbox_netkeiba_race_payouts; DROP TABLE IF EXISTS sandbox_netkeiba_race_results; DROP TABLE IF EXISTS sandbox_netkeiba_races; COMMIT;'); con.close(); print('dropped sandbox tables from:', db)"
precheck ready 条件(P1-15 着手ゲート):
status=ready になるまで write/readback には進まないP1-15 実施結果(手動DDL適用後の ready 確認):
docs/migrations/netkeiba_sandbox_tables.sql を手動適用python scripts/smoke_netkeiba_race_write_guard.py --expect-sandbox-precheck
verdict=pass / verdict_reason=sandbox-precheck-readyP1-16 実施内容(sandbox write 後 readback 検証):
sandbox_netkeiba_racessandbox_netkeiba_race_resultssandbox_netkeiba_race_payoutsrace_id + idempotency_keyrecords_written と readback count 一致idempotency_key 一致payload_hash 一致audit_payload 存在status=sandbox-readback-mismatch禁止事項(継続):
races, race_results, race_payouts) への write/readback は禁止feature flag ON の限定検証(永続化しない):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro\python-api
$env:NETKEIBA_RACE_WRITE_ENABLED = "true"
$env:ALLOW_STAGING_WRITE = "true"
$env:APP_ENV = "staging"
.venv\Scripts\python.exe main.py
別ターミナルで enabled-mode smoke:
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-enabled
enabled-mode では次を追加確認:
enabled-mode 付き suite(任意):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py --verify-write-guard-enabled
flag-only blocked 確認(任意):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro\python-api
$env:NETKEIBA_RACE_WRITE_ENABLED = "true"
$env:ALLOW_STAGING_WRITE = "false"
$env:APP_ENV = "development"
.venv\Scripts\python.exe main.py
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-flag-only
production 強制ブロック確認(任意):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro\python-api
$env:NETKEIBA_RACE_WRITE_ENABLED = "true"
$env:ALLOW_STAGING_WRITE = "true"
$env:APP_ENV = "production"
.venv\Scripts\python.exe main.py
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-production-block
staging lock 不足ブロック確認(任意):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro\python-api
$env:NETKEIBA_RACE_WRITE_ENABLED = "true"
$env:ALLOW_STAGING_WRITE = "false"
$env:APP_ENV = "staging"
.venv\Scripts\python.exe main.py
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-staging-lock-missing
suite optional:
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py --verify-write-guard-flag-only --verify-write-guard-production-block --verify-write-guard-staging-lock-missing
sandbox write smoke(明示実行時のみ):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro\python-api
$env:NETKEIBA_RACE_WRITE_ENABLED = "true"
$env:ALLOW_STAGING_WRITE = "true"
$env:APP_ENV = "staging"
.venv\Scripts\python.exe main.py
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-sandbox-write
注意:
sandbox write + readback smoke(明示実行時のみ):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-sandbox-write-readback
注意:
sandbox-readback-mismatch を返すsandbox precheck smoke(read-only, 明示実行時のみ):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_netkeiba_race_write_guard.py --expect-sandbox-precheck
suite optional(precheck + sandbox write を明示実行):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py --verify-write-guard-sandbox-precheck --verify-write-guard-sandbox-write
suite optional(sandbox write + readback を明示実行):
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py --verify-write-guard-sandbox-write-readback
8) Notion output
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\upload_to_notion.py
8.5) Notion output (Premium/Admin UI)
1. /notion-report を開く
2. レポート種別を選択
3. [preview 生成] で内容確認
4. [Notion へ送信] で送信
UI の安全条件:
NOTION_TOKEN / NOTION_PARENT_PAGE_ID は server-side env のみで利用4038.6) Model Redesign Workbench (MVP / read-only)
1. /model-redesign-workbench を開く
2. active model summary / metrics / feature warnings / 改善提案 preview を確認
3. 必要に応じて /notion-report へ遷移して共有
MVP 制約:
.active_model.json は自動変更しない.joblib 作成/上書きは行わない統合実行(Smoke Suite)
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py
Notion output UI/API smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_notion_report_api.py
補足:
KEIBA_AUTH_BEARER_TOKEN 未設定時は auth-required として warnKEIBA_AUTH_BEARER_TOKEN_NONPREMIUM を設定すると non-Premium/Admin の 403 検証を実施sent または config-missing/warn を許容Model redesign workbench smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_model_redesign_workbench.py
補足:
KEIBA_AUTH_BEARER_TOKEN 未設定時は auth-required として warnKEIBA_AUTH_BEARER_TOKEN_NONPREMIUM 未設定時の non-Premium 403 検証は warn 扱いfilePath, reportPath, modelPath, path, sourcePath) は拒否されることを検証not-implemented または disabled を検証Fetch summary history smoke
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\smoke_fetch_summary_history.py
補足:
KEIBA_AUTH_BEARER_TOKEN 未設定時は auth-required として warnlimit パラメータの適用、secret非露出、read-only(scrape_jobs row count不変)を検証warn 扱いで fail にはしないScrape speed benchmark (small + 10y estimate)
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python scripts\benchmark_scrape_speed.py --preset small
python scripts\benchmark_scrape_speed.py --preset estimate-10y
補足:
--max-live-days 既定 7)fetch_pipeline の既存実装を使用reports/scrape_benchmark_summary.jsonreports/*.json など生成物はコミットしないstrict preflight で統合実行する場合:
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
python-api\.venv\Scripts\python.exe scripts\run_keiba_smoke_suite.py --strict-preflight
UI連携 最終確認
cd C:\Users\yuki2\Documents\ws\keiba-ai-pro
npm run lint
npm run build
python scripts/run_keiba_smoke_suite.py
git grep -n -I "secret-prefix-check"
git status --short
確認方針:
notebooks/00_config.ipynb〜notebooks/08_reporting.ipynbreports/generated/notebooks/executed/reports/generated/notebooks/keiba_notebook_e2e_result.jsonreports/analyze_race_smoke_result.jsonreports/netkeiba_race_list_proxy_smoke_result.jsonreports/netkeiba_race_preflight_smoke_result.jsonreports/netkeiba_race_dry_run_smoke_result.jsonreports/netkeiba_race_payload_contract_diff.jsonreports/netkeiba_race_write_guard_smoke_result.jsonreports/netkeiba_race_write_guard_enabled_smoke_result.jsonreports/netkeiba_race_write_guard_flag_only_smoke_result.jsonreports/netkeiba_race_write_guard_production_smoke_result.jsonreports/netkeiba_race_write_guard_staging_lock_smoke_result.jsonreports/netkeiba_race_write_guard_sandbox_precheck_smoke_result.jsonreports/netkeiba_race_write_guard_sandbox_write_smoke_result.jsonreports/netkeiba_race_write_guard_sandbox_write_readback_smoke_result.jsonreports/keiba_smoke_suite_result.jsonreports/notion_report_smoke_result.jsonreports/fetch_summary_history_smoke_result.jsonreports/e2e_logs/docs/specs/model-redesign-workbench.mddocs/model-retrain-approval-design.mddocs/model-redesign-workbench.mdsrc/lib/model-retrain-approval-types.ts.joblib create/overwrite.active_model.json 更新ロードマップ(次フェーズ):
retrain_dry_run / approval / job submit の契約は型で固定済み追跡対象とローカル生成物の境界、依存環境およびDBの保管方針は
ローカルディレクトリ構成を正とします。
API用Python環境はnpm run setup:api、研究・Notebookを含む完全な環境はnpm run setup:researchでpython-api/.venvへ再構築できます。
keiba-ai-pro/
├── src/ # Next.js フロントエンド(Git追跡)
├── python-api/ # FastAPIと唯一のPython環境 .venv/
├── keiba/ # MLコードとローカル研究DB
├── notebooks/ # 追跡するNotebookソース
├── reports/ # evidence(追跡)/ generated(生成物)
├── scripts/ # 起動・検証・運用スクリプト
├── supabase/ # DBスキーマ定義
└── docs/ # ドキュメント
# PowerShell: 全サーバー起動
.\scripts\start-all.ps1
# PowerShell: 全サーバー停止
.\scripts\stop-all.ps1
# 開発モード(Next.jsのみ)
.\scripts\start-dev.ps1
バッチファイル版 (start-all.bat, stop-all.bat, start-dev.bat) も同等の動作をします。
| スクリプト | 用途 |
|---|---|
scripts/check_server.ps1 | サーバー起動状態の確認 |
scripts/create-desktop-shortcut.ps1 | デスクトップショートカット作成 |
scripts/start_playwright_server.ps1 | Playwright E2E テストサーバー起動 |
scripts/stop_playwright_server.ps1 | Playwright サーバー停止 |
本番 DB (keiba/data/keiba_ultimate.db) のデータ補完・診断のためのスクリプト群です。
patch_missing_data.py)注意: 実行中は移動不可のため、ルートディレクトリに配置しています。
# 確認のみ(DBは更新しない)
python-api\.venv\Scripts\python.exe patch_missing_data.py --dry-run
# Phase 指定実行
python-api\.venv\Scripts\python.exe patch_missing_data.py --phase 3
# 全フェーズ実行(バックグラウンド)
Start-Process python-api\.venv\Scripts\python.exe -ArgumentList "patch_missing_data.py" `
-RedirectStandardOutput tools\logs\patch_log.txt `
-RedirectStandardError tools\logs\patch_log_err.txt
| Phase | 対象 | 内容 |
|---|---|---|
| 0 | races_ultimate | レース名・天気・開催情報補完 |
| 1 | race_results_ultimate | 馬名・馬番・騎手など基本情報補完 |
| 2 | race_results_ultimate | 血統情報(sire/dam/damsire)補完 |
| 3 | race_results_ultimate | 前走情報補完(日付フィルタ付き) |
| ファイル | 用途 |
|---|---|
tools/check_dbs.py | DB一覧とレコード数確認 |
tools/inspect_db.py | DBフィールド別充填率詳細 |
tools/check_missing.py | 欠損フィールドチェック |
tools/run_local_pipeline.py | ローカルパイプライン実行テスト |
tools/repair_db.py | DB修復ユーティリティ |
tools/patch_horse_names.py | 馬名データ修正 |
tools/rescrape_horse_stats.py | 馬統計の再スクレイプ |
tools/retrain_local.py | ローカル再学習 |
tools/verify_pipeline_full.py | フルパイプライン検証 |
tools/leakage_audit.py | データリーク監査 |
開発・デバッグ・品質確認のためのスクリプト群です(本番運用向けではありません)。
# 充填率確認(パッチ完了後に必ず実行)
python-api\.venv\Scripts\python.exe validation\check_null_rates3.py
# データリーク診断
python-api\.venv\Scripts\python.exe validation\check_date_leakage.py
# 特徴量詳細確認
python-api\.venv\Scripts\python.exe validation\check_features_detail.py
# 最終特徴量一覧
python-api\.venv\Scripts\python.exe validation\check_final_features.py
| ファイル | 用途 | 実行タイミング |
|---|---|---|
check_null_rates3.py | DB全JSONキー充填率確認 | パッチ実行後・再学習前 |
check_date_leakage.py | 日付ズレ・データリーク診断 | 特徴量修正後 |
check_features_detail.py | 生特徴量 vs モデル入力の詳細比較 | FE変更後 |
check_final_features.py | 最終モデル入力特徴量の一覧 | モデル再学習前 |
これらのスクリプトはDBを読み取り専用で参照します(更新なし)。
詳細なドキュメントは docs/README.md を参照してください。
| ディレクトリ | 内容 |
|---|---|
| docs/setup/ | セットアップ・初期設定ガイド |
| docs/deployment/ | デプロイガイド (Vercel + Railway) |
| docs/development/ | 開発者向けドキュメント |
| docs/features/ | 機能・API仕様 |
develop ← 日常開発ブランチ(デフォルト作業場所)
│
│ git merge develop --ff-only
▼
main ← 安定版(PR・マージ後の状態)
│
│ git merge main --ff-only
▼
release ← 本番ブランチ(デプロイ済み)
│
│ git tag vN && git push origin vN
▼
GitHub Release(release.yml が自動作成)
| ブランチ | 役割 |
|---|---|
develop | 日常的な開発・実験 |
main | レビュー済み・テスト通過の安定コード |
release | デプロイ済み本番コード(タグ v1, v2, … が打たれる) |
# develop で作業してコミット
git checkout develop
git add .
git commit -m "feat: ○○機能を追加"
git push origin develop
# main にマージ
git checkout main
git merge develop --ff-only
git push origin main
# release にマージしてタグ
git checkout release
git merge main --ff-only
git push origin release
git tag v4
git push origin v4
# feature ブランチを作成
git checkout develop
git checkout -b feature/my-feature
| プレフィックス | 用途 |
|---|---|
feat: | 新機能 |
fix: | バグ修正 |
refactor: | リファクタリング |
chore: | 設定・依存関係変更 |
docs: | ドキュメント更新 |
ci: | GitHub Actions 変更 |
test: | テスト追加・修正 |
詳細は .github/skills/git-workflow/SKILL.md を参照。
"7-7-4-3" |
corner_positions_list | list[int] | [7, 7, 4, 3] |
prize_money | float | 30000 |
| GET | /api/realtime-odds/{race_id} | リアルタイムオッズ取得(キャッシュ) |
| POST | /api/realtime-odds/refresh | オッズ強制再取得(Playwright) |
| POST | /api/export/bet-list | 馬券購入リスト出力(JSON) |
| POST | /api/export/bet-list/csv | 馬券購入リスト出力(CSV) |
| POST | /api/profiling/start | ydata-profiling レポート生成ジョブ開始(保守用、通常UI非表示) |
| GET | /api/profiling/status/{job_id} | プロファイリングジョブ進捗(保守用、通常UI非表示) |
| GET | /api/profiling/html/{job_id} | プロファイリング HTML レポート取得(保守用、通常UI非表示) |
| POST | /api/backfill/nar-pedigree | NAR血統情報バックフィル |
| POST | /api/backfill/coat-color | 毛色情報バックフィル |
| GET | /health | サーバー死活確認 |