Spaces:
Sleeping
A newer version of the Streamlit SDK is available: 1.56.0
Hugging Face Spaces デプロイ設計
1. 目標とスコープ
デモアプリ(demo/app.py)を Hugging Face Spaces(公開)で動かす。
スコープ: サンプルクエリ専用モード
demo/data/samples.jsonに事前計算済みのクエリのみ動作する- 「推論実行」ボタンは FAISS・Wikipedia DB を使わずに事前計算済み結果を即時表示
- 「回答を生成」ボタン(LLM 再統合)は OpenAI API を呼ぶため、API キーが必要
- ライブ推論(任意クエリ入力)は行わない
Spaces の制約
- Singularity は使えない(Docker / Python 環境)
- リポジトリに大きなバイナリファイル(FAISS インデックス、SQLite DB)は置けない
- 環境変数(Secrets)で API キーを設定できる
2. 現行コードの移植可否分析
| 処理 | Spaces での動作 | 対応方針 |
|---|---|---|
samples.json / thresholds.csv の読み込み |
○ そのまま動く(リポジトリ内) | 変更不要 |
| 「推論実行」→ 事前計算済み結果の表示 | ○ API 呼び出しなし | 変更不要 |
「回答を生成」(reintegrate_subclaims) |
○ OpenAI API のみ | Secrets に OPENAI_API_KEY を設定 |
build_faiss_manager() / build_scorer() |
△ ファイルが存在しないためエラー | ライブ推論パスに到達しなければ呼ばれない(後述) |
@st.cache_resource でのリソース初期化 |
△ 呼ばれた時点でエラー | 環境フラグで無効化 |
.env の読み込み |
△ Spaces では .env ファイルは使わない |
Spaces Secrets → 環境変数として自動注入 |
ライブ推論パスが呼ばれる条件
現在のコードでは、build_faiss_manager() / build_scorer() はライブ推論パス内でしか呼ばれない。
# app.py 内の live inference ブランチ(sample_map にないクエリのみ)
faiss_manager = get_faiss_manager(dataset) ← ここでエラーになる
scorer = get_scorer(dataset)
サンプルクエリのプルダウンには samples.json 内のクエリしか表示されないため、
通常操作では このパスには到達しない。
ただし、予期しないエラー時のフォールバックや将来の機能追加を考慮し、
環境変数フラグ SPACES_DEMO=1 でライブ推論パスを明示的に無効化する。
3. 必要な変更
3-1. app.py の変更(最小限)
SPACES_DEMO=1 が設定されている場合、ライブ推論パスをブロックする。
import os
SPACES_DEMO = os.getenv("SPACES_DEMO", "0") == "1"
「推論実行」ボタン処理内:
if run_btn and query_input:
precomputed = sample_map.get(query_input)
if precomputed is not None and ...:
# 事前計算済み → そのまま表示(変更なし)
...
elif SPACES_DEMO:
st.error("このデモではサンプルクエリのみ対応しています。")
else:
# ライブ推論(ローカル環境のみ)
...
3-2. Spaces 用設定ファイル
Spaces は リポジトリの README.md(frontmatter) で設定を宣言する。
---
title: Response Quality Assessment Demo
emoji: 📊
colorFrom: blue
colorTo: green
sdk: streamlit
sdk_version: 1.43.2
app_file: demo/app.py
pinned: false
---
app_file に demo/app.py を指定することでリポジトリ構造を変えずに済む。
3-3. requirements.txt の整理(実装済み)
Spaces はリポジトリルートの requirements.txt を自動で pip install する。
リポジトリでは以下の構成を採用している:
| ファイル | 用途 |
|---|---|
requirements.txt |
HF Spaces 向け最小セット。Spaces はこのファイルを自動で読む |
requirements-dev.txt |
ローカル・Singularity 向け全依存。-r requirements.txt で共通部分を継承 |
requirements.txt(Spaces 向け最小セット、§4-1 調査結果に基づく):
openai>=2.0
python-dotenv>=1.0
numpy>=1.24
pandas>=2.0
pyyaml>=6.0
streamlit>=1.43
faiss-cpu / torch / transformers / sentence-transformers / langchain-core /
scikit-learn はいずれも不要(遅延 import により Spaces モードでは読み込まれない)。
ローカル開発時は pip install -r requirements-dev.txt を使う。
3-4. Secrets の設定
Spaces の Settings > Variables and secrets に以下を追加する:
| キー | 値 | 用途 |
|---|---|---|
OPENAI_API_KEY |
sk-... |
「回答を生成」ボタン(reintegrate_subclaims) |
SPACES_DEMO |
1 |
ライブ推論パスを無効化 |
.env ファイルは Spaces 環境では使わない(python-dotenv の load_dotenv() は
環境変数が既にセットされていれば上書きしないため、ローカルとの互換性は保たれる)。
4. 事前調査結果
4-1. src/ の import チェーンの影響調査(済)
sys.modules を import 前後で比較し、重い依存パッケージの混入を確認した。
原因: src.common.faiss_manager → src.common.file_manager → langchain_text_splitters
が torch / transformers / sentence_transformers 等を連鎖的に引き込む。
src.subclaim_processor.scorer.subclaim_scorer も同様。
対処: FAISSIndexManager / SubclaimScorer の import を build_faiss_manager() /
build_scorer() 関数内に移動(遅延 import)。型ヒントは TYPE_CHECKING ガードで維持。
これにより、サンプルクエリ専用モードでは import 時に重い依存が読み込まれない。
Spaces 向け最小パッケージセット(build_*() が呼ばれない前提):
openai
python-dotenv
numpy
pandas
pyyaml
langchain-core
streamlit>=1.43
faiss-cpu / torch / transformers / sentence-transformers / scipy /
scikit-learn は不要。
4-2. DATA_ROOT / config パスの解決(済)
_load_main_config() / _load_dataset_config() は関数呼び出し時に初めて実行される
(モジュールレベルでは実行されない)ため、DATA_ROOT 未設定でも import は成功する。
サンプルクエリ専用モードではこれらの関数は呼ばれないため問題なし。
5. デプロイ手順
HF Space の作成
huggingface.co/spaces/<username>/<space-name>を新規作成(SDK: Streamlit)
spacesremote を追加git remote add spaces https://huggingface.co/spaces/EQUES/Response-Quality-Assessment認証はリモート URL にトークンを埋め込む方法を使う(
.git/configは.gitignore対象外だがgitの管理ファイルであり GitHub にはプッシュされない):git remote set-url spaces https://<username>:<hf_token>@huggingface.co/spaces/EQUES/Response-Quality-AssessmentOrphan ブランチで push
HF Spaces はプッシュ時に 全コミット履歴 をスキャンし、10 MiB 超のファイルを拒否する。
feature/hf-spacesの祖先コミットに大きなデータファイル(data/out/Medlfqav2/等)が含まれるため、 履歴なしの Orphan ブランチを一時作成してから push する:git checkout --orphan spaces-deploy git add -A git rm --cached data/out/ data/raw/ -r --ignore-unmatch git commit -m "deploy: initial push to HF Spaces" git push --force spaces spaces-deploy:main git checkout feature/hf-spaces git branch -D spaces-deployなぜ Orphan か: 通常の push では過去コミットごと送られる。Orphan ブランチは親コミットが 存在しない「起点」なので、HF Spaces には現在のスナップショット 1 コミットだけが届く。 ローカルと GitHub の履歴には一切影響しない。
Secrets の設定
- Space の Settings > Variables and secrets に以下を追加:
キー 値 OPENAI_API_KEYsk-...SPACES_DEMO1動作確認
- サンプルクエリの表示・スライダー操作
- 「回答を生成」ボタン(OpenAI API 呼び出し)
再デプロイ(コード変更後)
コードを変更したら同じ Orphan 手順を繰り返す。spaces remote は設定済みのため手順 2 は不要。
6. 既知の問題・注意点
MedLFQA Marginal モードのサンプル一致
precompute.py は grouped データセット(medlf_qa)のサンプルを全て mode="conditional" で
生成するため、samples.json に mode="marginal" エントリが存在しない。
app.py では Marginal モード選択時にモード不一致でサンプルが見つからない問題を回避するため、
Marginal モードではサンプルの mode/group フィールドを無視してマッチさせる:
if (
precomputed is not None
and (
mode == "marginal"
or (precomputed["mode"] == mode and precomputed["group"] == group)
)
):
サブクレームスコア自体はモードに依存しないため、閾値(_lookup_q_hat が marginal 用 q_hat を
参照)との比較は正しく行われる。
7. 開発ステップ
| ステップ | 状態 | 担当ファイル |
|---|---|---|
| ① import 影響調査 | 完了 | — |
| ② requirements 整理 | 完了 | requirements.txt, requirements-dev.txt |
③ app.py にフラグ追加 |
完了 | demo/app.py |
④ README.md の作成 |
完了 | README.md |
| ⑤ Spaces へ push | 完了 | — |
| ⑥ Marginal モードのバグ修正 | 完了 | demo/app.py |