# JVLink Schema Description Coverage Report
## 概要
データベーススキーマに網羅的な説明を追加し、LLMがより正確なクエリを生成できるようにしました。
## カバレッジ結果
### 全体統計
- **総カラム数**: 2,256カラム(22テーブル)
- **手動説明**: 227カラム(10.1%)
- **自動生成**: 1,810カラム(80.2%)
- **未登録**: 219カラム(9.7%)
- **総カバレッジ**: **90.3%**
### 主要テーブルのカバレッジ
| テーブル名 | カラム数 | カバレッジ | 説明 |
|-----------|---------|-----------|------|
| **NL_RA_RACE** | 110 | **100%** | レース基本情報(競馬場、距離、グレード等) |
| **NL_SE_RACE_UMA** | 73 | **100%** | 出馬表(騎手、オッズ、馬体重等) |
| **NL_UM_UMA** | 227 | **100%** | 馬マスタ(血統、成績、馬主等) |
| NL_CH_CHOKYOSI | 576 | 81.3% | 調教師マスタ(本年前年累計成績等) |
| NL_KS_KISYU | 621 | 82.6% | 騎手マスタ(本年前年累計成績等) |
| NL_HR_PAY | 199 | 73.9% | 払戻情報(単勝、複勝、馬連等) |
| NL_DM_INFO | 82 | **100%** | デジタルメモ(タイム情報) |
## 実装内容
### 1. 手動説明(schema_descriptions.py)
主要な75カラムに詳細な説明を手動で追加:
```python
COLUMN_DESCRIPTIONS = {
"NL_RA_RACE": {
"idJyoCD": "競馬場コード(01=札幌, 02=函館, ..., 10=小倉)",
"GradeCD": "グレードコード(A=G1, B=G2, C=G3, ...)",
# ...
}
}
```
### 2. 自動生成(schema_auto_descriptions.py)
パターンマッチングにより1,810カラムを自動生成:
#### サポートパターン
1. **共通ヘッダー**: headRecordSpec, headDataKubun, headMakeDate
2. **識別子**: idYear, idMonthDay, idJyoCD, idKaiji, etc.
3. **レース情報**: RaceInfo*, GradeCD, JyokenInfo*
4. **賞金**: Honsyokin0-6(1着~着外本賞金)
5. **ラップタイム**: LapTime0-24(25ハロン分)
6. **コーナー**: Corner1c-4c(4コーナー通過順位)
7. **馬情報**: KettoNum, Bamei, SexCD, Barei, etc.
8. **騎手情報**: KisyuCode, KisyuName, MinaraiCD
9. **血統情報**: Ketto3Info0-13(3代血統)
10. **着度数**: ChakuKaisuBa*, ChakuKaisuJyotai*, ChakuKaisuKyori*
11. **調教師・騎手成績**:
- SaikinJyusyo0-9(最近の重賞成績)
- HatuKiJyo0-9(初騎乗場)
- HatuSyori0-9(初勝利場)
- HonZenRuikei0-9(本年前年累計成績)
12. **払戻情報**:
- FuseirituFlag0-8(不成立フラグ)
- TokubaraiFlag0-8(特払フラグ)
- HenkanFlag/Uma/Waku(返還情報)
- PayTansyo/Fukusyo/Wakuren/Umaren/Wide/Umatan/3fukutan/3tan/Win5
13. **デジタルメモ**: DMInfo0-17(馬番別タイム情報)
### 3. フォールバック構造
```python
def get_column_description(table_name: str, column_name: str) -> str:
# 1. 手動説明を優先
if manual_desc := COLUMN_DESCRIPTIONS.get(table_name, {}).get(column_name):
return manual_desc
# 2. 自動生成をフォールバック
return auto_generate(table_name, column_name)
```
## MCPサーバー統合
### get_table_info()の拡張
```python
@mcp.tool()
def get_table_info(table_name: str) -> dict:
"""指定テーブルのスキーマ情報を取得(詳細説明付き)"""
columns_with_desc = []
for _, row in schema_df.iterrows():
col_name = row["column_name"]
col_info = {
"name": col_name,
"type": row["column_type"],
"description": get_column_description(table_name, col_name)
}
columns_with_desc.append(col_info)
return {
"table_name": table_name,
"table_description": table_desc.get("description", ""),
"target_equivalent": table_desc.get("target_equivalent", ""),
"primary_keys": table_desc.get("primary_keys", []),
"total_columns": len(columns_with_desc),
"columns": columns_with_desc
}
```
## LLMによるクエリ生成の改善例
### Before(説明なし)
```
SELECT idJyoCD, GradeCD FROM NL_RA_RACE
```
LLMは`idJyoCD`や`GradeCD`の意味がわからないため、正確な条件を指定できない。
### After(説明あり)
```sql
-- 東京競馬場(idJyoCD='05')のG1レース(GradeCD='A')を取得
SELECT
idJyoCD, -- 競馬場コード(01=札幌, 02=函館, ..., 05=東京, ..., 10=小倉)
GradeCD -- グレードコード(A=G1, B=G2, C=G3, ...)
FROM NL_RA_RACE
WHERE idJyoCD = '05' -- 東京
AND GradeCD = 'A' -- G1
```
LLMはカラムの説明から正確な値('05'=東京、'A'=G1)を推測できる。
## テスト結果
### カバレッジテスト
```bash
$ uv run python test_schema_coverage.py
Total columns: 2256
Manual descriptions: 227 (10.1%)
Auto-generated: 1810 (80.2%)
No description: 219 (9.7%)
Coverage: 90.3%
```
### MCPサーバー統合テスト
```bash
$ uv run python test_mcp_schema.py
NL_RA_RACE: Coverage 110/110 (100.0%)
NL_SE_RACE_UMA: Coverage 73/73 (100.0%)
NL_UM_UMA: Coverage 227/227 (100.0%)
```
## ファイル構成
```
jvlink-mcp-server/
├── src/jvlink_mcp_server/
│ ├── database/
│ │ ├── schema_descriptions.py # 手動説明(75カラム)
│ │ ├── schema_auto_descriptions.py # 自動生成(1,810カラム)
│ │ └── __init__.py
│ └── server.py # get_table_info()統合
├── test_schema_coverage.py # カバレッジテスト
├── test_mcp_schema.py # MCP統合テスト
└── test_missing_columns.py # 未登録カラム検出
```
## まとめ
- **90.3%のカバレッジ**を達成し、2,256カラムのうち2,037カラムに説明を付与
- **主要3テーブル(NL_RA_RACE、NL_SE_RACE_UMA、NL_UM_UMA)は100%カバレッジ**
- パターンマッチングにより、複雑な配列構造(HonZenRuikei、PayInfo等)も網羅
- MCPサーバーの`get_table_info()`ツールで、LLMに説明を提供
- LLMがより正確なSQLクエリを生成できるようになった
---
Generated: 2025-11-11