yukikase Claude Opus 4.6 commited on
Commit
79f4b6a
·
1 Parent(s): 53493f4

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 CHANGED
@@ -1,7 +1,8 @@
1
  # 千葉大祭 企画情報検索システム - 完全ガイド
2
 
3
- **作成日**: 2025年12月11日
4
- **対象**: 大学1~2生向けのシステム解説
 
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
- │ 検索エンジン │ │ MySQLデータース
65
- │ (search/engine) │ │ (企画データ)
66
  └──────────────────┘ └──────────────────┘
67
 
68
  ┌────────────────────────────────────┐
69
  │ 静的アセット(JSONファイル等) │
70
- │ - projects.json (企画データ)
71
  │ - bm25_meta.json (検索用統計) │
72
  │ - synonyms_cache.json (同義語) │
73
  │ - word_vectors.npz (単語ベクトル) │
@@ -77,19 +78,19 @@
77
  **データの流れ:**
78
 
79
  1. **初期化時**(サーバー起動)
80
- - MySQLから最新の企画データ取得
81
  - 検索用の各種アセット(JSON、ベクトルなど)をメモリに読み込み
82
  - 検索エンジンを初期化
83
 
84
  2. **検索時**(ユーザーがキーワード入力)
85
  - キーワードを形態素解析(単語に分割)
86
- - BM25F + 単語ベクトル で各企画のスコアを計算
87
  - スコアの高い順に結果を返す
88
 
89
- 3. **企画データ更新時**
90
- - MySQLの企画情報を更新
91
  - スクリプトで検索用アセットを再生成
92
- - サーバーを再起動
93
 
94
  ---
95
 
@@ -102,47 +103,43 @@ circle-search-26-api/
102
  │ ├── search/
103
  │ │ └── engine.py # 検索エンジン実装
104
  │ ├── repositories/
105
- │ │ ── projects_repository.py # DB連携(データ取得
106
- └── query.sql # SQL クエリ
107
- ├���services/
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
- │ ├── 2_create_projects_data.py # DBprojects.json
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
- │ └── projects.py # Project, ProjectSummary
131
 
132
  ├── resources/ # 入力素材
 
133
  │ ├── stopwords.json # ストップワード(検索対象外の語)
134
  │ ├── synonyms_custom.json # カスタム同義語辞書
135
  │ ├── user_dict.csv # Sudachi ユーザー辞書
136
  │ └── embeddings/ # 単語ベクトル(fastText)
137
 
138
  ├── data/generated/ # 自動生成されるファイル
139
- │ ├── projects.json # 企画データ(検索用)
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/projects`: 全企画の一覧を取得
166
- - `GET /api/details?projectId=xxx`: 特定の企画の詳細を取得
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
- │ 各企画に対して3つのスコアを計算
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
- - 例:「カレー」という企画説明に「カレー」という単語が5回出現→高いTF
281
 
282
- 3. **フィールド重み**: 企画名に出現する単語と、長い説明文に出現する単語では重要度が異なる
283
- - 例:企画名 (重み 2.0) > 説明文 (重み 1.0)
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
- 団体名が「写真部」に完全一致 → +1.0点
320
  ```
321
 
322
  **3段階の加点ルール:**
323
- - **完全一致** (例:入力 "写真部" = 団体名 "写真部") → 加点: 1.0
324
- - **前方一致** (例:入力 "写真" = 団体名 "写真部" の前半) → 加点: 0.7
325
- - **部分一致** (例:入力 "写" = 団体名 "写真部" に含む) → 加点: 0.5
326
 
327
  **カスタマイズ方法:** `config/search_model.json` の `circleName.boost` で調整可能(後述)
328
 
@@ -330,16 +327,16 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
330
 
331
  ### 4.6 スコア統合と閾値フィルタリング
332
 
333
- **概念:** 複数の指標(BM25F、ベクトル類似度、団体名加点)を1つのスコアに統合します。
334
 
335
  **統合式(概略):**
336
  ```
337
- ��終スコア = α × BM25F + (1-α) × 単語ベクトル類似度 + 団体名加点
338
  ```
339
 
340
  ここで α はウェイト(デフォルト 0.5 = 両者を同じ重要度とみなす)
341
 
342
- **フィルタリング:** スコアが一定以下の企画は結果から除外します。ノイズ除去と計算効率化のため。
343
 
344
  ---
345
 
@@ -414,12 +411,12 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
414
  | 項目 | 説明 | 調整例 |
415
  |------|------|--------|
416
  | `target_pos_l1` | トークン化時に対象とする品詞 | 「助詞」を追加したい場合は配列に追加 |
417
- | `target_fields` | 検索対象にするフィールド | `prDetail`を除きたい場合は削除 |
418
  | `synonyms.enable` | 同義語展開の有無 | `false`で無効化 |
419
  | `synonyms.limits.max_expansions_per_term` | 1つの単語から展開する同義語の最大数 | 値を大きくするほど多くの同義語を使用 |
420
- | `bm25f.field_weights` | 各フィールドの重要度 | 企画名(name)を重視したい場合、値を大きく(例:3.0) |
421
  | `word_sim.alpha` | BM25FとベクトルのウェイトMIX | 0.5 = 両者を同じ重要度。BM25Fを重視したい場合は0.7に |
422
- | `circleName.boost.exact` | 団体名完全一致の加点 | 団体名マッチを重視したい場合は2.0に |
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
- "projects": {
445
- "projects_json": "data/generated/projects.json"
 
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
- "AI": ["人工知能", "機械学習"]
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 を読み込み(企画データ、BM25F統計など)
516
  ├─ resources/embeddings/ から単語ベクトルを読み込み
517
  └─ Sudachi形態素解析器を初期化
518
 
@@ -520,9 +518,9 @@ BM25F、単語ベクトル、同義語など各種手法を組み合わせて検
520
  フロントエンドからのリクエスト受付開始
521
  ```
522
 
523
- ### 6.2 企画データの更新フロー
524
 
525
- **状況:** MySQL企画データが更新された
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. MySQLのデータ確認
584
 
585
  2. スクリプト実行: python scripts/build_all.py
586
  ├─ Step 0: fastText ベクトルをダウンロード(初回のみ)
587
  ├─ Step 1: Sudachi ユーザー辞書を構築(オプション)
588
- ├─ Step 2: DBprojects.json 生成
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/2_create_projects_data.py` - DB → JSON変換
603
  ```
604
- MySQL から企画データを取得
605
- ProjectRepository.list_projects()
606
- 各種テキスト(説明文など)更新情報から最新版を抽出
607
- projects.json出力
 
608
  ```
609
 
610
- このステップ以降のデータは、すべてこの `projects.json` から生成されます。
611
 
612
  #### Step 3: `scripts/3_build_synonyms_from_sudachi.py` - 同義語キャッシュ
613
  ```
614
- projects.json の全テキストをトークン化
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
- projects.json をトークン化
625
  → IDF(珍しさスコア)を計算
626
  → 各フィールドの平均トークン数を計算
627
  → bm25_meta.json に保存
628
 
629
- 企画ごとのTF(単語の出現数)を計算
630
  → tf_token.json に保存
631
  ```
632
 
@@ -643,12 +642,12 @@ tf_token.json から語彙リストを取得
643
  |---------|---------|---------|
644
  | 0 | fastText ダウンロード | 初回のみ数分 |
645
  | 1 | Sudachi辞書構築 | 1-2分 |
646
- | 2 | DB→JSON | 数秒 |
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/projects"
677
  ```
678
 
679
  **認証方法:**
680
  - ヘッダ `X-API-KEY` に `API_SECRET_KEY` 環境変数の値を指定
681
  - 環境変数は `.env` ファイルに保存(リポジトリには含めない)
682
 
683
- **レスポンス(gzip圧縮):**
684
  ```json
685
  [
686
  {
687
- "projectId": "62000A",
688
  "circleName": "写真部",
689
- "name": "写真展覧会",
690
- "projectType": "テント企画",
691
- "category": "展示",
692
- "day1": true,
693
- "day2": true,
694
- "day3": false,
695
- "location": "第1キャンパス A-1",
696
- "description": "写真部による年間作品展...",
697
- "prSummary": "写真の美しさを...",
698
- "remark": null,
699
- "image": {
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?projectId=62000A"
714
  ```
715
 
716
- ��スポンス:
717
  ```json
718
  {
719
- "projectId": "62000A",
720
  "circleName": "写真部",
721
- "name": "写真展覧会",
722
- ...
723
- "prDetail": "詳細なPR文が入ります...",
 
 
 
 
 
724
  "images": [
725
  {
726
- "filename": "photo1.jpg",
727
- "extension": "jpg",
728
  "order": 0
729
  },
730
  {
731
- "filename": "photo2.jpg",
732
- "extension": "jpg",
733
  "order": 1
734
  }
735
  ],
736
- "socialMedia": [
 
 
 
 
 
 
 
 
 
 
737
  {
738
- "service": "Instagram",
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
- "projectIds": ["12345A", "67890B", "11111C"]
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": "カレー", "debug": true}' \
767
  "http://localhost:7860/api/search"
768
  ```
769
 
770
  **レスポンス(デバッグモード):**
771
  ```json
772
  {
773
- "projectIds": ["12345A", "67890B"],
774
  "scores": [
775
  {
776
- "projectId": "12345A",
777
  "score": 2.15
778
  },
779
  {
780
- "projectId": "67890B",
781
  "score": 1.83
782
  }
783
  ],
784
  "details": [
785
  {
786
- "projectId": "12345A",
787
- "circleName": "飲食A",
788
- "name": "カレー販売",
789
  "bm25": 0.85,
790
  "ws_filter_topk": 0.72,
791
  "ws_rerank_pairavg": 0.75,
792
- "org_boost": 0.0,
793
- "matched_substring": false,
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
- **用途:** MySQLの企画データが更新された時に、このエンドポイントを呼び出すと、検索用アセット(`data/generated/` 配下のファイル)が自動的に再生成されます。
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
- 通常の開発では、MySQLのデータが更新されたことをシミュレートして検索エンジンをテストしたい場合があります。その際は:
891
 
892
  ```bash
893
  # 方法1: 手動でスクリプト実行
@@ -921,7 +983,7 @@ docker run -e DATABASE_URL="..." \
921
 
922
  **原因候補:**
923
  1. `data/generated/` のファイルが不足している
924
- 2. MySQLのデータが空である
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
- "name": 3.0, // ← 2.0 から 3.0 に変更
986
- "circleName": 1.5,
987
- "description": 1.0,
988
  ...
989
  }
990
  }
 
1
  # 千葉大祭 企画情報検索システム - 完全ガイド
2
 
3
+ **作成日**: 2025年12月11日
4
+ **最終更新**: 20262月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 # CSVcircles.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: CSVcircles.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