Zun Iguchi commited on
Commit
09c8b6b
·
unverified ·
2 Parent(s): be2e53e1489413

Merge pull request #4 from chibafes-dev/docs

Browse files
README.md CHANGED
@@ -6,4 +6,208 @@ colorTo: green
6
  sdk: docker
7
  app_port: 7860
8
  pinned: false
9
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  sdk: docker
7
  app_port: 7860
8
  pinned: false
9
+ ---
10
+
11
+ # 千葉大祭団体企画情報の提供&検索システム
12
+ ## 目次
13
+ * [APIの仕様](#apiの仕様)
14
+ * [ローカル作業の準備](#ローカル作業の準備)
15
+ * [1. リポジトリをクローン](#1-リポジトリをクローン)
16
+ * [2. ローカルで`.env`ファイルを作成](#2-ローカルで-env-ファイルを作成)
17
+ * [3. 適切なバージョンのPythonを入手する](#3-適切なバージョンのpythonを入手する)
18
+ * [4. Pythonの仮想環境を作成・有効化](#4-pythonの仮想環境を作成有効化)
19
+ * [5. ライブラリをインストール](#5-ライブラリをインストール)
20
+ * [開発用サーバーの起動](#開発用サーバーの起動)
21
+ * [仕組み・企画データ更新](#仕組み・企画データ更新)
22
+
23
+ ## APIの仕様
24
+ [`docs/api_spec.md`](./docs/api_spec.md) を参照してください。
25
+
26
+ ## ローカル作業の準備
27
+ ### 1. リポジトリをクローン
28
+ ```bash
29
+ git clone https://github.com/chibafes-dev/chibafes-website-api-v2.git
30
+ ```
31
+ > クローンのためには `Read` 以上のリポジトリ権限が必要です。また、作業をしてプッシュをするには `Write` 以上のリポジトリ権限が必要です。
32
+
33
+ ---
34
+
35
+ ### 2. ローカルで `.env` ファイルを作成
36
+
37
+ 以下の`...`部分を適切な値に変更する。(なんらかの方法で引き継いでもらう。)
38
+ ```sh
39
+ # API側 GitHub の secrets と HF Spaces の Secrets の両方に登録
40
+ HF_TOKEN=...
41
+ # HF Spaces の secrets に登録
42
+ API_SECRET_KEY=...
43
+ HF_EMBEDDINGS_REPO_ID=...
44
+ ```
45
+
46
+ > **!!! 注意 !!!**
47
+ > .envは機密情報なので絶対にリポジトリにプッシュしないこと!!.gitignoreに含まれていることを確認してください。
48
+
49
+ ---
50
+
51
+ ### 3. 適切なバージョンのPythonを入手する
52
+
53
+ やり方はいくつかありますが、筆者は `pyenv` を使用しました。適切なバージョンがインストールできればなんでもいいですが、ここでは `pyenv` を紹介します。
54
+ 英語が読める人は [公式リポジトリ](https://github.com/pyenv/pyenv?tab=readme-ov-file#a-getting-pyenv) を参考にしてインストールしてください。
55
+
56
+ ---
57
+
58
+ #### 英語が読めないWindowsユーザー向け(WSL2はLinux向けを確認)
59
+ <details>
60
+
61
+ 残念ながら `pyenv` はWindowsに対応していないので、代わりに `pyenv-win` を使いましょう。
62
+
63
+ 参考:https://github.com/pyenv-win/pyenv-win/blob/master/docs/installation.md#powershell
64
+
65
+ 1. Powershellで以下のコマンドを実行
66
+ ```powershell
67
+ Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"
68
+ ```
69
+
70
+ もし `UnauthorizedAccess` エラーが出たら、以下のコマンドを「管理者権限で」実行したのち、上のコマンドを再度試してください。
71
+ ```powershell
72
+ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
73
+ ```
74
+
75
+ 2. インストールができたことを確認する
76
+
77
+ 以下のコマンドを実行:
78
+ ```powershell
79
+ pyenv --version
80
+ ```
81
+ バージョン情報が出れば成功。
82
+ </details>
83
+
84
+ #### 英語が読めないMacユーザー向け
85
+ <details>
86
+
87
+ 参考:https://github.com/pyenv/pyenv/blob/master/README.md#macos
88
+
89
+ 1. どうにかしてHomebrewを入れる
90
+
91
+ やり方はググってください。
92
+
93
+ 2. Homebrew経由で `pyenv` をインストール
94
+
95
+ ```bash
96
+ brew update
97
+ brew install pyenv
98
+ ```
99
+
100
+ 3. シェル環境を整える
101
+
102
+ `zsh` を使っている人向けの説明。自分が何を使っているかわからない人は `echo $SHELL` を実行。
103
+ > 最近のMacだと `zsh` がデフォルトらしいです。敢えて `bash` とか `fish` にするような方には私の説明は不要だと思うので、[公式リポジトリ](https://github.com/pyenv/pyenv/blob/master/README.md#b-set-up-your-shell-environment-for-pyenv) を見てご自身でうまいことやってほしいです。
104
+
105
+ 以下の3つのコマンドを1行ずつ順に実行する。
106
+ ```bash
107
+ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
108
+ echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
109
+ echo 'eval "$(pyenv init - zsh)"' >> ~/.zshrc
110
+ ```
111
+
112
+ 4. シェルを再起動する
113
+
114
+ ```bash
115
+ exec "$SHELL"
116
+ ```
117
+ </details>
118
+
119
+ #### 英語が読めないLinuxユーザー向け(WSL2含む)
120
+ <details>
121
+
122
+ 参考:https://github.com/pyenv/pyenv/blob/master/README.md#linuxunix
123
+
124
+ 1. 自動インストーラを使用する
125
+
126
+ ```bash
127
+ curl -fsSL https://pyenv.run | bash
128
+ ```
129
+ </details>
130
+
131
+ ---
132
+
133
+ #### <全OS共通> Python をインストールする
134
+ 1. `.python-version` に記載された Python のバージョンを確認
135
+
136
+ ���下例として `3.12.11` の場合を仮定します。
137
+
138
+ 2. `pyenv` で Python をインストールする
139
+ ```bash
140
+ pyenv install 3.12.11
141
+ ```
142
+
143
+ 3. Python のバージョンを切り替え
144
+
145
+ `pyenv` が `.python-version` を自動で読み取って切り替えてくれるので操作は不要です。
146
+
147
+ 以下のコマンドで Python のバージョンを確認しましょう。
148
+ ```bash
149
+ pyenv version
150
+ ```
151
+ (`versions`ではない)
152
+
153
+ <details>
154
+ <summary>手動で切り替えたい場合</summary>
155
+
156
+ このリポジトリ `chibafes-website-api-v2` があるフォルダに移動し、以下のコマンドを実行
157
+ ```bash
158
+ pyenv local 3.12.11
159
+ ```
160
+ これを実行すると、そのフォルダの中で `3.12.11` が有効化されます。
161
+ > 全部のフォルダで有効化したい場合は `local` の代わりに `global` としてください。
162
+ </details>
163
+
164
+ ---
165
+
166
+ ### 4. Pythonの仮想環境を作成・有効化
167
+ ```bash
168
+ python -m venv .venv
169
+ source .venv/bin/activate
170
+ ```
171
+
172
+ ---
173
+
174
+ ### 5. ライブラリをインストール
175
+ ```bash
176
+ pip install --upgrade pip
177
+ pip install -r requirements.txt
178
+ ```
179
+
180
+ ## 開発用サーバーの起動
181
+ ### (初回のみ)必要ファイルの作成
182
+ > **!!! 注意 !!!**
183
+ > **大容量(約6GB)のダウンロードが発生するので、必ずWiFi環境で行うこと!!**
184
+ ```bash
185
+ python scripts/build_all.py
186
+ ```
187
+
188
+ ### サーバー起動
189
+ ```bash
190
+ uvicorn api.main:app --reload
191
+ ```
192
+
193
+ `http://localhost:8000`からアクセスできます。
194
+
195
+ ## 仕組み・企画データ更新
196
+ 企画データ更新のためには以下のステップを実行してください。
197
+ 1. Google Spreadsheets でデータの整理 → CSVファイルのダウンロード
198
+ 2. CSVファイルを `resources/` に設置
199
+ 3. `config/files.json` 内 `projects > original_csv` を実際のパス・ファイル名に変更
200
+ ```json
201
+ {
202
+ ...
203
+ "projects": {
204
+ "original_csv": "resources/original_projects.csv", // ここを変更
205
+ "projects_json": "data/generated/projects.json"
206
+ },
207
+ ...
208
+ }
209
+ ```
210
+ 4. `python scripts/build_all.py` を実行 → ローカルで動作確認
211
+ 5. GitHubにプッシュ(`main` ブランチにマージ)
212
+
213
+ 詳細な仕組み等は [`docs/project_overview.md`](./docs/project_overview.md) を確認してください。
docs/api_spec.md ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # フロントエンド向け API 仕様書
2
+
3
+ 本ドキュメントはフロントエンドから本APIを利用するための仕様書です。認証ヘッダ、各エンドポイントの入出力、エラー応答、注意点をまとめています。
4
+
5
+ ベースURLはデプロイ環境に依存します(例: 同一オリジンの `/api/...` または環境変数 `FASTAPI_URL` で指定)。バージョン付けは現状ありません。
6
+
7
+ ---
8
+
9
+ ## 目次
10
+
11
+ - [認証](#認証)
12
+ - [エンドポイント一覧](#エンドポイント一覧)
13
+ - [スキーマ定義](#スキーマ定義)
14
+ - [リクエスト/レスポンス例](#リクエストレスポンス例)
15
+ - [エラーとステータスコード](#エラーとステータスコード)
16
+ - [実装メモ(フロントエンド)](#実装メモフロントエンド)
17
+ - [クライアント例](#クライアント例)
18
+ - [環境変数の例(フロントエンド側)](#環境変数の例フロントエンド側)
19
+
20
+ ---
21
+
22
+ ## 認証
23
+
24
+ 本番(HF Spaces・プライベート)では二段階の認証が必要です。
25
+
26
+ - レイヤ1: HF Spaces へのアクセス(プライベート用)
27
+ - ヘッダ: `Authorization: Bearer <API_ACCESS_KEY>`
28
+ - 値の例: 環境変数 `API_ACCESS_KEY`(フロントのサーバーサイドに設定)
29
+ - 失敗時: `401 Unauthorized`(またはプラットフォーム側のエラー)
30
+
31
+ - レイヤ2: アプリ内APIキー
32
+ - ヘッダ: `X-API-KEY: <API_SECRET_KEY>`(ヘッダ名は大小無視: `X-API-Key` でも可)
33
+ - 値の例: 環境変数 `API_SECRET_KEY`(README 参照)
34
+ - 対象: `/api/projects`, `/api/details`, `/api/search`
35
+ - 失敗時: `403 Forbidden`(キー不正または未設定)
36
+
37
+ 推奨: ブラウザから直接APIを呼ばず、Next.js等のサーバーサイド(API Routes や server actions)から呼び出してください。クライアントに秘密鍵を露出しないためです。
38
+
39
+ ---
40
+
41
+ ## エンドポイント一覧
42
+
43
+ 1) ヘルスチェック(認証不要)
44
+ - メソッド/URL: `GET /api/health`
45
+ - リクエスト: なし
46
+ - レスポンス: `200 OK`
47
+ - 本文: `{ "status": "ok" }`
48
+
49
+ 2) 企画一覧(圧縮レスポンス)
50
+ - メソッド/URL: `GET /api/projects`
51
+ - ヘッダ:
52
+ - `Authorization: Bearer <API_ACCESS_KEY>`
53
+ - `X-API-KEY: <API_SECRET_KEY>`
54
+ - レスポンス: `200 OK`
55
+ - ヘッダ: `Content-Encoding: gzip`, `Content-Type: application/json`
56
+ - 本文: `ProjectSummary[]`(ブラウザは自動で解凍します)
57
+
58
+ 3) 企画詳細
59
+ - メソッド/URL: `GET /api/details?projectId=<ID>`
60
+ - ヘッダ:
61
+ - `Authorization: Bearer <API_ACCESS_KEY>`
62
+ - `X-API-KEY: <API_SECRET_KEY>`
63
+ - クエリ: `projectId`(必須)
64
+ - レスポンス:
65
+ - 成功: `200 OK` 本文: `ProjectDetail`
66
+ - 失敗: `404 Not Found`(存在しないID)
67
+
68
+ 4) 検索
69
+ - メソッド/URL: `POST /api/search`
70
+ - ヘッダ:
71
+ - `Authorization: Bearer <API_ACCESS_KEY>`
72
+ - `X-API-KEY: <API_SECRET_KEY>`
73
+ - ボディ(JSON): `{ "query": string, "debug": boolean (省略可, 既定:false) }`
74
+ - レスポンス:
75
+ - 成功: `200 OK`
76
+ - `debug=false` のとき: `ProjectIds`
77
+ - `debug=true` のとき: `SearchDebugResponse`
78
+ - 失敗: `400 Bad Request`(`query` が空)
79
+
80
+ ---
81
+
82
+ ## スキーマ定義
83
+
84
+ 型はすべて JSON オブジェクト(配列含む)です。文字列は UTF-8。日付型はありません。
85
+
86
+ 共通: `genre` の値は次のいずれかです。
87
+ `"パフォーマンス" | "飲食" | "物販" | "展示" | "参加型" | "音楽" | "その他"`
88
+
89
+ 1) ProjectSummary(企画一覧用)
90
+ - `projectId`: string(例: "62000A")
91
+ - `organization`: string(団体名)
92
+ - `title`: string(企画名)
93
+ - `genre`: string(上記のいずれか)
94
+ - `day1`: boolean
95
+ - `day2`: boolean
96
+ - `day3`: boolean
97
+ - `place`: string
98
+ - `description`: string
99
+ - `prComment`: string
100
+ - `note`: string | null
101
+ - `imageName`: string
102
+
103
+ 2) ProjectDetail(企画詳細用)
104
+ - `projectId`, `organization`, `title`, `genre`, `day1`, `day2`, `day3`, `place`, `description`, `prComment`, `note`, `imageName`: ProjectSummary と同じ
105
+ - 追加フィールド:
106
+ - `prCommentLong`: string | null
107
+ - `prImageName1`〜`prImageName5`: string | null
108
+ - `urlX`, `urlInstagram`, `urlOfficialWebsite`, `urlYoutube`, `urlOther`: string | null
109
+
110
+ 3) ProjectIds(検索の通常応答)
111
+ - `projectIds`: string[](スコアの高い順)
112
+
113
+ 4) SearchDebugResponse(`debug=true` のとき)
114
+ - `projectIds`: string[](最終並べ替え順)
115
+ - `scores`: { `projectId`: string, `score`: number }[](最終スコア)
116
+ - `details`: { 下記 }[](内部スコアの内訳)
117
+ - `projectId`: string
118
+ - `bm25`: number(単語一致の強さ)
119
+ - `ws_filter_topk`: number(意味類似: ふるい落とし段階)
120
+ - `ws_rerank_pairavg`: number | null(意味類似: 最終並べ替えに使用)
121
+ - `org_boost`: number(団体名/読みの一致による加点)
122
+ - `fused_filter`: number(フィルタ段階の合成スコア)
123
+ - `fused_final`: number(最終合成スコア = 並べ替え基準)
124
+
125
+ ---
126
+
127
+ ## リクエスト/レスポンス例
128
+
129
+ 1) 企画一覧
130
+ ```
131
+ GET /api/projects
132
+ Authorization: Bearer <API_ACCESS_KEY>
133
+ X-API-KEY: <API_SECRET_KEY>
134
+
135
+ 200 OK
136
+ Content-Encoding: gzip
137
+ Content-Type: application/json
138
+
139
+ [
140
+ {
141
+ "projectId": "62000A",
142
+ "organization": "〇〇サークル",
143
+ "title": "△△展示",
144
+ "genre": "展示",
145
+ "day1": true,
146
+ "day2": false,
147
+ "day3": true,
148
+ "place": "1号館",
149
+ "description": "…",
150
+ "prComment": "…",
151
+ "note": null,
152
+ "imageName": "image.jpg"
153
+ }
154
+ ]
155
+ ```
156
+
157
+ 2) 企画詳細
158
+ ```
159
+ GET /api/details?projectId=62000A
160
+ Authorization: Bearer <API_ACCESS_KEY>
161
+ X-API-KEY: <API_SECRET_KEY>
162
+
163
+ 200 OK
164
+ {
165
+ "projectId": "62000A",
166
+ "organization": "〇〇サークル",
167
+ "title": "△△展示",
168
+ "genre": "展示",
169
+ "day1": true,
170
+ "day2": false,
171
+ "day3": true,
172
+ "place": "1号館",
173
+ "description": "…",
174
+ "prComment": "…",
175
+ "prCommentLong": null,
176
+ "note": null,
177
+ "imageName": "image.jpg",
178
+ "prImageName1": null,
179
+ "prImageName2": null,
180
+ "prImageName3": null,
181
+ "prImageName4": null,
182
+ "prImageName5": null,
183
+ "urlX": null,
184
+ "urlInstagram": null,
185
+ "urlOfficialWebsite": null,
186
+ "urlYoutube": null,
187
+ "urlOther": null
188
+ }
189
+ ```
190
+
191
+ 3) 検索(通常)
192
+ ```
193
+ POST /api/search
194
+ Authorization: Bearer <API_ACCESS_KEY>
195
+ X-API-KEY: <API_SECRET_KEY>
196
+ Content-Type: application/json
197
+
198
+ {
199
+ "query": "焼きそば",
200
+ "debug": false
201
+ }
202
+
203
+ 200 OK
204
+ {
205
+ "projectIds": ["62000A", "62000B", "62000C"]
206
+ }
207
+ ```
208
+
209
+ 4) 検索(デバッグ付き)
210
+ ```
211
+ POST /api/search
212
+ Authorization: Bearer <API_ACCESS_KEY>
213
+ X-API-KEY: <API_SECRET_KEY>
214
+ Content-Type: application/json
215
+
216
+ {
217
+ "query": "吹奏楽",
218
+ "debug": true
219
+ }
220
+
221
+ 200 OK
222
+ {
223
+ "projectIds": ["62010A", "62003C"],
224
+ "scores": [
225
+ {"projectId": "62010A", "score": 0.83},
226
+ {"projectId": "62003C", "score": 0.78}
227
+ ],
228
+ "details": [
229
+ {
230
+ "projectId": "62010A",
231
+ "bm25": 0.62,
232
+ "ws_filter_topk": 0.55,
233
+ "ws_rerank_pairavg": 0.57,
234
+ "org_boost": 0.70,
235
+ "fused_filter": 0.59,
236
+ "fused_final": 1.32
237
+ }
238
+ ]
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## エラーとステータスコード
245
+
246
+ - `400 Bad Request`: `query` が空(検索)
247
+ - `401 Unauthorized`: HF Spaces のプライベートアクセス認証に失敗
248
+ - `403 Forbidden`: アプリ内APIキー認証に失敗(`X-API-KEY`)
249
+ - `404 Not Found`: 企画が見つからない(詳細)
250
+ - `422 Unprocessable Entity`: リクエスト形式が不正(必須フィールド欠落など、FastAPI標準)
251
+
252
+ ---
253
+
254
+ ## 実装メモ(フロントエンド)
255
+
256
+ - 圧縮レスポンス: `/api/projects` は `Content-Encoding: gzip`。ブラウザの `fetch` は自動解凍します。低レベルHTTPクライアントを使う場合は自動解凍設定を確認してください。
257
+ - 認証ヘッダ: すべての保護エンドポイントに `X-API-KEY` を付けてください。
258
+ - デバッグ検索: チューニング時のみ利用を推奨。ユーザー向けUIでは通常 `debug=false`。
259
+ - 並び順: 検索結果はスコアの高い順です。`projectIds` の順序をそのまま利用してください。
260
+
261
+ ---
262
+
263
+ ## クライアント例
264
+
265
+ 1) fetch(ブラウザ)
266
+ ```js
267
+ // 検索
268
+ const res = await fetch(`${process.env.FASTAPI_URL}/api/search`, {
269
+ method: 'POST',
270
+ headers: {
271
+ 'Content-Type': 'application/json',
272
+ // レイヤ1: HF Spaces(プライベート)
273
+ 'Authorization': `Bearer ${process.env.API_ACCESS_KEY}`,
274
+ // レイヤ2: アプリ内APIキー
275
+ 'X-API-Key': process.env.API_SECRET_KEY,
276
+ },
277
+ body: JSON.stringify({ query: '唐揚げ', debug: false }),
278
+ });
279
+ const data = await res.json();
280
+ console.log(data.projectIds);
281
+ ```
282
+
283
+ 2) curl
284
+ ```bash
285
+ curl -X POST \
286
+ -H "Authorization: Bearer $API_ACCESS_KEY" \
287
+ -H "X-API-KEY: $API_SECRET_KEY" \
288
+ -H "Content-Type: application/json" \
289
+ -d '{"query":"焼きそば"}' \
290
+ "$FASTAPI_URL/api/search"
291
+ ```
292
+
293
+ ---
294
+
295
+ ## 環境変数の例(フロントエンド側)
296
+
297
+ - `FASTAPI_URL`: バックエンドのベースURL(例: `https://<space-name>.<org>.hf.space`)
298
+ - `API_SECRET_KEY`: アプリ内APIキー(バックエンド側 `.env` の `API_SECRET_KEY` と一致させる)
299
+ - `API_ACCESS_KEY`: HF Spaces のプライベートアクセス用トークン(Bearer で送る)
docs/files.md DELETED
@@ -1,15 +0,0 @@
1
- # オフライン生成
2
- - `cc.ja.300.bin`: OOV(Out of Vocabrary)時に使用 更新不要
3
- - `generated/`: 毎年更新
4
- - `projects.json`: API返却用データ
5
- - `scripts/1_build_dict.py`で生成
6
- - `bm25_meta.json`: BM25Fメタデータ
7
- - `scripts/4_prepare_bm25f_meta.py`で生成
8
- - `tf_token.json`: フィールド別TF/トークン
9
- - `scripts/5_prepare_tf_token.py`で生成
10
- - `synonyms_cache.json`: Sudachi同義語キャッシュ
11
- - `scripts/3_build_synonyms_from_sudachi.py`から生成
12
- - `word_vocab.json`
13
- - `scripts/6_build_word_embeddings.py`で生成
14
- - `word_vectors.npz`
15
- - `scripts/6_build_word_embeddings.py`で生成
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/old_index.md DELETED
@@ -1,245 +0,0 @@
1
- ```py
2
- import os
3
- import sys
4
- import json
5
- import gzip
6
- import logging
7
- from dotenv import load_dotenv
8
- from fastapi import FastAPI, Response, Depends, HTTPException, Security, Query
9
- from fastapi.responses import RedirectResponse
10
- from fastapi.security import APIKeyHeader
11
- from pydantic import BaseModel
12
- from sudachipy import dictionary
13
- from dataclasses import asdict, fields
14
- from typing import Optional, Literal, List, Dict
15
- from pathlib import Path
16
-
17
- # デバッグ用
18
- from contextlib import asynccontextmanager
19
- from fastapi.routing import APIRoute
20
-
21
- # ローカルモジュールのインポート
22
- from api import search_preprocess
23
- from api import data_fetch
24
- from api.search import pipeline as search_pipeline
25
- from api.model import Project, ProjectSummary, ProjectDetail, ProjectIds
26
-
27
- # 親ディレクトリをパスに追加して設定ファイルをインポート
28
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
29
- from scripts.config import cfg_file, cfg_search_model, cfg_search_params
30
-
31
- # --- ロギング設定 ---
32
- _level_name = os.getenv("LOG_LEVEL", "INFO").upper()
33
- _level = getattr(logging, _level_name, logging.INFO)
34
- logging.basicConfig(level=_level, format="%(asctime)s [%(levelname)s]: %(message)s")
35
- log = logging.getLogger(__name__)
36
- log.setLevel(_level)
37
-
38
-
39
- # --- グローバル変数 ---
40
- # このファイルの場所を基準にプロジェクトルートを特定
41
- PROJECT_ROOT = Path(__file__).resolve().parent.parent
42
-
43
- # 設定情報
44
- settings_file = cfg_file()
45
- settings_search_model = cfg_search_model()
46
- settings_search_params = cfg_search_params()
47
-
48
- # モデルとデータ
49
- tokenizer_obj = None
50
- sentence_model = None # kept for compatibility; no longer used
51
- all_projects: List[Project] = [] # populated via search_pipeline
52
- project_map: Dict[str, Project] = {}
53
- docs_for_search: Dict[str, Dict] = {}
54
- bm25_meta: Dict = {}
55
- synonyms_cache: Dict = {}
56
- custom_synonyms: Dict = {}
57
-
58
-
59
- # --- 初期化処理 ---
60
-
61
-
62
- def initialize_sudachi_tokenizer() -> dictionary.Dictionary:
63
- """SudachiPyトークナイザをユーザー辞書と共に初期化する"""
64
- sudachi_config_path = PROJECT_ROOT / "scripts/sudachi.json"
65
- try:
66
- if sudachi_config_path.exists():
67
- log.info(
68
- "SudachiPy設定ファイルが見つかりました。ユーザー辞書で初期化します。"
69
- )
70
- with open(sudachi_config_path, "r", encoding="utf-8") as f:
71
- config = json.load(f)
72
-
73
- # userDictのパスを絶対パスに変換
74
- if "userDict" in config:
75
- config["userDict"] = [str(PROJECT_ROOT / p) for p in config["userDict"]]
76
-
77
- # configオブジェクトをJSON文字列に変換して渡す
78
- return dictionary.Dictionary(config=json.dumps(config)).create()
79
- else:
80
- log.info(
81
- "SudachiPy設定ファイルが見つかりません。システム辞書のみ使用します。"
82
- )
83
- return dictionary.Dictionary().create()
84
- except Exception as e:
85
- log.error(f"SudachiPyの初期化に失敗しました: {e}")
86
- raise
87
-
88
-
89
- def load_all_data():
90
- """Deprecated: Data is now loaded by api.search.pipeline.initialize()."""
91
- log.info("load_all_data() is deprecated; using search_pipeline.initialize().")
92
-
93
-
94
- @asynccontextmanager
95
- async def lifespan(app: FastAPI):
96
- """アプリケーションの起動時と終了時に実行される処理"""
97
- # --- 起動時処理 ---
98
- global tokenizer_obj, sentence_model, all_projects, project_map
99
-
100
- log.info("アプリケーションを起動します...")
101
-
102
- # 環境変数をロード
103
- load_dotenv()
104
-
105
- # トークナイザの初期化
106
- tokenizer_obj = initialize_sudachi_tokenizer()
107
- search_preprocess.set_tokenizer(tokenizer_obj)
108
-
109
- # 生成物の確保(ローカル or HF Datasets からフェッチ)
110
- try:
111
- fetch_summary = data_fetch.orchestrate_fetch_and_validate(settings_search_model)
112
- missing = fetch_summary.get("missing_before", [])
113
- fetched = fetch_summary.get("fetched", {})
114
- checks = fetch_summary.get("checks", {})
115
- ok_list = [k for k, v in checks.items() if v]
116
- log.info(
117
- f"DATA PREP: missing_before={missing}, fetched={list(k for k,v in fetched.items() if v)}, checks_ok={ok_list}"
118
- )
119
- ws_conf = settings_search_model.get("word_sim", {}) or {}
120
- ws_enabled = bool(ws_conf.get("enable"))
121
- ws_mode = (ws_conf.get("mode") or "avg").lower()
122
- ws_ready = (
123
- checks.get("word_vectors.npz", False)
124
- and checks.get("word_vocab.json", False)
125
- and (True if ws_mode != "avg" else checks.get("doc_vectors.npy", False))
126
- )
127
- if ws_enabled:
128
- log.info(f"WORD_SIM: requested mode={ws_mode}, assets_ready={ws_ready}")
129
- except Exception as e:
130
- log.warning(f"Data fetch/validate step failed: {e}")
131
-
132
- # 検索パイプラインの初期化���データロード含む)
133
- search_pipeline.initialize(tokenizer_obj)
134
- # 既存エンドポイント互換のためローカル参照も持つ
135
- all_projects = search_pipeline.get_projects()
136
- project_map = search_pipeline.get_project_map()
137
-
138
- # ルート情報のログ出力
139
- for r in app.routes:
140
- if isinstance(r, APIRoute):
141
- log.info(f"ROUTE {list(r.methods)} {r.path}")
142
- log.info(f"DOCS={app.docs_url} OPENAPI={app.openapi_url} REDOC={app.redoc_url}")
143
- log.info("アプリケーションの準備が整いました。")
144
-
145
- yield
146
-
147
- # --- 終了時処理 ---
148
- log.info("アプリケーションをシャットダウンします。")
149
-
150
-
151
- # --- FastAPIアプリケーション設定 ---
152
- app = FastAPI(lifespan=lifespan)
153
-
154
- # .envファイルから環境変数を読み込む
155
- load_dotenv()
156
-
157
-
158
- # --- 認証設定 ---
159
- API_KEY_NAME = "X-API-KEY"
160
- API_SECRET_KEY = os.getenv("API_SECRET_KEY")
161
- api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=True)
162
-
163
-
164
- async def get_api_key(key: str = Security(api_key_header)):
165
- """APIキーを検証する依存関係"""
166
- if not API_SECRET_KEY or key != API_SECRET_KEY:
167
- raise HTTPException(status_code=403, detail="Could not validate credentials.")
168
- return key
169
-
170
-
171
- # --- APIエンドポイント ---
172
-
173
-
174
- @app.get("/", include_in_schema=False)
175
- def root():
176
- """ルートURLへのアクセスはドキュメントへリダイレクト"""
177
- return RedirectResponse("/docs")
178
-
179
-
180
- @app.get("/api/health")
181
- def health_check():
182
- """ヘルスチェック用エンドポイント"""
183
- return {"status": "ok"}
184
-
185
-
186
- @app.get(
187
- "/api/projects",
188
- response_model=List[ProjectSummary],
189
- dependencies=[Depends(get_api_key)],
190
- )
191
- def get_summary_data():
192
- """全企画のサマリー情報をGZIP圧縮して返す"""
193
- summary_fields = {f.name for f in fields(ProjectSummary)}
194
- summaries = [
195
- {k: v for k, v in asdict(p).items() if k in summary_fields}
196
- for p in search_pipeline.get_projects()
197
- ]
198
-
199
- content = json.dumps(summaries, ensure_ascii=False).encode("utf-8")
200
- return Response(
201
- content=gzip.compress(content),
202
- headers={"Content-Encoding": "gzip", "Content-Type": "application/json"},
203
- )
204
-
205
-
206
- @app.get(
207
- "/api/details",
208
- response_model=ProjectDetail,
209
- dependencies=[Depends(get_api_key)],
210
- )
211
- def get_project_detail(projectId: str = Query(..., description="取得したい企画のID")):
212
- """指定されたIDの企画詳細情報を返す"""
213
- project = search_pipeline.get_project_map().get(projectId)
214
- if not project:
215
- raise HTTPException(status_code=404, detail="Project not found")
216
-
217
- detail_fields = {f.name for f in fields(ProjectDetail)}
218
- project_dict = asdict(project)
219
- return {key: project_dict[key] for key in detail_fields if key in project_dict}
220
-
221
-
222
- # --- 検索エンドポイント ---
223
-
224
-
225
- class SearchRequest(BaseModel):
226
- query: str
227
- # fusion は非推奨: 設定ファイルで制御し、ここでは受け取っても無視する
228
- fusion: Optional[Literal["add", "mul"]] = None
229
- debug: bool = False
230
-
231
-
232
- @app.post(
233
- "/api/search",
234
- response_model=ProjectIds,
235
- dependencies=[Depends(get_api_key)],
236
- )
237
- def search(request: SearchRequest) -> ProjectIds:
238
- """BM25F中心の新パイプラインで検索し、企画IDを返す。"""
239
- if not request.query:
240
- raise HTTPException(status_code=400, detail="Query cannot be empty")
241
- result_ids = search_pipeline.search(request.query, request.debug)
242
- log.info(f"検索完了: {len(result_ids)} 件を返却")
243
- return ProjectIds(projectIds=result_ids)
244
-
245
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/project_overview.md ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # プロジェクトの全体像と調整ガイド
2
+
3
+ このドキュメントは、千葉大祭の「企画情報の提供&検索システム」の中身を、初学者向けにやさしく説明したものです。環境構築の手順は README.md にあるので、ここでは仕組みと調整のポイントに絞ります。
4
+
5
+ ---
6
+
7
+ ## 目次
8
+
9
+ - [1. 全体像(何をしているか)](#1-全体像何をしているか)
10
+ - [2. ディレクトリと主な役割](#2-ディレクトリと主な役割)
11
+ - [3. 検索システムの仕組み(やさしく解説)](#3-検索システムの仕組みやさしく解説)
12
+ - [4. パラメータ解説(どこをどう調整する?)](#4-パラメータ解説どこをどう調整する)
13
+ - [4.1 search_model.json(主なもの)](#41-search_modeljson主なもの)
14
+ - [4.2 files.json(主なもの)](#42-filesjson主なもの)
15
+ - [5. データ作成パイプライン(いつ何を再実行?)](#5-データ作成パイプラインいつ何を再実行)
16
+ - [6. API の使い方(超概要)](#6-api-の使い方超概要)
17
+ - [7. よくある調整手順の例](#7-よくある調整手順の例)
18
+ - [8. ちょっとした注意点とチェックリスト](#8-ちょっとした注意点とチェックリスト)
19
+ - [9. 用語ミニ辞典](#9-用語ミニ辞典)
20
+
21
+ ---
22
+
23
+ ## 1. 全体像(何をしているか)
24
+
25
+ - 企画情報(団体名・企画名・説明など)を CSV から取り込み、検索しやすい形に整えます。
26
+ - Web API(FastAPI)を通じて、検索キーワードから「関係が強い企画」を並べて返します。
27
+ - 検索は「単語の一致(BM25F)」と「意味の近さ(単語ベクトル)」を組み合わせ、必要なら同義語も広げます。
28
+
29
+ 主な流れは次の通りです。
30
+ 1) データ作成スクリプトで各種ファイルを生成(後述の scripts/ 参照)
31
+ 2) API 起動時に検索用のデータを読み込み(`api/main.py` → `api/search/engine.py`)
32
+ 3) リクエストで受け取ったキーワードを分かち書き(SudachiPy)→ 同義語で拡張 → スコアリング → 上位を返す
33
+
34
+ ---
35
+
36
+ ## 2. ディレクトリと主な役割
37
+
38
+ - `api/`
39
+ - `main.py`: FastAPI のエントリポイント。エンジン初期化、エンドポイント(`/api/search` など)定義。
40
+ - `search/engine.py`: 検索の本体。トークン化、同義語展開、BM25F、意味類似スコア、並び替え等。
41
+
42
+ - `scripts/`(データ作成パイプライン)
43
+ - `build_all.py`: 0→6 の全ステップを順番に実行するランナー。
44
+ - `0_download_data.py`: 単語ベクトル(fastText)のダウンロード。
45
+ - `1_build_dict.py`: Sudachi のユーザー辞書をビルド(任意だが推奨)。
46
+ - `2_create_projects_data.py`: CSV から `projects.json` を生成。
47
+ - `3_build_synonyms_from_sudachi.py`: 同義語辞書(Sudachi公式+カスタム)から、コーパスにある語だけを抜き出してキャッシュ化。
48
+ - `4_prepare_bm25f_meta.py`: BM25F の IDF や平均長を計算して `bm25_meta.json` に保存。
49
+ - `5_prepare_tf_token.py`: 各文書のフィールド別 TF/長さを `tf_token.json` に保存。
50
+ - `6_build_word_embeddings.py`: コーパス語彙に対応する fastText ベクトルを抽出して保存。
51
+
52
+ - `config/`
53
+ - `search_model.json`: 検索のふるまい(同義語の有無、重み、しきい値など)の設定。
54
+ - `files.json`: 各種入力・出力ファイルのパス定義。
55
+
56
+ - `schemas/`
57
+ - `projects.py`: API レスポンスのデータ形(一覧・詳細・検索結果の ID 群)。
58
+ - `tf_token.py`: 検索前処理で使う中間データの形(TF/長さなど)。
59
+
60
+ - `utils/`
61
+ - `json.py`: 設定ファイルから値を取得、JSON の安全書き出しなど。
62
+ - `io.py`: 行ストリーム読み込みなどの小物。
63
+ - `text_process.py`: 見えない制御文字の除去など。
64
+
65
+ - その他
66
+ - `resources/`: 入力素材(CSV、同義語辞書、ストップワード、ユーザー辞書、ベクトル等)。
67
+ - `Dockerfile`: コンテナ実行向けの定義(概要のみ把握でOK)。
68
+ - `.github/workflows/deploy_to_hf_space.yaml`: Hugging Face Spaces へのデプロイ用ワークフロー(概要のみ把握でOK)。
69
+
70
+ ---
71
+
72
+ ## 3. 検索システムの仕組み(やさしく解説)
73
+
74
+ 検索は「単語での一致」と「意味の近さ」をミックスして、必要なら「団体名などの前方一致」も加点して並び順を決めます。
75
+
76
+ 1) トークン化(単語に分ける)
77
+ - SudachiPy を使い、名詞・動詞・形容詞などの重要品詞だけを対象にします。
78
+ - ストップワード(よく出るが意味が薄い語)や禁止語は除外します。
79
+
80
+ 2) 同義語の拡張(必要に応じて)
81
+ - 例:「大学祭」→「学園祭」も一緒に検索する、のようにキーワードを広げます。
82
+ - Sudachi の同義語辞書+プロジェクト内語彙にあるものだけを採用。出しすぎないように上限を設けます。
83
+
84
+ 3) BM25F で「単語一致の強さ」を計算
85
+ - BM25 は「その単語がどれくらい珍しいか(IDF)」や「どれくらい繰り返し出るか(TF)」をもとにスコア化します。
86
+ - F は Field の F。タイトルは重要、長文は少し抑えめ…といった「フィールドごとの重み」を付けられます。
87
+
88
+ 4) 単語ベクトルで「意味の近さ」を計算
89
+ - fastText などの単語ベクトルを使い、「言い換え」でも近さを捉えます。
90
+ - 資料がない単語は、fastText の subword 機能で「なんとなくの方向」を推測して補います(OOV対応)。
91
+ - まずフィルタ用に top-k 集約でざっくり類似度を出し、最終並べ替え時に平均方式でもう一度なめらかに評価します。
92
+
93
+ 5) 団体名などへのブースト(加点)
94
+ - 入力文字列が「団体名」や「読み」に完全一致/前方一致/部分一致した場合、加点して上に来やすくします。
95
+
96
+ 6) 閾値と上限で絞る → 並べ替え
97
+ - 弱すぎる候補は捨て、最大件数までに絞って、合成スコアの高い順に返します。
98
+
99
+ ---
100
+
101
+ ## 4. パラメータ解説(どこをどう調整する?)
102
+
103
+ 調整の中心は `config/search_model.json` と `config/files.json` です。まずは `search_model.json` から。
104
+
105
+ ### 4.1 search_model.json(主なもの)
106
+
107
+ - 同義語関連(`synonyms`)
108
+ - `enable`: 同義語を使うか。誤ヒットが増えたら false も検討。
109
+ - `sources.sudachi`: Sudachi の同義語辞書を使うか。
110
+ - `sources.custom_json`: 追加入力のパス(`resources/synonyms_custom.json`)。独自の言い換えを足せます。
111
+ - `limits.max_expansions_per_term`: 1語あたり何個まで広げるか。多いほど網羅的、誤ヒットも増えやすい。
112
+ - `limits.max_query_variants`: 全体の拡張上限。過剰拡張を防ぐ安全弁。
113
+ - `limits.min_char_len`: これ未満の短い語は拡張しない。
114
+ - `banlist`: 拡張・検索対象から外す語(例: 「部」「会」など一般的すぎる語)。
115
+
116
+ - BM25F(`bm25f`)
117
+ - `k1`/`b`: BM25 の基本パラメータ。通常は標準値(例: 1.2/0.75)から大きく動かさない。
118
+ - `field_weights`: フィールドごとの重要度。`title` を上げるとタイトル一致が強くなります。
119
+
120
+ - 意味類似(`word_sim`)
121
+ - `enable`: 有効化するか。無関係の表現で拾いすぎるなら false を検討。
122
+ - `alpha`: BM25 と意味類似の混ぜ具合。1.0 に近いほど BM25 寄り、0.0 に近いほど意味寄り。
123
+ - `topk_k`: フィルタ段階で使う「上位 k 個」をどれだけ拾うか。小さいと厳しめ。
124
+ - `rerank`: 最終並べ替えに使う類似度の計算方式。`pair_avg` はなめらかで扱いやすい。
125
+
126
+ - サブワード(`query_subword`)
127
+ - `enable`: fastText の subword で未知語を補うか。
128
+ - `oov_weight`: 未知語ベクトルの重み(小さいほど控えめに採用)。
129
+
130
+ - 団体名ブースト(`organization.boost`)
131
+ - `exact`/`prefix`/`substring`: それぞれ完全一致/前方一致/部分一致の加点量。
132
+ - `min_len`: 短すぎる入力で過剰加点しないための最小文字数。
133
+
134
+ - フィルタ(`filter`)
135
+ - `min_results`/`max_results`: 返す件数の下限/上限。
136
+ - `bm25_min`/`word_sim_min`/`fused_min`: それぞれ BM25・意味類似・合成スコアでの最低ライン。
137
+ - `fused_rel_top_ratio`: 「トップのスコアに対する割合」で足切りする係数。0.5 ならトップの半分未満を落とすイメージ。
138
+
139
+ 調整のコツ(例)
140
+ - 「タイトル一致をもっと強く」→ `bm25f.field_weights.title` を上げる。
141
+ - 「言い換えも拾いたいが誤ヒットが増える」→ `word_sim.alpha` を少し上げる(BM25寄りに)。
142
+ - 「似ている語で広がりすぎる」→ `synonyms.enable=false` か `limits` を厳しめに。
143
+ - 「団体名の指名検索を最優先に」→ `organization.boost.exact` を上げる。
144
+ - 「全体的に弱い」→ `filter.fused_min` を下げるか、`fused_rel_top_ratio` を下げて緩める。
145
+
146
+ ### 4.2 files.json(主なもの)
147
+
148
+ - `sudachi.*`: Sudachi の設定(設定 JSON、ユーザー辞書、同義語、ストップワード、同義語キャッシュの保存先)。
149
+ - `projects.*`: 入力 CSV と、出力される `projects.json` の場所。
150
+ - `bm25.*`: BM25 のメタ情報(IDF/平均長)と `tf_token.json` の場所。
151
+ - `embeddings.*`: fastText の `.vec` / `.bin` と、抽出した語彙・ベクトルの保存先。
152
+
153
+ パスを変えると生成物の出力先も変わるので、変更後は該当ステップを作り直してください。
154
+
155
+ ---
156
+
157
+ ## 5. データ作成パイプライン(いつ何を再実行?)
158
+
159
+ 全自動は `python scripts/build_all.py` ですが、調整時は必要な所だけを再実行すると速いです。
160
+
161
+ - CSV を更新したら → `2`、`3`、`4`、`5`(+ `6` も必要なら)
162
+ - `synonyms_custom.json` を更新したら → `3`
163
+ - `search_model.json` の品詞や `target_fields` を変えたら → `3`、`4`、`5`(+ `6`)
164
+ - `field_weights` やフィルタ閾値だけ変えた → 再ビルド不要(API 再起動のみ)
165
+ - fastText を入れ替えた → `6`
166
+
167
+ 各ステップの要約
168
+ - 0: fastText のダウンロード(大容量。最初だけ)
169
+ - 1: ユーザー辞書ビルド(任意。固有名詞を強くしたいときに)
170
+ - 2: CSV → `projects.json`
171
+ - 3: 同義語キャッシュ生成
172
+ - 4: BM25F メタ情報(IDF/平均長)
173
+ - 5: フィールド別 TF/長さ
174
+ - 6: 語彙ベクトル抽出(`.vec` がある場合)
175
+
176
+ ---
177
+
178
+ ## 6. API の使い方(超概要)
179
+
180
+ - `GET /api/health`: ヘルスチェック。
181
+ - `GET /api/projects`: 企画一覧(圧縮返却)。
182
+ - `GET /api/details?projectId=...`: 個別企画の詳細。
183
+ - `POST /api/search`(本文: `{ "query": "...", "debug": false }`): 関連が高い順の `projectIds` を返す。`debug=true` で内部スコアの簡易情報も返却。
184
+ - ヘッダ `X-API-KEY` が必要(キー名のみ言及。値は README 参照)。
185
+
186
+ ---
187
+
188
+ ## 7. よくある調整手順の例
189
+
190
+ 例1: 「タイトル一致を最優先にしたい」
191
+ 1) `config/search_model.json` の `bm25f.field_weights.title` を少し上げる(例: 2.0 → 2.5)。
192
+ 2) API を再起動して挙動を確認。
193
+
194
+ 例2: 「言い換えの拾いすぎを抑えたい」
195
+ 1) `synonyms.enable` を false にする、または `limits.max_expansions_per_term` を下げる。
196
+ 2) 適宜 `word_sim.alpha` を上げて BM25 寄りに。
197
+ 3) 必要に応じて `scripts/3_build_synonyms_from_sudachi.py` を再実行。
198
+
199
+ 例3: 「“団体名”の指名検索をもっと強く」
200
+ 1) `organization.boost.exact` や `prefix` を上げる。
201
+ 2) 極端になりすぎたら `filter` の閾値で下支え。
202
+
203
+ ---
204
+
205
+ ## 8. ちょっとした注意点とチェックリスト
206
+
207
+ - Sudachi の対象品詞(`target_pos_l1`)や対象フィールド(`target_fields`)を変えると、多くの中間データを作り直す必要があります(3,4,5,6)。
208
+ - `.bin`(fastText Subword)は OOV 補助用、`.vec` は語彙ベクトル抽出用です。両方あると最もリッチに動きます。
209
+ - デバッグ時は `POST /api/search` で `debug=true` を使うと、BM25/類似/ブースト/合成の内訳が見られます。
210
+ - 値は README の .env を参照(ここではキー名のみ)。例: `API_SECRET_KEY`, `HF_TOKEN`, `HF_EMBEDDINGS_REPO_ID`。
211
+
212
+ ---
213
+
214
+ ## 9. 用語ミニ辞典
215
+
216
+ - BM25/BM25F: 単語の一致度をスコア化する仕組み。F はフィールド重み付きの拡張版。
217
+ - IDF: 珍しい単語ほど高い値(差別力がある)。
218
+ - fastText: 単語をベクトルにする技術。未知語も subword で推測可能。
219
+ - OOV: 語彙外(Out-Of-Vocabulary)。手元の語彙にない単語のこと。
220
+
221
+ ---
222
+
223
+ 困ったら、まずは `config/search_model.json` を少しずつ調整して、効果を `debug=true` で確認するのがおすすめです。
224
+
docs/search_implementation_plan.md DELETED
@@ -1,141 +0,0 @@
1
- # 検索実装計画書(BM25F + fastText OOV, .vec/.bin 運用)
2
-
3
- 目的
4
- - BM25F を主軸に、任意の語義近接スコア(fastText 由来の事前生成行列)を融合し、精度と速度を両立する。
5
- - OOV(語彙外)クエリは fastText のサブワードを `.bin` でランタイム生成して補う。
6
- - Hugging Face Spaces 無料CPU(RAM 16GB)で安定運用できる構成にする。
7
-
8
- 結論(運用方針)
9
- - `.vec`(1.82GB)は「ビルド専用」。語彙を間引いた行列 `word_vectors.npz` と `doc_vectors.npy` を生成して配布する。
10
- - `.bin`(838MB)は「ランタイムの OOV 専用」。起動時に1回ロードし、未知語のベクトルを生成する。
11
- - 量子化 `.ftz` は不使用(fastText 公式の quantize は教師ありモデルのみサポート)。
12
-
13
- ---
14
-
15
- ## 1. 全体アーキテクチャ
16
- - 検索スコア
17
- - BM25F: 既存のトークン化・フィールド重み(`config/search_model.json`)。
18
- - 語義近接(word_sim): `word_vectors.npz/doc_vectors.npy` を利用。`mode=avg|soft` を設定可能。
19
- - 融合: `score = alpha * bm25f + (1 - alpha) * word_sim`(`alpha` は 0.6–0.8 目安)。
20
- - OOV 対応
21
- - 既知語: `word_vocab.json + word_vectors.npz` から即時ルックアップ(L2 正規化済)。
22
- - 未知語: fastText `.bin` をロードし `get_word_vector` でサブワードから生成(取得後に L2 正規化)。
23
- - キャッシュ: OOV 語ベクトルに LRU キャッシュを適用(例: 50k 語)。
24
- - フォールバック
25
- - `.bin` が未配置/取得失敗時: OOV をゼロベクトル扱い → word_sim 寄与が自然に減衰、BM25F 単体でも整合。
26
-
27
- ## 2. ビルド(ローカル・年次)
28
- - 入力
29
- - `resources/embeddings/cc.ja.300.vec`(1.82GB):fastText の公開ベクトル、または自前学習の `.vec`。
30
- - 注意: `.vec` と `.bin` は必ず同一モデル(同じ学習元/版)を用意する。
31
- - 実行
32
- - `python scripts/build_all.py`(Step 6 が `.vec` を検出した場合に実行)
33
- - 出力(`data/generated/`)
34
- - `projects.json`, `bm25_meta.json`, `tf_token.json`(BM25F 用)
35
- - `word_vocab.json`, `word_vectors.npz`, `doc_vectors.npy`(word_sim 用)
36
- - 配布
37
- - Hugging Face Datasets(private)に `generated/` 一式をアップロード。年度タグ(例: `2025.0`)で管理。
38
-
39
- ## 3. ランタイム(Spaces, 16GB RAM)
40
- - 起動時取得
41
- - Datasets から `generated/` をダウンロード → `data/generated/` へ配置。
42
- - Models から `cc.ja.300.bin` をダウンロード → `resources/embeddings/` へ配置。
43
- - 起動時ロード
44
- - `word_vectors.npz/doc_vectors.npy/word_vocab.json` を読み込み。
45
- - 設定 `query_subword.enable=true` なら `fasttext.load_model(cc.ja.300.bin)` を1回だけ実行。
46
- - メモリ目安
47
- - `.bin` ロード: 1.0–1.5GB 程度
48
- - 事前生成行列ほか: 10–40MB 程度
49
- - 余裕を見て合計 1.5–2.5GB 程度(RAM 16GB 内に十分収まる)。
50
- - コールドスタート
51
- - `.bin` の取得(~0.8GB)+ロードで数十秒〜数分(I/O 依存)。起動時のみ発生。
52
-
53
- ## 4. クエリ処理フロー(擬似コード)
54
- 1) クエリを正規化 → トークン化(SudachiPy)。
55
- 2) 各トークンについて:
56
- - 語彙に存在 → `word_vectors` から取得
57
- - なければ(OOV) → `.bin` から `get_word_vector`(あれば LRU から)
58
- - ベクトルは L2 正規化
59
- 3) word_sim を計算
60
- - `avg`: IDF 加重平均ベクトルと `doc_vectors.npy` のコサイン類似
61
- - `soft`: 各クエリ語に対し文書語群との最大コサインを合算(短クエリに強い)
62
- 4) BM25F と融合
63
- - `score = alpha * bm25f + (1 - alpha) * word_sim`
64
- - OOV が多いクエリは `oov_weight < 1.0` で寄与を減衰可能
65
-
66
- ## 5. 設定(例: `config/search_model.json`)
67
- - 主要キー
68
- - `bm25f`: 既存のフィールド定義/重み
69
- - `word_sim.enable`: `true|false`
70
- - `word_sim.mode`: `"soft"|"avg"`(既定は `soft` 推奨)
71
- - `word_sim.alpha`: `0.6–0.8` 目安
72
- - `query_subword.enable`: `true|false`
73
- - `query_subword.path`: `"resources/embeddings/cc.ja.300.bin"`
74
- - `query_subword.oov_weight`: `0.6–1.0`(既定 0.8 例)
75
- - `query_subword.cache_size`: `50000` など
76
-
77
- ## 6. 環境変数(Spaces/ローカル)
78
- - 取得制御
79
- - `FETCH_FROM_HF_ON_STARTUP=true`
80
- - `DATASET_REPO`, `DATASET_REVISION`, `HF_TOKEN`
81
- - `MODEL_REPO`, `MODEL_REVISION`, `MODEL_FILENAME=cc.ja.300.bin`
82
- - ログ
83
- - `LOG_LEVEL=INFO|DEBUG`
84
- - API
85
- - `API_SECRET_KEY`(必須)
86
-
87
- ## 7. 運用(HF Hub レイアウト)
88
- - Datasets(private): `generated/` 一式(年度タグで版管理)
89
- - Models(private): `cc.ja.300.bin`(年度非依存、基本更新不要)
90
- - フォールバック: 取得失敗時は BM25F のみで起動可(word_sim を自動無効化)
91
-
92
- ## 8. 検証・品質
93
- - スモーク
94
- - `GET /api/health` → 200
95
- - `GET /api/projects`(要 `X-API-KEY`)
96
- - 精度
97
- - 代表クエリの MRR/nDCG を比較、`alpha` と `mode` を探索
98
- - `.vec`/`.bin` 同一モデルであることを確認(空間ずれ防止)
99
- - 性能
100
- - OOV キャッシュを有効化(ヒット率確認)
101
- - 大文字/小文字/正規化の統一でキャッシュ効率を上げる
102
-
103
- ## 9. 既存ドキュメントとの整合
104
- - 量子化 `.ftz` は使用しない(fastText 公式の quantize は教師ありのみ)。
105
- - 該当箇所の表記を `.bin` 前提へ統一(本コミットで主要2ファイルを修正)。
106
-
107
- ## 10. リスクと対策
108
- - モデル不一致(`.vec` と `.bin` の学習元が異なる)
109
- - 同一ソース/版を必須とし、ハッシュで管理
110
- - コールドスタート時間
111
- - `.bin` は Models(private)からの取得を前提。I/O が遅い場合は Space をスリープさせない運用も検討
112
- - メモリ
113
- - 16GB で十分だが、同時常駐データに注意。OOV キャッシュサイズを調整
114
-
115
- ---
116
-
117
- 実装タスクリスト(要約)
118
- 1) `.vec` を `resources/embeddings/` に配置し `build_all.py` 実行 → `generated/` 生成
119
- 2) `generated/` を HF Datasets(private, 年度タグ)へアップロード
120
- 3) `.bin` を HF Models(private)へアップロード
121
- 4) 起動時取得ロジックを有効化(環境変数と設定を反映)
122
- 5) `query_subword`(.bin ロード + LRU キャッシュ)を有効化
123
- 6) パラメータ(`alpha`, `mode`, `oov_weight`)をスモーク+代表クエリでチューニング
124
-
125
- ## 11. 組織名(organization)フィルタ/ブースト
126
- - 目的: 固有名詞(団体名)に対して、シンプルな包含一致を検索条件として扱えるようにする。
127
- - 正規化: NFKC 正規化 → 全角/半角の統一、空白の正規化(連続空白の1個化、前後トリム)。必要に応じてひらがな/カタカナの片寄せは行わない(誤爆回避)。
128
- - モード
129
- - filter: `organization` に部分一致するドキュメントのみを候補集合にする(候補ゼロ時は自動フォールバックで無視するオプションも可)。
130
- - boost: 部分一致ドキュメントに定数加点(例: `org_boost=+0.2`)または乗算(例: `org_boost_mul=1.1`)。
131
- - パラメータ(API; 例)
132
- - `org`: 文字列。部分一致の対象
133
- - `org_mode`: `filter|boost`(既定 `boost`)
134
- - `org_boost`: `0.0–2.0`(既定 `0.2`)
135
- - 実装は検索前処理で `organization` フィールドの正規化文字列へ substring 判定を実施
136
- - 融合との関係
137
- - filter: BM25F/word_sim の計算対象集合を限定
138
- - boost: 最終スコアに加点(または BM25F スコアに加点)
139
- - 注意点
140
- - 正規化は検索側/索引側で同一処理を適用(ビルド時に `org_norm` を持たせると高速)
141
- - 候補ゼロの UX を考慮し、`filter` 選択時のみ `fallback_if_empty=true` を許可
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/test.md DELETED
@@ -1,2063 +0,0 @@
1
- # 電子
2
- {
3
- "projectIds": [
4
- "dummy-01",
5
- "dummy-48",
6
- "dummy-33",
7
- "dummy-25",
8
- "dummy-18",
9
- "dummy-47",
10
- "dummy-36",
11
- "dummy-40",
12
- "dummy-24",
13
- "dummy-21",
14
- "dummy-11",
15
- "dummy-10",
16
- "dummy-02",
17
- "dummy-15",
18
- "dummy-13",
19
- "dummy-17",
20
- "dummy-35",
21
- "dummy-34",
22
- "dummy-38",
23
- "dummy-07",
24
- "dummy-42",
25
- "dummy-31",
26
- "dummy-06",
27
- "dummy-19",
28
- "dummy-45",
29
- "dummy-43",
30
- "dummy-03",
31
- "dummy-30",
32
- "dummy-04",
33
- "dummy-37",
34
- "dummy-23",
35
- "dummy-46",
36
- "dummy-32",
37
- "dummy-08",
38
- "dummy-27",
39
- "dummy-14",
40
- "dummy-09",
41
- "dummy-29",
42
- "dummy-39",
43
- "dummy-20",
44
- "dummy-22",
45
- "dummy-44",
46
- "dummy-05",
47
- "dummy-50",
48
- "dummy-41",
49
- "dummy-28",
50
- "dummy-49",
51
- "dummy-12",
52
- "dummy-26",
53
- "dummy-16"
54
- ],
55
- "scores": [
56
- {
57
- "projectId": "dummy-01",
58
- "score": 3.431400775909424
59
- },
60
- {
61
- "projectId": "dummy-48",
62
- "score": 0.12993203103542328
63
- },
64
- {
65
- "projectId": "dummy-33",
66
- "score": 0.12524525821208954
67
- },
68
- {
69
- "projectId": "dummy-25",
70
- "score": 0.1251281201839447
71
- },
72
- {
73
- "projectId": "dummy-18",
74
- "score": 0.11960740387439728
75
- },
76
- {
77
- "projectId": "dummy-47",
78
- "score": 0.11708493530750275
79
- },
80
- {
81
- "projectId": "dummy-36",
82
- "score": 0.11684656143188477
83
- },
84
- {
85
- "projectId": "dummy-40",
86
- "score": 0.11507218331098557
87
- },
88
- {
89
- "projectId": "dummy-24",
90
- "score": 0.11465374380350113
91
- },
92
- {
93
- "projectId": "dummy-21",
94
- "score": 0.11193694919347763
95
- },
96
- {
97
- "projectId": "dummy-11",
98
- "score": 0.10999565571546555
99
- },
100
- {
101
- "projectId": "dummy-10",
102
- "score": 0.1098007932305336
103
- },
104
- {
105
- "projectId": "dummy-02",
106
- "score": 0.10925329476594925
107
- },
108
- {
109
- "projectId": "dummy-15",
110
- "score": 0.10827929526567459
111
- },
112
- {
113
- "projectId": "dummy-13",
114
- "score": 0.10723763704299927
115
- },
116
- {
117
- "projectId": "dummy-17",
118
- "score": 0.10687872767448425
119
- },
120
- {
121
- "projectId": "dummy-35",
122
- "score": 0.10368791967630386
123
- },
124
- {
125
- "projectId": "dummy-34",
126
- "score": 0.10351861268281937
127
- },
128
- {
129
- "projectId": "dummy-38",
130
- "score": 0.1032002866268158
131
- },
132
- {
133
- "projectId": "dummy-07",
134
- "score": 0.10318154841661453
135
- },
136
- {
137
- "projectId": "dummy-42",
138
- "score": 0.10294974595308304
139
- },
140
- {
141
- "projectId": "dummy-31",
142
- "score": 0.10262186080217361
143
- },
144
- {
145
- "projectId": "dummy-06",
146
- "score": 0.10227564722299576
147
- },
148
- {
149
- "projectId": "dummy-19",
150
- "score": 0.1011614054441452
151
- },
152
- {
153
- "projectId": "dummy-45",
154
- "score": 0.10093759745359421
155
- },
156
- {
157
- "projectId": "dummy-43",
158
- "score": 0.10040473192930222
159
- },
160
- {
161
- "projectId": "dummy-03",
162
- "score": 0.0997525006532669
163
- },
164
- {
165
- "projectId": "dummy-30",
166
- "score": 0.09739913791418076
167
- },
168
- {
169
- "projectId": "dummy-04",
170
- "score": 0.09715365618467331
171
- },
172
- {
173
- "projectId": "dummy-37",
174
- "score": 0.09643036872148514
175
- },
176
- {
177
- "projectId": "dummy-23",
178
- "score": 0.09422503411769867
179
- },
180
- {
181
- "projectId": "dummy-46",
182
- "score": 0.09410383552312851
183
- },
184
- {
185
- "projectId": "dummy-32",
186
- "score": 0.09380743652582169
187
- },
188
- {
189
- "projectId": "dummy-08",
190
- "score": 0.09357142448425293
191
- },
192
- {
193
- "projectId": "dummy-27",
194
- "score": 0.09273825585842133
195
- },
196
- {
197
- "projectId": "dummy-14",
198
- "score": 0.09264349937438965
199
- },
200
- {
201
- "projectId": "dummy-09",
202
- "score": 0.09263898432254791
203
- },
204
- {
205
- "projectId": "dummy-29",
206
- "score": 0.0914716124534607
207
- },
208
- {
209
- "projectId": "dummy-39",
210
- "score": 0.09003062546253204
211
- },
212
- {
213
- "projectId": "dummy-20",
214
- "score": 0.08615680038928986
215
- },
216
- {
217
- "projectId": "dummy-22",
218
- "score": 0.08371936529874802
219
- },
220
- {
221
- "projectId": "dummy-44",
222
- "score": 0.08343670517206192
223
- },
224
- {
225
- "projectId": "dummy-05",
226
- "score": 0.08266183733940125
227
- },
228
- {
229
- "projectId": "dummy-50",
230
- "score": 0.08221913129091263
231
- },
232
- {
233
- "projectId": "dummy-41",
234
- "score": 0.08188144117593765
235
- },
236
- {
237
- "projectId": "dummy-28",
238
- "score": 0.07737957686185837
239
- },
240
- {
241
- "projectId": "dummy-49",
242
- "score": 0.07631941884756088
243
- },
244
- {
245
- "projectId": "dummy-12",
246
- "score": 0.07623115181922913
247
- },
248
- {
249
- "projectId": "dummy-26",
250
- "score": 0.0692358985543251
251
- },
252
- {
253
- "projectId": "dummy-16",
254
- "score": 0.06431126594543457
255
- }
256
- ]
257
- }
258
-
259
- # こうさく
260
- {
261
- "projectIds": [
262
- "dummy-01",
263
- "dummy-19",
264
- "dummy-40",
265
- "dummy-42",
266
- "dummy-25",
267
- "dummy-13",
268
- "dummy-34",
269
- "dummy-48",
270
- "dummy-38",
271
- "dummy-27",
272
- "dummy-02",
273
- "dummy-17",
274
- "dummy-24",
275
- "dummy-35",
276
- "dummy-03",
277
- "dummy-10",
278
- "dummy-45",
279
- "dummy-26",
280
- "dummy-14",
281
- "dummy-33",
282
- "dummy-29",
283
- "dummy-23",
284
- "dummy-50",
285
- "dummy-21",
286
- "dummy-32",
287
- "dummy-36",
288
- "dummy-49",
289
- "dummy-04",
290
- "dummy-30",
291
- "dummy-05",
292
- "dummy-12",
293
- "dummy-15",
294
- "dummy-39",
295
- "dummy-31",
296
- "dummy-37",
297
- "dummy-08",
298
- "dummy-11",
299
- "dummy-09",
300
- "dummy-44",
301
- "dummy-06",
302
- "dummy-43",
303
- "dummy-18",
304
- "dummy-46",
305
- "dummy-20",
306
- "dummy-22",
307
- "dummy-28",
308
- "dummy-41",
309
- "dummy-07",
310
- "dummy-47",
311
- "dummy-16"
312
- ],
313
- "scores": [
314
- {
315
- "projectId": "dummy-01",
316
- "score": 2.958343744277954
317
- },
318
- {
319
- "projectId": "dummy-19",
320
- "score": 0.10967567563056946
321
- },
322
- {
323
- "projectId": "dummy-40",
324
- "score": 0.1078089252114296
325
- },
326
- {
327
- "projectId": "dummy-42",
328
- "score": 0.10442163795232773
329
- },
330
- {
331
- "projectId": "dummy-25",
332
- "score": 0.10362518578767776
333
- },
334
- {
335
- "projectId": "dummy-13",
336
- "score": 0.10124309360980988
337
- },
338
- {
339
- "projectId": "dummy-34",
340
- "score": 0.10120446979999542
341
- },
342
- {
343
- "projectId": "dummy-48",
344
- "score": 0.10025734454393387
345
- },
346
- {
347
- "projectId": "dummy-38",
348
- "score": 0.09987800568342209
349
- },
350
- {
351
- "projectId": "dummy-27",
352
- "score": 0.09944233298301697
353
- },
354
- {
355
- "projectId": "dummy-02",
356
- "score": 0.0974903553724289
357
- },
358
- {
359
- "projectId": "dummy-17",
360
- "score": 0.09713833779096603
361
- },
362
- {
363
- "projectId": "dummy-24",
364
- "score": 0.09597335755825043
365
- },
366
- {
367
- "projectId": "dummy-35",
368
- "score": 0.09521206468343735
369
- },
370
- {
371
- "projectId": "dummy-03",
372
- "score": 0.09464053064584732
373
- },
374
- {
375
- "projectId": "dummy-10",
376
- "score": 0.09453081339597702
377
- },
378
- {
379
- "projectId": "dummy-45",
380
- "score": 0.09366767108440399
381
- },
382
- {
383
- "projectId": "dummy-26",
384
- "score": 0.09298509359359741
385
- },
386
- {
387
- "projectId": "dummy-14",
388
- "score": 0.09244585782289505
389
- },
390
- {
391
- "projectId": "dummy-33",
392
- "score": 0.09236441552639008
393
- },
394
- {
395
- "projectId": "dummy-29",
396
- "score": 0.09188427776098251
397
- },
398
- {
399
- "projectId": "dummy-23",
400
- "score": 0.0905442014336586
401
- },
402
- {
403
- "projectId": "dummy-50",
404
- "score": 0.09030000120401382
405
- },
406
- {
407
- "projectId": "dummy-21",
408
- "score": 0.09017764776945114
409
- },
410
- {
411
- "projectId": "dummy-32",
412
- "score": 0.09004317224025726
413
- },
414
- {
415
- "projectId": "dummy-36",
416
- "score": 0.08987368643283844
417
- },
418
- {
419
- "projectId": "dummy-49",
420
- "score": 0.0897449180483818
421
- },
422
- {
423
- "projectId": "dummy-04",
424
- "score": 0.08967691659927368
425
- },
426
- {
427
- "projectId": "dummy-30",
428
- "score": 0.08961175382137299
429
- },
430
- {
431
- "projectId": "dummy-05",
432
- "score": 0.08941705524921417
433
- },
434
- {
435
- "projectId": "dummy-12",
436
- "score": 0.08816639333963394
437
- },
438
- {
439
- "projectId": "dummy-15",
440
- "score": 0.08616558462381363
441
- },
442
- {
443
- "projectId": "dummy-39",
444
- "score": 0.0843149945139885
445
- },
446
- {
447
- "projectId": "dummy-31",
448
- "score": 0.08405950665473938
449
- },
450
- {
451
- "projectId": "dummy-37",
452
- "score": 0.08368293941020966
453
- },
454
- {
455
- "projectId": "dummy-08",
456
- "score": 0.08284597098827362
457
- },
458
- {
459
- "projectId": "dummy-11",
460
- "score": 0.08270086348056793
461
- },
462
- {
463
- "projectId": "dummy-09",
464
- "score": 0.08264289796352386
465
- },
466
- {
467
- "projectId": "dummy-44",
468
- "score": 0.08196865022182465
469
- },
470
- {
471
- "projectId": "dummy-06",
472
- "score": 0.08190039545297623
473
- },
474
- {
475
- "projectId": "dummy-43",
476
- "score": 0.08157528936862946
477
- },
478
- {
479
- "projectId": "dummy-18",
480
- "score": 0.08130253106355667
481
- },
482
- {
483
- "projectId": "dummy-46",
484
- "score": 0.07963813096284866
485
- },
486
- {
487
- "projectId": "dummy-20",
488
- "score": 0.07923799008131027
489
- },
490
- {
491
- "projectId": "dummy-22",
492
- "score": 0.07898101210594177
493
- },
494
- {
495
- "projectId": "dummy-28",
496
- "score": 0.07825343310832977
497
- },
498
- {
499
- "projectId": "dummy-41",
500
- "score": 0.07740511000156403
501
- },
502
- {
503
- "projectId": "dummy-07",
504
- "score": 0.07709077000617981
505
- },
506
- {
507
- "projectId": "dummy-47",
508
- "score": 0.07670128345489502
509
- },
510
- {
511
- "projectId": "dummy-16",
512
- "score": 0.07050929963588715
513
- }
514
- ]
515
- }
516
-
517
- # 空想地図
518
- {
519
- "projectIds": [
520
- "dummy-02",
521
- "dummy-32",
522
- "dummy-20",
523
- "dummy-27",
524
- "dummy-50",
525
- "dummy-19",
526
- "dummy-31",
527
- "dummy-37",
528
- "dummy-18",
529
- "dummy-21",
530
- "dummy-03",
531
- "dummy-30",
532
- "dummy-39",
533
- "dummy-01",
534
- "dummy-06",
535
- "dummy-09",
536
- "dummy-10",
537
- "dummy-23",
538
- "dummy-11",
539
- "dummy-46",
540
- "dummy-08",
541
- "dummy-35",
542
- "dummy-22",
543
- "dummy-07",
544
- "dummy-17",
545
- "dummy-48",
546
- "dummy-40",
547
- "dummy-47",
548
- "dummy-45",
549
- "dummy-26",
550
- "dummy-05",
551
- "dummy-36",
552
- "dummy-24",
553
- "dummy-44",
554
- "dummy-13",
555
- "dummy-15",
556
- "dummy-38",
557
- "dummy-42",
558
- "dummy-14",
559
- "dummy-41",
560
- "dummy-29",
561
- "dummy-34",
562
- "dummy-25",
563
- "dummy-43",
564
- "dummy-33",
565
- "dummy-12",
566
- "dummy-04",
567
- "dummy-28",
568
- "dummy-49",
569
- "dummy-16"
570
- ],
571
- "scores": [
572
- {
573
- "projectId": "dummy-02",
574
- "score": 5.931369781494141
575
- },
576
- {
577
- "projectId": "dummy-32",
578
- "score": 2.6352267265319824
579
- },
580
- {
581
- "projectId": "dummy-20",
582
- "score": 0.1919010877609253
583
- },
584
- {
585
- "projectId": "dummy-27",
586
- "score": 0.1807546764612198
587
- },
588
- {
589
- "projectId": "dummy-50",
590
- "score": 0.1795060932636261
591
- },
592
- {
593
- "projectId": "dummy-19",
594
- "score": 0.17730551958084106
595
- },
596
- {
597
- "projectId": "dummy-31",
598
- "score": 0.17708048224449158
599
- },
600
- {
601
- "projectId": "dummy-37",
602
- "score": 0.17596064507961273
603
- },
604
- {
605
- "projectId": "dummy-18",
606
- "score": 0.1759600043296814
607
- },
608
- {
609
- "projectId": "dummy-21",
610
- "score": 0.1722804307937622
611
- },
612
- {
613
- "projectId": "dummy-03",
614
- "score": 0.16904908418655396
615
- },
616
- {
617
- "projectId": "dummy-30",
618
- "score": 0.16836203634738922
619
- },
620
- {
621
- "projectId": "dummy-39",
622
- "score": 0.16769906878471375
623
- },
624
- {
625
- "projectId": "dummy-01",
626
- "score": 0.16594183444976807
627
- },
628
- {
629
- "projectId": "dummy-06",
630
- "score": 0.1649368703365326
631
- },
632
- {
633
- "projectId": "dummy-09",
634
- "score": 0.16446015238761902
635
- },
636
- {
637
- "projectId": "dummy-10",
638
- "score": 0.16347283124923706
639
- },
640
- {
641
- "projectId": "dummy-23",
642
- "score": 0.16043199598789215
643
- },
644
- {
645
- "projectId": "dummy-11",
646
- "score": 0.1600879430770874
647
- },
648
- {
649
- "projectId": "dummy-46",
650
- "score": 0.1599961817264557
651
- },
652
- {
653
- "projectId": "dummy-08",
654
- "score": 0.15916672348976135
655
- },
656
- {
657
- "projectId": "dummy-35",
658
- "score": 0.15472930669784546
659
- },
660
- {
661
- "projectId": "dummy-22",
662
- "score": 0.15420827269554138
663
- },
664
- {
665
- "projectId": "dummy-07",
666
- "score": 0.15418641269207
667
- },
668
- {
669
- "projectId": "dummy-17",
670
- "score": 0.15405218303203583
671
- },
672
- {
673
- "projectId": "dummy-48",
674
- "score": 0.1535654515028
675
- },
676
- {
677
- "projectId": "dummy-40",
678
- "score": 0.15275800228118896
679
- },
680
- {
681
- "projectId": "dummy-47",
682
- "score": 0.15079064667224884
683
- },
684
- {
685
- "projectId": "dummy-45",
686
- "score": 0.14808325469493866
687
- },
688
- {
689
- "projectId": "dummy-26",
690
- "score": 0.1474611759185791
691
- },
692
- {
693
- "projectId": "dummy-05",
694
- "score": 0.14566561579704285
695
- },
696
- {
697
- "projectId": "dummy-36",
698
- "score": 0.14557917416095734
699
- },
700
- {
701
- "projectId": "dummy-24",
702
- "score": 0.14543066918849945
703
- },
704
- {
705
- "projectId": "dummy-44",
706
- "score": 0.14542552828788757
707
- },
708
- {
709
- "projectId": "dummy-13",
710
- "score": 0.14524437487125397
711
- },
712
- {
713
- "projectId": "dummy-15",
714
- "score": 0.14392796158790588
715
- },
716
- {
717
- "projectId": "dummy-38",
718
- "score": 0.14303478598594666
719
- },
720
- {
721
- "projectId": "dummy-42",
722
- "score": 0.1425190418958664
723
- },
724
- {
725
- "projectId": "dummy-14",
726
- "score": 0.13991902768611908
727
- },
728
- {
729
- "projectId": "dummy-41",
730
- "score": 0.1398436278104782
731
- },
732
- {
733
- "projectId": "dummy-29",
734
- "score": 0.13916610181331635
735
- },
736
- {
737
- "projectId": "dummy-34",
738
- "score": 0.13802099227905273
739
- },
740
- {
741
- "projectId": "dummy-25",
742
- "score": 0.13733211159706116
743
- },
744
- {
745
- "projectId": "dummy-43",
746
- "score": 0.13702505826950073
747
- },
748
- {
749
- "projectId": "dummy-33",
750
- "score": 0.13551051914691925
751
- },
752
- {
753
- "projectId": "dummy-12",
754
- "score": 0.13336017727851868
755
- },
756
- {
757
- "projectId": "dummy-04",
758
- "score": 0.1325426697731018
759
- },
760
- {
761
- "projectId": "dummy-28",
762
- "score": 0.1268766224384308
763
- },
764
- {
765
- "projectId": "dummy-49",
766
- "score": 0.1265995353460312
767
- },
768
- {
769
- "projectId": "dummy-16",
770
- "score": 0.12136942893266678
771
- }
772
- ]
773
- }
774
-
775
- # ジェミニシティ
776
- {
777
- "projectIds": [
778
- "dummy-02",
779
- "dummy-07",
780
- "dummy-09",
781
- "dummy-30",
782
- "dummy-01",
783
- "dummy-06",
784
- "dummy-45",
785
- "dummy-05",
786
- "dummy-13",
787
- "dummy-21",
788
- "dummy-50",
789
- "dummy-27",
790
- "dummy-36",
791
- "dummy-40",
792
- "dummy-43",
793
- "dummy-46",
794
- "dummy-34",
795
- "dummy-47",
796
- "dummy-32",
797
- "dummy-42",
798
- "dummy-18",
799
- "dummy-20",
800
- "dummy-31",
801
- "dummy-25",
802
- "dummy-29",
803
- "dummy-15",
804
- "dummy-04",
805
- "dummy-39",
806
- "dummy-10",
807
- "dummy-19",
808
- "dummy-37",
809
- "dummy-11",
810
- "dummy-24",
811
- "dummy-38",
812
- "dummy-03",
813
- "dummy-23",
814
- "dummy-08",
815
- "dummy-17",
816
- "dummy-22",
817
- "dummy-33",
818
- "dummy-35",
819
- "dummy-14",
820
- "dummy-12",
821
- "dummy-41",
822
- "dummy-26",
823
- "dummy-48",
824
- "dummy-44",
825
- "dummy-49",
826
- "dummy-16",
827
- "dummy-28"
828
- ],
829
- "scores": [
830
- {
831
- "projectId": "dummy-02",
832
- "score": 2.456648588180542
833
- },
834
- {
835
- "projectId": "dummy-07",
836
- "score": 0.162680983543396
837
- },
838
- {
839
- "projectId": "dummy-09",
840
- "score": 0.15619903802871704
841
- },
842
- {
843
- "projectId": "dummy-30",
844
- "score": 0.15498758852481842
845
- },
846
- {
847
- "projectId": "dummy-01",
848
- "score": 0.15436750650405884
849
- },
850
- {
851
- "projectId": "dummy-06",
852
- "score": 0.15225842595100403
853
- },
854
- {
855
- "projectId": "dummy-45",
856
- "score": 0.1520552635192871
857
- },
858
- {
859
- "projectId": "dummy-05",
860
- "score": 0.1514219343662262
861
- },
862
- {
863
- "projectId": "dummy-13",
864
- "score": 0.15118378400802612
865
- },
866
- {
867
- "projectId": "dummy-21",
868
- "score": 0.15101128816604614
869
- },
870
- {
871
- "projectId": "dummy-50",
872
- "score": 0.1507887840270996
873
- },
874
- {
875
- "projectId": "dummy-27",
876
- "score": 0.1502055525779724
877
- },
878
- {
879
- "projectId": "dummy-36",
880
- "score": 0.14961348474025726
881
- },
882
- {
883
- "projectId": "dummy-40",
884
- "score": 0.14926813542842865
885
- },
886
- {
887
- "projectId": "dummy-43",
888
- "score": 0.14891408383846283
889
- },
890
- {
891
- "projectId": "dummy-46",
892
- "score": 0.1488681137561798
893
- },
894
- {
895
- "projectId": "dummy-34",
896
- "score": 0.14862272143363953
897
- },
898
- {
899
- "projectId": "dummy-47",
900
- "score": 0.14832982420921326
901
- },
902
- {
903
- "projectId": "dummy-32",
904
- "score": 0.14759908616542816
905
- },
906
- {
907
- "projectId": "dummy-42",
908
- "score": 0.14659354090690613
909
- },
910
- {
911
- "projectId": "dummy-18",
912
- "score": 0.1465129405260086
913
- },
914
- {
915
- "projectId": "dummy-20",
916
- "score": 0.14555582404136658
917
- },
918
- {
919
- "projectId": "dummy-31",
920
- "score": 0.14508406817913055
921
- },
922
- {
923
- "projectId": "dummy-25",
924
- "score": 0.14451518654823303
925
- },
926
- {
927
- "projectId": "dummy-29",
928
- "score": 0.14375324547290802
929
- },
930
- {
931
- "projectId": "dummy-15",
932
- "score": 0.1436637043952942
933
- },
934
- {
935
- "projectId": "dummy-04",
936
- "score": 0.14246971905231476
937
- },
938
- {
939
- "projectId": "dummy-39",
940
- "score": 0.14222663640975952
941
- },
942
- {
943
- "projectId": "dummy-10",
944
- "score": 0.14208325743675232
945
- },
946
- {
947
- "projectId": "dummy-19",
948
- "score": 0.14109577238559723
949
- },
950
- {
951
- "projectId": "dummy-37",
952
- "score": 0.140916109085083
953
- },
954
- {
955
- "projectId": "dummy-11",
956
- "score": 0.14039728045463562
957
- },
958
- {
959
- "projectId": "dummy-24",
960
- "score": 0.13975919783115387
961
- },
962
- {
963
- "projectId": "dummy-38",
964
- "score": 0.13965779542922974
965
- },
966
- {
967
- "projectId": "dummy-03",
968
- "score": 0.13717900216579437
969
- },
970
- {
971
- "projectId": "dummy-23",
972
- "score": 0.13443152606487274
973
- },
974
- {
975
- "projectId": "dummy-08",
976
- "score": 0.13416489958763123
977
- },
978
- {
979
- "projectId": "dummy-17",
980
- "score": 0.13373295962810516
981
- },
982
- {
983
- "projectId": "dummy-22",
984
- "score": 0.13138151168823242
985
- },
986
- {
987
- "projectId": "dummy-33",
988
- "score": 0.13107235729694366
989
- },
990
- {
991
- "projectId": "dummy-35",
992
- "score": 0.1308753341436386
993
- },
994
- {
995
- "projectId": "dummy-14",
996
- "score": 0.1284683346748352
997
- },
998
- {
999
- "projectId": "dummy-12",
1000
- "score": 0.12798088788986206
1001
- },
1002
- {
1003
- "projectId": "dummy-41",
1004
- "score": 0.12646889686584473
1005
- },
1006
- {
1007
- "projectId": "dummy-26",
1008
- "score": 0.12549281120300293
1009
- },
1010
- {
1011
- "projectId": "dummy-48",
1012
- "score": 0.12391983717679977
1013
- },
1014
- {
1015
- "projectId": "dummy-44",
1016
- "score": 0.11978904157876968
1017
- },
1018
- {
1019
- "projectId": "dummy-49",
1020
- "score": 0.11888212710618973
1021
- },
1022
- {
1023
- "projectId": "dummy-16",
1024
- "score": 0.10489151626825333
1025
- },
1026
- {
1027
- "projectId": "dummy-28",
1028
- "score": 0.09725114703178406
1029
- }
1030
- ]
1031
- }
1032
-
1033
- # ボードゲーム
1034
- {
1035
- "projectIds": [
1036
- "dummy-03",
1037
- "dummy-18",
1038
- "dummy-14",
1039
- "dummy-31",
1040
- "dummy-26",
1041
- "dummy-32",
1042
- "dummy-01",
1043
- "dummy-05",
1044
- "dummy-38",
1045
- "dummy-40",
1046
- "dummy-23",
1047
- "dummy-11",
1048
- "dummy-13",
1049
- "dummy-25",
1050
- "dummy-48",
1051
- "dummy-45",
1052
- "dummy-43",
1053
- "dummy-34",
1054
- "dummy-15",
1055
- "dummy-37",
1056
- "dummy-21",
1057
- "dummy-09",
1058
- "dummy-08",
1059
- "dummy-35",
1060
- "dummy-29",
1061
- "dummy-36",
1062
- "dummy-10",
1063
- "dummy-30",
1064
- "dummy-39",
1065
- "dummy-46",
1066
- "dummy-42",
1067
- "dummy-24",
1068
- "dummy-41",
1069
- "dummy-19",
1070
- "dummy-47",
1071
- "dummy-27",
1072
- "dummy-07",
1073
- "dummy-06",
1074
- "dummy-02",
1075
- "dummy-20",
1076
- "dummy-44",
1077
- "dummy-12",
1078
- "dummy-50",
1079
- "dummy-22",
1080
- "dummy-17",
1081
- "dummy-33",
1082
- "dummy-49",
1083
- "dummy-04",
1084
- "dummy-28",
1085
- "dummy-16"
1086
- ],
1087
- "scores": [
1088
- {
1089
- "projectId": "dummy-03",
1090
- "score": 4.6026997566223145
1091
- },
1092
- {
1093
- "projectId": "dummy-18",
1094
- "score": 3.9451470375061035
1095
- },
1096
- {
1097
- "projectId": "dummy-14",
1098
- "score": 1.7676340341567993
1099
- },
1100
- {
1101
- "projectId": "dummy-31",
1102
- "score": 1.6230387687683105
1103
- },
1104
- {
1105
- "projectId": "dummy-26",
1106
- "score": 0.1631765365600586
1107
- },
1108
- {
1109
- "projectId": "dummy-32",
1110
- "score": 0.15479955077171326
1111
- },
1112
- {
1113
- "projectId": "dummy-01",
1114
- "score": 0.15239496529102325
1115
- },
1116
- {
1117
- "projectId": "dummy-05",
1118
- "score": 0.15059471130371094
1119
- },
1120
- {
1121
- "projectId": "dummy-38",
1122
- "score": 0.15034374594688416
1123
- },
1124
- {
1125
- "projectId": "dummy-40",
1126
- "score": 0.14852960407733917
1127
- },
1128
- {
1129
- "projectId": "dummy-23",
1130
- "score": 0.14833544194698334
1131
- },
1132
- {
1133
- "projectId": "dummy-11",
1134
- "score": 0.14826737344264984
1135
- },
1136
- {
1137
- "projectId": "dummy-13",
1138
- "score": 0.147711381316185
1139
- },
1140
- {
1141
- "projectId": "dummy-25",
1142
- "score": 0.1473732441663742
1143
- },
1144
- {
1145
- "projectId": "dummy-48",
1146
- "score": 0.1472303867340088
1147
- },
1148
- {
1149
- "projectId": "dummy-45",
1150
- "score": 0.14652344584465027
1151
- },
1152
- {
1153
- "projectId": "dummy-43",
1154
- "score": 0.14301139116287231
1155
- },
1156
- {
1157
- "projectId": "dummy-34",
1158
- "score": 0.14235596358776093
1159
- },
1160
- {
1161
- "projectId": "dummy-15",
1162
- "score": 0.14173288643360138
1163
- },
1164
- {
1165
- "projectId": "dummy-37",
1166
- "score": 0.14097197353839874
1167
- },
1168
- {
1169
- "projectId": "dummy-21",
1170
- "score": 0.14076118171215057
1171
- },
1172
- {
1173
- "projectId": "dummy-09",
1174
- "score": 0.1392628401517868
1175
- },
1176
- {
1177
- "projectId": "dummy-08",
1178
- "score": 0.13878723978996277
1179
- },
1180
- {
1181
- "projectId": "dummy-35",
1182
- "score": 0.13797332346439362
1183
- },
1184
- {
1185
- "projectId": "dummy-29",
1186
- "score": 0.13737985491752625
1187
- },
1188
- {
1189
- "projectId": "dummy-36",
1190
- "score": 0.1361878663301468
1191
- },
1192
- {
1193
- "projectId": "dummy-10",
1194
- "score": 0.133790522813797
1195
- },
1196
- {
1197
- "projectId": "dummy-30",
1198
- "score": 0.1336868703365326
1199
- },
1200
- {
1201
- "projectId": "dummy-39",
1202
- "score": 0.13292568922042847
1203
- },
1204
- {
1205
- "projectId": "dummy-46",
1206
- "score": 0.13286401331424713
1207
- },
1208
- {
1209
- "projectId": "dummy-42",
1210
- "score": 0.1324540674686432
1211
- },
1212
- {
1213
- "projectId": "dummy-24",
1214
- "score": 0.1314363181591034
1215
- },
1216
- {
1217
- "projectId": "dummy-41",
1218
- "score": 0.1313837617635727
1219
- },
1220
- {
1221
- "projectId": "dummy-19",
1222
- "score": 0.1310305893421173
1223
- },
1224
- {
1225
- "projectId": "dummy-47",
1226
- "score": 0.13075384497642517
1227
- },
1228
- {
1229
- "projectId": "dummy-27",
1230
- "score": 0.130295529961586
1231
- },
1232
- {
1233
- "projectId": "dummy-07",
1234
- "score": 0.1289217174053192
1235
- },
1236
- {
1237
- "projectId": "dummy-06",
1238
- "score": 0.12773872911930084
1239
- },
1240
- {
1241
- "projectId": "dummy-02",
1242
- "score": 0.12749534845352173
1243
- },
1244
- {
1245
- "projectId": "dummy-20",
1246
- "score": 0.12316787987947464
1247
- },
1248
- {
1249
- "projectId": "dummy-44",
1250
- "score": 0.12286143749952316
1251
- },
1252
- {
1253
- "projectId": "dummy-12",
1254
- "score": 0.12097669392824173
1255
- },
1256
- {
1257
- "projectId": "dummy-50",
1258
- "score": 0.11809731274843216
1259
- },
1260
- {
1261
- "projectId": "dummy-22",
1262
- "score": 0.11734956502914429
1263
- },
1264
- {
1265
- "projectId": "dummy-17",
1266
- "score": 0.11496858298778534
1267
- },
1268
- {
1269
- "projectId": "dummy-33",
1270
- "score": 0.11183813214302063
1271
- },
1272
- {
1273
- "projectId": "dummy-49",
1274
- "score": 0.10802316665649414
1275
- },
1276
- {
1277
- "projectId": "dummy-04",
1278
- "score": 0.10751412063837051
1279
- },
1280
- {
1281
- "projectId": "dummy-28",
1282
- "score": 0.105849988758564
1283
- },
1284
- {
1285
- "projectId": "dummy-16",
1286
- "score": 0.09239037334918976
1287
- }
1288
- ]
1289
- }
1290
-
1291
- # インタラクティブ
1292
- {
1293
- "projectIds": [
1294
- "dummy-01",
1295
- "dummy-18",
1296
- "dummy-03",
1297
- "dummy-21",
1298
- "dummy-45",
1299
- "dummy-32",
1300
- "dummy-37",
1301
- "dummy-05",
1302
- "dummy-47",
1303
- "dummy-27",
1304
- "dummy-11",
1305
- "dummy-13",
1306
- "dummy-46",
1307
- "dummy-25",
1308
- "dummy-24",
1309
- "dummy-31",
1310
- "dummy-35",
1311
- "dummy-09",
1312
- "dummy-30",
1313
- "dummy-34",
1314
- "dummy-29",
1315
- "dummy-15",
1316
- "dummy-43",
1317
- "dummy-48",
1318
- "dummy-40",
1319
- "dummy-50",
1320
- "dummy-39",
1321
- "dummy-20",
1322
- "dummy-10",
1323
- "dummy-26",
1324
- "dummy-38",
1325
- "dummy-14",
1326
- "dummy-06",
1327
- "dummy-02",
1328
- "dummy-23",
1329
- "dummy-36",
1330
- "dummy-08",
1331
- "dummy-07",
1332
- "dummy-17",
1333
- "dummy-22",
1334
- "dummy-42",
1335
- "dummy-19",
1336
- "dummy-33",
1337
- "dummy-41",
1338
- "dummy-44",
1339
- "dummy-49",
1340
- "dummy-04",
1341
- "dummy-12",
1342
- "dummy-28",
1343
- "dummy-16"
1344
- ],
1345
- "scores": [
1346
- {
1347
- "projectId": "dummy-01",
1348
- "score": 2.0224697589874268
1349
- },
1350
- {
1351
- "projectId": "dummy-18",
1352
- "score": 0.1717991828918457
1353
- },
1354
- {
1355
- "projectId": "dummy-03",
1356
- "score": 0.15875877439975739
1357
- },
1358
- {
1359
- "projectId": "dummy-21",
1360
- "score": 0.14773328602313995
1361
- },
1362
- {
1363
- "projectId": "dummy-45",
1364
- "score": 0.13539406657218933
1365
- },
1366
- {
1367
- "projectId": "dummy-32",
1368
- "score": 0.13098303973674774
1369
- },
1370
- {
1371
- "projectId": "dummy-37",
1372
- "score": 0.1267457902431488
1373
- },
1374
- {
1375
- "projectId": "dummy-05",
1376
- "score": 0.12650640308856964
1377
- },
1378
- {
1379
- "projectId": "dummy-47",
1380
- "score": 0.12536215782165527
1381
- },
1382
- {
1383
- "projectId": "dummy-27",
1384
- "score": 0.1250198483467102
1385
- },
1386
- {
1387
- "projectId": "dummy-11",
1388
- "score": 0.12491045892238617
1389
- },
1390
- {
1391
- "projectId": "dummy-13",
1392
- "score": 0.12490887939929962
1393
- },
1394
- {
1395
- "projectId": "dummy-46",
1396
- "score": 0.12423489987850189
1397
- },
1398
- {
1399
- "projectId": "dummy-25",
1400
- "score": 0.12377213686704636
1401
- },
1402
- {
1403
- "projectId": "dummy-24",
1404
- "score": 0.12340930104255676
1405
- },
1406
- {
1407
- "projectId": "dummy-31",
1408
- "score": 0.12207731604576111
1409
- },
1410
- {
1411
- "projectId": "dummy-35",
1412
- "score": 0.12206392735242844
1413
- },
1414
- {
1415
- "projectId": "dummy-09",
1416
- "score": 0.12170739471912384
1417
- },
1418
- {
1419
- "projectId": "dummy-30",
1420
- "score": 0.12082985788583755
1421
- },
1422
- {
1423
- "projectId": "dummy-34",
1424
- "score": 0.1205349788069725
1425
- },
1426
- {
1427
- "projectId": "dummy-29",
1428
- "score": 0.1192193254828453
1429
- },
1430
- {
1431
- "projectId": "dummy-15",
1432
- "score": 0.11767151951789856
1433
- },
1434
- {
1435
- "projectId": "dummy-43",
1436
- "score": 0.11537515372037888
1437
- },
1438
- {
1439
- "projectId": "dummy-48",
1440
- "score": 0.11534177511930466
1441
- },
1442
- {
1443
- "projectId": "dummy-40",
1444
- "score": 0.11425547301769257
1445
- },
1446
- {
1447
- "projectId": "dummy-50",
1448
- "score": 0.11390717327594757
1449
- },
1450
- {
1451
- "projectId": "dummy-39",
1452
- "score": 0.11368876695632935
1453
- },
1454
- {
1455
- "projectId": "dummy-20",
1456
- "score": 0.11208880692720413
1457
- },
1458
- {
1459
- "projectId": "dummy-10",
1460
- "score": 0.11158151179552078
1461
- },
1462
- {
1463
- "projectId": "dummy-26",
1464
- "score": 0.11129496991634369
1465
- },
1466
- {
1467
- "projectId": "dummy-38",
1468
- "score": 0.10989216715097427
1469
- },
1470
- {
1471
- "projectId": "dummy-14",
1472
- "score": 0.10894030332565308
1473
- },
1474
- {
1475
- "projectId": "dummy-06",
1476
- "score": 0.10804328322410583
1477
- },
1478
- {
1479
- "projectId": "dummy-02",
1480
- "score": 0.10758388787508011
1481
- },
1482
- {
1483
- "projectId": "dummy-23",
1484
- "score": 0.10621065646409988
1485
- },
1486
- {
1487
- "projectId": "dummy-36",
1488
- "score": 0.10569079965353012
1489
- },
1490
- {
1491
- "projectId": "dummy-08",
1492
- "score": 0.1055106520652771
1493
- },
1494
- {
1495
- "projectId": "dummy-07",
1496
- "score": 0.10404599457979202
1497
- },
1498
- {
1499
- "projectId": "dummy-17",
1500
- "score": 0.10389190167188644
1501
- },
1502
- {
1503
- "projectId": "dummy-22",
1504
- "score": 0.10377786308526993
1505
- },
1506
- {
1507
- "projectId": "dummy-42",
1508
- "score": 0.10317900776863098
1509
- },
1510
- {
1511
- "projectId": "dummy-19",
1512
- "score": 0.10277500003576279
1513
- },
1514
- {
1515
- "projectId": "dummy-33",
1516
- "score": 0.10180061310529709
1517
- },
1518
- {
1519
- "projectId": "dummy-41",
1520
- "score": 0.09484352916479111
1521
- },
1522
- {
1523
- "projectId": "dummy-44",
1524
- "score": 0.09008049964904785
1525
- },
1526
- {
1527
- "projectId": "dummy-49",
1528
- "score": 0.08740860968828201
1529
- },
1530
- {
1531
- "projectId": "dummy-04",
1532
- "score": 0.08725807070732117
1533
- },
1534
- {
1535
- "projectId": "dummy-12",
1536
- "score": 0.08704513311386108
1537
- },
1538
- {
1539
- "projectId": "dummy-28",
1540
- "score": 0.08287956565618515
1541
- },
1542
- {
1543
- "projectId": "dummy-16",
1544
- "score": 0.06936758756637573
1545
- }
1546
- ]
1547
- }
1548
-
1549
- # LED
1550
- {
1551
- "projectIds": [
1552
- "dummy-13",
1553
- "dummy-01",
1554
- "dummy-29",
1555
- "dummy-33",
1556
- "dummy-45",
1557
- "dummy-38",
1558
- "dummy-35",
1559
- "dummy-43",
1560
- "dummy-22",
1561
- "dummy-39",
1562
- "dummy-25",
1563
- "dummy-24",
1564
- "dummy-44",
1565
- "dummy-04",
1566
- "dummy-31",
1567
- "dummy-09",
1568
- "dummy-05",
1569
- "dummy-08",
1570
- "dummy-46",
1571
- "dummy-27",
1572
- "dummy-10",
1573
- "dummy-37",
1574
- "dummy-11",
1575
- "dummy-42",
1576
- "dummy-48",
1577
- "dummy-07",
1578
- "dummy-03",
1579
- "dummy-41",
1580
- "dummy-49",
1581
- "dummy-18",
1582
- "dummy-21",
1583
- "dummy-06",
1584
- "dummy-28",
1585
- "dummy-30",
1586
- "dummy-16",
1587
- "dummy-14",
1588
- "dummy-50",
1589
- "dummy-47",
1590
- "dummy-15",
1591
- "dummy-40",
1592
- "dummy-12",
1593
- "dummy-20",
1594
- "dummy-36",
1595
- "dummy-19",
1596
- "dummy-23",
1597
- "dummy-17",
1598
- "dummy-02",
1599
- "dummy-32",
1600
- "dummy-34",
1601
- "dummy-26"
1602
- ],
1603
- "scores": [
1604
- {
1605
- "projectId": "dummy-13",
1606
- "score": 2.0778872966766357
1607
- },
1608
- {
1609
- "projectId": "dummy-01",
1610
- "score": 1.6898093223571777
1611
- },
1612
- {
1613
- "projectId": "dummy-29",
1614
- "score": 0.10570766776800156
1615
- },
1616
- {
1617
- "projectId": "dummy-33",
1618
- "score": 0.09935978055000305
1619
- },
1620
- {
1621
- "projectId": "dummy-45",
1622
- "score": 0.0951630100607872
1623
- },
1624
- {
1625
- "projectId": "dummy-38",
1626
- "score": 0.09483207762241364
1627
- },
1628
- {
1629
- "projectId": "dummy-35",
1630
- "score": 0.09441396594047546
1631
- },
1632
- {
1633
- "projectId": "dummy-43",
1634
- "score": 0.0936182290315628
1635
- },
1636
- {
1637
- "projectId": "dummy-22",
1638
- "score": 0.0923035740852356
1639
- },
1640
- {
1641
- "projectId": "dummy-39",
1642
- "score": 0.09194866567850113
1643
- },
1644
- {
1645
- "projectId": "dummy-25",
1646
- "score": 0.09147751331329346
1647
- },
1648
- {
1649
- "projectId": "dummy-24",
1650
- "score": 0.09100349992513657
1651
- },
1652
- {
1653
- "projectId": "dummy-44",
1654
- "score": 0.08923175185918808
1655
- },
1656
- {
1657
- "projectId": "dummy-04",
1658
- "score": 0.08885731548070908
1659
- },
1660
- {
1661
- "projectId": "dummy-31",
1662
- "score": 0.08837521076202393
1663
- },
1664
- {
1665
- "projectId": "dummy-09",
1666
- "score": 0.08757206797599792
1667
- },
1668
- {
1669
- "projectId": "dummy-05",
1670
- "score": 0.08745519071817398
1671
- },
1672
- {
1673
- "projectId": "dummy-08",
1674
- "score": 0.08742401003837585
1675
- },
1676
- {
1677
- "projectId": "dummy-46",
1678
- "score": 0.08670942485332489
1679
- },
1680
- {
1681
- "projectId": "dummy-27",
1682
- "score": 0.08608520030975342
1683
- },
1684
- {
1685
- "projectId": "dummy-10",
1686
- "score": 0.08546361327171326
1687
- },
1688
- {
1689
- "projectId": "dummy-37",
1690
- "score": 0.08545397222042084
1691
- },
1692
- {
1693
- "projectId": "dummy-11",
1694
- "score": 0.08495649695396423
1695
- },
1696
- {
1697
- "projectId": "dummy-42",
1698
- "score": 0.08428287506103516
1699
- },
1700
- {
1701
- "projectId": "dummy-48",
1702
- "score": 0.08367615938186646
1703
- },
1704
- {
1705
- "projectId": "dummy-07",
1706
- "score": 0.08359388262033463
1707
- },
1708
- {
1709
- "projectId": "dummy-03",
1710
- "score": 0.08242101967334747
1711
- },
1712
- {
1713
- "projectId": "dummy-41",
1714
- "score": 0.08240765333175659
1715
- },
1716
- {
1717
- "projectId": "dummy-49",
1718
- "score": 0.08240342140197754
1719
- },
1720
- {
1721
- "projectId": "dummy-18",
1722
- "score": 0.08168061077594757
1723
- },
1724
- {
1725
- "projectId": "dummy-21",
1726
- "score": 0.08138646185398102
1727
- },
1728
- {
1729
- "projectId": "dummy-06",
1730
- "score": 0.08057239651679993
1731
- },
1732
- {
1733
- "projectId": "dummy-28",
1734
- "score": 0.07947935163974762
1735
- },
1736
- {
1737
- "projectId": "dummy-30",
1738
- "score": 0.07906509935855865
1739
- },
1740
- {
1741
- "projectId": "dummy-16",
1742
- "score": 0.07739663869142532
1743
- },
1744
- {
1745
- "projectId": "dummy-14",
1746
- "score": 0.07670343667268753
1747
- },
1748
- {
1749
- "projectId": "dummy-50",
1750
- "score": 0.07632601261138916
1751
- },
1752
- {
1753
- "projectId": "dummy-47",
1754
- "score": 0.07593720406293869
1755
- },
1756
- {
1757
- "projectId": "dummy-15",
1758
- "score": 0.07524865865707397
1759
- },
1760
- {
1761
- "projectId": "dummy-40",
1762
- "score": 0.07505342364311218
1763
- },
1764
- {
1765
- "projectId": "dummy-12",
1766
- "score": 0.0750349760055542
1767
- },
1768
- {
1769
- "projectId": "dummy-20",
1770
- "score": 0.07358665019273758
1771
- },
1772
- {
1773
- "projectId": "dummy-36",
1774
- "score": 0.07355395704507828
1775
- },
1776
- {
1777
- "projectId": "dummy-19",
1778
- "score": 0.0733858272433281
1779
- },
1780
- {
1781
- "projectId": "dummy-23",
1782
- "score": 0.07254036515951157
1783
- },
1784
- {
1785
- "projectId": "dummy-17",
1786
- "score": 0.07079058140516281
1787
- },
1788
- {
1789
- "projectId": "dummy-02",
1790
- "score": 0.06835603713989258
1791
- },
1792
- {
1793
- "projectId": "dummy-32",
1794
- "score": 0.06082003191113472
1795
- },
1796
- {
1797
- "projectId": "dummy-34",
1798
- "score": 0.056489575654268265
1799
- },
1800
- {
1801
- "projectId": "dummy-26",
1802
- "score": 0.05596642196178436
1803
- }
1804
- ]
1805
- }
1806
-
1807
- # カレー
1808
- {
1809
- "projectIds": [
1810
- "dummy-04",
1811
- "dummy-49",
1812
- "dummy-22",
1813
- "dummy-12",
1814
- "dummy-28",
1815
- "dummy-14",
1816
- "dummy-33",
1817
- "dummy-24",
1818
- "dummy-41",
1819
- "dummy-44",
1820
- "dummy-31",
1821
- "dummy-30",
1822
- "dummy-39",
1823
- "dummy-38",
1824
- "dummy-25",
1825
- "dummy-10",
1826
- "dummy-07",
1827
- "dummy-23",
1828
- "dummy-09",
1829
- "dummy-05",
1830
- "dummy-26",
1831
- "dummy-32",
1832
- "dummy-43",
1833
- "dummy-29",
1834
- "dummy-03",
1835
- "dummy-16",
1836
- "dummy-47",
1837
- "dummy-45",
1838
- "dummy-20",
1839
- "dummy-02",
1840
- "dummy-06",
1841
- "dummy-01",
1842
- "dummy-37",
1843
- "dummy-48",
1844
- "dummy-15",
1845
- "dummy-35",
1846
- "dummy-36",
1847
- "dummy-27",
1848
- "dummy-18",
1849
- "dummy-13",
1850
- "dummy-40",
1851
- "dummy-08",
1852
- "dummy-19",
1853
- "dummy-34",
1854
- "dummy-50",
1855
- "dummy-11",
1856
- "dummy-21",
1857
- "dummy-46",
1858
- "dummy-17",
1859
- "dummy-42"
1860
- ],
1861
- "scores": [
1862
- {
1863
- "projectId": "dummy-04",
1864
- "score": 2.6254711151123047
1865
- },
1866
- {
1867
- "projectId": "dummy-49",
1868
- "score": 0.18292959034442902
1869
- },
1870
- {
1871
- "projectId": "dummy-22",
1872
- "score": 0.1800815910100937
1873
- },
1874
- {
1875
- "projectId": "dummy-12",
1876
- "score": 0.1737019568681717
1877
- },
1878
- {
1879
- "projectId": "dummy-28",
1880
- "score": 0.16631010174751282
1881
- },
1882
- {
1883
- "projectId": "dummy-14",
1884
- "score": 0.14709749817848206
1885
- },
1886
- {
1887
- "projectId": "dummy-33",
1888
- "score": 0.14541301131248474
1889
- },
1890
- {
1891
- "projectId": "dummy-24",
1892
- "score": 0.13671286404132843
1893
- },
1894
- {
1895
- "projectId": "dummy-41",
1896
- "score": 0.13063141703605652
1897
- },
1898
- {
1899
- "projectId": "dummy-44",
1900
- "score": 0.1156868264079094
1901
- },
1902
- {
1903
- "projectId": "dummy-31",
1904
- "score": 0.1143571063876152
1905
- },
1906
- {
1907
- "projectId": "dummy-30",
1908
- "score": 0.1113339439034462
1909
- },
1910
- {
1911
- "projectId": "dummy-39",
1912
- "score": 0.11087211966514587
1913
- },
1914
- {
1915
- "projectId": "dummy-38",
1916
- "score": 0.11070612818002701
1917
- },
1918
- {
1919
- "projectId": "dummy-25",
1920
- "score": 0.1086856871843338
1921
- },
1922
- {
1923
- "projectId": "dummy-10",
1924
- "score": 0.10852580517530441
1925
- },
1926
- {
1927
- "projectId": "dummy-07",
1928
- "score": 0.10763086378574371
1929
- },
1930
- {
1931
- "projectId": "dummy-23",
1932
- "score": 0.10729862004518509
1933
- },
1934
- {
1935
- "projectId": "dummy-09",
1936
- "score": 0.10714579373598099
1937
- },
1938
- {
1939
- "projectId": "dummy-05",
1940
- "score": 0.10656001418828964
1941
- },
1942
- {
1943
- "projectId": "dummy-26",
1944
- "score": 0.10479174554347992
1945
- },
1946
- {
1947
- "projectId": "dummy-32",
1948
- "score": 0.10145216435194016
1949
- },
1950
- {
1951
- "projectId": "dummy-43",
1952
- "score": 0.09996487945318222
1953
- },
1954
- {
1955
- "projectId": "dummy-29",
1956
- "score": 0.09960292279720306
1957
- },
1958
- {
1959
- "projectId": "dummy-03",
1960
- "score": 0.0987028107047081
1961
- },
1962
- {
1963
- "projectId": "dummy-16",
1964
- "score": 0.09857852756977081
1965
- },
1966
- {
1967
- "projectId": "dummy-47",
1968
- "score": 0.09719719737768173
1969
- },
1970
- {
1971
- "projectId": "dummy-45",
1972
- "score": 0.09627760946750641
1973
- },
1974
- {
1975
- "projectId": "dummy-20",
1976
- "score": 0.09507821500301361
1977
- },
1978
- {
1979
- "projectId": "dummy-02",
1980
- "score": 0.09484118968248367
1981
- },
1982
- {
1983
- "projectId": "dummy-06",
1984
- "score": 0.09443477541208267
1985
- },
1986
- {
1987
- "projectId": "dummy-01",
1988
- "score": 0.09416937828063965
1989
- },
1990
- {
1991
- "projectId": "dummy-37",
1992
- "score": 0.09407279640436172
1993
- },
1994
- {
1995
- "projectId": "dummy-48",
1996
- "score": 0.09399788826704025
1997
- },
1998
- {
1999
- "projectId": "dummy-15",
2000
- "score": 0.09398657828569412
2001
- },
2002
- {
2003
- "projectId": "dummy-35",
2004
- "score": 0.09319526702165604
2005
- },
2006
- {
2007
- "projectId": "dummy-36",
2008
- "score": 0.09293168038129807
2009
- },
2010
- {
2011
- "projectId": "dummy-27",
2012
- "score": 0.09266433119773865
2013
- },
2014
- {
2015
- "projectId": "dummy-18",
2016
- "score": 0.09237142652273178
2017
- },
2018
- {
2019
- "projectId": "dummy-13",
2020
- "score": 0.0918302908539772
2021
- },
2022
- {
2023
- "projectId": "dummy-40",
2024
- "score": 0.0913495421409607
2025
- },
2026
- {
2027
- "projectId": "dummy-08",
2028
- "score": 0.091004878282547
2029
- },
2030
- {
2031
- "projectId": "dummy-19",
2032
- "score": 0.09041927009820938
2033
- },
2034
- {
2035
- "projectId": "dummy-34",
2036
- "score": 0.09039682894945145
2037
- },
2038
- {
2039
- "projectId": "dummy-50",
2040
- "score": 0.08850666880607605
2041
- },
2042
- {
2043
- "projectId": "dummy-11",
2044
- "score": 0.08840479701757431
2045
- },
2046
- {
2047
- "projectId": "dummy-21",
2048
- "score": 0.08591099828481674
2049
- },
2050
- {
2051
- "projectId": "dummy-46",
2052
- "score": 0.08536650240421295
2053
- },
2054
- {
2055
- "projectId": "dummy-17",
2056
- "score": 0.08424936980009079
2057
- },
2058
- {
2059
- "projectId": "dummy-42",
2060
- "score": 0.08145570755004883
2061
- }
2062
- ]
2063
- }