mcpField
F
football-betting-analysis

football-betting-analysis

github.com/josearroyaveデータ・データベース0268 回インストール28 週間前に更新
リポジトリ

football-betting-analysis について

サッカーの試合前および進行中の分析を行い、FlashScore のデータから根拠に基づく構造化レポートを作成するスキルです。試合情報を正規化し、断定や予想の保証ではなく確率的な表現を用います。

主な機能

  1. 01チームと日付から試合を検索
  2. 02FlashScore データから正規化した試合コンテキストを作成
  3. 03オッズ、調子、ラインアップ、大会情報を統合
  4. 04データソースごとに根拠を表示
  5. 05確率表現による構造化分析を生成

活用シーン

  1. 01特定の開催前サッカー試合の確認
  2. 02進行中の試合の分析
  3. 03オッズと調子、試合背景の比較
  4. 04分析前に利用できないデータを確認

MCP 対応

試合の検索とデータ取得に FlashScore MCP を使用し、前処理スクリプトを介して情報をまとめます。このスキル自体は MCP ツールを提供しません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/josearroyave/agent-skills --skill football-betting-analysis

インストール後、Claude Code で/football-betting-analysisを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
data/
all_tournaments.csv
main_leagues.csv
main_teams.csv
scripts/
build_match_context.py
generate_teams_csv.py

詳細

サッカー賭博分析

1. 概要

サッカーの試合前分析です。自然言語での問い合わせを受け取り、FlashScore MCP で 試合を検索し、前処理スクリプト build_match_context.py を実行して正規化された JSON コンテキストを取得し、確率的な表現を用いた 構造化レポートを作成します。

notstarted および inprogress の試合のみ対象です。 finished の試合には、このスキルは適用されません。

モデルは、試合の検索を除き、エンドポイントを直接呼び出しません。 すでにクリーンアップ、正規化、統合されたデータを final_context で受け取ります。

分析は、利用可能なエビデンスの解釈です。ベットの推奨ではありません。 結果を保証するものでもありません。「可能性があります」「兆候」「示唆しています」のような表現を常に使います。 「勝ちます」「確実です」「オーバーは間違いなく成立します」といった表現は使いません。


2. 使用する場合/使用しない場合

使用する場合:
  • ユーザーが特定のサッカーの試合について試合前分析を求めている場合。
  • 試合の状態が notstarted の場合。
使用しない場合:
  • 試合がすでに finished の場合 → このスキルは適用されません。
  • 特定の試合ではなく、トーナメントやチームについての問い合わせの場合 → 対応できません。そのチームの今後の試合を検索することを提案できます。
  • final_context を生成できなかった場合。

アーキテクチャ上の注記: このバージョンでは、機械学習モデルに基づく予測を利用できません。 設計上、予測レイヤーはオッズ主導です。数値確率はオッズからのみ得られます。 指標は信頼度を調整するだけで、パーセンテージは算出しません。


3. データソース — FlashScore MCP

使用するエンドポイント
エンドポイント用途
Get_Match_Detailsイベント全体+オッズ
Get_Match_H2H直接対戦
Get_Match_Stats履歴内の各試合(matches[i])に対して並列で呼び出し — 集計済みの form.advanced を構築する場合のみ使用
Get_Match_Lineups存在する場合はラインナップ+欠場者の名前と欠場理由を含む missingPlayers
Get_Team_Results試合検索(フォールバック)+チームの試合結果履歴
Get_Team_Fixtures試合検索(優先)— チームの今後の試合
Get_Tournament_Standingsトーナメント内の調子/順位のコンテキスト
Get_Match_Standings_OverUnderオーバー/アンダー順位表
Get_Tournament_Top_Scorersトーナメントの得点王
Get_Match_Standings_Form試合内での最近の調子
FlashScore に存在しないもの(決して捏造してはいけません)
存在しないもの影響
試合前に確認済みのラインアップエンドポイント Get_Match_Lineups は存在しますが、確認済みのラインアップがまだ利用できない場合があります。空の場合もあります。ラインアップを捏造してはいけません。利用可能であれば predictedLineups を使用してください — スクリプトが自動的に処理します。
既往の負傷履歴API には存在しません。捏造してはいけません。
Get_Match_Lineups にデータがない場合の missingPlayersエンドポイントに含まれていない場合 → 理由や選手を捏造してはいけません。

build_match_context.py における normalize_lineups の動作:

スクリプトは、優先度の異なる 2 つの段階でラインアップを処理します。

  1. startingLineups が存在する場合(公式に確認済みのラインアップ):

    • starting_lineups — 確認済みの先発ラインアップ [API]
    • missing_players — 欠場選手とその理由 [API]
    • substitutes — 出場可能な交代選手 [API]
    • predicted_lineups → 空
    • unsure_missing — 空
  2. startingLineups は存在しないが、predictedLineups が存在する場合(予測/未確認のラインアップ):

    • unsure_missing — 欠場が確認されていない選手 [API]
    • missing_players → 欠場選手とその理由 [API]
    • predicted_lineups — 予測ラインアップ [API]
    • starting_lineups → 空
    • substitutes → 空

注: ラインアップが確認済みでなくても、Get_Match_Lineups に missingPlayers(名前+理由)が含まれる場合があります。利用可能であれば、そのデータを使用してください。

分析における各シグナルのソース:

タグ意味
[API]FlashScore MCP のエンドポイントから直接取得したデータ
[ODDS]市場オッズから算出
[IND]API データをもとに算出した指標
[N/A]API で利用できないデータ

4. 分析パイプライン

4.1 クエリの解析

自然言語のクエリから抽出します:

{
  home_team:  string,      // equipo mencionado primero
  away_team:  string,       // equipo mencionado segundo (o null si solo uno)
  date_from:  ISO 8601,    // fecha inicio del rango
  date_to:    ISO 8601,    // fecha fin del rango
  league:     string|null,  // competición mencionada (o null)
  analysis_mode: "prematch"  // único modo soportado
}

解析ルール:

状況ルール
ユーザーが「今日」と言った場合date_from = hoy、date_to = hoy
ユーザーが「明日」と言った場合date_from = mañana、date_to = mañana
ユーザーが「今週末」と言った場合date_from = viernes、date_to = domingo
ユーザーが曜日を指定せずに「今週」と言った場合date_from = lunes、date_to = domingo
チームが1つだけ挙げられた場合7日間の範囲で、そのチームの次の試合を検索します
アクセント付きの名前(「Atletico」、「Inter」)検索前にアクセントを除去して正規化します
部分的な名前(「Barça」、「Atleti」)結果の home_team/away_team とファジーマッチングします
大会名が挙げられた場合(「la league」、「champions」)検索後に league_id で絞り込みます

| status = "notstarted" | analysis_mode = "prematch" → 続行 | | status = "inprogress" | → ライブ分析を続行します。 | | status = "finished" | → 該当しません。ユーザーに通知します。 |

チームは1つだけ一致し、相手チームの候補が複数ある場合: → 必ず確認します。「[チーム] 対 [候補 A] と [候補 B] のどちらをお探しですか?」

4.2 試合の検索(フェーズ 1 — MCP を直接使用するのはこのフェーズのみ)

ステップ 1 — チーム検索 API で検索:

1. Normalizar nombres de ambos equipos (minúsculas, sin acentos, sin puntos)
2. Usar la API de búsqueda:
   GET https://s.livesport.services/api/v2/search/?q=<query>&lang-id=13&type-ids=1,2,3,4&project-id=202&project-type-id=1
3. Filtrar resultados: sport.id=1 (fútbol), type.id=2 (Team)
4. Retorna: team_id, name, url, country, country_id
   → Si 1 resultado: proceed con ese team_id
   → Si >1 resultado: clarificación obligatoria ("¿Buscás [equipo A] o [equipo B]?")
   → Si 0 resultados: ir a paso 5 (fallback a /data/main_teams.csv)

検索 API 用の正規化:

  • 小文字にする
  • アクセントを除去する:á→a、é→e、í→i、ó→o、ú→u
  • ピリオドを除去する:「Atl.」→「Atl」
  • 余分なスペースを除去する

ステップ 2 — /data/main_teams.csv でチームを検索(フォールバック):

5. Si no se encontró el equipo en la API, buscar en /data/main_teams.csv:
   Normalizar nombres (mismas reglas que arriba)
6. Buscar match exacto en /data/main_teams.csv después de normalizar
   → Si 1 resultado: proceed
   → Si >1 resultado: clarificación obligatoria
7. Si 0 resultados exactos: intentar substring match
   → Si 1 resultado: proceed
   → Si >1 resultado: clarificación obligatoria
8. Si 0 resultados: buscar coincidencias cercanas (similitud string)
   → Calcular similaridad entre nombre buscado y todos los nombres en /data/main_teams.csv
   → Si hay candidatos con similitud ≥ umbral (ej: Levenshtein ratio ≥ 0.6):
      → Si 1 candidato cercano: "¿Quisiste decir [nombre]?"
      → Si >1 candidatos: mostrar top 3: "¿Buscás alguno de estos? [A], [B], [C]"
   → Si ningún candidato cercano: "No encontré '[equipo]'. Verificá el nombre."

類似する候補の提示:

  • 類似度のしきい値:ratio ≥ 0.6(Levenshtein など)
  • 類似度の高い順に最大 3 件の候補を表示する
  • 候補には CSV の元の名前(正規化前)を含める

ステップ 3 — fixtures の重複範囲を検索:

 9. Get_Team_Fixtures(team_id_home, page=1)
10. Get_Team_Fixtures(team_id_away, page=1)
11. Filtrar fixtures del rango date_from..date_to
12. Buscar partido donde home_team_id o away_team_id de un fixture
    coincida con el team_id del otro equipo (overlap)
    → Si 1 resultado: proceed
    → Si >1 resultado: priorizar por fecha más cercana al rango solicitado
    → Si 0: ir a paso 4

ステップ 4 — results にフォールバック:

13. Get_Team_Results(team_id_home, page=1)
14. Get_Team_Results(team_id_away, page=1)
15. Buscar overlap (misma lógica que fixtures)
    → Si 1 resultado: proceed
    → Si >1: priorizar por fecha más cercana
    → Si 0: "No encontré el partido [Home] vs [Away] en las fechas indicadas."

ステップ 5 — 確認と抽出:

16. Verificar estado del evento:
    → status = "notstarted" → proceed con análisis pre-partido
    → status = "inprogress" → proceed con análisis en vivo
    → status = "finished" → "El partido ya finalizó. Esta skill es solo pre-partido y en vivo."
17. Extraer IDs:
    → event_id = del evento encontrado
    → home_team_id = del equipo local
    → away_team_id = del equipo visitante

候補が複数ある場合の優先順位:

  1. ユーザーがリーグを指定した場合は、そのリーグを優先します。
  2. ユーザーが正確な日付を指定した場合は、その日付のみを対象とします。
  3. 該当するチームの組み合わせの試合がちょうど 1 件の場合は、その試合を使用します。
  4. 同じチームの試合が複数ある場合は、クエリの日付範囲に最も近い試合を優先します。 これにより、不要な確認を避けられます。
  5. それ以外の場合は、必ず確認します。
4.3 前処理(フェーズ 2 — build_match_context.py)

分析用データを取得する唯一の方法です。試合データの取得に MCP エンドポイントを直接使用しないでください。

python scripts/build_match_context.py <event_id> <home_team_id> <away_team_id>

スクリプトの処理内容:

  1. FlashScore のエンドポイントへの呼び出しをすべて内部で実行します
  2. 両チームの結果履歴から H2H を構築します
  3. 直近の状況を集計します(GF、GC、over、BTTS、ホーム/アウェイ別) — form.basic
  4. Get_Match_Lineups から欠場情報を抽出し、正規化します
  5. 実際に存在する odds マーケットを検証し、存在しないものをブロックします
  6. 履歴内の各試合の高度な統計(シュート、ポゼッション、xG など)を統合します — form.advanced の構築にのみ使用します(個々のデータは最終 JSON に出力されません)
  7. チームごとの過去の選手統計を集計します(player_stats.as_historical_home/away)
  8. トーナメントのコンテキスト(順位、直近の状況、over/under の順位表)を取り込みます
  9. 整合性に関する警告を出力します
  10. すべての情報を 1 つの JSON(final_context)にまとめます

スクリプトの出力 — final_context:

{
  "meta": {
    event_id, home_team_id, away_team_id, generated_at
  },
  "match": {
    event_id,
    home_team: { id, name, event_participant_id },
    away_team: { id, name, event_participant_id },
    tournament: { id, stage_id, name },
    country, referee, timestamp, datetime, status,
    scores: {
      home, away,
      home_total, away_total,
      home_1st_half, away_1st_half,
      home_2nd_half, away_2nd_half,
      home_extra_time, away_extra_time,
      home_penalties, away_penalties
    },
    "lineups": {
      home: {
        formation,              // formación reportada (de predictedFormation)
        lineup_count,           // cantidad de jugadores en alineación
        // -- Si hay alineaciones oficiales (startingLineups existe): --
        starting_lineups: [...],       // alineación titular oficial [API]
        missing_players: [...],        // jugadores ausentes con motivo [API]
        substitutes: [...],            // sustitutos disponibles [API]
        predicted_lineups: [],        // vacío
        unsure_missing: [],           // vacío
        // -- Si NO hay oficiales pero hay predicción (predictedLineups): --
        starting_lineups: [],          // vacío
        missing_players: [],           // vacío
        substitutes: [],               // vacío
        predicted_lineups: [...],      // alineación predicha [API]
        unsure_missing: [...],         // ausencias no confirmadas [API]
      },
      away: { formation, lineup_count, starting_lineups, missing_players, substitutes, predicted_lineups, unsure_missing },
      warnings: string[] | null
    },
    "preview": string | null,  // texto de previa del partido — web scraping de FlashScore (DOM o contentParsed embebido)
    // --- inprogress or finished only ---
    "summary": object | null,    // eventos clave del partido (goles, tarjetas, etc.) [API]
    "commentary": object | null, // narración minuto a minuto [API]
    "match_stats": object | null, // estadísticas del partido (posesión, tiros, xG, etc.) [API]
    "player_stats": object | null, // stats de jugadores del partido actual [API]
    "live_analysis": {           // solo para status = "inprogress"
      total_goals_over_25: { probability, signal, confidence },
      total_goals_under_25: { ... },
      btts_yes: { ... },
      btts_no: { ... },
      next_goal_home: { ... },
      next_goal_away: { ... },
      next_goal_no_more: { ... },  // explicit class — no more goals in the match
      next_corner: { lean, signal, confidence, mode: "signal_only" },
      total_cards: { lean, signal, confidence, mode: "signal_only" },
      1x2_final: { probabilities: { home, draw, away }, model_version, calibrated, inputs_quality, top_factors, prematch_source, confidence, warnings },
      match_minute: int,
      current_score: { home: int, away: int },
      warnings: []
    } | null,
    warnings: string[] | null
  },
  "odds": {
    available_markets: string[],
    odds_home, odds_draw, odds_away,
    odds_over_25, odds_under_25,
    odds_btts_yes, odds_btts_no,
    warnings: string[]
  },
  "implied_probs": {
    prob_home, prob_draw, prob_away,
    prob_over_25, prob_btts_yes
  },
  "h2h": {
    total_matches,
    home_team_wins,   // partidos ganados por el equipo local del análisis
    away_team_wins,   // partidos ganados por el equipo visitante del análisis
    draws,
    home_team_goals_for,   // goles anotados por el equipo local del análisis en H2H
    away_team_goals_for,   // goles anotados por el equipo visitante del análisis en H2H
    total_goals, both_teams_scored,
    matches: [{
      match_id, timestamp,
      home_score, away_score,
      home_team, away_team,
      tournament_id, tournament_name,
      team_is_home, opponent
    }],
    basic_stats: {               // stats básicos del historial H2H
      // home_team = stats of the team that IS home in the CURRENT match (Bayern)
      // away_team = stats of the team that IS away in the CURRENT match (Real Madrid)
      // form_string/points/home_ppg/away_ppg excluded — not meaningful in h2h context
      home_team: {
        wins, losses, draws,
        gf_avg, gc_avg, total_goals
      },
      away_team: {
        wins, losses, draws,
        gf_avg, gc_avg, total_goals
      },
      both_teams_scored,
      over_25_freq, btts_freq,
      total_matches,
      total_goals
    },
    advanced_stats: {             // stats agregadas del historial (de advanced_stats de cada match)
      // For home_team_results / away_team_results: same flat structure
      // For h2h: split by current-match home/away identity
      // Each sub-dict has: { overall: {...}, first_half: {...}, second_half: {...} }
      home_team: {
        overall: { attack: {...}, defense: {...}, control: {...}, set_pieces_and_territory: {...}, discipline: {...}, duels_and_defending: {...}, efficiency: {...}, derived: {...} },
        first_half: { /* mismo formato que overall */ },
        second_half: { /* mismo formato que overall */ }
      },
      away_team: {
        overall: { attack: {...}, defense: {...}, control: {...}, set_pieces_and_territory: {...}, discipline: {...}, duels_and_defending: {...}, efficiency: {...}, derived: {...} },
        first_half: { /* mismo formato que overall */ },
        second_half: { /* mismo formato que overall */ }
      }
    },
    home_team_player_stats: {
      general: { minutes_total, goals_total, assists_total, shots_total, shots_on_target_total, key_passes_total, tackles_won_total, interceptions_total, ball_recoveries_total, yellow_cards_total, red_cards_total, goals_per_90, assists_per_90, shots_per_90, position_distribution, in_base_lineup_count, substitute_count },
      individual: {
        [player_name]: {
          "position": "Goalkeeper" | "Defender" | "Midfielder" | "Forward" | "Unknown",
          offense:    { GOALS, EXPECTED_GOALS, ASSISTS_GOAL, EXPECTED_ASSISTS, SHOTS_TOTAL, SHOTS_ON_TARGET, BIG_CHANCES_CREATED, BIG_CHANCES_MISSED },
          creation:   { KEY_PASSES, FINAL_THIRD_ENTRIES_TOTAL, BOX_ENTRIES, THROUGH_BALLS },
          possession: { TOUCHES_TOTAL, MATCH_MINUTES_PLAYED, PASSES_TOTAL },
          defense:    { DUELS_WON, DUELS_TOTAL, DUELS_EFFICIENCY, TACKLES_WON, INTERCEPTIONS, BALL_RECOVERIES },
          efficiency: { PASSES_ACCURACY, LONG_BALLS_ACCURACY, CROSSES_ACCURACY, DRIBBLES_EFFICIENCY },
          discipline: { FOULS_COMMITTED, FOULS_SUFFERED, CARDS_YELLOW, CARDS_RED, TURNOVERS, ERRORS_LEAD_TO_SHOT, ERRORS_LEAD_TO_GOAL },
          goalkeeping:{ SAVES_TOTAL, GOALS_CONCEDED, GOALS_PREVENTED, EXPECTED_GOALS_ON_TARGET_FACED, BIG_CHANCES_SAVED },
          derived:    { xg_per_shot, goals_minus_xg, xa_minus_assists, duels_win_pct, actions_per_90 }
        }, ...
      }
    },
    away_team_player_stats: { /* misma estructura */ },
    warnings: string[]
  },
  "home_team_results": {
    matches: [{
      match_id, timestamp,
      goals_for, goals_against, total_goals, both_teams_scored,
      tournament_id, tournament_name,
      team_is_home, opponent
    }],
    basic_stats: {               // stats básicos del historial
      all_matches, form_string, points,
      home_gf_avg, home_gc_avg,
      over_25_freq, btts_freq,
      home_ppg, home_gf_avg, home_gc_avg,
      away_ppg, away_team_gf_avg, away_team_gc_avg,
      total_matches, wins, draws, losses,
      goals_for, goals_against, total_goals, both_teams_scored
    },
    advanced_stats: {            // stats agregadas del historial (de advanced_stats de cada match)
      overall: {
        attack: {
          goals_for_avg, xg_for_avg, xgot_for_avg, xa_for_avg,
          shots_for_avg, shots_on_target_for_avg, shots_off_target_for_avg,
          blocked_shots_for_avg, shots_inside_box_for_avg, shots_outside_box_for_avg,
          big_chances_for_avg, touches_in_opposition_box_avg, hit_woodwork_avg
        },
        defense: {
          goals_against_avg, xg_against_avg, xgot_faced_avg,
          shots_against_avg, shots_on_target_against_avg, shots_off_target_against_avg,
          blocked_shots_against_avg, shots_inside_box_against_avg, shots_outside_box_against_avg,
          big_chances_against_avg, touches_in_opposition_box_against_avg,
          goalkeeper_saves_avg, errors_leading_to_shot_avg, errors_leading_to_goal_avg, goals_prevented_avg
        },
        control: {
          possession_avg, passes_accuracy_avg, passes_completed_avg, passes_attempted_avg,
          long_pass_accuracy_avg, long_passes_completed_avg, long_passes_attempted_avg,
          final_third_pass_accuracy_avg, final_third_passes_completed_avg, final_third_passes_attempted_avg,
          accurate_through_passes_avg
        },
        set_pieces_and_territory: {
          corners_for_avg, corners_against_avg, offsides_for_avg, offsides_against_avg,
          free_kicks_for_avg, free_kicks_against_avg, throw_ins_for_avg, throw_ins_against_avg,
          cross_accuracy_avg, crosses_completed_avg, crosses_attempted_avg
        },
        discipline: {
          yellow_cards_avg, red_cards_avg, cards_total_avg, fouls_committed_avg
        },
        duels_and_defending: {
          tackles_success_pct_avg, tackles_won_avg, tackles_attempted_avg,
          duels_won_avg, clearances_avg, interceptions_avg
        },
        efficiency: {
          shot_accuracy_pct, goal_conversion_pct, big_chance_conversion_pct,
          xg_per_shot, shots_on_target_faced_per_goal_against, save_pct,
          finishing_overperformance, conceding_overperformance
        },
        derived: {
          xg_balance_avg, xg_ratio, shots_share, shots_on_target_share,
          big_chances_balance_avg, corners_balance_avg, discipline_balance_avg
        }
      },
      first_half: { /* mismo formato que overall */ },
      second_half: { /* mismo formato que overall */ },
      warnings: []
    },
    player_stats_as_home: {
      general: { minutes_total, goals_total, assists_total, shots_total, shots_on_target_total, key_passes_total, tackles_won_total, interceptions_total, ball_recoveries_total, yellow_cards_total, red_cards_total, goals_per_90, assists_per_90, shots_per_90, position_distribution, in_base_lineup_count, substitute_count },
      individual: {
        [player_name]: {
          "position": "Goalkeeper" | "Defender" | "Midfielder" | "Forward" | "Unknown",
          offense:    { GOALS, EXPECTED_GOALS, ASSISTS_GOAL, EXPECTED_ASSISTS, SHOTS_TOTAL, SHOTS_ON_TARGET, BIG_CHANCES_CREATED, BIG_CHANCES_MISSED },
          creation:   { KEY_PASSES, FINAL_THIRD_ENTRIES_TOTAL, BOX_ENTRIES, THROUGH_BALLS },
          possession: { TOUCHES_TOTAL, MATCH_MINUTES_PLAYED, PASSES_TOTAL },
          defense:    { DUELS_WON, DUELS_TOTAL, DUELS_EFFICIENCY, TACKLES_WON, INTERCEPTIONS, BALL_RECOVERIES },
          efficiency: { PASSES_ACCURACY, LONG_BALLS_ACCURACY, CROSSES_ACCURACY, DRIBBLES_EFFICIENCY },
          discipline: { FOULS_COMMITTED, FOULS_SUFFERED, CARDS_YELLOW, CARDS_RED, TURNOVERS, ERRORS_LEAD_TO_SHOT, ERRORS_LEAD_TO_GOAL },
          goalkeeping:{ SAVES_TOTAL, GOALS_CONCEDED, GOALS_PREVENTED, EXPECTED_GOALS_ON_TARGET_FACED, BIG_CHANCES_SAVED },
          derived:    { xg_per_shot, goals_minus_xg, xa_minus_assists, duels_win_pct, actions_per_90 }
        }, ...
      }
    },
    player_stats_as_away: { /* misma estructura */ },
    warnings: string[] | null
  },
  "away_team_results": {
    matches: [{
      match_id, timestamp,
      goals_for, goals_against, total_goals, both_teams_scored,
      tournament_id, tournament_name,
      team_is_home, opponent
    }],
    basic_stats: {               // stats básicos del historial
      all_matches, form_string, points,
      home_gf_avg, home_gc_avg,
      over_25_freq, btts_freq,
      home_ppg, home_gf_avg, home_gc_avg,
      away_ppg, away_team_gf_avg, away_team_gc_avg,
      total_matches, wins, draws, losses,
      goals_for, goals_against, total_goals, both_teams_scored
    },
    advanced_stats: {            // stats agregadas del historial (de advanced_stats de cada match)
      overall: { /* mismo formato que en home_team_results */ },
      first_half: { /* mismo formato */ },
      second_half: { /* mismo formato */ },
      warnings: []
    },
    player_stats_as_home: {
      general: { minutes_total, goals_total, assists_total, shots_total, shots_on_target_total, key_passes_total, tackles_won_total, interceptions_total, ball_recoveries_total, yellow_cards_total, red_cards_total, goals_per_90, assists_per_90, shots_per_90, position_distribution, in_base_lineup_count, substitute_count },
      individual: {
        [player_name]: {
          "position": "Goalkeeper" | "Defender" | "Midfielder" | "Forward" | "Unknown",
          offense:    { GOALS, EXPECTED_GOALS, ASSISTS_GOAL, EXPECTED_ASSISTS, SHOTS_TOTAL, SHOTS_ON_TARGET, BIG_CHANCES_CREATED, BIG_CHANCES_MISSED },
          creation:   { KEY_PASSES, FINAL_THIRD_ENTRIES_TOTAL, BOX_ENTRIES, THROUGH_BALLS },
          possession: { TOUCHES_TOTAL, MATCH_MINUTES_PLAYED, PASSES_TOTAL },
          defense:    { DUELS_WON, DUELS_TOTAL, DUELS_EFFICIENCY, TACKLES_WON, INTERCEPTIONS, BALL_RECOVERIES },
          efficiency: { PASSES_ACCURACY, LONG_BALLS_ACCURACY, CROSSES_ACCURACY, DRIBBLES_EFFICIENCY },
          discipline: { FOULS_COMMITTED, FOULS_SUFFERED, CARDS_YELLOW, CARDS_RED, TURNOVERS, ERRORS_LEAD_TO_SHOT, ERRORS_LEAD_TO_GOAL },
          goalkeeping:{ SAVES_TOTAL, GOALS_CONCEDED, GOALS_PREVENTED, EXPECTED_GOALS_ON_TARGET_FACED, BIG_CHANCES_SAVED },
          derived:    { xg_per_shot, goals_minus_xg, xa_minus_assists, duels_win_pct, actions_per_90 }
        }, ...
      }
    },
    player_stats_as_away: { /* misma estructura */ },
    warnings: string[] | null
  },
  "standings": {
    teams: { [team_id]: { position, name, points, wins, draws, losses, goals, goal_difference } },
    warnings: string[] | null
  },
  "overunder_standings": {
    teams: { [team_id]: { over, under, average_goals } },
    warnings: string[] | null
  },
  "form_standings": {
    teams: { [team_id]: { points, form_string } },
    warnings: string[] | null
  },
  "top_scorers": {
    home_scorers: [{ name, player_id, team, goals, assists }],
    away_scorers: [...],
    warnings: string[] | null
  },
  "tournament_top_scorers": {
    home_scorers: [...],
    away_scorers: [...],
    warnings: string[] | null
  }
}

スクリプトの使用ルール:

  • final_context を受け取った後、モデルは MCP エンドポイントを再度呼び出しません。
  • 分析に使用するすべてのデータは JSON から取得します。
  • スクリプトが警告を出力した場合 → モデルはその内容を考慮し、矛盾する内容を述べてはなりません。
  • JSON 内で [N/A] と示されている場合 → モデルはそのまま [N/A] として使用し、データを推測しません。
  • スクリプトは決定論的です。同じ入力に対して、常に同じ出力を生成します。
  • モデルは、正規化済みのデータについて、その意味を説明する場合を除き、再計算や再解釈を行いません。

スクリプトが失敗した場合、または実行できない場合: → 「試合のコンテキストを生成できませんでした。分析を実行できません。」


5. 8 つのレイヤー — 正確な定義

各レイヤーには、入力、計算、必須の出力、縮退時の処理があります。


レイヤー 1 — 基本コンテキスト

入力: odds_home、odds_draw、odds_away、odds_over_25、 odds_btts_yes。

計算:

  • 市場の暗黙の確率:1 / odds
  • Over 2.5 の暗黙の確率:1 / odds_over_25
  • BTTS の暗黙の確率:1 / odds_btts_yes
  • バイアス:明確な本命(>60%)、やや有利な本命(50-60%)、拮抗(<50%)

必須の出力:

## 1. Contexto del Partido
- Partido: [Home] vs [Away]
- Competición: [Liga]
- Fecha/hora: [ISO 8601]
- Estado: [notstarted/inprogress]
---
Mercado dice [ODDS]:
- [Home]: [prob]% | Draw: [prob]% | [Away]: [prob]%
- Over 2.5: [cuota] → prob implícita [prob]%
- BTTS: [cuota] → prob implícita [prob]%
---
Indicadores históricos [IND]:
- Indicadores favorecen: [Home/Away/Equilibrado]
- Soporte histórico: [bajo/medio/alto]
Mercado vs datos: [alineados/parcialmente alineados/en conflicto] — [explicación breve]

縮退時の処理:

  • odds がない場合 → 市場と指標の差は使用しません。利用可能な場合に限り、odds のみを使用します。
  • どちらもない場合 → レイヤー 1 は大幅に縮退します。

postmatch 用の出力:

Mercado esperaba [ODDS]:
- [Home]: [prob]% | Draw: [prob]% | [Away]: [prob]%
- Over 2.5: [cuota] → prob implícita [prob]%
- BTTS: [cuota] → prob implícita [prob]%

Resultado vs expectativa [ODDS]/[IND]:
- El resultado [fue alineado / parcialmente alineado / sorpresivo] con las probabilidades del mercado

レイヤー 2 — チームの記述

入力: Get_Team_Results(履歴)、Get_Match_H2H、Get_Tournament_Standings(該当する場合)、final["preview"](ウェブスクレイピング — FlashScore の試合プレビュー文)。

計算:

  • 直近 N 試合のポイント:チームの結果から取得
  • 直近の状況:最近の結果(W/D/L)
  • 平均得点:goals_scored / matches_played および goals_conceded / matches_played
  • Over 2.5 の頻度:直近 N 試合のうち、合計得点が 2.5 を超えた試合を数えます
  • BTTS の頻度:直近 N 試合のうち、両チームが得点した試合を数えます
  • プロフィール:生成したゴールと実際のゴールに基づきます(期待値を上回る/下回る)
  • H2H:Get_Match_H2H が存在する場合に使用します

必須の出力:

## 2. [Qué Viene Pasando / Lo Que Está Pasando]

[Adaptar según estado del partido:]
- status = notstarted → "Qué Viene Pasando"
- status = inprogress → "Lo Que Está Pasando" + marcador actual + minuto
- status = finished → "Lo Que Pasó"

[Home] [IND] [marcador si inprogress]:
  Forma: [form_string] — [pts] pts / [N] pts posibles
  Goles: [home_gf_avg]/[home_gc_avg]
  Over 2.5: [X/N] | BTTS: [X/N] (si hay datos)
  En casa: [home_ppg] ppg | [home_goals]GF / [home_goals]GC
  Perfil: [ofensivo/conservador/equilibrado/inestable]

[ away ] [IND]:
  [mismo formato]

H2H [IND]: [X]PJ — [home_team_wins]V-local [draws]E [away_team_wins]V-visitante | goles local [home_team_goals_for] / visitante [away_team_goals_for]
  [score reciente 1]
  [score reciente 2]

Nota: [si la forma se extrajo solo del evento actual (N=1), indicarlo]

[Para notstarted]: Previa del partido [IND]: [texto de previa de FlashScore — si no disponible: "Previa no disponible [N/A]"]
[Para inprogress]: Eventos recientes: [resumen de últimos eventos del partido — de summary.events]
[Para finished]: Resultado final: [home] [H] - [A] [away] | [marcador final]
[Para postmatch — analysis_mode = "postmatch"]:
  El resultado [confirma/rompe] la tendencia reciente de cada equipo
  Resultado vs forma: [el partido fue consistente/inconsistente con la forma previa]

**Nota:** La previa del partido debe ser concisa. Si excede 3-4 oraciones, resumir los puntos más relevantes para el análisis.

縮退時の処理:

  • home_form が空の場合 → 利用可能な H2H のみを列挙します。直近の状況は N/A とします。
  • H2H がない場合 → H2H セクションを省略します。
  • サンプル数が N < 5 の場合は、必ず注記を含めます。
  • preview が利用できない場合 → テキスト「プレビューを利用できません [N/A]」を使用します。
  • status = inprogress の場合 → 現在のスコアと分数を含む「現在の状況」を使用します。
  • status = finished の場合 → 最終結果を含む「試合の結果」を使用します。
  • analysis_mode = "postmatch" の場合:結果と試合前の直近の状況、および試合前の odds を比較します。

レイヤー 3 — 主な選手

全体ルール(新規): レイヤー 3 は事実と最小限のコンテキストのみを扱います。解釈、重み付け、結論、因果関係を示す表現は含めません。

Capa 3 NO puede contener:
- conclusiones
- inferencias
- lenguaje causal ("porque", "esto implica", "esto sugiere")

Solo descripción estructurada de datos.

唯一の問い: 何があるか?(「それは何を意味するか?」ではありません)

2 つの経路を使うロジック(常に両方を試みる):

  • レーン A: Get_Match_Player_Stats → 個人成績(ゴール、アシスト、シュートなど)
  • レーン B: Get_Match_Lineups → missingPlayers(名前+欠場理由)

レーン A が失敗しても、レーン B が missingPlayers を返す場合は、レーン B を使用します。 両方とも失敗した場合は、[N/A] を付けて縮退レイヤーに切り替えます。


レーン A — Get_Match_Player_Stats からの直接入力: goals、assists、shots、shots_on_target、key_passes、 tackles_won、interceptions、ball_recoveries、yellow_cards、 red_cards、minutes。

ルール — ヒューリスティックな数式は使用しない:

  • 重み(0.3、0.5 など)や、独自に考案した正規化は使用しないでください。
  • 「インパクトスコア」、「攻撃面の影響」など、複合指標を計算しないでください。
  • 許可されるもの: 個別の指標による直接的なランキング(ゴール数、キーパス数など)。
  • 依存度: 選手のゴール/生産量とチーム全体の直接的な比率。

ルール(新規): レイヤー 3 の出力は純粋に事実を記述するものとします。「チームに影響します」、「重要な戦力を欠きます」、「攻撃力が低下します」などの表現は含めないでください。解釈はすべてレイヤー 5 で行います。

選手ごとの出力(直接データ、数式なし):

[Jugador X] ([pos]):
  Producción:
  - Goles: X
  - Asistencias: X

  Volumen ofensivo:
  - Tiros: X (X a puerta)

  Creación:
  - Pases clave: X

  Disciplina:
  - Amarillas: X | Rojas: X

チームごとの上位 N 人(指標による直接的なランキング — 複合スコアなし):

Top generadores de gol [IND]:
1. [Jugador A] — [X] goles
2. [Jugador B] — [X] goles

Top creadores [IND]:
1. [Jugador A] — [X] pases clave
2. [Jugador B] — [X] pases clave

攻撃面の依存度:

  • 計算式:(選手のゴール数 / チームの総ゴール数) * 100
  • しきい値:40% 超 → 「攻撃面の依存度が高い [IND]」
  • 1 人の選手がチームの生産量の 50% 超を占める場合は、アラートを出します。

レーン B — Get_Match_Lineups からの情報源:

スクリプト build_match_context.py は、優先度が 2 段階に定義されたラインナップを処理します(normalize_lineups で定義)。

  • レベル 1 — 公式ラインナップ(startingLineups が存在する場合):

    • missing_players:name、player_id、reason、country を含む欠場選手のリスト [API]
    • substitutes:出場可能な交代選手 [API]
    • starting_lineups:公式の先発ラインナップ [API]
    • predicted_lineups:空
    • unsure_missing:空
  • レベル 2 — 予想ラインナップ(startingLineups が存在せず、predictedLineups が存在する場合):

    • unsure_missing:欠場が確定していない選手 [API]
    • predicted_lineups:予想ラインナップ [API]
    • missing_players:空
    • substitutes:空
    • starting_lineups:空

missing_players に欠場選手が含まれている場合は、以下で定義する欠場レベルに従って報告します。

欠場レベル(詳細度の高い順):

  1. 確認された欠場 [API]: API が返す名前と理由(Injury、Inactive、Leg Injury など)。API に示された情報のみを報告します。

  2. 文脈情報を付加した欠場 [IND]: その選手が Get_Tournament_Top_Scorers に登場する、または過去の試合の Get_Team_Results や Get_Match_Player_Stats で重要な得点者/チャンスメーカーとして記録されている場合 → 「[選手名] は欠場 — トップスコアラー/大会の上位選手として記録 [IND]」と示します。事実としてのみ報告し、影響を解釈しないでください。

  3. 影響度不明 [N/A/IND]: 選手が欠場しているものの、その影響度を推定するのに十分な根拠がない場合 → 「影響度を正確に定量化できません [N/A]」と示します。影響を決して推測しないでください。

レーン B のみを使用する場合のルール:

  • 追加の文脈がない欠場(重要な得点者としての記録がなく、裏付けとなるランキングもない場合)→ 観察された事実としてのみ報告します [API]。
  • 欠場選手ごとにチームの戦力がどの程度低下するかを計算しないでください。
  • 失われるゴール数や、ゴールの確率への影響を推定しないでください。
  • 同じチームに欠場選手が重なっていることは、文脈上の観察事項として述べてもかまいません(チームのパフォーマンス上限に関する確実性が下がります)。ただし、根拠のない数値は示さないでください。

必須の出力:

## 3. Protagonistas

[Carril A — Si hay player-stats:]
[Jugador] ([pos]):
  Producción:
  - Goles: X
  - Asistencias: X
  Volumen ofensivo:
  - Tiros: X (X a puerta)
  Creación:
  - Pases clave: X
  Disciplina:
  - Amarillas: X | Rojas: X

[Top del partido]

Dependencia ofensiva:
- [Equipo]: [jugador] → [X]% de los goles del equipo [IND]

[Carril B — Si hay missingPlayers sin player-stats:]
Ausencias observadas [API]:
- [Equipo]: [Jugador] — [motivo de la API]
- [Equipo]: [Jugador] — [motivo de la API]

[Si hay ausencia contextualizada:]
- [Equipo]: [Jugador] — ausente [IND] — [goleador/top scorer del torneo / pieza habitual]

Lectura contextual [IND]:
- [Equipo] llega con [X] bajas registradas en la API.
- La influencia de las ausencias se considera [alta/moderada/baja] solo si coincide con
  otras señales disponibles (producción en排行榜, dependencia ofensiva, ranking histórico, etc.).
- Influencia no cuantificable con precisión [N/A]: [jugador(es)].

[Si commentary disponible Y coincide con indicadores:]
Patrón de estilo observado: [descripción]

[Si commentary disponible pero NO coincide con indicadores:]
Patrón de estilo: [N/A] — dato observacional no respaldado por stats.

縮退:

  • player-stats と missingPlayers のどちらもない場合 → 「主役のデータなし [N/A] — 劣化レイヤー。」選手や欠場者を捏造しないでください。
  • missingPlayers だけがある場合(レーン B)→ レーン A は N/A、レーン B は上記で定義した欠場レベルに従います。
  • 両方ある場合 → レーン A を使用し、レーン B を補足情報として追加します。
  • analysis_mode = "postmatch" の場合:player_stats を主な情報源、summary.events を二次情報源、commentary を三次的な補助情報として使用します。commentary は展開の描写にのみ使用し、構造的な優位性を捏造してはいけません。player_stats はないものの summary.events がある場合 → 展開の描写はイベントに基づきます。

レイヤー 4 — 複合指標

ルール(新規): レイヤー 4 では指標名を示せますが、なぜ重要なのかは説明しません。

レイヤー役割
4測定する
5説明する

唯一の問い: どれくらいか?(「なぜ重要なのか?」ではありません)

Capa 4 NO puede contener:
- conclusiones
- inferencias
- lenguaje causal ("porque", "esto implica", "esto sugiere")
- "esto favorece a X"
- "esto indica partido abierto"

Solo descripción estructurada de datos comparables entre equipos.

入力: チームの履歴(home_team_results、away_team_results、h2h)、オッズ。

計算:

  • 攻撃力の優位性:両チームの平均ゴール数の差
  • 守備の脆弱性:アウェイチームの失点数/試合数
  • 好調度の差:各チームの直近 N 試合の勝ち点
  • ホーム/アウェイの差:ホーム時の PPG とアウェイ時の PPG
  • Over 2.5 のリスク:両チームの Over 頻度の平均
  • BTTS のリスク:両チームの BTTS 頻度の平均
  • 規律:両チームのカード数の平均(Get_Match_Stats による)
  • 変動性:各チームの直近 5 試合のゴール数の標準偏差
  • サンプルの安定性:利用可能な試合数 N と最低 5 試合との比較

市場とデータの整合性 [IND]:

  • 高い: 市場と過去のデータが同じ側を支持し、かつその度合いも同程度です。
  • 中程度: 同じ側を支持していますが、強度が異なります。
  • 低い: 異なる側を支持しているか、一方が均衡と見なし、もう一方がそう見なしていません。

(整合性の測定 — その理由の解釈ではありません。)

必須出力:

## 4. Indicadores Compuestos

Ventaja ofensiva: [Home] por [+/-X.XX] goles [IND]
Fragilidad: [Away] recibe [X.XX] goles/partido [IND]
Gap forma (últimos [N]): [Home] [+/-X pts] sobre [Away] [IND]
Gap casa/fuera: [Home] [+/-X.X] ppg [IND]
Riesgo Over 2.5: [X%] [IND]
Riesgo BTTS: [X%] [IND]
Volatilidad: [baja/media/alta] [IND]
Índice disciplina: [bajo (<1.0 yc/pp) / medio (1.0-1.5) / alto (>1.5)] [IND]
Mercado vs datos: [alta/moderada/baja]
Estabilidad muestra: [N] partidos / mínimo 5 — [suficiente/limitado]

劣化時の処理:

  • 好調度のデータがない場合(N<3)→ 好調度の差と変動性を N/A とします。

4.5 全体的な冗長性回避ルール

各レイヤーは新しい情報を提供する必要があります。 すでに前のレイヤーで表現された考えは、別の形に変換されない限り 繰り返してはいけません(例:データ → 解釈)。

禁止事項:

  • レイヤー 5 でレイヤー 2 の好調度、オッズ、連勝・連敗を再説明すること
  • レイヤー 6 でレイヤー 5 の診断を再度行うこと
  • レイヤー 8 でレイヤー 6 のシグナルを要約すること

許可事項:

  • レイヤー 5 でレイヤー 2 のデータを使い、因果関係に基づいて解釈すること
  • レイヤー 6 でレイヤー 5 のシグナルを使い、重要度に応じて重み付けすること
  • レイヤー 8 でレイヤー 6 の優先順位付けを使い、最終的な見解に反映すること

正しい流れ:

datos (capa 2) → medición (capa 4) → interpretación (capa 5) → priorización (capa 6) → predicción (capa 7) → síntesis (capa 8)

レイヤー 5 — 診断

唯一の問い: それらのシグナルが重要なのはなぜか?

範囲: 原因 + 矛盾 + 持続可能性

入力: レイヤー 1、2、4 の出力。

因果関係のルール(新規): 原因を数値データの直接的な言い換えにしてはいけません。指標を繰り返すのではなく、そのメカニズムを説明する必要があります。

❌ 悪い例: 「Madrid がホームで優位に立つのは、PPG が 2.25 だからです」

✅ 良い例: 「Madrid がホームで優位に立つのは、トランジション時の強度とハイプレスによるものです。Bernabéu は単なる勝ち点の平均ではなく、そのダイナミクスを増幅します」

必須出力:

## 5. [Por Qué Pasa / Qué Está Pasando]

[Adaptar según estado:]
- notstarted: "Por Qué Pasa" — análisis de tendencias pre-partido
- inprogress: "Qué Está Pasando" — evaluación de lo que ocurre vs lo esperado + comparación con pre-match

Causas [IND]:
- [Causa real derivada de datos — explicar el MECANISMO, no reformular la métrica]
- [Causa 2]

Contradicciones:
- [contradicción 1 — mercado vs stats, forma vs contexto]
- [contradicción 2]

¿Sostenible?
- sí/no — [razón basada en evidencia]

[Para inprogress: usando `live_analysis` y `summary.events`]
Análisis en vivo: El marcador [X-X] [refleja/no refleja] lo que muestran las stats (xG: [xg_home]-[xg_away], posesión: [pos_home]%-[pos_away]%).
[¿El equipo [X] domina aunque no esté ganando?]
[¿Los últimos eventos cambiaron la dinámica?]
[`live_analysis` proporciona probabilidades actualizadas para cada mercado.]

劣化時の処理:

  • stats がない場合 → 原因や持続可能性の分析は生成できません。
  • 好調度のデータがない場合 → 「傾向を診断するにはデータが不十分です [N/A]。」
  • analysis_mode = "postmatch" の場合:焦点は「何が起こるか」から「何が起こり、その理由は何か」に変わります。事前の予想と実際の展開の矛盾を含めてください。

レイヤー 6 — シグナルの重み付け

重み付けルール:

重み対象となるもの対象とならないもの
強いシュート、一貫性(N≥5)、市場と指標の一致、主要選手のパフォーマンス裏付けのない短い連勝、単発のスコア
中程度N=3-4のフォーム、ホーム/アウェイの文脈を考慮していないH2H、市場と過去データの中程度の差、他のレイヤーで裏付けられた複数の欠場者
弱いN<3、市場と過去データの大きな差、指標で裏付けられていない市場、他のレイヤーで裏付けられていない欠場者

役割のルール(新規): レイヤー 6 は優先順位付けを行う唯一のレイヤーです。レイヤー 8 では繰り返しません。

レイヤー 6 で行ってはならないこと:

  • 新たな因果関係の解釈(これはレイヤー 5 の役割です)
  • 市場の推奨(これはレイヤー 8 の役割です)
  • すでに述べたシグナルの繰り返し

行うのは、ソースを示しながら重みに基づいて優先順位を付けること(強い/中程度/弱い/利用不可)だけです。

Get_Match_Lineups.missingPlayers に記録された複数の欠場者:

  • 他のレイヤー(チームのパフォーマンス、ランキング、攻撃への依存度、フォーム)と一致する場合に限り、中程度のシグナルとして扱えます。
  • それだけで強いシグナルとして扱うことはできません。欠場者のリストだけで分析を左右してはなりません。
  • 欠場が統計データやランキングで裏付けられていない場合は、弱いシグナルです。

必須の出力:

## 6. Qué Señales Pesan Más

Fuertes [fuente]:
- [señal + razón]

Moderadas [fuente]:
- [señal + razón]

Débiles [razón]:
- [señal]

No utilizables [razón]:
- [señal]

Outliers:
- [partido + razón]

劣化時の対応:

  • レイヤー 4 が利用できない場合 → シグナルは利用不可です。
  • analysis_mode = "postmatch" の場合、焦点を「何が起こる可能性があるか」から「何が起きたか、そしてその理由」へと切り替えます。事前の予想と実際の展開の矛盾を含めてください。

レイヤー 7 — 予測(オッズ主導)

組み合わせルール:

条件対応
オッズを利用できるオッズを確率の基本ソースとして使用する
指標を利用できる信頼度の引き上げ/引き下げのみに使用し、パーセンテージを作り出さない
オッズも指標も利用できない予測を出さない。信頼度は非常に低い。
市場と過去データが一致するシグナルを強める
市場と過去データが矛盾する弱める。「不確実」と明記する。

数値で示す確率は、オッズからのみ算出できます。指標からパーセンテージを作り出してはなりません。

必須の出力:

## 7. [Qué Podría Pasar / Qué Se Espera]

[Adaptar según estado:]
- notstarted: "Qué Podría Pasar" — predicción pre-partido
- inprogress: "Qué Se Espera" — predicción actualizada con marcador actual y stats en vivo

[Para notstarted:]
Resultado: [Home] [X]% | Empate [Y]% | [Away] [Z]%
Goles esperados: [Home] [X.XX] | [Away] [Y.XX] (basado en avg histórico)
Over 1.5: [X]% | Over 2.5: [Y]% | BTTS: [Z]%
Tipo: [cerrado/abierto/defensivo/competido]
Confianza: [muy baja/baja/media/media-alta/alta] — [motivo de la calificación]

[Para inprogress: usando `live_analysis`]
Marcador actual: [Home] [X] - [Y] [Away] — [minuto]'
xG acumulado: [Home] [X.XX] | [Away] [Y.XX]

Señales en vivo [live_analysis]:
- Over 2.5: [probability]% | BTTS: [probability]%
- Próximo gol — Home: [probability]% | Away: [probability]% | No más goles: [probability]%
- Esquinas próximo: [lean] | Señal: [signal]
- 1X2 final — Home: [probability]% | Empate: [probability]% | Away: [probability]%

Resultado esperado al final: [Home] [X] - [Y] [Away] (basado en xG y tiempo restante)
Tipo de partido: [cerrado/abierto/defensivo/competido]
Confianza: [muy baja/baja/media/media-alta/alta] — [motivo de la calificación]
[¿El equipo que está perdiendo merece estar abajo?]

Nota: "Predictiva basada en `live_analysis` + odds históricas."

劣化時の対応:

  • オッズも指標もない場合 → 「予測は利用できません [N/A] — データが不十分です。」
  • 市場と過去データが矛盾する場合 → 次を追加します:「⚠ 市場と過去データが一致していません。 不確実性が高まっています。」

第8層 — 最終的な読み取り

唯一の質問: 最終的にどのような見方をすればよいですか?

範囲: 人間による解釈+十分な根拠のある1〜2市場(概念的であり、数値は用いない)

ルール(新規): 第8層は概念レベルで扱います。シナリオ、リスク、読み取りを示します。第7層の確率や数値指標を繰り返さず、データを用いて再度根拠を説明せず、第6層のシグナルを要約しません。

第8層では、以下を行ってはいけません。

  • 最も強いシグナルの一覧(第6層ですでに提示済み)
  • アラートの一覧(第5層ですでに提示済み)
  • 根拠のある市場の一覧(第7層ですでに提示済み)
  • 確率や数値指標

次の内容だけを読み解きます:最も妥当なシナリオ、隠れたリスク、買うべきでないもの。

必須の出力:

## 8. Lectura Final

- Qué escenario tiene más sentido
- Qué escenario tiene riesgo oculto
- Qué NO comprar del partido

Opcional:
- 1-2 mercados bien justificados (no lista — solo decisión final bien fundamentada)

Nota: "Las lecturas anteriores son las mejor soportadas por la evidencia disponible."

品質低下時:

  • 第7層の信頼度が非常に低い場合 → 第8層は「市場の推奨を伴わないシナリオの読み取り」に縮小します。
  • analysis_mode = "postmatch" の場合:焦点を「何が起こる可能性があるか」から「何が起きたか、そしてなぜ起きたか」に切り替えます。事前の予想と実際の展開との矛盾を含めてください。

9. 信頼度ポリシー

全体的な信頼度
データの状態最大信頼度
試合+オッズ+統計+選手統計+履歴高
試合+オッズ+統計(選手統計なし)中〜高
試合+オッズ(統計なし)中
試合のみ低
試合なし非常に低い/分析不可能
層ごとの調整
不足しているデータ影響を受ける層低下内容
オッズなし第1層、第7層最大で中
統計なし(シュート、コーナー、xG)第4層、第5層ゴールのシグナルが弱まる
選手統計なし第3層N/A のテキストのみ
履歴なし(N<3)第2層、第4層過去のデータを利用できない
H2H なし第2層H2H = N/A
層ごと

各層には、次のいずれかを指定できます。

  • 完全 — すべての入力データを利用できます。
  • 部分的 — 一部の入力データが不足しているため、該当する [N/A] テキストを使用します。
  • 利用不可 — 必要な入力データが不足しており、計算できません。

11. 不当な合理化の禁止

ここはガードレールのセクションです。以下の内容は必ず遵守してください。 以下の行為は、例外なく禁止です。

データを捏造しない
  • [N/A] → 捏造しないでください。 選手統計がない場合 → 第3層は「十分なデータがありません」とします。
  • ラインナップ、負傷者、出場停止選手、監督を捏造しないでください。
  • Get_Match_H2H が空または null の場合、H2H を捏造しないでください。
外部ソースを使用しない
  • 分析中に、Bzzoiro、SofaScore、Transfermarkt、Wikipedia、その他のソースを参照しないでください。
  • 有効な唯一の情報源は FlashScore MCP です(セクション3に記載のエンドポイント経由)。
モデルを即興で作らない
  • 学習モデルに基づく予測の代わりとなるヒューリスティックモデルを構築しないでください。
  • このバージョンは設計上、オッズ主導です。独自のモデルを即興で作らないでください。
  • 第7層の数値確率は、オッズのみを根拠とします。独自の計算は使用しません。
  • 第3層で重み(0.3、0.5 など)や複合指標を使用しないでください。
確実性を誇張しない
  • 「勝つ」「確実だ」「オーバーは必ず成立する」と言わないでください。
  • 必ず「可能性がある」「シグナル」「示唆している」「証拠は〜を示している」と表現してください。
  • 必ず信頼度を示してください。
矛盾を省略しない
  • 市場と指標が異なる方向を示す場合は、明確に述べてください。
  • あるシグナルが傾向ではなくノイズである場合は、その旨を述べてください。
中身のない文で埋めない
  • 各セクションには、必須フィールドを含む定義済みの出力があります。
  • フィールドのデータがない場合 → 「[N/A]」+簡潔な理由。
  • セクションを「空白」のままにしたり、何も伝えない一般的な文で埋めたりしないでください。
FlashScore MCP を誤解しない
  • Get_Team_Fixtures / Get_Team_Results が試合を見つける手段です。 /api/events/?team=X は存在しません。
  • inprogress の試合:ライブ分析が可能です — 試合の最新データを使用してください。
  • finished の試合:analysis_mode = "postmatch" → 試合後分析を進めてください。

postmatch のルール:

  • 原因を捏造しないでください。Commentary は一連のプレーや局面を描写するためのものであり、構造的な優位性を作り上げるためのものではありません。
  • match_stats がない場合 → 結果+イベント+直近の調子+試合前オッズに限定してください。
  • summary.events がない場合 → 「転換点」について述べないでください(出力形式ですでにフィルタリングされています:「summary.events または commentary に根拠がある場合のみ」)。
  • 完全な読み解きにデータが不十分な場合 → 「展開の部分的な分析」というラベルを使い、利用できなかったデータを列挙してください。
  • Commentary は観察データであり、主要なシグナルではありません。他のレイヤーの指標と一致する場合にのみ有効です。

12. 分析の出力形式

必須の構成(以下の順序を厳守):

# [Home] vs [Away] — [Competición] ([Fecha])

[8 capas completas]

---
Confianza global: [muy baja/baja/media/media-alta/alta]

セクションごとの目安の長さ:

  • レイヤー 1~2:各 8~15 行。
  • レイヤー 3~6:各 5~12 行。
  • レイヤー 7~8:各 8~15 行。

利用できないデータ:

  • 欠損データには常に [N/A] を使用してください。
  • 理由を常に簡潔に説明してください。

強い/中程度/弱いシグナル:

  • 散文ではなく、箇条書きで示してください。

分析の締めくくり:

  • 全体的な信頼度を必ず含めてください。
  • 強いシグナルと弱い相関を必ず区別してください。
  • 断定的な文で締めくくらないでください。
データの保存
  1. この分析を ./claude/skills/football-betting-analysis/football-analysis にある txt ファイルに保存します。
  2. 大会名に対応する名前のフォルダーを作成します。例:Champions League 25-26。
  3. ファイル名を [DD_MM_AAAA] [EQUIPO LOCAL] vs [EQUIPO VISITANTE] [JORNADA_X].txt として保存します。[JORNADA_X] の X には、開催される節の番号、または該当する場合は準々決勝やラウンド 16 を指定します。

13. クイックリファレンス

CONSULTA NL → parsing → { home, away, date_from, date_to, league? }

Fase 1 (MCP directo):
  Match discovery:   livesport search API → /data/main_teams.csv (fallback) → Get_Team_Fixtures → Get_Team_Results
  → analysis_mode = prematch | live | postmatch
  → Extraer: event_id, home_team_id, away_team_id

Fase 2 (build_match_context.py):
  python scripts/build_match_context.py <event_id> <home_team_id> <away_team_id>
  → Devuelve final_context JSON con todos los datos normalizados

NO LLAMAR ENDPOINTS MCP DIRECTAMENTE PARA DATOS DEL PARTIDO.
Solo Get_Team_Fixtures / Get_Team_Results para match discovery.

信頼度のしきい値:

データ最大信頼度
イベント + オッズ + 統計 + 選手統計 + 履歴高
イベント + オッズ + 統計(選手統計なし)中~高
イベント + オッズ(統計なし)中
イベントのみ低
イベントなし実行不可能

分析全体で必須の情報源ラベル: [API] [ODDS] [IND] [N/A]


重複を避けるルール(クイックリファレンス)

レイヤー固有の質問してはいけないこと
3何があるか?解釈しない、結論を出さない
4どのくらいか?なぜ重要かを説明しない
5なぜか?数値データを言い換えず、仕組みを説明する
6何が重要か?レイヤー 5 の診断を繰り返さない
8何を結論として持ち帰るか?レイヤー 6 のシグナルを要約しない、レイヤー 7 の数値を繰り返さない

14. 解消済みの不備

合理化対策
「FlashScore になかったので Bzzoiro/SofaScore/別のソースを使った」禁止です。 FlashScore MCP のみを使用してください。例外はありません。
「player-stats がなかったので、ラインナップをでっち上げた」禁止です。 レイヤー 3 のレーン B:Get_Match_Lineups に missingPlayers があれば使用してください。何もなければ → [N/A]。
「学習モデルベースの予測の代わりに、ヒューリスティックモデルを構築した」禁止です。 レイヤー 7 = オッズ主導です。確率をでっち上げないでください。
「入力として架空の xG を使った」禁止です。 FlashScore が Get_Match_Stats で xG を返さない場合、でっち上げないでください。API が提供するデータを使用してください。
「試合はすでに始まっているが、それでも分析したい」該当しません。 このスキルは notstarted の試合のみを対象とします。inprogress または finished の試合については、処理を進めないでください。
「injuries data がなかったので、負傷者をでっち上げた」禁止です。 missingPlayers が利用できない場合、欠場者やその理由をでっち上げないでください。
「欠場者ごとにチームの戦力がどれだけ低下するか計算した」禁止です。 欠場の数値的な影響を推定しないでください。API が提供する情報と、定義済みの欠場レベルのみを使用してください。
「missingPlayers を単独で強いシグナルとして使った」禁止です。 他のレイヤーで裏付けられていない欠場情報は弱いシグナルです。強いシグナルとして扱ってはいけません。
「H2H が返されなかったので、記憶を頼りに書いた」禁止です。 Get_Match_H2H = null の場合は「H2H は利用できません [N/A]。」としてください。
「イベントしかなかったのに『高』とした」禁止です。 イベントのみの場合、信頼度は「低」です。しきい値テーブルに従う必要があります。
「オーバーは確実に入る」禁止です。 常に確率的な表現を使用してください。
「レイヤー 3 を記憶に頼った名前で埋めた」禁止です。 各レイヤーには定義済みの出力があります。データがなければ → [N/A]。
「2 試合のサンプルは代表性がある」禁止です。 N<5 の場合は「サンプル数が限られています。」としてください。指標の重みは小さくなります。
「0.3/0.5 の重みで impact score を計算した」禁止です。 レイヤー 3 では、ヒューリスティックな数式ではなく、直接データとランキングを使用します。
「モデルから直接 Get_Match_Details/H2H/Stats を呼び出した」禁止です。 試合データの収集はすべて build_match_context.py を経由します。試合の発見には Get_Team_Fixtures/Get_Team_Results のみを使用してください。
「スクリプトが警告を出したが、データは正しいと思った」禁止です。 スクリプトの警告は指示です。スクリプトがデータを疑わしいと判定した場合は、それに従い、異議を唱えないでください。
「JSON は [N/A] だったが、記憶ではデータを知っていた」禁止です。 JSON は利用可能なデータを正規化し、印を付けています。[N/A] とマークされたデータを再びでっち上げないでください。
「JSON を受け取った後、追加の MCP エンドポイントを実行した」禁止です。 final_context を受け取った後は、分析に使うすべてのデータを JSON から取得してください。追加のエンドポイントを照会しないでください。

コメント