Download deploy.ja.md from NagaYu/EchoCache: direct link, hf CLI and curl.
- Browser
- Download file 18.6 kB
-
https://huggingface.co/spaces/NagaYu/EchoCache/resolve/main/deploy.ja.md
- Command line
-
hf download hf://spaces/NagaYu/EchoCache/deploy.ja.md
-
curl -L -o deploy.ja.md https://huggingface.co/spaces/NagaYu/EchoCache/resolve/main/deploy.ja.md
EchoCache デプロイ手順(Hugging Face Spaces 無料枠)
所要時間 約10分。コピペで完了します。
前提: ブラウザ、git、python3。有料プラン・GPU・モデルのダウンロードは一切不要です。
English version:
deploy.md
1. Space を新規作成する
次のとおり入力する
項目 値 Owner 自分のアカウント Space name EchoCache(任意。以降<user>/EchoCacheと表記)License apache-2.0SDK Gradio Space hardware CPU basic · 2 vCPU · 16 GB · FREE Visibility Public または Private(どちらでも動作します) Create Space を押す。空のリポジトリが作られます。
Gradio のテンプレート選択画面が出た場合は「Blank」で構いません。どうせ全ファイルを差し替えます。
2. ファイルを配置して push する
まず書き込みトークンで一度だけログインします(write 権限のトークンを https://huggingface.co/settings/tokens で発行)。
hf auth login # 古い環境では: huggingface-cli login
以下をそのまま実行してください(<user> だけ自分のアカウント名に置換)。
USER=<user>; SPACE=EchoCache; SRC="$(pwd)"
git clone https://huggingface.co/spaces/$USER/$SPACE ~/$SPACE-space && \
cp "$SRC/app.py" "$SRC/requirements.txt" "$SRC/deploy.md" "$SRC/deploy.ja.md" ~/$SPACE-space/ && \
cp "$SRC/README.gradio.md" ~/$SPACE-space/README.md && \
cd ~/$SPACE-space && git add app.py requirements.txt README.md deploy.md deploy.ja.md && \
git commit -m "Deploy EchoCache: semantic cache + cost observability" && git push
git clone で認証を求められたら、ユーザー名に自分のアカウント名、パスワードにアクセストークンを入力します。
push すると Space が自動でビルドされます(Building → Running、初回1〜2分)。 ビルドログは Space ページの右上 Logs から確認できます。
README.gradio.mdをREADME.mdとして配置する点に注意してください(公開リポジトリのREADME.mdはsdk: staticです)。
sdk_versionでビルドが失敗する場合は、README.mdのsdk_version:を Space の Settings に表示されている選択肢のバージョンへ書き換えて push し直してください。それ以外は変更不要です。
3. Secrets に HF_TOKEN を設定する(任意・設定しなくても全機能が動く)
埋め込みAPIによる再ランキングを試したいときだけ設定します。
- Space ページ → Settings → Variables and secrets
- New secret → Name:
HF_TOKEN/ Value: 自分のトークン → Save (HUGGINGFACE_HUB_TOKEN/HUGGINGFACEHUB_API_TOKENも同じ意味で読み取られます。 ローカル実行時にこれらをシェルに export していると、意図せずlocal+embeddingで起動して 外部通信が発生します。UIヘッダーとHealthタブのmodeで必ず確認してください) - Restart this Space
| HF_TOKEN なし | HF_TOKEN あり | |
|---|---|---|
| 類似判定 | ローカル完結(既定・推奨) | ローカル + 埋め込みの加重平均 |
| 主機能 | すべて動作 | すべて動作 |
| 失敗時 | — | 402/429/タイムアウトでも自動でローカルのみに戻る |
無料アカウントの推論クレジットは月 $0.10 程度しかありません。設定しない運用が既定だと考えてください。
UI のヘッダーに local-only / local+embedding のどちらで動いているかが常時表示されます。
同じ画面で任意の環境変数も設定できます(よく使うもの):
PRICE_IN_PER_1K = 0.003 # 自社の入力単価。設定しないと節約額は $0 のまま
PRICE_OUT_PER_1K = 0.015 # 自社の出力単価
MAX_ENTRIES = 5000 # テナント毎の上限
MAX_TOTAL_ENTRIES = 20000 # 全テナント合計の上限(メモリの実質的な歯止め)
MAX_TENANTS = 256 # 区画数の上限。超えると最も古いテナントの区画が丸ごと消える
DEFAULT_THRESHOLD = 0.92 # 既定は保守的。Sweep を見てから動かす
4. 動作確認(UI で3分)
Space を開く(
https://<user>-echocache.hf.space)。ヘッダーにMode: local-onlyと出ていれば正常です。Store で1件登録
Storeタブ →tenant_idにacme、routeにsupportprompt:How do I reset my password?response:Open Settings > Security > Reset password.- Store を押す →
"stored": trueとentry_idが返ります。 - (
Seed 3 demo entriesボタンで3件まとめて投入することもできます)
Lookup でヒットを確認
Lookupタブ →tenant_idにacmeprompt:Hi team, how do I reset my password? Thanks in advance!- Lookup →
"hit": true, "stage": "exact"。挨拶と結びが正規化で除去され、完全一致キーに落ちています。 - 次に
How can I reset my password?を threshold0.80で試すと"stage": "cosine"のヒットになります(実測の類似度は約 0.85。既定の0.92のままだと miss になり、best_similarityに 0.85 が返ります。この数字を見てから閾値を決めるのが正しい手順です)。 - 誤ヒット防止の確認:
Why do I need to reset my password?を threshold0.70で実行 →"reason": "guard_rejected"、guard_reasonsにquestion_type_mismatch:q={why},c={how}。類似度が高くてもヒットしないことが確認できます。 - テナント分離の確認:
tenant_idをglobexに変えて同じ質問 →"reason": "empty_index"(他テナントの登録内容は一切見えません)。
Dashboard で記録を確認
Dashboardタブ → Refreshrequests / hits / misses / hit rate / saved tokens / estimated savingの1行サマリ、ヒット率の時系列、テナント別の累積節約、p90レイテンシ、再利用上位、索引の健全性が出ます。- Export events CSV でイベントログを持ち出せます(既定ではプロンプト本文は記録されません)。
Sweep で閾値を決める
Sweepタブ → Download a sample log でサンプルCSVを取得 → そのままrequest log CSVにアップロード- 単価(
price in/out)を入れて Run sweep - 「この閾値なら何%再利用でき、いくら浮くか」が文章と表とグラフで出ます。MismatchGuard on/off の2本が並ぶので、保守的な設定でいくら浮くかがそのまま稟議資料になります。
実際のAPIパスを取得
API Docsタブ: 稼働中のサーバーから組み立てた実パス(curl と gradio_client)が表示されます。- 画面最下部の Use via API リンク(Gradio 自身が生成)が常に正典です。
CLI で確認したい場合
SPACE="https://<user>-echocache.hf.space"
EVENT_ID=$(curl -s -X POST "$SPACE/gradio_api/call/store" -H "Content-Type: application/json" \
-d '{"data": ["acme","How do I reset my password?","Open Settings > Security > Reset password.","support",86400]}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['event_id'])")
curl -s -N "$SPACE/gradio_api/call/store/$EVENT_ID"
EVENT_ID=$(curl -s -X POST "$SPACE/gradio_api/call/lookup" -H "Content-Type: application/json" \
-d '{"data": ["acme","Hi team, how do I reset my password? Thanks!",0.92,"support"]}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['event_id'])")
curl -s -N "$SPACE/gradio_api/call/lookup/$EVENT_ID"
pip install gradio_client
python3 - <<'PY'
from gradio_client import Client
c = Client("<user>/EchoCache")
print(c.predict("acme", "how do i reset my password", 0.92, "support", api_name="/lookup"))
print(c.predict(api_name="/health"))
PY
5. 運用の型
5-1. スリープ前にエクスポート、復帰後にインポート
無料枠はディスク非永続・48時間無操作でスリープ(スリープ時間は変更不可)。索引はメモリ上にしかないので、明示的に持ち出すのが運用の型です。
Backupタブ →include response bodiesにチェック → Export indexindex.jsonをダウンロードして手元(または S3 / Drive / リポジトリ外の安全な場所)に保管- スリープや再起動のあと、Space を開く →
Backupタブ →index.jsonをアップロード → Import index - 結果の
imported / skipped_no_response / expired / rejected_by_safetyを確認
- インポート時はベクトルを再計算するので、
VECTOR_DIMを変えていても取り込めます。 - インポート時に安全検査が再実行されます(
skip_safetyは原則オフのまま)。 - 応答本文を含めずにエクスポートしたファイルは、分析用です。取り込んでもキャッシュとしては使えません(
skipped_no_responseに計上されます)。
日次でバックアップしたいなら、手元から叩くだけです:
python3 - <<'PY'
from gradio_client import Client
import datetime
c = Client("<user>/EchoCache")
path, receipt = c.predict(True, "", api_name="/export_index")
dst = "echocache-%s.json" % datetime.date.today()
open(dst, "w").write(open(path).read())
print(dst, receipt)
PY
5-2. 閾値は Sweep の結果を見てから決める
- 既定の
0.92は保守的です。この値では主に「完全一致+表記ゆれ(挨拶・敬語・句読点・大文字小文字)」が再利用されます。 - 言い回しが変わる本当の言い換えは、文字 n-gram では 0.80〜0.90 に落ちるのが普通です。
- 本番のログを1日分 CSV(
tenant_id, prompt, response, route)にして Sweep にかけ、再利用率・節約額・境界ヒット件数・Guard 棄却件数を見てから決めてください。勘で下げないこと。 - 下げる前に、まず
Auditタブで Guard の棄却理由を見ます。「棄却が多い=閾値が高すぎる」とは限らず、「そもそも意味が違う質問が多い」だけのこともあります。 - 公開ベンチマーク(
echocache-guard-benchmark)では 閾値0.80 + Guard有効が正答率99.2%・再利用率100%・誤再利用1/90でした。ただしこれは合成データ1セットの結果です。自分のログで測ってください。
5-3. 定期的に見る場所
| 頻度 | 見るもの | 判断 |
|---|---|---|
| 毎日 | Dashboard の hit rate / saved cost | 期待どおり伸びているか |
| 毎日 | Audit の Guard rejections | 棄却理由の傾向。誤ヒットの予兆 |
| 週次 | Dashboard の索引健全性(evicted / expired) |
追い出しが多いなら TTL か上限を見直す |
| 週次 | Sweep を最新ログで再実行 | 閾値の再確認 |
| 変更時 | Health の mode |
local-only から意図せず変わっていないか |
6. よくある失敗と対処
メモリを食う / Space が落ちる
索引メモリはほぼ エントリ数 × VECTOR_DIM × 4 バイトで、**MAX_ENTRIES はテナント毎**です。既定では 1テナント満杯で約 80 MB、全体は MAX_TOTAL_ENTRIES=20000 で約 330 MB に抑えられています。
対処(Settings → Variables and secrets、上から順に効きます):
MAX_TOTAL_ENTRIES = 8000 # 全体の歯止め。まずここを下げる
MAX_ENTRIES = 2000 # テナント毎の上限
MAX_TEXT_CHARS = 4000 # 1エントリの保存文字数
VECTOR_DIM = 2048 # メモリ半減。精度低下はわずか
EVENT_BUFFER = 2000 # イベントのリングバッファ
Health タブの approx_memory_mb と vector_matrix_mb_per_full_tenant で実測値を確認できます。
VECTOR_DIM を変えたら、既存のエクスポートは再インポートで作り直されます(ベクトルは再計算されるので互換性の心配は不要です)。
保存したはずのテナントが空になっている
テナント数が MAX_TENANTS(既定256)に達すると、新しいテナントの初回書き込み時に、
最も長く使われていないテナントの区画が丸ごと破棄されます(削除のみで、テナント間でデータが
混ざることはありません)。実際のテナント数より十分大きい値を設定してください。
全体のエントリ数が MAX_TOTAL_ENTRIES に達した場合は、最大の区画から1件ずつ古い順に追い出されます。
ヒットしない
下げる前に順番に確認してください。
Lookupの結果のreasonを見るempty_index… そのテナントに1件も入っていない(tenant_idの打ち間違いが最多)no_candidates… SimHash の帯に候補なし。文面が大きく違うbelow_threshold…best_similarityが出ます。この数字を見てから閾値を判断するguard_rejected… 意味が違うと判定された。次を見る
Auditタブで Guard の棄却理由を見る(numeric_mismatch/negation_mismatch/proper_noun_mismatch/temporal_mismatch/question_type_mismatch)。理由が妥当なら、それは正しい miss です。閾値を下げても直りません。- 正規化の効きを確認する:
StoreしたプロンプトとLookupのプロンプトが、挨拶・敬語・記号を除いて本当に同じか。cached_prompt_previewが返ってくるので見比べられます。 - それでも取りこぼしているなら、Sweep で 0.86 → 0.82 のように段階的に下げ、
borderline_hitsとguard_blocksの増え方を見る。 - 特定の検査が業務に合わないと確信できた場合のみ、
GUARD_DISABLE=proper_nounのように個別に外す(全部外すのは非推奨)。
誤ヒットが出た(最優先で対処)
- 閾値を上げる(例:
0.92→0.95)。即効性があります。 Auditタブ、またはDashboard→ most reused entries で該当のentry_idを特定する。Auditタブ下部の Invalidate entry にtenant_idとentry_id(先頭16文字で十分)を入れて失効させる。- ルート単位なら
key_prefixにルート名(例:support) - テナント全体なら
*
- ルート単位なら
- 同じ型の誤ヒットが再発するなら、その事例を CSV に追加して Sweep を回し直し、その型を弾ける閾値を採用する。
- 意味の差が Guard の5種(否定・数量・固有名詞・時制・疑問種別)で説明できない型なら、その質問カテゴリはキャッシュ対象から外す(
routeを分けて、その route を定期的にinvalidateする運用が簡単です)。
埋め込みAPIが 402 / 429 になる
402 Payment Required… 無料枠の推論クレジット(月 $0.10 程度)が尽きています。429 Too Many Requests… レート制限。
HF_TOKENを消したのにlocal+embeddingのままなら、HUGGINGFACE_HUB_TOKEN/HUGGINGFACEHUB_API_TOKENが残っていないか確認してください(同じ意味で読まれます)。
どちらでも主機能は止まりません。 例外は握りつぶされ、ローカルベクトルのみで判定が続きます。連続失敗が閾値を超えるとサーキットブレーカーが開き、EMBED_COOLDOWN_SEC(既定600秒)は呼び出しを止めて無駄な課金を防ぎます。
Health タブの embedding.last_error と cooldown_remaining_sec で状態を確認できます。恒久的に止めるなら Secrets から HF_TOKEN を削除して Restart してください(local-only に戻ります)。
Space が 48時間で寝てしまう / 起きたら索引が空
無料枠の仕様です(スリープ時間は変更不可、ディスクは非永続)。5-1 の運用(エクスポート→インポート)で引き継いでください。 常時起動が必要なら有料ハードウェアか、自前ホストへ同じ app.py を置くことになります(python app.py で動きます)。
/store が stored: false で返る
安全検査で弾かれています。reason を見てください。
| reason | 意味 |
|---|---|
credit_card_luhn |
Luhn 検証を通った数字列(カード番号の可能性)。注文番号などは通過します |
credential_prefix |
sk- / ghp_ / AKIA / xoxb- / AIza / hf_ などの資格情報 |
jwt_structure |
JWT の三部構造 |
high_entropy_secret |
長い高エントロピー英数字列(識別子らしい構造のものは除外されます) |
contact_pii_excess |
メールアドレスや電話番号が規定数を超過 |
private_key_block |
PEM 秘密鍵ブロック |
これは正常動作です。 秘密情報をキャッシュに入れないための最後の防波堤なので、閾値を緩める前に、そもそもその本文をキャッシュに入れてよいのかを検討してください。
API のパスが見つからない / 404
Gradio のバージョンでパスが変わります(Gradio 5・6 は /gradio_api/call/<name>、4 は /call/<name>)。
ハードコードせず、API Docs タブか画面最下部の Use via API リンクから取得してください。gradio_client を使えばパスの違いは吸収されます。
付録: ローカルで動かす
pip install -r requirements.txt
python3 app.py # http://localhost:7860
Space と同じ挙動です。環境変数はそのままシェルで渡せます:
PRICE_IN_PER_1K=0.003 PRICE_OUT_PER_1K=0.015 MAX_ENTRIES=1000 python3 app.py