0005: LLM-Wikiのembeddingsをローカル推論(mac-mini)へ分離する
背景
OpenCode Goは/embeddingsエンドポイントを提供しない(0004 の検証結果)。llmwikiは語彙検索フォールバックで動作するが、embeddingsがあれば ハイブリッド検索(semantic + BM25 + wikilinkグラフ)の質が上がる。embeddingsは 軽量モデルのローカル推論で十分賄える。
決定
- chat(compile/query)はOpenCode Goのまま、embeddingsだけローカルのOpenAI互換 エンドポイントへ分離する。llmwikiの既存機能
OPENAI_EMBEDDINGS_BASE_URLとLLMWIKI_EMBEDDING_MODELを使い、paper-civic-kit側の改造はしない。 - embeddingsの提供先は
mac-miniprofile(architecture.mdで「軽いPDF変換」を担う 常時稼働ホスト)に置く。実装はOllama等のOpenAI互換/v1/embeddingsを提供する 既存ランタイムを使い、自前のembeddingsサーバーは書かない。 - モデルは多言語対応のembeddingモデル(例: bge-m3系)を第一候補とし、実測で決める。
理由
- llmwikiのソース(
src/utils/provider.ts)で、LLMWIKI_PROVIDER=openaiのままOPENAI_EMBEDDINGS_BASE_URLだけ別ホストへ向けられることを確認した。 - 検証用のOpenAI互換シムを
127.0.0.1:8765に立ててllmwiki compileを実行した ところ、「Skipped embeddings update: 404」だった箇所が「Embeddings updated (4 pages, 26 chunks)」になり、.llmwiki/embeddings.jsonに指定モデル名で ベクトルが保存された。経路はllmwiki側の変更なしで機能する(2026-07-05、 tasks/0003)。 - embeddingsは数百ms級・小メモリの推論であり、
rtx-worker(重いOCR/画像処理) ではなく常時稼働のmac-miniに置くのがprofile分担(architecture.md)に合う。
影響
- llmwikiプロジェクトの
.envにOPENAI_EMBEDDINGS_BASE_URL=http://<mac-mini>:11435とLLMWIKI_EMBEDDING_MODEL=<モデル名>を追加すれば有効になる(未設定なら従来どおり 語彙検索フォールバック)。注意: llmwikiはembeddingsBaseURLをopenai SDKのbaseURLにそのまま渡すだけで/v1を自動付与しない。バックエンドが実際に応答する パスと厳密に一致させること(infinity-embはルート直下/embeddingsで応答するため/v1を付けない。Ollamaバックエンドの場合は/v1が必要)。 - embeddingモデルを変えると既存ベクトルと非互換になるため、モデル確定後に
.llmwiki/embeddings.jsonを再生成する。
実装結果(2026-07-05)
- ランタイム: hf-mount + infinity-emb(
--backend infinity)を採用。hf-mount start repo BAAI/bge-m3 <path>でHF Hub safetensorsをFUSEマウントinfinity_emb v2 --model-id <path>でMetal(MPS)推論- GGUF変換不要、
uv tool install --with "optimum<2" --with "pyarrow<21" --with "click<8.2" "infinity-emb[all]"で依存解決(infinity-emb 0.0.77との互換性を固定) - launchd plistで常駐(
~/Library/LaunchAgents/com.paper-civic.infinity-emb.plist) - Ollamaの既定ポート
11434との衝突を避けるため、infinity-embは11435で待ち受ける。
- モデル:
BAAI/bge-m3(1024次元、多言語対応) - エンドポイント確認:
curl http://localhost:11435/embeddingsでベクトル返答を確認済み - Tailscale IP:
100.72.76.31(vps側のOPENAI_EMBEDDINGS_BASE_URLに設定) - launchd起動失敗の原因と修正: daemonコンテキストで
HOME未設定→infinity_embのキャッシュディレクトリ解決が/にフォールバックし/.infinity_cache(読み取り専用のシステムボリューム直下)へのmkdirで crash-loopしていた。plistのEnvironmentVariablesにHOMEを追加して解決。 - vps側からの疎通確認完了(2026-07-05):
curl http://100.72.76.31:11435/embeddingsでHTTP 200・bge-m3実ベクトル応答を確認。.env設定 (OPENAI_EMBEDDINGS_BASE_URL=http://100.72.76.31:11435、/v1なし)でllmwiki compileを実行し、.llmwiki/embeddings.jsonがdimensions: 1024の実ベクトル(21ページ)に更新されたことを確認、llmwiki queryでの引用付き回答生成も確認済み。
scripts/setup-embeddings-mac-mini.sh は --backend ollama|infinity 両対応。 Ollamaバックエンド(GGUFモデル)も引き続き選択可能。
インシデントと復旧(2026-07-07)
上記の疎通確認直後、Obsidian Sync(obsidian-headless/ob CLI、mirror-remote モード)を実地検証した際、リモートに未登録だったLLM-Wikiシンボリックリンクが 「ローカルのみの差分」と判定され、リンク先の実体(compileされたwikiページ21件) ごと削除された。復旧作業中にllmwiki compileをstate.json未クリアのまま実行し、 embeddings.jsonも一時的に空(dimensions: 0)へ上書きしてしまう二次被害も発生した。
- 原因:
mirror-remoteはリモート状態を正として、ローカルのみに存在する ファイル/フォルダを物理削除する。LLM-Wikiは一度もリモートへpushされていな かったため削除対象になった。 - 再発防止:
ob sync-config --excluded-foldersにLLM-Wikiを追加し、以後のob sync実行で対象外にした(既存のhermes-memory,hermes-memories,copilotと同様の扱い)。symlinkは除外設定後に再作成済み。 - データ復旧:
sources/5ファイルは無事だったため、state.jsonのsources/indexHashをクリアしてmimo-v2.5-proで再コンパイルし、日本語の 概念ページ24件・embeddings.json(dimensions: 1024)を再生成した。 復旧過程でkimi-k2.6(tasks/0006で不採用と判明済みのモデル)や無印mimo-v2.5も試したが、いずれも日本語出力が得られず英語化した (LLMWIKI_OUTPUT_LANGは実装が存在せず無効な設定値であることも判明。 日本語出力はmimo-v2.5-pro固有の挙動であり、設定で保証されているもの ではない)。既定モデルは引き続きmimo-v2.5-proとする。
DeepTutorへの接続(2026-07-08)
同一Mac mini embeddings(http://100.72.76.31:11435、bge-m3)を、 /home/hkawaguc/deeptutor/で稼働中のDeepTutor(HKUDS、docker-compose、 ポート3782/8001)にも接続した。PUT http://127.0.0.1:8001/api/v1/settings/catalog (services.embeddingにbinding: "custom"のプロファイルを追加)で設定し、 POST http://127.0.0.1:8001/api/v1/system/test/embeddingsで疎通確認した。
新たな落とし穴: DeepTutorのcustom(openai_compat)バインディングは、 llm-wiki-compilerと異なりbase_urlに/embeddingsを自動付与しない。 base_url: http://100.72.76.31:11435のままではDeepTutor側がルート直下へ POSTしようとして405 Method Not Allowedになる。base_urlに/embeddingsまで 含めたフルパス(http://100.72.76.31:11435/embeddings)を指定して解決した (success: true、応答時間約1.3秒、model: bge-m3を確認)。同じMac mini・ 同じバックエンドでも、接続するクライアント側の実装によって「base_urlに何を 含めるべきか」の規約が異なる。新しいクライアントを繋ぐたびに、まずcurlで POST先の実際のパスを確認してから設定すること。
追記: LLM-Wiki廃止(2026-07-22時点の注記)
本文中のLLM-Wikiは0004「LLM-Wiki生成 エンジンにllm-wiki-compilerを採用」の追記(2026-07-15、実運用でほぼ使われ なかったため廃止)により廃止済み。bge-m3のローカル埋め込み自体は現役 (icd11-cron等の実データJOINで使用中)で、このタイトルの決定内容は生きている。