# 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` を付けます。 ## 翻訳重複の修復 日本語原作と翻訳版が両方 `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は好みスコアには入れず、表示またはフィルタとして扱います。