Files

176 lines
6.8 KiB
Markdown

# ASMR Vector Browser
`asmr_works.jsonl` からベクトル検索データを生成し、近い雰囲気のASMR作品を閲覧するWebUIです。
## データ生成
依存なしで動くローカルハッシュベクトルを生成します。
```bash
uv run python scripts/build_vector_data.py --input asmr_works.jsonl --output-dir web/data
```
意味ベクトルで生成したい場合は `semantic` extra を有効にして実行します。
```bash
uv run --extra semantic python scripts/build_vector_data.py --method sentence-transformers --model intfloat/multilingual-e5-small
```
`intfloat/multilingual-e5-small` を使う場合は、生成スクリプト側で自動的に `passage:` prefix を付けます。
OpenAI互換のリモート埋め込みAPIで生成する場合:
```bash
uv run --extra remote python scripts/build_vector_data_remote.py
```
APIキーが必要な場合は `EMBEDDING_API_KEY` に設定します。
OpenAI互換APIで生成する場合:
```bash
EMBEDDING_API_KEY="..." uv run python scripts/build_vector_data_remote.py \
--input asmr_works.jsonl \
--output-dir web/data \
--base-url https://reasonable-consensus-reproduce-cute.trycloudflare.com \
--model text-embedding-qwen3-embedding-8b \
--batch-size 32 \
--concurrency 10
```
認証不要のAPIなら `EMBEDDING_API_KEY` は省略できます。小件数で試す場合は `--limit 10` を付けます。
APIキーはコードに書かず、環境変数で渡してください。
`--concurrency` は同時リクエスト数です。API側が重い場合は `2` から `4` 程度に下げてください。
remote生成では、通信切断やレスポンス途中切れが起きたbatchをデフォルトで成功するまで再試行します。止めたい場合は `Ctrl+C`、有限回で諦めたい場合は `--no-retry-forever --retries 3` を指定します。
## 日次データ更新
最新順APIから新規作品だけを取得し、ローカルJSONL、フィルタ済みJSONL、`web/data` を更新します。
```bash
uv run python scripts/update_daily_asmr_data.py
```
`web/data` は `manifest.json` の `remote-openai-compatible` 設定を引き継ぎます。既存作品のembeddingは再利用し、新規または検索テキストが変わった作品だけembedding APIへ送ります。
ファイルを書かずに確認する場合:
```bash
uv run python scripts/update_daily_asmr_data.py --dry-run
```
dry-runでは新規作品、既存作品の更新、フィルタ差分、embedding APIへ送る作品と理由を表示します。各セクションの表示件数を変える場合:
```bash
uv run python scripts/update_daily_asmr_data.py --dry-run --diff-limit 100
```
全件表示する場合は `--diff-limit 0` を使います。
新規作品がなくてもフィルタと `web/data` を作り直す場合は `--force` を付けます。
## pull後のベクトル差分更新
Actions が更新した `asmr_works.jsonl` / `asmr_works.filtered.jsonl` を pull した後、ローカルの `web/data` だけを差分更新します。
```bash
git pull --rebase --autostash
uv run --extra remote python scripts/update_pulled_vector_data.py
```
このスクリプトは `asmr_works.filtered.jsonl` と `web/data/works.json` を比較し、既存作品のembeddingは `web/data/embeddings.f32` からコピーして再利用します。新規作品、またはタイトル・タグ・声優が変わった作品だけembedding APIへ送ります。
ファイルを書かずに確認する場合:
```bash
uv run --extra remote python scripts/update_pulled_vector_data.py --dry-run
```
APIキーが必要な場合は `EMBEDDING_API_KEY` に設定します。接続先やモデルは `web/data/manifest.json` の `remote-openai-compatible` 設定を使います。
## 翻訳重複の修復
日本語原作と翻訳版が両方 `asmr_works.filtered.jsonl` / `web/data` に入ってしまった場合は、自動検出して翻訳版だけ除外できます。
```bash
uv run python scripts/prune_duplicate_works.py --dry-run
```
問題なければ実更新します。
```bash
uv run python scripts/prune_duplicate_works.py
```
このスクリプトは `asmr_works.jsonl` には触らず、`asmr_works.filtered.jsonl`、`web/data/works.json`、`web/data/embeddings.f32`、`web/data/manifest.json` を更新します。`embeddings.f32` は既存ベクトルをコピーして再構築するため、embedding APIには送信しません。
## WebUI起動
WebUIはAstroで動きます。初回は依存関係を入れます。
```bash
cd web
npm install
```
APIなしで静的UIだけ開発サーバーを起動する場合:
```bash
cd web
npm run dev
```
ブラウザで `http://127.0.0.1:4321` を開きます。
ビルドする場合:
```bash
cd web
npm run build
```
## サーバー計算つきで起動
ローカルの `sentence-transformers` で生成した `web/data` を検索する場合:
```bash
cd web && npm run build && cd ..
uv run --extra semantic python scripts/search_server.py
```
リモート埋め込みAPIで生成した `web/data` を検索する場合:
```bash
cd web && npm run build && cd ..
uv run --extra remote python scripts/search_server.py
```
APIキーが必要な場合:
```bash
EMBEDDING_API_KEY=xxx uv run --extra remote python scripts/search_server.py
```
このサーバーは起動時に `web/data/works.json` と `web/data/embeddings.f32` を読み込みます。ブラウザはこれらのファイルを直接読み込まず、作品一覧は `/api/works`、作品詳細は `/api/work`、自然文検索は `/api/search`、作品ページのMyDNAは `/api/similar` から取得します。
リモート検索では `web/data/manifest.json` の `baseUrl` と `model` を使って、検索文を同じOpenAI互換APIへ送ります。必要なら起動時に上書きできます。
```bash
uv run --extra remote python scripts/search_server.py --remote-base-url https://example.com --remote-query-prefix "query: "
```
ブラウザで `http://127.0.0.1:8000` を開くと、トップページには最近追加されたASMRが表示されます。検索欄に自然文を入れて「自然文検索」を押します。
検索結果の作品を開くと `/work/?id=RJxxxx` で作品ページに移動し、MyDNAとして近い作品を表示します。
例:
```text
低音のお姉さんに囁かれながら寝落ちしたい
甘やかしてくれる耳かきで落ち着く作品
雨音と添い寝っぽい雰囲気
```
`intfloat/multilingual-e5-small` を使う場合、検索文にはサーバー側で自動的に `query:` prefix を付けます。リモート検索ではデフォルトでprefixを付けません。
推薦スコアは `0.8 * ベクトル類似度 + 0.2 * タグ一致` です。サークル・尺・年齢区分・NSFWは好みスコアには入れず、表示またはフィルタとして扱います。