Spaces:
Sleeping
Sleeping
feat: SNSリンクにhandleフィールド追加 + ドキュメント更新
Browse files- 全サークルのSNSデータにhandleフィールドを追加
- Twitter/Instagram: アカウント名(例: @chiba_photo)
- LINE: 「LINE公式アカウント」
- WEBSITE: 「公式サイト」
- YOUTUBE/GITHUB: 適切なタイトル
- docs/システムガイド.md を現在のシステムに合わせて全面更新
- 「企画」→「サークル」への用語統一
- エンドポイント名、フィールド名の修正
- MySQL → CSV データソースへの変更
- APIレスポンス例の更新
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- docs/システムガイド.md +207 -145
- resources/original_circles.csv +9 -9
docs/システムガイド.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
# 千葉大祭 企画情報検索システム - 完全ガイド
|
| 2 |
|
| 3 |
-
**作成日**: 2025年12月11日
|
| 4 |
-
**
|
|
|
|
| 5 |
**言語**: 日本語
|
| 6 |
|
| 7 |
---
|
|
@@ -24,22 +25,22 @@
|
|
| 24 |
|
| 25 |
### 何をするシステムか
|
| 26 |
|
| 27 |
-
このシステムは、千葉大祭に出
|
| 28 |
|
| 29 |
**具体的な機能:**
|
| 30 |
-
- **
|
| 31 |
-
- **高度な検索**: キーワードから関連する
|
| 32 |
- **API提供**: Web・モバイルアプリなどのフロントエンドに情報を提供
|
| 33 |
|
| 34 |
### なぜ「高度な検索」が必要か
|
| 35 |
|
| 36 |
単純なキーワード完全一致だけでは、ユーザーの満足度が低いです。例えば:
|
| 37 |
|
| 38 |
-
- ユーザーが「学園祭」と検索 → 「大学祭」という
|
| 39 |
-
- ユーザーが「
|
| 40 |
-
- ユーザーが「
|
| 41 |
|
| 42 |
-
このシステムは、以下の手法を組み合わせて、こうした「意味的に関連した」
|
| 43 |
|
| 44 |
1. **BM25F**: 単語の出現頻度と珍しさから「単語一致の強さ」を計算
|
| 45 |
2. **単語ベクトル**: 単語の「意味的な近さ」を計算
|
|
@@ -61,13 +62,13 @@
|
|
| 61 |
└─────────────────────┘
|
| 62 |
↓ ↓
|
| 63 |
┌──────────────────┐ ┌──────────────────┐
|
| 64 |
-
│ 検索エンジン │ │
|
| 65 |
-
│ (search/engine) │ │
|
| 66 |
└──────────────────┘ └──────────────────┘
|
| 67 |
↓
|
| 68 |
┌────────────────────────────────────┐
|
| 69 |
│ 静的アセット(JSONファイル等) │
|
| 70 |
-
│ -
|
| 71 |
│ - bm25_meta.json (検索用統計) │
|
| 72 |
│ - synonyms_cache.json (同義語) │
|
| 73 |
│ - word_vectors.npz (単語ベクトル) │
|
|
@@ -77,19 +78,19 @@
|
|
| 77 |
**データの流れ:**
|
| 78 |
|
| 79 |
1. **初期化時**(サーバー起動)
|
| 80 |
-
-
|
| 81 |
- 検索用の各種アセット(JSON、ベクトルなど)をメモリに読み込み
|
| 82 |
- 検索エンジンを初期化
|
| 83 |
|
| 84 |
2. **検索時**(ユーザーがキーワード入力)
|
| 85 |
- キーワードを形態素解析(単語に分割)
|
| 86 |
-
- BM25F + 単語ベクトル で各
|
| 87 |
- スコアの高い順に結果を返す
|
| 88 |
|
| 89 |
-
3. **
|
| 90 |
-
-
|
| 91 |
- スクリプトで検索用アセットを再生成
|
| 92 |
-
- サーバーを再起動
|
| 93 |
|
| 94 |
---
|
| 95 |
|
|
@@ -102,47 +103,43 @@ circle-search-26-api/
|
|
| 102 |
│ ├── search/
|
| 103 |
│ │ └── engine.py # 検索エンジン実装
|
| 104 |
│ ├── repositories/
|
| 105 |
-
│ │
|
| 106 |
-
│
|
| 107 |
-
│
|
| 108 |
-
│ │ └── projects_service.py # ビジネスロジック
|
| 109 |
-
│ ├── db/
|
| 110 |
-
│ │ └── session.py # DB接続管理
|
| 111 |
-
│ └── api/
|
| 112 |
-
│ └── routers/ # API エンドポイント定義
|
| 113 |
│
|
| 114 |
├── scripts/ # データ生成パイプライン
|
| 115 |
│ ├── build_all.py # 全ステップを実行(推奨)
|
| 116 |
│ ├── 0_download_data.py # fastText ベクトルをダウンロード
|
| 117 |
│ ├── 1_build_dict.py # Sudachi ユーザー辞書を構築
|
| 118 |
-
│ ├──
|
| 119 |
│ ├── 3_build_synonyms_from_sudachi.py # 同義語キャッシュ生成
|
| 120 |
│ ├── 4_prepare_bm25f_meta.py # BM25F統計を計算
|
| 121 |
│ ├── 5_prepare_tf_token.py # TF/トークン情報を保存
|
| 122 |
│ ├── 6_build_word_embeddings.py # 単語ベクトルを抽出
|
| 123 |
-
│ └── 7_prepare_circle_names.py #
|
| 124 |
│
|
| 125 |
├── config/ # 設定ファイル
|
| 126 |
│ ├── search_model.json # 検索挙動の詳細設定
|
| 127 |
│ └── files.json # ファイルパスマッピング
|
| 128 |
│
|
| 129 |
├── schemas/ # データモデル定義
|
| 130 |
-
│ └──
|
| 131 |
│
|
| 132 |
├── resources/ # 入力素材
|
|
|
|
| 133 |
│ ├── stopwords.json # ストップワード(検索対象外の語)
|
| 134 |
│ ├── synonyms_custom.json # カスタム同義語辞書
|
| 135 |
│ ├── user_dict.csv # Sudachi ユーザー辞書
|
| 136 |
│ └── embeddings/ # 単語ベクトル(fastText)
|
| 137 |
│
|
| 138 |
├── data/generated/ # 自動生成されるファイル
|
| 139 |
-
│ ├──
|
| 140 |
│ ├── bm25_meta.json # BM25F用統計
|
| 141 |
│ ├── tf_token.json # TF/トークン情報
|
| 142 |
│ ├── synonyms_cache.json # 処理済み同義語
|
| 143 |
│ ├── word_vocab.json # 単語→インデックスの対応表
|
| 144 |
│ ├── word_vectors.npz # 単語ベクトル行列
|
| 145 |
-
│ ├── circle_names.json #
|
| 146 |
│ └── substring_index.json # 部分文字列マッチ用インデックス
|
| 147 |
│
|
| 148 |
├── utils/ # ユーティリティ
|
|
@@ -162,8 +159,8 @@ FastAPIで、フロントエンドからのリクエストを受け取り、検
|
|
| 162 |
|
| 163 |
**主なエンドポイント:**
|
| 164 |
- `GET /api/health`: サーバーの状態確認
|
| 165 |
-
- `GET /api/
|
| 166 |
-
- `GET /api/details?
|
| 167 |
- `POST /api/search`: キーワード検索を実行
|
| 168 |
|
| 169 |
#### `app/search/engine.py` - 検索エンジンの核
|
|
@@ -186,21 +183,21 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 186 |
### 全体フロー
|
| 187 |
|
| 188 |
```
|
| 189 |
-
ユーザーの入力 "
|
| 190 |
↓
|
| 191 |
┌──────────────────┐
|
| 192 |
-
│ トークン化(形態素解析)│ → ["
|
| 193 |
└──────────────────┘
|
| 194 |
↓
|
| 195 |
┌──────────────────┐
|
| 196 |
-
│ 同義語を展開 │ → ["
|
| 197 |
└──────────────────┘
|
| 198 |
↓
|
| 199 |
┌─────────────────────────────┐
|
| 200 |
-
│ 各
|
| 201 |
│ 1. BM25F (単語一致度) │
|
| 202 |
│ 2. 単語ベクトル類似度 │
|
| 203 |
-
│ 3.
|
| 204 |
└─────────────────────────────┘
|
| 205 |
↓
|
| 206 |
┌──────────────────┐
|
|
@@ -222,10 +219,10 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 222 |
|
| 223 |
例:
|
| 224 |
```
|
| 225 |
-
入力:"
|
| 226 |
↓
|
| 227 |
-
トークン化:["
|
| 228 |
-
(助詞「
|
| 229 |
```
|
| 230 |
|
| 231 |
**実装:** SudachiPyという形態素解析エンジンを使用します。これは日本語を高精度に分析できます。
|
|
@@ -272,15 +269,15 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 272 |
|
| 273 |
**簡単な説明:**
|
| 274 |
|
| 275 |
-
1. **IDF(珍しさスコア)**: 多くの
|
| 276 |
-
- 例:「
|
| 277 |
-
- 例:「
|
| 278 |
|
| 279 |
-
2. **TF(繰り返し度)**: ある
|
| 280 |
-
- 例:「
|
| 281 |
|
| 282 |
-
3. **フィールド重み**:
|
| 283 |
-
- 例:
|
| 284 |
|
| 285 |
**実装の詳細は「検索アルゴリズム詳説」を参照**
|
| 286 |
|
|
@@ -293,12 +290,12 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 293 |
例:
|
| 294 |
```
|
| 295 |
← 意味が似ている →
|
| 296 |
-
"
|
| 297 |
-
"
|
| 298 |
```
|
| 299 |
|
| 300 |
**メリット:**
|
| 301 |
-
- 「
|
| 302 |
- 同義語辞書に登録していない言い換えでも対応
|
| 303 |
|
| 304 |
**実装:** fastTextという機械学習モデルを使用。このモデルは、大量の日本語テキストから事前に学習済みです。
|
|
@@ -308,21 +305,21 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 308 |
|
| 309 |
---
|
| 310 |
|
| 311 |
-
### 4.5
|
| 312 |
|
| 313 |
-
**概念:** ユーザーの入力が「
|
| 314 |
|
| 315 |
例:
|
| 316 |
```
|
| 317 |
入力:"写真部"
|
| 318 |
↓
|
| 319 |
-
|
| 320 |
```
|
| 321 |
|
| 322 |
**3段階の加点ルール:**
|
| 323 |
-
- **完全一致** (例:入力 "写真部" =
|
| 324 |
-
- **前方一致** (例:入力 "写真" =
|
| 325 |
-
- **部分一致** (例:入力 "写" =
|
| 326 |
|
| 327 |
**カスタマイズ方法:** `config/search_model.json` の `circleName.boost` で調整可能(後述)
|
| 328 |
|
|
@@ -330,16 +327,16 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 330 |
|
| 331 |
### 4.6 スコア統合と閾値フィルタリング
|
| 332 |
|
| 333 |
-
**概念:** 複数の指標(BM25F、ベクトル類似度、
|
| 334 |
|
| 335 |
**統合式(概略):**
|
| 336 |
```
|
| 337 |
-
|
| 338 |
```
|
| 339 |
|
| 340 |
ここで α はウェイト(デフォルト 0.5 = 両者を同じ重要度とみなす)
|
| 341 |
|
| 342 |
-
**フィルタリング:** スコアが一定以下の
|
| 343 |
|
| 344 |
---
|
| 345 |
|
|
@@ -414,12 +411,12 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 414 |
| 項目 | 説明 | 調整例 |
|
| 415 |
|------|------|--------|
|
| 416 |
| `target_pos_l1` | トークン化時に対象とする品詞 | 「助詞」を追加したい場合は配列に追加 |
|
| 417 |
-
| `target_fields` | 検索対象にするフィールド | `
|
| 418 |
| `synonyms.enable` | 同義語展開の有無 | `false`で無効化 |
|
| 419 |
| `synonyms.limits.max_expansions_per_term` | 1つの単語から展開する同義語の最大数 | 値を大きくするほど多くの同義語を使用 |
|
| 420 |
-
| `bm25f.field_weights` | 各フィールドの重要度 |
|
| 421 |
| `word_sim.alpha` | BM25FとベクトルのウェイトMIX | 0.5 = 両者を同じ重要度。BM25Fを重視したい場合は0.7に |
|
| 422 |
-
| `circleName.boost.exact` |
|
| 423 |
| `filter.max_results` | 返す結果の最大件数 | 結果を少なくしたい場合は20に減らす |
|
| 424 |
| `filter.fused_min` | スコアの下限(閾値) | 値を上げるとフィルタリングが厳しくなる |
|
| 425 |
|
|
@@ -441,8 +438,9 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 441 |
"sudachi_config": "scripts/sudachi.json",
|
| 442 |
"stopwords": "resources/stopwords.json"
|
| 443 |
},
|
| 444 |
-
"
|
| 445 |
-
"
|
|
|
|
| 446 |
},
|
| 447 |
"bm25": {
|
| 448 |
"bm25_meta": "data/generated/bm25_meta.json",
|
|
@@ -460,15 +458,15 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 460 |
|
| 461 |
### 5.3 `resources/synonyms_custom.json` - カスタム同義語
|
| 462 |
|
| 463 |
-
|
| 464 |
|
| 465 |
**編集例:**
|
| 466 |
```json
|
| 467 |
{
|
| 468 |
-
"
|
| 469 |
-
"ス
|
| 470 |
-
"学園祭": ["大学祭"],
|
| 471 |
-
"
|
| 472 |
}
|
| 473 |
```
|
| 474 |
|
|
@@ -512,7 +510,7 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 512 |
2. 検索エンジン初期化 (engine.initialize())
|
| 513 |
├─ config/search_model.json を読み込み
|
| 514 |
├─ config/files.json からファイルパスを取得
|
| 515 |
-
├─ data/generated/*.json を読み込み(
|
| 516 |
├─ resources/embeddings/ から単語ベクトルを読み込み
|
| 517 |
└─ Sudachi形態素解析器を初期化
|
| 518 |
↓
|
|
@@ -520,9 +518,9 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
|
|
| 520 |
フロントエンドからのリクエスト受付開始
|
| 521 |
```
|
| 522 |
|
| 523 |
-
### 6.2
|
| 524 |
|
| 525 |
-
**状況:**
|
| 526 |
|
| 527 |
本システムでは、データ更新を以下の3つの方法で実現できます。
|
| 528 |
|
|
@@ -540,7 +538,7 @@ GitHub Actions が自動実行
|
|
| 540 |
↓
|
| 541 |
スクリプト build_all.py が実行
|
| 542 |
↓
|
| 543 |
-
新しい
|
| 544 |
```
|
| 545 |
|
| 546 |
**設定ファイル:** `.github/workflows/update_data.yaml`
|
|
@@ -580,38 +578,39 @@ curl -X POST \
|
|
| 580 |
|
| 581 |
**詳細な流れ:**
|
| 582 |
```
|
| 583 |
-
1.
|
| 584 |
↓
|
| 585 |
2. スクリプト実行: python scripts/build_all.py
|
| 586 |
├─ Step 0: fastText ベクトルをダウンロード(初回のみ)
|
| 587 |
├─ Step 1: Sudachi ユーザー辞書を構築(オプション)
|
| 588 |
-
├─ Step 2:
|
| 589 |
├─ Step 3: 同義語キャッシュを生成
|
| 590 |
├─ Step 4: BM25F統計を計算
|
| 591 |
├─ Step 5: TF/トークン情報を保存
|
| 592 |
├─ Step 6: 単語ベクトルを抽出
|
| 593 |
-
└─ Step 7:
|
| 594 |
↓
|
| 595 |
-
3. サーバーを再起動(
|
| 596 |
↓
|
| 597 |
-
4. フロントエンドで新しい
|
| 598 |
```
|
| 599 |
|
| 600 |
**詳細:**
|
| 601 |
|
| 602 |
-
#### Step 2: `scripts/
|
| 603 |
```
|
| 604 |
-
|
| 605 |
-
→
|
| 606 |
-
→
|
| 607 |
-
→
|
|
|
|
| 608 |
```
|
| 609 |
|
| 610 |
-
このステップ以降のデータは、すべてこの `
|
| 611 |
|
| 612 |
#### Step 3: `scripts/3_build_synonyms_from_sudachi.py` - 同義語キャッシュ
|
| 613 |
```
|
| 614 |
-
|
| 615 |
→ 出現語彙を収集
|
| 616 |
→ Sudachi同義語辞書 + custom synonyms.json から該当するもののみ抽出
|
| 617 |
→ synonyms_cache.json に保存
|
|
@@ -621,12 +620,12 @@ projects.json の全テキストをトークン化
|
|
| 621 |
|
| 622 |
#### Step 4-5: `scripts/4_prepare_bm25f_meta.py` と `scripts/5_prepare_tf_token.py` - BM25F統計
|
| 623 |
```
|
| 624 |
-
|
| 625 |
→ IDF(珍しさスコア)を計算
|
| 626 |
→ 各フィールドの平均トークン数を計算
|
| 627 |
→ bm25_meta.json に保存
|
| 628 |
|
| 629 |
-
各
|
| 630 |
→ tf_token.json に保存
|
| 631 |
```
|
| 632 |
|
|
@@ -643,12 +642,12 @@ tf_token.json から語彙リストを取得
|
|
| 643 |
|---------|---------|---------|
|
| 644 |
| 0 | fastText ダウンロード | 初回のみ数分 |
|
| 645 |
| 1 | Sudachi辞書構築 | 1-2分 |
|
| 646 |
-
| 2 |
|
| 647 |
| 3 | 同義語キャッシュ | 10-30秒 |
|
| 648 |
| 4 | BM25F統計 | 1-2分 |
|
| 649 |
| 5 | TF/トークン | 1-2分 |
|
| 650 |
| 6 | 単語ベクトル | 1-2分 |
|
| 651 |
-
| 7 |
|
| 652 |
|
| 653 |
**全ステップ実行** → 合計約5-10分
|
| 654 |
|
|
@@ -669,92 +668,157 @@ curl "http://localhost:7860/api/health"
|
|
| 669 |
}
|
| 670 |
```
|
| 671 |
|
| 672 |
-
### 7.2
|
| 673 |
|
| 674 |
```bash
|
| 675 |
curl -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 676 |
-
"http://localhost:7860/api/
|
| 677 |
```
|
| 678 |
|
| 679 |
**認証方法:**
|
| 680 |
- ヘッダ `X-API-KEY` に `API_SECRET_KEY` 環境変数の値を指定
|
| 681 |
- 環境変数は `.env` ファイルに保存(リポジトリには含めない)
|
| 682 |
|
| 683 |
-
**レスポンス(gzip圧縮):**
|
| 684 |
```json
|
| 685 |
[
|
| 686 |
{
|
| 687 |
-
"
|
| 688 |
"circleName": "写真部",
|
| 689 |
-
"
|
| 690 |
-
"
|
| 691 |
-
"
|
| 692 |
-
"
|
| 693 |
-
"
|
| 694 |
-
"
|
| 695 |
-
"
|
| 696 |
-
"
|
| 697 |
-
"
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
"filename": "photo1.jpg",
|
| 701 |
-
"extension": "jpg",
|
| 702 |
"order": 0
|
| 703 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 704 |
},
|
| 705 |
...
|
| 706 |
]
|
| 707 |
```
|
| 708 |
|
| 709 |
-
### 7.3
|
| 710 |
|
| 711 |
```bash
|
| 712 |
curl -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 713 |
-
"http://localhost:7860/api/details?
|
| 714 |
```
|
| 715 |
|
| 716 |
-
|
| 717 |
```json
|
| 718 |
{
|
| 719 |
-
"
|
| 720 |
"circleName": "写真部",
|
| 721 |
-
"
|
| 722 |
-
|
| 723 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
"images": [
|
| 725 |
{
|
| 726 |
-
"
|
| 727 |
-
"
|
| 728 |
"order": 0
|
| 729 |
},
|
| 730 |
{
|
| 731 |
-
"
|
| 732 |
-
"
|
| 733 |
"order": 1
|
| 734 |
}
|
| 735 |
],
|
| 736 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
{
|
| 738 |
-
"
|
| 739 |
-
"url": "https://instagram.com/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
}
|
| 741 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 742 |
}
|
| 743 |
```
|
| 744 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 745 |
### 7.4 検索
|
| 746 |
|
| 747 |
```bash
|
| 748 |
curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 749 |
-H "Content-Type: application/json" \
|
| 750 |
-
-d '{"query": "
|
| 751 |
"http://localhost:7860/api/search"
|
| 752 |
```
|
| 753 |
|
| 754 |
**レスポンス(通常モード):**
|
| 755 |
```json
|
| 756 |
{
|
| 757 |
-
"
|
| 758 |
}
|
| 759 |
```
|
| 760 |
|
|
@@ -763,34 +827,33 @@ curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
|
| 763 |
```bash
|
| 764 |
curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 765 |
-H "Content-Type: application/json" \
|
| 766 |
-
-d '{"query": "
|
| 767 |
"http://localhost:7860/api/search"
|
| 768 |
```
|
| 769 |
|
| 770 |
**レスポンス(デバッグモード):**
|
| 771 |
```json
|
| 772 |
{
|
| 773 |
-
"
|
| 774 |
"scores": [
|
| 775 |
{
|
| 776 |
-
"
|
| 777 |
"score": 2.15
|
| 778 |
},
|
| 779 |
{
|
| 780 |
-
"
|
| 781 |
"score": 1.83
|
| 782 |
}
|
| 783 |
],
|
| 784 |
"details": [
|
| 785 |
{
|
| 786 |
-
"
|
| 787 |
-
"circleName": "
|
| 788 |
-
"name": "カレー販売",
|
| 789 |
"bm25": 0.85,
|
| 790 |
"ws_filter_topk": 0.72,
|
| 791 |
"ws_rerank_pairavg": 0.75,
|
| 792 |
-
"org_boost":
|
| 793 |
-
"matched_substring":
|
| 794 |
"fused_filter": 0.78,
|
| 795 |
"fused_final": 2.15
|
| 796 |
},
|
|
@@ -803,8 +866,8 @@ curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
|
| 803 |
- `bm25`: BM25Fスコア(単語一致度)
|
| 804 |
- `ws_filter_topk`: 単語ベクトル類似度(フィルタリング用、top-k方式)
|
| 805 |
- `ws_rerank_pairavg`: 単語ベクトル類似度(リランキング用、pair-average方式)
|
| 806 |
-
- `org_boost`:
|
| 807 |
-
- `matched_substring`: 部分文字列マッチングで
|
| 808 |
- `fused_filter`: フィルタリング段階での統合スコア
|
| 809 |
- `fused_final`: 最終統合スコア
|
| 810 |
|
|
@@ -816,7 +879,7 @@ curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
|
| 816 |
"http://localhost:7860/tasks/update"
|
| 817 |
```
|
| 818 |
|
| 819 |
-
**用途:**
|
| 820 |
|
| 821 |
**レスポンス:**
|
| 822 |
```json
|
|
@@ -853,7 +916,6 @@ curl -X POST -H "X-API-KEY: key" \
|
|
| 853 |
|
| 854 |
**前提条件:**
|
| 855 |
- Python 3.11以上
|
| 856 |
-
- MySQL(ローカルまたはリモート)
|
| 857 |
- pip または conda
|
| 858 |
|
| 859 |
**手順:**
|
|
@@ -871,10 +933,10 @@ venv\Scripts\activate # Windows
|
|
| 871 |
# 3. 依存パッケージをインストール
|
| 872 |
pip install -r requirements.txt
|
| 873 |
|
| 874 |
-
# 4. .env ファイルを作成(セキュリティ情報は
|
| 875 |
-
# DATABASE_URL=mysql://user:pass@host:3306/db
|
| 876 |
# API_SECRET_KEY=...
|
| 877 |
# HF_TOKEN=...
|
|
|
|
| 878 |
|
| 879 |
# 5. データ生成スクリプトを実行
|
| 880 |
python scripts/build_all.py
|
|
@@ -887,7 +949,7 @@ python -m uvicorn app.main:app --reload --port 7860
|
|
| 887 |
|
| 888 |
**ローカル開発中のデータ更新の流れ:**
|
| 889 |
|
| 890 |
-
通常の開発では、
|
| 891 |
|
| 892 |
```bash
|
| 893 |
# 方法1: 手動でスクリプト実行
|
|
@@ -921,7 +983,7 @@ docker run -e DATABASE_URL="..." \
|
|
| 921 |
|
| 922 |
**原因候補:**
|
| 923 |
1. `data/generated/` のファイルが不足している
|
| 924 |
-
2.
|
| 925 |
3. 設定ファイルのパスが間違っている
|
| 926 |
|
| 927 |
**対処:**
|
|
@@ -976,15 +1038,15 @@ python scripts/build_all.py
|
|
| 976 |
|
| 977 |
## 付録:よくある調整例
|
| 978 |
|
| 979 |
-
###
|
| 980 |
|
| 981 |
```json
|
| 982 |
{
|
| 983 |
"bm25f": {
|
| 984 |
"field_weights": {
|
| 985 |
-
"
|
| 986 |
-
"
|
| 987 |
-
"
|
| 988 |
...
|
| 989 |
}
|
| 990 |
}
|
|
|
|
| 1 |
# 千葉大祭 企画情報検索システム - 完全ガイド
|
| 2 |
|
| 3 |
+
**作成日**: 2025年12月11日
|
| 4 |
+
**最終更新**: 2026年2月16日
|
| 5 |
+
**対象**: 大学1~2年生向けのシステム解説
|
| 6 |
**言語**: 日本語
|
| 7 |
|
| 8 |
---
|
|
|
|
| 25 |
|
| 26 |
### 何をするシステムか
|
| 27 |
|
| 28 |
+
このシステムは、千葉大祭に出展する**サークル**の情報を効率よく提供・検索するためのものです。
|
| 29 |
|
| 30 |
**具体的な機能:**
|
| 31 |
+
- **サークルデータの管理**: サークル名、説明、SNS情報、写真などを一元管理
|
| 32 |
+
- **高度な検索**: キーワードから関連するサークルを「正確に」「素早く」見つける
|
| 33 |
- **API提供**: Web・モバイルアプリなどのフロントエンドに情報を提供
|
| 34 |
|
| 35 |
### なぜ「高度な検索」が必要か
|
| 36 |
|
| 37 |
単純なキーワード完全一致だけでは、ユーザーの満足度が低いです。例えば:
|
| 38 |
|
| 39 |
+
- ユーザーが「学園祭」と検索 → 「大学祭」というサークルも関連しているはず
|
| 40 |
+
- ユーザーが「音楽」と検索 → 「軽音」「吹奏楽」なども同じくらい検索対象にしたい
|
| 41 |
+
- ユーザーが「スポーツ サッカー」と検索 → どちらかのキーワードだけでなく、両方関連するサークルを上位に
|
| 42 |
|
| 43 |
+
このシステムは、以下の手法を組み合わせて、こうした「意味的に関連した」サークルを見つけ出します:
|
| 44 |
|
| 45 |
1. **BM25F**: 単語の出現頻度と珍しさから「単語一致の強さ」を計算
|
| 46 |
2. **単語ベクトル**: 単語の「意味的な近さ」を計算
|
|
|
|
| 62 |
└─────────────────────┘
|
| 63 |
↓ ↓
|
| 64 |
┌──────────────────┐ ┌──────────────────┐
|
| 65 |
+
│ 検索エンジン │ │ CSVデータソース │
|
| 66 |
+
│ (search/engine) │ │ (サークルデータ) │
|
| 67 |
└──────────────────┘ └──────────────────┘
|
| 68 |
↓
|
| 69 |
┌────────────────────────────────────┐
|
| 70 |
│ 静的アセット(JSONファイル等) │
|
| 71 |
+
│ - circles.json (サークルデータ) │
|
| 72 |
│ - bm25_meta.json (検索用統計) │
|
| 73 |
│ - synonyms_cache.json (同義語) │
|
| 74 |
│ - word_vectors.npz (単語ベクトル) │
|
|
|
|
| 78 |
**データの流れ:**
|
| 79 |
|
| 80 |
1. **初期化時**(サーバー起動)
|
| 81 |
+
- CSVから生成されたJSONファイル(circles.json)を読み込み
|
| 82 |
- 検索用の各種アセット(JSON、ベクトルなど)をメモリに読み込み
|
| 83 |
- 検索エンジンを初期化
|
| 84 |
|
| 85 |
2. **検索時**(ユーザーがキーワード入力)
|
| 86 |
- キーワードを形態素解析(単語に分割)
|
| 87 |
+
- BM25F + 単語ベクトル で各サークルのスコアを計算
|
| 88 |
- スコアの高い順に結果を返す
|
| 89 |
|
| 90 |
+
3. **サークルデータ更新時**
|
| 91 |
+
- CSVファイル(original_circles.csv)を更新
|
| 92 |
- スクリプトで検索用アセットを再生成
|
| 93 |
+
- サーバーを再起動(またはHF Spacesへデプロイ)
|
| 94 |
|
| 95 |
---
|
| 96 |
|
|
|
|
| 103 |
│ ├── search/
|
| 104 |
│ │ └── engine.py # 検索エンジン実装
|
| 105 |
│ ├── repositories/
|
| 106 |
+
│ │ └── circles_repository.py # サークルデータ取得
|
| 107 |
+
│ └── services/
|
| 108 |
+
│ └── circles_service.py # ビジネスロジック
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
│
|
| 110 |
├── scripts/ # データ生成パイプライン
|
| 111 |
│ ├── build_all.py # 全ステップを実行(推奨)
|
| 112 |
│ ├── 0_download_data.py # fastText ベクトルをダウンロード
|
| 113 |
│ ├── 1_build_dict.py # Sudachi ユーザー辞書を構築
|
| 114 |
+
│ ├── 2_create_circles_data.py # CSV → circles.json
|
| 115 |
│ ├── 3_build_synonyms_from_sudachi.py # 同義語キャッシュ生成
|
| 116 |
│ ├── 4_prepare_bm25f_meta.py # BM25F統計を計算
|
| 117 |
│ ├── 5_prepare_tf_token.py # TF/トークン情報を保存
|
| 118 |
│ ├── 6_build_word_embeddings.py # 単語ベクトルを抽出
|
| 119 |
+
│ └── 7_prepare_circle_names.py # サークル名インデックスを生成
|
| 120 |
│
|
| 121 |
├── config/ # 設定ファイル
|
| 122 |
│ ├── search_model.json # 検索挙動の詳細設定
|
| 123 |
│ └── files.json # ファイルパスマッピング
|
| 124 |
│
|
| 125 |
├── schemas/ # データモデル定義
|
| 126 |
+
│ └── circles.py # Circle, CircleSummary, CircleDetail 等
|
| 127 |
│
|
| 128 |
├── resources/ # 入力素材
|
| 129 |
+
│ ├── original_circles.csv # サークルデータ(元データ)
|
| 130 |
│ ├── stopwords.json # ストップワード(検索対象外の語)
|
| 131 |
│ ├── synonyms_custom.json # カスタム同義語辞書
|
| 132 |
│ ├── user_dict.csv # Sudachi ユーザー辞書
|
| 133 |
│ └── embeddings/ # 単語ベクトル(fastText)
|
| 134 |
│
|
| 135 |
├── data/generated/ # 自動生成されるファイル
|
| 136 |
+
│ ├── circles.json # サークルデータ(検索用)
|
| 137 |
│ ├── bm25_meta.json # BM25F用統計
|
| 138 |
│ ├── tf_token.json # TF/トークン情報
|
| 139 |
│ ├── synonyms_cache.json # 処理済み同義語
|
| 140 |
│ ├── word_vocab.json # 単語→インデックスの対応表
|
| 141 |
│ ├── word_vectors.npz # 単語ベクトル行列
|
| 142 |
+
│ ├── circle_names.json # サークル名インデックス
|
| 143 |
│ └── substring_index.json # 部分文字列マッチ用インデックス
|
| 144 |
│
|
| 145 |
├── utils/ # ユーティリティ
|
|
|
|
| 159 |
|
| 160 |
**主なエンドポイント:**
|
| 161 |
- `GET /api/health`: サーバーの状態確認
|
| 162 |
+
- `GET /api/circles`: 全サークルの一覧を取得(CircleSummary形式)
|
| 163 |
+
- `GET /api/details?circleId=xxx`: 特定のサークルの詳細を取得(CircleDetail形式)
|
| 164 |
- `POST /api/search`: キーワード検索を実行
|
| 165 |
|
| 166 |
#### `app/search/engine.py` - 検索エンジンの核
|
|
|
|
| 183 |
### 全体フロー
|
| 184 |
|
| 185 |
```
|
| 186 |
+
ユーザーの入力 "音楽 サークル"
|
| 187 |
↓
|
| 188 |
┌──────────────────┐
|
| 189 |
+
│ トークン化(形態素解析)│ → ["音楽", "サークル"]
|
| 190 |
└──────────────────┘
|
| 191 |
↓
|
| 192 |
┌──────────────────┐
|
| 193 |
+
│ 同義語を展開 │ → ["音楽", "サークル", "演奏", "部活"]
|
| 194 |
└──────────────────┘
|
| 195 |
↓
|
| 196 |
┌─────────────────────────────┐
|
| 197 |
+
│ 各サークルに対して3つのスコアを計算 │
|
| 198 |
│ 1. BM25F (単語一致度) │
|
| 199 |
│ 2. 単語ベクトル類似度 │
|
| 200 |
+
│ 3. サークル名マッチングボーナス │
|
| 201 |
└─────────────────────────────┘
|
| 202 |
↓
|
| 203 |
┌──────────────────┐
|
|
|
|
| 219 |
|
| 220 |
例:
|
| 221 |
```
|
| 222 |
+
入力:"写真部で活動している学生"
|
| 223 |
↓
|
| 224 |
+
トークン化:["写真部", "活動", "学生"]
|
| 225 |
+
(助詞「で」「している」などは除外)
|
| 226 |
```
|
| 227 |
|
| 228 |
**実装:** SudachiPyという形態素解析エンジンを使用します。これは日本語を高精度に分析できます。
|
|
|
|
| 269 |
|
| 270 |
**簡単な説明:**
|
| 271 |
|
| 272 |
+
1. **IDF(珍しさスコア)**: 多くのサークルに出現する語は価値が低く、少ないサークルにしか出現しない語は価値が高い
|
| 273 |
+
- 例:「活動」は多くのサークルに出現→低い価値
|
| 274 |
+
- 例:「茶道」は少ないサークルにしか出現→高い価値
|
| 275 |
|
| 276 |
+
2. **TF(繰り返し度)**: あるサークルの中で、その単語がどれくらい繰り返し出ているか
|
| 277 |
+
- 例:「写真」という単語が写真部の説明に5回出現→高いTF
|
| 278 |
|
| 279 |
+
3. **フィールド重み**: サークル名に出現する単語と、長い説明文に出現する単語では重要度が異なる
|
| 280 |
+
- 例:サークル名 (重み 2.0) > 説明文 (重み 1.0)
|
| 281 |
|
| 282 |
**実装の詳細は「検索アルゴリズム詳説」を参照**
|
| 283 |
|
|
|
|
| 290 |
例:
|
| 291 |
```
|
| 292 |
← 意味が似ている →
|
| 293 |
+
"音楽" と "演奏" は空間上で近い
|
| 294 |
+
"音楽" と "プログラミング" は空間上で遠い
|
| 295 |
```
|
| 296 |
|
| 297 |
**メリット:**
|
| 298 |
+
- 「音楽」で検索 → 「演奏」「楽器」なども関連として見つかる
|
| 299 |
- 同義語辞書に登録していない言い換えでも対応
|
| 300 |
|
| 301 |
**実装:** fastTextという機械学習モデルを使用。このモデルは、大量の日本語テキストから事前に学習済みです。
|
|
|
|
| 305 |
|
| 306 |
---
|
| 307 |
|
| 308 |
+
### 4.5 サークル名マッチング - 直接一致の加点
|
| 309 |
|
| 310 |
+
**概念:** ユーザーの入力が「サークル名」や「サークル名の読み仮名」に合致した場合、スコアを加算します。
|
| 311 |
|
| 312 |
例:
|
| 313 |
```
|
| 314 |
入力:"写真部"
|
| 315 |
↓
|
| 316 |
+
サークル名が「写真部」に完全一致 → +1.0点
|
| 317 |
```
|
| 318 |
|
| 319 |
**3段階の加点ルール:**
|
| 320 |
+
- **完全一致** (例:入力 "写真部" = サークル名 "写真部") → 加点: 1.0
|
| 321 |
+
- **前方一致** (例:入力 "写真" = サークル名 "写真部" の前半) → 加点: 0.7
|
| 322 |
+
- **部分一致** (例:入力 "写" = サークル名 "写真部" に含む) → 加点: 0.5
|
| 323 |
|
| 324 |
**カスタマイズ方法:** `config/search_model.json` の `circleName.boost` で調整可能(後述)
|
| 325 |
|
|
|
|
| 327 |
|
| 328 |
### 4.6 スコア統合と閾値フィルタリング
|
| 329 |
|
| 330 |
+
**概念:** 複数の指標(BM25F、ベクトル類似度、サークル名加点)を1つのスコアに統合します。
|
| 331 |
|
| 332 |
**統合式(概略):**
|
| 333 |
```
|
| 334 |
+
最終スコア = α × BM25F + (1-α) × 単語ベクトル類似度 + サークル名加点
|
| 335 |
```
|
| 336 |
|
| 337 |
ここで α はウェイト(デフォルト 0.5 = 両者を同じ重要度とみなす)
|
| 338 |
|
| 339 |
+
**フィルタリング:** スコアが一定以下のサークルは結果から除外します。ノイズ除去と計算効率化のため。
|
| 340 |
|
| 341 |
---
|
| 342 |
|
|
|
|
| 411 |
| 項目 | 説明 | 調整例 |
|
| 412 |
|------|------|--------|
|
| 413 |
| `target_pos_l1` | トークン化時に対象とする品詞 | 「助詞」を追加したい場合は配列に追加 |
|
| 414 |
+
| `target_fields` | 検索対象にするフィールド | `detailDescription`を除きたい場合は削除 |
|
| 415 |
| `synonyms.enable` | 同義語展開の有無 | `false`で無効化 |
|
| 416 |
| `synonyms.limits.max_expansions_per_term` | 1つの単語から展開する同義語の最大数 | 値を大きくするほど多くの同義語を使用 |
|
| 417 |
+
| `bm25f.field_weights` | 各フィールドの重要度 | サークル名(circleName)を重視したい場合、値を大きく(例:3.0) |
|
| 418 |
| `word_sim.alpha` | BM25FとベクトルのウェイトMIX | 0.5 = 両者を同じ重要度。BM25Fを重視したい場合は0.7に |
|
| 419 |
+
| `circleName.boost.exact` | サークル名完全一致の加点 | サークル名マッチを重視したい場合は2.0に |
|
| 420 |
| `filter.max_results` | 返す結果の最大件数 | 結果を少なくしたい場合は20に減らす |
|
| 421 |
| `filter.fused_min` | スコアの下限(閾値) | 値を上げるとフィルタリングが厳しくなる |
|
| 422 |
|
|
|
|
| 438 |
"sudachi_config": "scripts/sudachi.json",
|
| 439 |
"stopwords": "resources/stopwords.json"
|
| 440 |
},
|
| 441 |
+
"circles": {
|
| 442 |
+
"original_csv": "resources/original_circles.csv",
|
| 443 |
+
"circles_json": "data/generated/circles.json"
|
| 444 |
},
|
| 445 |
"bm25": {
|
| 446 |
"bm25_meta": "data/generated/bm25_meta.json",
|
|
|
|
| 458 |
|
| 459 |
### 5.3 `resources/synonyms_custom.json` - カスタム同義語
|
| 460 |
|
| 461 |
+
サークル検索に特化した同義語を手動で登録します。
|
| 462 |
|
| 463 |
**編集例:**
|
| 464 |
```json
|
| 465 |
{
|
| 466 |
+
"音楽": ["演奏", "楽器"],
|
| 467 |
+
"スポーツ": ["運動", "体育"],
|
| 468 |
+
"学園祭": ["大学祭", "千葉大祭"],
|
| 469 |
+
"部活": ["サークル", "部"]
|
| 470 |
}
|
| 471 |
```
|
| 472 |
|
|
|
|
| 510 |
2. 検索エンジン初期化 (engine.initialize())
|
| 511 |
├─ config/search_model.json を読み込み
|
| 512 |
├─ config/files.json からファイルパスを取得
|
| 513 |
+
├─ data/generated/*.json を読み込み(サークルデータ、BM25F統計など)
|
| 514 |
├─ resources/embeddings/ から単語ベクトルを読み込み
|
| 515 |
└─ Sudachi形態素解析器を初期化
|
| 516 |
↓
|
|
|
|
| 518 |
フロントエンドからのリクエスト受付開始
|
| 519 |
```
|
| 520 |
|
| 521 |
+
### 6.2 サークルデータの更新フロー
|
| 522 |
|
| 523 |
+
**状況:** CSVファイル(original_circles.csv)のサークルデータが更新された
|
| 524 |
|
| 525 |
本システムでは、データ更新を以下の3つの方法で実現できます。
|
| 526 |
|
|
|
|
| 538 |
↓
|
| 539 |
スクリプト build_all.py が実行
|
| 540 |
↓
|
| 541 |
+
新しいサークルデータで検索エンジンが更新
|
| 542 |
```
|
| 543 |
|
| 544 |
**設定ファイル:** `.github/workflows/update_data.yaml`
|
|
|
|
| 578 |
|
| 579 |
**詳細な流れ:**
|
| 580 |
```
|
| 581 |
+
1. CSVファイル(original_circles.csv)を更新
|
| 582 |
↓
|
| 583 |
2. スクリプト実行: python scripts/build_all.py
|
| 584 |
├─ Step 0: fastText ベクトルをダウンロード(初回のみ)
|
| 585 |
├─ Step 1: Sudachi ユーザー辞書を構築(オプション)
|
| 586 |
+
├─ Step 2: CSV → circles.json 生成
|
| 587 |
├─ Step 3: 同義語キャッシュを生成
|
| 588 |
├─ Step 4: BM25F統計を計算
|
| 589 |
├─ Step 5: TF/トークン情報を保存
|
| 590 |
├─ Step 6: 単語ベクトルを抽出
|
| 591 |
+
└─ Step 7: サークル名インデックスを生成
|
| 592 |
↓
|
| 593 |
+
3. サーバーを再起動(またはHF Spacesへデプロイ)
|
| 594 |
↓
|
| 595 |
+
4. フロントエンドで新しいサークルデータが見える
|
| 596 |
```
|
| 597 |
|
| 598 |
**詳細:**
|
| 599 |
|
| 600 |
+
#### Step 2: `scripts/2_create_circles_data.py` - CSV → JSON変換
|
| 601 |
```
|
| 602 |
+
CSV(original_circles.csv)からサークルデータを読み込み
|
| 603 |
+
→ JSON形式のsnsフィールドをパース(type, url, handle)
|
| 604 |
+
→ JSON形式のimagesフィールドをパース
|
| 605 |
+
→ 年間予定を新形式(period, event)に変換
|
| 606 |
+
→ circles.json に出力
|
| 607 |
```
|
| 608 |
|
| 609 |
+
このステップ以降のデータは、すべてこの `circles.json` から生成されます。
|
| 610 |
|
| 611 |
#### Step 3: `scripts/3_build_synonyms_from_sudachi.py` - 同義語キャッシュ
|
| 612 |
```
|
| 613 |
+
circles.json の全テキストをトークン化
|
| 614 |
→ 出現語彙を収集
|
| 615 |
→ Sudachi同義語辞書 + custom synonyms.json から該当するもののみ抽出
|
| 616 |
→ synonyms_cache.json に保存
|
|
|
|
| 620 |
|
| 621 |
#### Step 4-5: `scripts/4_prepare_bm25f_meta.py` と `scripts/5_prepare_tf_token.py` - BM25F統計
|
| 622 |
```
|
| 623 |
+
circles.json をトークン化
|
| 624 |
→ IDF(珍しさスコア)を計算
|
| 625 |
→ 各フィールドの平均トークン数を計算
|
| 626 |
→ bm25_meta.json に保存
|
| 627 |
|
| 628 |
+
各サークルごとのTF(単語の出現数)を計算
|
| 629 |
→ tf_token.json に保存
|
| 630 |
```
|
| 631 |
|
|
|
|
| 642 |
|---------|---------|---------|
|
| 643 |
| 0 | fastText ダウンロード | 初回のみ数分 |
|
| 644 |
| 1 | Sudachi辞書構築 | 1-2分 |
|
| 645 |
+
| 2 | CSV→JSON | 数秒 |
|
| 646 |
| 3 | 同義語キャッシュ | 10-30秒 |
|
| 647 |
| 4 | BM25F統計 | 1-2分 |
|
| 648 |
| 5 | TF/トークン | 1-2分 |
|
| 649 |
| 6 | 単語ベクトル | 1-2分 |
|
| 650 |
+
| 7 | サークル名インデックス | 数秒 |
|
| 651 |
|
| 652 |
**全ステップ実行** → 合計約5-10分
|
| 653 |
|
|
|
|
| 668 |
}
|
| 669 |
```
|
| 670 |
|
| 671 |
+
### 7.2 サークル一覧取得
|
| 672 |
|
| 673 |
```bash
|
| 674 |
curl -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 675 |
+
"http://localhost:7860/api/circles"
|
| 676 |
```
|
| 677 |
|
| 678 |
**認証方法:**
|
| 679 |
- ヘッダ `X-API-KEY` に `API_SECRET_KEY` 環境変数の値を指定
|
| 680 |
- 環境変数は `.env` ファイルに保存(リポジトリには含めない)
|
| 681 |
|
| 682 |
+
**レスポンス(gzip圧縮、CircleSummary形式):**
|
| 683 |
```json
|
| 684 |
[
|
| 685 |
{
|
| 686 |
+
"circleId": "circle-009",
|
| 687 |
"circleName": "写真部",
|
| 688 |
+
"circleNameKana": "しゃしんぶ",
|
| 689 |
+
"projectId": null,
|
| 690 |
+
"projectName": null,
|
| 691 |
+
"genre": "CULTURE",
|
| 692 |
+
"areaCode": "R",
|
| 693 |
+
"pamphletNumber": 9,
|
| 694 |
+
"shortIntro": "一瞬を永遠に。カメラで世界を切り取ろう",
|
| 695 |
+
"detailDescription": "# 写真部 詳細ガイド...",
|
| 696 |
+
"mainImage": {
|
| 697 |
+
"url": "https://placehold.co/800x600?text=Photo1",
|
| 698 |
+
"alt": "撮影会の様子",
|
|
|
|
|
|
|
| 699 |
"order": 0
|
| 700 |
+
},
|
| 701 |
+
"memberCount": 45,
|
| 702 |
+
"memberNote": "学年問わず歓迎",
|
| 703 |
+
"annualFee": 5000,
|
| 704 |
+
"otherCosts": "機材レンタル料(任意、月500円)",
|
| 705 |
+
"activityFrequency": "WEEKLY_1_2",
|
| 706 |
+
"activityFrequencyNote": "撮影会は主に土日",
|
| 707 |
+
"activityLocation": "学生会館・学外",
|
| 708 |
+
"isArchived": false
|
| 709 |
},
|
| 710 |
...
|
| 711 |
]
|
| 712 |
```
|
| 713 |
|
| 714 |
+
### 7.3 サークル詳細取得
|
| 715 |
|
| 716 |
```bash
|
| 717 |
curl -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 718 |
+
"http://localhost:7860/api/details?circleId=circle-009"
|
| 719 |
```
|
| 720 |
|
| 721 |
+
**レスポンス(CircleDetail形式):**
|
| 722 |
```json
|
| 723 |
{
|
| 724 |
+
"circleId": "circle-009",
|
| 725 |
"circleName": "写真部",
|
| 726 |
+
"circleNameKana": "しゃしんぶ",
|
| 727 |
+
"projectId": null,
|
| 728 |
+
"projectName": null,
|
| 729 |
+
"genre": "CULTURE",
|
| 730 |
+
"areaCode": "R",
|
| 731 |
+
"pamphletNumber": 9,
|
| 732 |
+
"shortIntro": "一瞬を永遠に。カメラで世界を切り取ろう",
|
| 733 |
+
"detailDescription": "# 写真部 詳細ガイド...",
|
| 734 |
"images": [
|
| 735 |
{
|
| 736 |
+
"url": "https://placehold.co/800x600?text=Photo1",
|
| 737 |
+
"alt": "撮影会の様子",
|
| 738 |
"order": 0
|
| 739 |
},
|
| 740 |
{
|
| 741 |
+
"url": "https://placehold.co/800x600?text=Photo2",
|
| 742 |
+
"alt": "展示会風景",
|
| 743 |
"order": 1
|
| 744 |
}
|
| 745 |
],
|
| 746 |
+
"mainImage": {
|
| 747 |
+
"url": "https://placehold.co/800x600?text=Photo1",
|
| 748 |
+
"alt": "撮影会の様子",
|
| 749 |
+
"order": 0
|
| 750 |
+
},
|
| 751 |
+
"sns": [
|
| 752 |
+
{
|
| 753 |
+
"type": "TWITTER",
|
| 754 |
+
"url": "https://twitter.com/chiba_photo",
|
| 755 |
+
"handle": "@chiba_photo"
|
| 756 |
+
},
|
| 757 |
{
|
| 758 |
+
"type": "INSTAGRAM",
|
| 759 |
+
"url": "https://instagram.com/chiba_photo",
|
| 760 |
+
"handle": "@chiba_photo"
|
| 761 |
+
},
|
| 762 |
+
{
|
| 763 |
+
"type": "LINE",
|
| 764 |
+
"url": "https://line.me/ti/p/chiba_photo",
|
| 765 |
+
"handle": "LINE公式アカウント"
|
| 766 |
+
},
|
| 767 |
+
{
|
| 768 |
+
"type": "YOUTUBE",
|
| 769 |
+
"url": "https://youtube.com/@chiba_photo",
|
| 770 |
+
"handle": "@chiba_photo"
|
| 771 |
+
},
|
| 772 |
+
{
|
| 773 |
+
"type": "WEBSITE",
|
| 774 |
+
"url": "https://example.com/chiba-photo",
|
| 775 |
+
"handle": "公式サイト"
|
| 776 |
+
},
|
| 777 |
+
{
|
| 778 |
+
"type": "GITHUB",
|
| 779 |
+
"url": "https://github.com/chiba-photo",
|
| 780 |
+
"handle": "GitHub"
|
| 781 |
}
|
| 782 |
+
],
|
| 783 |
+
"memberCount": 45,
|
| 784 |
+
"memberNote": "学年問わず歓迎",
|
| 785 |
+
"annualFee": 5000,
|
| 786 |
+
"otherCosts": "機材レンタル料(任意、月500円)",
|
| 787 |
+
"activityFrequency": "WEEKLY_1_2",
|
| 788 |
+
"activityFrequencyNote": "撮影会は主に土日",
|
| 789 |
+
"activityLocation": "学生会館・学外",
|
| 790 |
+
"annualSchedule": [
|
| 791 |
+
{"period": "4月", "event": "新歓撮影会"},
|
| 792 |
+
{"period": "4月〜6月", "event": "春季撮影ツアー"},
|
| 793 |
+
{"period": "5月〜6月", "event": "春季写真展"},
|
| 794 |
+
{"period": "8月", "event": "夏合宿(撮影旅行)"},
|
| 795 |
+
{"period": "11月", "event": "大祭写真展"},
|
| 796 |
+
{"period": "3月", "event": "卒業写真展・追いコン"}
|
| 797 |
+
],
|
| 798 |
+
"contactInfo": "photo-club@example.chiba-u.jp",
|
| 799 |
+
"websiteUrl": "https://example.com/chiba-photo",
|
| 800 |
+
"isArchived": false
|
| 801 |
}
|
| 802 |
```
|
| 803 |
|
| 804 |
+
**SNSフィールドについて:**
|
| 805 |
+
- `type`: SNSの種類(TWITTER, INSTAGRAM, LINE, YOUTUBE, WEBSITE, GITHUB等)
|
| 806 |
+
- `url`: リンクURL
|
| 807 |
+
- `handle`: 表示用のタイトル/アカウント名(Twitter/Instagramはアカウント名、LINE/WEBSITEは任意のタイトル)
|
| 808 |
+
|
| 809 |
### 7.4 検索
|
| 810 |
|
| 811 |
```bash
|
| 812 |
curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 813 |
-H "Content-Type: application/json" \
|
| 814 |
+
-d '{"query": "写真"}' \
|
| 815 |
"http://localhost:7860/api/search"
|
| 816 |
```
|
| 817 |
|
| 818 |
**レスポンス(通常モード):**
|
| 819 |
```json
|
| 820 |
{
|
| 821 |
+
"circleIds": ["circle-009", "circle-004"]
|
| 822 |
}
|
| 823 |
```
|
| 824 |
|
|
|
|
| 827 |
```bash
|
| 828 |
curl -X POST -H "X-API-KEY: YOUR_SECRET_KEY" \
|
| 829 |
-H "Content-Type: application/json" \
|
| 830 |
+
-d '{"query": "写真", "debug": true}' \
|
| 831 |
"http://localhost:7860/api/search"
|
| 832 |
```
|
| 833 |
|
| 834 |
**レスポンス(デバッグモード):**
|
| 835 |
```json
|
| 836 |
{
|
| 837 |
+
"circleIds": ["circle-009", "circle-004"],
|
| 838 |
"scores": [
|
| 839 |
{
|
| 840 |
+
"circleId": "circle-009",
|
| 841 |
"score": 2.15
|
| 842 |
},
|
| 843 |
{
|
| 844 |
+
"circleId": "circle-004",
|
| 845 |
"score": 1.83
|
| 846 |
}
|
| 847 |
],
|
| 848 |
"details": [
|
| 849 |
{
|
| 850 |
+
"circleId": "circle-009",
|
| 851 |
+
"circleName": "写真部",
|
|
|
|
| 852 |
"bm25": 0.85,
|
| 853 |
"ws_filter_topk": 0.72,
|
| 854 |
"ws_rerank_pairavg": 0.75,
|
| 855 |
+
"org_boost": 1.0,
|
| 856 |
+
"matched_substring": true,
|
| 857 |
"fused_filter": 0.78,
|
| 858 |
"fused_final": 2.15
|
| 859 |
},
|
|
|
|
| 866 |
- `bm25`: BM25Fスコア(単語一致度)
|
| 867 |
- `ws_filter_topk`: 単語ベクトル類似度(フィルタリング用、top-k方式)
|
| 868 |
- `ws_rerank_pairavg`: 単語ベクトル類似度(リランキング用、pair-average方式)
|
| 869 |
+
- `org_boost`: サークル名マッチングボーナス
|
| 870 |
+
- `matched_substring`: 部分文字列マッチングでサークル名に合致したか
|
| 871 |
- `fused_filter`: フィルタリング段階での統合スコア
|
| 872 |
- `fused_final`: 最終統合スコア
|
| 873 |
|
|
|
|
| 879 |
"http://localhost:7860/tasks/update"
|
| 880 |
```
|
| 881 |
|
| 882 |
+
**用途:** CSVファイル(original_circles.csv)が更新された時に、このエンドポイントを呼び出すと、検索用アセット(`data/generated/` 配下のファイル)が自動的に再生成されます。
|
| 883 |
|
| 884 |
**レスポンス:**
|
| 885 |
```json
|
|
|
|
| 916 |
|
| 917 |
**前提条件:**
|
| 918 |
- Python 3.11以上
|
|
|
|
| 919 |
- pip または conda
|
| 920 |
|
| 921 |
**手順:**
|
|
|
|
| 933 |
# 3. 依存パッケージをインストール
|
| 934 |
pip install -r requirements.txt
|
| 935 |
|
| 936 |
+
# 4. .env ファイルを作成(セキュリティ情報は引き継ぎで取得)
|
|
|
|
| 937 |
# API_SECRET_KEY=...
|
| 938 |
# HF_TOKEN=...
|
| 939 |
+
# HF_EMBEDDINGS_REPO_ID=...
|
| 940 |
|
| 941 |
# 5. データ生成スクリプトを実行
|
| 942 |
python scripts/build_all.py
|
|
|
|
| 949 |
|
| 950 |
**ローカル開発中のデータ更新の流れ:**
|
| 951 |
|
| 952 |
+
通常の開発では、CSVファイルが更新された後に検索エンジンをテストしたい場合があります。その際は:
|
| 953 |
|
| 954 |
```bash
|
| 955 |
# 方法1: 手動でスクリプト実行
|
|
|
|
| 983 |
|
| 984 |
**原因候補:**
|
| 985 |
1. `data/generated/` のファイルが不足している
|
| 986 |
+
2. CSVファイル(original_circles.csv)が空である
|
| 987 |
3. 設定ファイルのパスが間違っている
|
| 988 |
|
| 989 |
**対処:**
|
|
|
|
| 1038 |
|
| 1039 |
## 付録:よくある調整例
|
| 1040 |
|
| 1041 |
+
### サークル名をもっと重視したい
|
| 1042 |
|
| 1043 |
```json
|
| 1044 |
{
|
| 1045 |
"bm25f": {
|
| 1046 |
"field_weights": {
|
| 1047 |
+
"circleName": 3.0, // ← 1.5 から 3.0 に変更
|
| 1048 |
+
"shortIntro": 1.5,
|
| 1049 |
+
"detailDescription": 1.0,
|
| 1050 |
...
|
| 1051 |
}
|
| 1052 |
}
|
resources/original_circles.csv
CHANGED
|
@@ -47,7 +47,7 @@ circle-001,千葉大学サッカー部,ちばだいがくさっかーぶ,true,,,
|
|
| 47 |
```
|
| 48 |
練習日程:月・火・水・木・土
|
| 49 |
場所:総合グラウンド
|
| 50 |
-
```",50,,WEEKLY_3_4,,総合グラウンド,24000,,"[{""month"": 4, ""events"": [""新歓"", ""春季リーグ開幕""]}, {""month"": 5, ""events"": [""リーグ戦""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 11, ""events"": [""大祭"", ""秋季リーグ""]}, {""month"": 12, ""events"": [""冬合宿""]}, {""month"": 3, ""events"": [""追いコン""]}]",soccer@example.chiba-u.jp,https://example.com/chiba-soccer,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_soccer""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_soccer""}]","[{""url"": ""https://placehold.co/800x600?text=Soccer1"", ""alt"": ""サッカー部練習風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Soccer2"", ""alt"": ""試合の様子"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Soccer3"", ""alt"": ""集合写真"", ""order"": 2}]",false
|
| 51 |
circle-002,千葉大学吹奏楽団,ちばだいがくすいそうがくだん,true,,,STAGE,CULTURE,MUSIC_PERFORM,MUSIC,MUSIC,L,2,創部70年超の伝統!定期演奏会で1000人動員,定期演奏会や学祭での演奏を行っています。初心者から経験者まで幅広く活動中。,"# 千葉大学吹奏楽団
|
| 52 |
|
| 53 |
創部70年以上の歴史を持つ、千葉大学を代表する音楽団体です。
|
|
@@ -98,7 +98,7 @@ circle-002,千葉大学吹奏楽団,ちばだいがくすいそうがくだん,t
|
|
| 98 |
見学随時受付中!
|
| 99 |
```
|
| 100 |
|
| 101 |
-
詳細は[公式サイト](https://example.com/chiba-brass)へ",60,,WEEKLY_3_4,,音楽棟練習室,12000,,"[{""month"": 4, ""events"": [""新歓"", ""新入生歓迎演奏会""]}, {""month"": 6, ""events"": [""定期演奏会""]}, {""month"": 8, ""events"": [""夏合宿"", ""コンクール""]}, {""month"": 10, ""events"": [""学園祭演奏""]}, {""month"": 12, ""events"": [""クリスマスコンサート""]}, {""month"": 3, ""events"": [""追いコン"", ""卒業演奏会""]}]",brass@example.chiba-u.jp,https://example.com/chiba-brass,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_brass""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_brass""}]","[{""url"": ""https://placehold.co/800x600?text=Brass1"", ""alt"": ""演奏会の様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Brass2"", ""alt"": ""練習風景"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Brass3"", ""alt"": ""集合写真"", ""order"": 2}]",false
|
| 102 |
circle-003,軽音楽サークル SOUNDWAVE,けいおんがくさーくる さうんどうぇーぶ,false,,,STAGE,CULTURE,MUSIC_PERFORM,MUSIC,MUSIC,K,3,ジャンル自由!バンドを組んで音楽を楽しもう,バンド活動を通じて音楽を楽しむサークルです。初心者大歓迎!,"# 軽音楽サークル SOUNDWAVE
|
| 103 |
|
| 104 |
ロック、ポップス、ジャズなど様々なジャンルの音楽を楽しむサークルです。
|
|
@@ -158,7 +158,7 @@ circle-003,軽音楽サークル SOUNDWAVE,けいおんがくさーくる さう
|
|
| 158 |
|
| 159 |
年会費**6,000円**(スタジオ利用料込み)
|
| 160 |
|
| 161 |
-
[Twitter](https://twitter.com/chiba_keion)で活動情報を発信中!",30,,WEEKLY_1_2,,音楽棟・外部ライブハウス,6000,,"[{""month"": 4, ""events"": [""新歓ライブ""]}, {""month"": 5, ""events"": [""春の定期ライブ""]}, {""month"": 7, ""events"": [""七夕ライブ""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 11, ""events"": [""大祭ステージ""]}, {""month"": 12, ""events"": [""クリスマスライブ""]}, {""month"": 3, ""events"": [""追いコン""]}]",keion@example.chiba-u.jp,,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_keion""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_keion""}]","[{""url"": ""https://placehold.co/800x600?text=Keion1"", ""alt"": ""ライブ演奏"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Keion2"", ""alt"": ""練習風景"", ""order"": 1}]",false
|
| 162 |
circle-004,アニメーション研究会,あにめーしょんけんきゅうかい,false,,,ROOM,CULTURE,VISUAL_ARTS,EXHIBITION,CULTURE,S,4,観るだけでなく作る!アニメ制作に挑戦,アニメーション制作や鑑賞会を行っています。創作活動が好きな人集まれ!,"# アニメーション研究会
|
| 163 |
|
| 164 |
アニメーションの制作・鑑賞を中心に活動するサークルです。
|
|
@@ -225,7 +225,7 @@ circle-004,アニメーション研究会,あにめーしょんけんきゅう
|
|
| 225 |
|
| 226 |
技術的なスキルがなくても、*やる気があれば大歓迎*です!
|
| 227 |
|
| 228 |
-
詳細は[公式サイト](https://example.com/chiba-anime)をご覧ください。",25,,WEEKLY_1_2,,学生会館,3000,,"[{""month"": 4, ""events"": [""新歓""]}, {""month"": 5, ""events"": [""春の上映会""]}, {""month"": 8, ""events"": [""コミケ参加"", ""夏合宿""]}, {""month"": 11, ""events"": [""大祭展示・上映""]}, {""month"": 2, ""events"": [""冬の上映会""]}, {""month"": 3, ""events"": [""追いコン""]}]",anime@example.chiba-u.jp,https://example.com/chiba-anime,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_anime""}]","[{""url"": ""https://placehold.co/800x600?text=Anime1"", ""alt"": ""制作風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Anime2"", ""alt"": ""展示の様子"", ""order"": 1}]",false
|
| 229 |
circle-005,バドミントンサークル B.A.S.S.,ばどみんとんさーくる ばす,false,,,OUTSIDE_BOOTH,SPORTS,RACKET_SPORTS,PERFORMANCE,SPORTS,G,5,初心者歓迎!自分のペースで楽しくバドミントン,初心者から経験者まで楽しくバドミントン! 週2回の練習で無理なく続けられます。,"# バドミントンサークル B.A.S.S.
|
| 230 |
|
| 231 |
B.A.S.S.(Badminton Association of Sports & Smash)は、楽しくバドミントンをすることをモットーに活動しています。
|
|
@@ -284,7 +284,7 @@ B.A.S.S.(Badminton Association of Sports & Smash)は、楽しくバドミン
|
|
| 284 |
|
| 285 |
*ラケットのレンタルあり(初心者向け)*
|
| 286 |
|
| 287 |
-
[Instagram](https://instagram.com/chiba_bass)で練習風景を公開中!",40,,WEEKLY_1_2,,体育館,5000,シャトル代(実費),"[{""month"": 4, ""events"": [""新歓""]}, {""month"": 6, ""events"": [""学内大会""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 9, ""events"": [""他大学交流戦""]}, {""month"": 11, ""events"": [""大祭""]}, {""month"": 12, ""events"": [""忘年会""]}, {""month"": 3, ""events"": [""追いコン""]}]",bass-badminton@example.chiba-u.jp,,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_bass""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_bass""}]","[{""url"": ""https://placehold.co/800x600?text=Badminton1"", ""alt"": ""練習風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Badminton2"", ""alt"": ""集合写真"", ""order"": 1}]",false
|
| 288 |
circle-006,プログラミングサークル CODE,ぷろぐらみんぐさーくる こーど,false,,,ROOM,CULTURE,IT_TECH,EXHIBITION,ACADEMIC,D,6,Web・ゲーム・AIまで。技術で世界を変えよう,Webアプリからゲーム開発まで、プログラミングを楽しむサークルです。,"# プログラミングサークル CODE
|
| 289 |
|
| 290 |
技術を学び、創造力を発揮するサークルです。
|
|
@@ -354,7 +354,7 @@ welcome('新入生')
|
|
| 354 |
|
| 355 |
プログラミング未経験でも**大歓迎**!基礎から丁寧に教えます。
|
| 356 |
|
| 357 |
-
詳細は[公式サイト](https://example.com/chiba-code)をご覧ください。",35,,WEEKLY_1_2,,情報処理センター,4000,,"[{""month"": 4, ""events"": [""新歓"", ""基礎講座開始""]}, {""month"": 5, ""events"": [""春季ハッカソン""]}, {""month"": 8, ""events"": [""夏合宿"", ""開発合宿""]}, {""month"": 10, ""events"": [""技術カンファレンス参加""]}, {""month"": 11, ""events"": [""大祭展示""]}, {""month"": 3, ""events"": [""成果発表会""]}]",code@example.chiba-u.jp,https://example.com/chiba-code,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_code""}, {""type"": ""GITHUB"", ""url"": ""https://github.com/chiba-code""}]","[{""url"": ""https://placehold.co/800x600?text=Code1"", ""alt"": ""ハッカソンの様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Code2"", ""alt"": ""勉強会風景"", ""order"": 1}]",false
|
| 358 |
circle-007,茶道部,さどうぶ,true,,,ROOM,CULTURE,TRADITIONAL,EXHIBITION,CULTURE,H,7,一期一会の心で日本の伝統文化を学ぶ,日本の伝統文化「茶道」を学び、心を磨くサークルです。,"# 茶道部
|
| 359 |
|
| 360 |
日本の伝統文化である茶道を学ぶ部活動です。
|
|
@@ -417,7 +417,7 @@ circle-007,茶道部,さどうぶ,true,,,ROOM,CULTURE,TRADITIONAL,EXHIBITION,CUL
|
|
| 417 |
- 男女問わず歓迎
|
| 418 |
- 留学生も多数在籍
|
| 419 |
|
| 420 |
-
茶道を通じて、*日本の心*を学んでみませんか?",20,,WEEKLY_1_2,,学生会館和室,8000,"お稽古代 月1,500円(お菓子・お抹茶代含む)","[{""month"": 4, ""events"": [""新歓""]}, {""month"": 5, ""events"": [""新入部員お稽古開始""]}, {""month"": 7, ""events"": [""七夕茶会""]}, {""month"": 11, ""events"": [""大祭茶会""]}, {""month"": 1, ""events"": [""初釜""]}, {""month"": 3, ""events"": [""卒業茶会""]}]",sado@example.chiba-u.jp,https://example.com/chiba-sado,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_sado""}]","[{""url"": ""https://placehold.co/800x600?text=Sado1"", ""alt"": ""お稽古風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Sado2"", ""alt"": ""茶会の様子"", ""order"": 1}]",false
|
| 421 |
circle-008,国際交流サークル GLOBAL,こくさいこうりゅうさーくる ぐろーばる,false,,,ROOM,CULTURE,SOCIAL,EXHIBITION,CULTURE,E,8,留学生と日本人が一緒に学び交流するサークル,留学生と日本人学生が交流するサークル。英語力向上にも!,"# 国際交流サークル GLOBAL
|
| 422 |
|
| 423 |
留学生と日本人学生が一緒に活動するサークルです。
|
|
@@ -483,7 +483,7 @@ circle-008,国際交流サークル GLOBAL,こくさいこうりゅうさーく
|
|
| 483 |
|
| 484 |
*英語が苦手でも大丈夫!* みんなで助け合いながら成長しましょう。
|
| 485 |
|
| 486 |
-
詳細は[Instagram](https://instagram.com/chiba_global)をチェック!",50,,WEEKLY_1_2,,国際交流会館,3000,,"[{""month"": 4, ""events"": [""新歓パーティー""]}, {""month"": 5, ""events"": [""料理会""]}, {""month"": 7, ""events"": [""国際BBQ""]}, {""month"": 8, ""events"": [""夏旅行""]}, {""month"": 11, ""events"": [""大祭出展""]}, {""month"": 12, ""events"": [""クリスマスパーティー""]}, {""month"": 2, ""events"": [""送別会""]}]",global@example.chiba-u.jp,,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_global""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_global""}]","[{""url"": ""https://placehold.co/800x600?text=Global1"", ""alt"": ""交流イベント"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Global2"", ""alt"": ""パーティーの様子"", ""order"": 1}]",false
|
| 487 |
circle-009,写真部,しゃしんぶ,true,,,ROOM,CULTURE,VISUAL_ARTS,EXHIBITION,CULTURE,R,9,一瞬を永遠に。カメラで世界を切り取ろう,カメラと写真を愛する仲間が集まるサークルです。撮影会や展示会を定期開催。,"# 写真部
|
| 488 |
|
| 489 |
カメラを通じて世界を切り取る喜びを共有するサークルです。
|
|
@@ -571,4 +571,4 @@ circle-009,写真部,しゃしんぶ,true,,,ROOM,CULTURE,VISUAL_ARTS,EXHIBITION,
|
|
| 571 |
|
| 572 |
詳細は[公式サイト](https://example.com/chiba-photo)をご覧ください。
|
| 573 |
|
| 574 |
-
お問い合わせは photo-club@example.chiba-u.jp まで!",45,学年問わず歓迎,WEEKLY_1_2,撮影会は主に土日,学生会館・学外,5000,"機材レンタル料(任意、月500円)","[{""period"": ""4月"", ""event"": ""新歓撮影会""}, {""period"": ""4月〜6月"", ""event"": ""春季撮影ツアー""}, {""period"": ""春"", ""event"": ""春季写真展準備""}, {""period"": ""5月〜6月"", ""event"": ""春季写真展""}, {""period"": ""8月"", ""event"": ""夏合宿(撮影旅行)""}, {""period"": ""11月"", ""event"": ""大祭写真展""}, {""period"": ""随時"", ""event"": ""月例撮影会""}, {""period"": ""不定期"", ""event"": ""外部コンテスト応募""}, {""period"": ""3月"", ""event"": ""卒業写真展・追いコン""}]",photo-club@example.chiba-u.jp,https://example.com/chiba-photo,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_photo"", ""handle"": ""@chiba_photo""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_photo"", ""handle"": ""@chiba_photo""}, {""type"": ""LINE"", ""url"": ""https://line.me/ti/p/chiba_photo""}, {""type"": ""YOUTUBE"", ""url"": ""https://youtube.com/@chiba_photo""}, {""type"": ""WEBSITE"", ""url"": ""https://example.com/chiba-photo""}, {""type"": ""GITHUB"", ""url"": ""https://github.com/chiba-photo""}]","[{""url"": ""https://placehold.co/800x600?text=Photo1"", ""alt"": ""撮影会の様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Photo2"", ""alt"": ""展示会風景"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Photo3"", ""alt"": ""作品例"", ""order"": 2}]",false
|
|
|
|
| 47 |
```
|
| 48 |
練習日程:月・火・水・木・土
|
| 49 |
場所:総合グラウンド
|
| 50 |
+
```",50,,WEEKLY_3_4,,総合グラウンド,24000,,"[{""month"": 4, ""events"": [""新歓"", ""春季リーグ開幕""]}, {""month"": 5, ""events"": [""リーグ戦""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 11, ""events"": [""大祭"", ""秋季リーグ""]}, {""month"": 12, ""events"": [""冬合宿""]}, {""month"": 3, ""events"": [""追いコン""]}]",soccer@example.chiba-u.jp,https://example.com/chiba-soccer,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_soccer"", ""handle"": ""@chiba_soccer""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_soccer"", ""handle"": ""@chiba_soccer""}]","[{""url"": ""https://placehold.co/800x600?text=Soccer1"", ""alt"": ""サッカー部練習風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Soccer2"", ""alt"": ""試合の様子"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Soccer3"", ""alt"": ""集合写真"", ""order"": 2}]",false
|
| 51 |
circle-002,千葉大学吹奏楽団,ちばだいがくすいそうがくだん,true,,,STAGE,CULTURE,MUSIC_PERFORM,MUSIC,MUSIC,L,2,創部70年超の伝統!定期演奏会で1000人動員,定期演奏会や学祭での演奏を行っています。初心者から経験者まで幅広く活動中。,"# 千葉大学吹奏楽団
|
| 52 |
|
| 53 |
創部70年以上の歴史を持つ、千葉大学を代表する音楽団体です。
|
|
|
|
| 98 |
見学随時受付中!
|
| 99 |
```
|
| 100 |
|
| 101 |
+
詳細は[公式サイト](https://example.com/chiba-brass)へ",60,,WEEKLY_3_4,,音楽棟練習室,12000,,"[{""month"": 4, ""events"": [""新歓"", ""新入生歓迎演奏会""]}, {""month"": 6, ""events"": [""定期演奏会""]}, {""month"": 8, ""events"": [""夏合宿"", ""コンクール""]}, {""month"": 10, ""events"": [""学園祭演奏""]}, {""month"": 12, ""events"": [""クリスマスコンサート""]}, {""month"": 3, ""events"": [""追いコン"", ""卒業演奏会""]}]",brass@example.chiba-u.jp,https://example.com/chiba-brass,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_brass"", ""handle"": ""@chiba_brass""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_brass"", ""handle"": ""LINE公式アカウント""}]","[{""url"": ""https://placehold.co/800x600?text=Brass1"", ""alt"": ""演奏会の様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Brass2"", ""alt"": ""練習風景"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Brass3"", ""alt"": ""集合写真"", ""order"": 2}]",false
|
| 102 |
circle-003,軽音楽サークル SOUNDWAVE,けいおんがくさーくる さうんどうぇーぶ,false,,,STAGE,CULTURE,MUSIC_PERFORM,MUSIC,MUSIC,K,3,ジャンル自由!バンドを組んで音楽を楽しもう,バンド活動を通じて音楽を楽しむサークルです。初心者大歓迎!,"# 軽音楽サークル SOUNDWAVE
|
| 103 |
|
| 104 |
ロック、ポップス、ジャズなど様々なジャンルの音楽を楽しむサークルです。
|
|
|
|
| 158 |
|
| 159 |
年会費**6,000円**(スタジオ利用料込み)
|
| 160 |
|
| 161 |
+
[Twitter](https://twitter.com/chiba_keion)で活動情報を発信中!",30,,WEEKLY_1_2,,音楽棟・外部ライブハウス,6000,,"[{""month"": 4, ""events"": [""新歓ライブ""]}, {""month"": 5, ""events"": [""春の定期ライブ""]}, {""month"": 7, ""events"": [""七夕ライブ""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 11, ""events"": [""大祭ステージ""]}, {""month"": 12, ""events"": [""クリスマスライブ""]}, {""month"": 3, ""events"": [""追いコン""]}]",keion@example.chiba-u.jp,,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_keion"", ""handle"": ""@chiba_keion""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_keion"", ""handle"": ""@chiba_keion""}]","[{""url"": ""https://placehold.co/800x600?text=Keion1"", ""alt"": ""ライブ演奏"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Keion2"", ""alt"": ""練習風景"", ""order"": 1}]",false
|
| 162 |
circle-004,アニメーション研究会,あにめーしょんけんきゅうかい,false,,,ROOM,CULTURE,VISUAL_ARTS,EXHIBITION,CULTURE,S,4,観るだけでなく作る!アニメ制作に挑戦,アニメーション制作や鑑賞会を行っています。創作活動が好きな人集まれ!,"# アニメーション研究会
|
| 163 |
|
| 164 |
アニメーションの制作・鑑賞を中心に活動するサークルです。
|
|
|
|
| 225 |
|
| 226 |
技術的なスキルがなくても、*やる気があれば大歓迎*です!
|
| 227 |
|
| 228 |
+
詳細は[公式サイト](https://example.com/chiba-anime)をご覧ください。",25,,WEEKLY_1_2,,学生会館,3000,,"[{""month"": 4, ""events"": [""新歓""]}, {""month"": 5, ""events"": [""春の上映会""]}, {""month"": 8, ""events"": [""コミケ参加"", ""夏合宿""]}, {""month"": 11, ""events"": [""大祭展示・上映""]}, {""month"": 2, ""events"": [""冬の上映会""]}, {""month"": 3, ""events"": [""追いコン""]}]",anime@example.chiba-u.jp,https://example.com/chiba-anime,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_anime"", ""handle"": ""@chiba_anime""}]","[{""url"": ""https://placehold.co/800x600?text=Anime1"", ""alt"": ""制作風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Anime2"", ""alt"": ""展示の様子"", ""order"": 1}]",false
|
| 229 |
circle-005,バドミントンサークル B.A.S.S.,ばどみんとんさーくる ばす,false,,,OUTSIDE_BOOTH,SPORTS,RACKET_SPORTS,PERFORMANCE,SPORTS,G,5,初心者歓迎!自分のペースで楽しくバドミントン,初心者から経験者まで楽しくバドミントン! 週2回の練習で無理なく続けられます。,"# バドミントンサークル B.A.S.S.
|
| 230 |
|
| 231 |
B.A.S.S.(Badminton Association of Sports & Smash)は、楽しくバドミントンをすることをモットーに活動しています。
|
|
|
|
| 284 |
|
| 285 |
*ラケットのレンタルあり(初心者向け)*
|
| 286 |
|
| 287 |
+
[Instagram](https://instagram.com/chiba_bass)で練習風景を公開中!",40,,WEEKLY_1_2,,体育館,5000,シャトル代(実費),"[{""month"": 4, ""events"": [""新歓""]}, {""month"": 6, ""events"": [""学内大会""]}, {""month"": 8, ""events"": [""夏合宿""]}, {""month"": 9, ""events"": [""他大学交流戦""]}, {""month"": 11, ""events"": [""大祭""]}, {""month"": 12, ""events"": [""忘年会""]}, {""month"": 3, ""events"": [""追いコン""]}]",bass-badminton@example.chiba-u.jp,,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_bass"", ""handle"": ""@chiba_bass""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_bass"", ""handle"": ""LINE公式アカウント""}]","[{""url"": ""https://placehold.co/800x600?text=Badminton1"", ""alt"": ""練習風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Badminton2"", ""alt"": ""集合写真"", ""order"": 1}]",false
|
| 288 |
circle-006,プログラミングサークル CODE,ぷろぐらみんぐさーくる こーど,false,,,ROOM,CULTURE,IT_TECH,EXHIBITION,ACADEMIC,D,6,Web・ゲーム・AIまで。技術で世界を変えよう,Webアプリからゲーム開発まで、プログラミングを楽しむサークルです。,"# プログラミングサークル CODE
|
| 289 |
|
| 290 |
技術を学び、創造力を発揮するサークルです。
|
|
|
|
| 354 |
|
| 355 |
プログラミング未経験でも**大歓迎**!基礎から丁寧に教えます。
|
| 356 |
|
| 357 |
+
詳細は[公式サイト](https://example.com/chiba-code)をご覧ください。",35,,WEEKLY_1_2,,情報処理センター,4000,,"[{""month"": 4, ""events"": [""新歓"", ""基礎講座開始""]}, {""month"": 5, ""events"": [""春季ハッカソン""]}, {""month"": 8, ""events"": [""夏合宿"", ""開発合宿""]}, {""month"": 10, ""events"": [""技術カンファレンス参加""]}, {""month"": 11, ""events"": [""大祭展示""]}, {""month"": 3, ""events"": [""成果発表会""]}]",code@example.chiba-u.jp,https://example.com/chiba-code,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_code"", ""handle"": ""@chiba_code""}, {""type"": ""GITHUB"", ""url"": ""https://github.com/chiba-code"", ""handle"": ""GitHub""}]","[{""url"": ""https://placehold.co/800x600?text=Code1"", ""alt"": ""ハッカソンの様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Code2"", ""alt"": ""勉強会風景"", ""order"": 1}]",false
|
| 358 |
circle-007,茶道部,さどうぶ,true,,,ROOM,CULTURE,TRADITIONAL,EXHIBITION,CULTURE,H,7,一期一会の心で日本の伝統文化を学ぶ,日本の伝統文化「茶道」を学び、心を磨くサークルです。,"# 茶道部
|
| 359 |
|
| 360 |
日本の伝統文化である茶道を学ぶ部活動です。
|
|
|
|
| 417 |
- 男女問わず歓迎
|
| 418 |
- 留学生も多数在籍
|
| 419 |
|
| 420 |
+
茶道を通じて、*日本の心*を学んでみませんか?",20,,WEEKLY_1_2,,学生会館和室,8000,"お稽古代 月1,500円(お菓子・お抹茶代含む)","[{""month"": 4, ""events"": [""新歓""]}, {""month"": 5, ""events"": [""新入部員お稽古開始""]}, {""month"": 7, ""events"": [""七夕茶会""]}, {""month"": 11, ""events"": [""大祭茶会""]}, {""month"": 1, ""events"": [""初釜""]}, {""month"": 3, ""events"": [""卒業茶会""]}]",sado@example.chiba-u.jp,https://example.com/chiba-sado,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_sado"", ""handle"": ""@chiba_sado""}]","[{""url"": ""https://placehold.co/800x600?text=Sado1"", ""alt"": ""お稽古風景"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Sado2"", ""alt"": ""茶会の様子"", ""order"": 1}]",false
|
| 421 |
circle-008,国際交流サークル GLOBAL,こくさいこうりゅうさーくる ぐろーばる,false,,,ROOM,CULTURE,SOCIAL,EXHIBITION,CULTURE,E,8,留学生と日本人が一緒に学び交流するサークル,留学生と日本人学生が交流するサークル。英語力向上にも!,"# 国際交流サークル GLOBAL
|
| 422 |
|
| 423 |
留学生と日本人学生が一緒に活動するサークルです。
|
|
|
|
| 483 |
|
| 484 |
*英語が苦手でも大丈夫!* みんなで助け合いながら成長しましょう。
|
| 485 |
|
| 486 |
+
詳細は[Instagram](https://instagram.com/chiba_global)をチェック!",50,,WEEKLY_1_2,,国際交流会館,3000,,"[{""month"": 4, ""events"": [""新歓パーティー""]}, {""month"": 5, ""events"": [""料理会""]}, {""month"": 7, ""events"": [""国際BBQ""]}, {""month"": 8, ""events"": [""夏旅行""]}, {""month"": 11, ""events"": [""大祭出展""]}, {""month"": 12, ""events"": [""クリスマスパーティー""]}, {""month"": 2, ""events"": [""送別会""]}]",global@example.chiba-u.jp,,"[{""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_global"", ""handle"": ""@chiba_global""}, {""type"": ""LINE"", ""url"": ""https://line.me/chiba_global"", ""handle"": ""LINE公式アカウント""}]","[{""url"": ""https://placehold.co/800x600?text=Global1"", ""alt"": ""交流イベント"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Global2"", ""alt"": ""パーティーの様子"", ""order"": 1}]",false
|
| 487 |
circle-009,写真部,しゃしんぶ,true,,,ROOM,CULTURE,VISUAL_ARTS,EXHIBITION,CULTURE,R,9,一瞬を永遠に。カメラで世界を切り取ろう,カメラと写真を愛する仲間が集まるサークルです。撮影会や展示会を定期開催。,"# 写真部
|
| 488 |
|
| 489 |
カメラを通じて世界を切り取る喜びを共有するサークルです。
|
|
|
|
| 571 |
|
| 572 |
詳細は[公式サイト](https://example.com/chiba-photo)をご覧ください。
|
| 573 |
|
| 574 |
+
お問い合わせは photo-club@example.chiba-u.jp まで!",45,学年問わず歓迎,WEEKLY_1_2,撮影会は主に土日,学生会館・学外,5000,"機材レンタル料(任意、月500円)","[{""period"": ""4月"", ""event"": ""新歓撮影会""}, {""period"": ""4月〜6月"", ""event"": ""春季撮影ツアー""}, {""period"": ""春"", ""event"": ""春季写真展準備""}, {""period"": ""5月〜6月"", ""event"": ""春季写真展""}, {""period"": ""8月"", ""event"": ""夏合宿(撮影旅行)""}, {""period"": ""11月"", ""event"": ""大祭写真展""}, {""period"": ""随時"", ""event"": ""月例撮影会""}, {""period"": ""不定期"", ""event"": ""外部コンテスト応募""}, {""period"": ""3月"", ""event"": ""卒業写真展・追いコン""}]",photo-club@example.chiba-u.jp,https://example.com/chiba-photo,"[{""type"": ""TWITTER"", ""url"": ""https://twitter.com/chiba_photo"", ""handle"": ""@chiba_photo""}, {""type"": ""INSTAGRAM"", ""url"": ""https://instagram.com/chiba_photo"", ""handle"": ""@chiba_photo""}, {""type"": ""LINE"", ""url"": ""https://line.me/ti/p/chiba_photo"", ""handle"": ""LINE公式アカウント""}, {""type"": ""YOUTUBE"", ""url"": ""https://youtube.com/@chiba_photo"", ""handle"": ""@chiba_photo""}, {""type"": ""WEBSITE"", ""url"": ""https://example.com/chiba-photo"", ""handle"": ""公式サイト""}, {""type"": ""GITHUB"", ""url"": ""https://github.com/chiba-photo"", ""handle"": ""GitHub""}]","[{""url"": ""https://placehold.co/800x600?text=Photo1"", ""alt"": ""撮影会の様子"", ""order"": 0}, {""url"": ""https://placehold.co/800x600?text=Photo2"", ""alt"": ""展示会風景"", ""order"": 1}, {""url"": ""https://placehold.co/800x600?text=Photo3"", ""alt"": ""作品例"", ""order"": 2}]",false
|