digital-go-jp/genai-web (源内 Web) と digital-go-jp/genai-ai-api (源内 AI-API / lawsy) を、Apple Silicon Mac 1 台のローカル環境 (ローカルサービス / ローカル LLM) で動かすためのパッチ集です。
アプリ層 (web / gateway / qdrant / searxng / lawsy) は Docker で起動し、ローカル LLM はホスト OS で MLX を直接叩きます。AWS / GCP の構築なしで、以下が一通り動きます:
- チャット (
/chat、画像添付対応) - 文章生成 / 翻訳 (
/generate,/translate) - 画像生成 (
/image) - ダイアグラム生成 (
/diagram) - 音声書き起こし (
/transcribe) - AI アプリ (ExApp) (
/apps) - lawsy 法令レポート生成 (BigQuery を Qdrant、Web Search を SearXNG に置き換え)
上流コードには極力触らず、Bedrock / Vertex 呼出は今回用に新規に作成した gateway モジュールに吸収させて LLM 等の呼び出しを切り替えています。
本リポジトリに上流 (digital-go-jp/genai-web / digital-go-jp/genai-ai-api) の本体コードは含まれていません。入っているのは追加・修正した差分のみで、上流を別途 clone してそこに apply.sh を当てると動く構成です。
ローカル PoC / 検証専用の派生物のため、セキュリティ・運用上の留意点があります。詳細は末尾の留意事項を参照してください。
ローカル環境で動かしたときの画面例です。
ホーム画面 (AI アプリ一覧)
法令レポート生成 (lawsy ローカル版)
最短で動かす手順です。chat / embeddings / ASR (音声書き起こし) は vllm-mlx 1 プロセスで兼任します。画像生成のみ別プロセス (mflux-server.py) です。
役割分担:
quicksetup.sh… 初回セットアップ (clone / install / patch / docker build)。1 回走らせれば終わります。genai-local.sh… day-2 以降のサーバー起動・停止・状態確認。日常はこちらだけ使います。
下記 Step 1〜6 のうち初回セットアップに必要な部分 (上流 clone + install + patch + docker build) を 1 コマンドにまとめたスクリプトを用意しています。
git clone https://github.com/digital-go-jp/genai-web.git ../genai-web
git clone https://github.com/digital-go-jp/genai-ai-api.git ../genai-ai-api
git clone https://github.com/rtoki/genai-local-llm.git
cd genai-local-llm
bash quicksetup.sh中身は順に install.sh (pipx + モデル DL ~45GB)、上流 clone (sibling)、apply.sh、docker compose up。冪等なので途中で失敗しても再実行で続きから走ります。完走後は次節の ./genai-local.sh start でサーバー一式を起動してください。
day-2 以降はこのスクリプトだけで、host 推論サーバ (vllm-mlx + mflux) と Docker スタック (web + gateway + qdrant + searxng + lawsy)、必要なら OrbStack VM 自体までまとめて制御します。
./genai-local.sh start # 全部起動 (orb VM → docker → host LLM)
./genai-local.sh start --no-image # 画像生成 (mflux) はスキップ
./genai-local.sh stop # 全部停止
./genai-local.sh stop --orb # OrbStack VM 自体も停止
./genai-local.sh restart # stop → start
./genai-local.sh status # 稼働状況一覧
./genai-local.sh logs vllm # ログを tail (vllm | mflux | gateway | qdrant | web)
./genai-local.sh smoke # smoke-test.sh を実行start は内部で vllm-mlx serve (chat + embed + ASR + 画像認識) と mflux-server.py (画像生成) をバックグラウンドで起動し、/v1/models の ready を待ってから全体 status を表示します。host 側のログは ~/.local/run/*.log です。フォアグラウンドで動かしたい場合は bash genai-web/local/scripts/start-vllm-mlx.sh を直叩きしても構いません。
起動後ブラウザで http://localhost:5173 を開いてください。useAuth ダミーが SystemAdminGroup で自動サインインします。何か質問を投げて応答が返れば疎通 OK です (チャット履歴はリロードで消えます)。
lawsy (法令検索) を使う場合のみ、Qdrant に法令ベクトルを投入します。
./genai-local.sh setup-lawsy # law_articles / law_titles の状態を診断・修復
./genai-local.sh setup-lawsy --force # law_titles を強制再構築law_articles は e-Gov 法令 XML からの初回投入が必要なので、未投入なら使い方を表示して exit します (実投入は index-from-xml.py 直叩き)。law_titles の欠落・空のときだけ rebuild-law-titles.py を venv (uv sync で自動構築) の python で実行します。
quicksetup.sh を使わず手動で進めたい場合
おおよそ次の流れです。詳細は quicksetup.sh 本体を読んでください。
brew install pipx && pipx install vllm-mlx --python python3.11~/.local/pipx/venvs/vllm-mlx/bin/python genai-web/local/scripts/setup-whisper.py(HF Discussion #4 の暫定対応で whisper の不足ファイルを補完)- 上流を clone:
git clone digital-go-jp/genai-web ../genai-web+ 同genai-ai-api bash genai-web/scripts/apply.sh ../genai-web+ 同genai-ai-apidocker compose up -d --buildを web → ai-api 順./genai-local.sh startで host LLM (vllm-mlx + mflux) を起動- ブラウザで
http://localhost:5173
初回はモデル DL に時間がかかります (合計 ~45GB)。
検証環境は M5 MacBook Pro 128GB / macOS 15 (Sequoia) です。メモリがもっと少ない Apple Silicon Mac でも、後述の「軽量プリセット (24GB Mac 向け)」でメモリに収まるモデルを選べば動きます (動作報告いただけると助かります)。
| 項目 | 要件 | 備考 |
|---|---|---|
| ハード | Apple Silicon Mac (M1〜M5) | MLX 必須 |
| OS | macOS 14+ (Sonoma 以降) | 検証は macOS 15 (Sequoia)。MLX の前提 |
| メモリ | 24GB から動作可能 | 既定 (Qwen3-VL-32B-8bit、~33GB) は 64GB+ 推奨。24GB Mac は下記「軽量プリセット」を使ってください |
| ディスク空き | ~50GB 程度 (既定モデル一式)、~10GB (軽量プリセット) | 既定: chat ~33GB + embed ~600MB + ASR ~1.5GB + 画像生成 ~10GB + Docker + node_modules |
| Docker | OrbStack または Docker Desktop 4.30+ | 検証は OrbStack。Apple Silicon ネイティブで軽いのでこちら推奨。Docker Desktop は未検証だが動くはず |
| Node | 20+ | 上流側で npm install を走らせる場合のみ。Docker だけで完結させるなら不要 |
| Python | 3.11 | vllm-mlx の前提 (3.12 は未検証) |
| Homebrew | 任意 | pipx や text-embeddings-inference 等を入れるときに |
補足: macOS では Docker Desktop / OrbStack いずれも Metal/GPU はコンテナから見えないので、推論サーバ (chat / embeddings) はホスト OS で直接立ち上げて、コンテナ側からは host.docker.internal:<port> で参照する構成にしています。
vllm-mlx :8000 1 プロセスが chat / VLM / embeddings / ASR の 4 役を兼任、mflux-server :8002 が画像生成を担当します。
| 役割 | モデル (HF id) | サイズ | エンジン | UI 経路 |
|---|---|---|---|---|
| chat / 画像認識 (VLM) | mlx-community/Qwen3-VL-32B-Instruct-8bit |
~33GB | vllm-mlx :8000 | /chat (添付画像も同モデル) |
| Embedding (日本語 ModernBERT) | cl-nagoya/ruri-v3-310m |
~600MB | vllm-mlx :8000 (--embedding-model) |
lawsy の Qdrant retrieval |
| ASR (音声書き起こし) | mlx-community/whisper-large-v3-turbo |
~1.5GB | vllm-mlx :8000 (auto-load) | /transcribe |
| 画像生成 | mlx-community/Qwen-Image-2512-4bit |
~10GB (Q4) | mflux + 自作 FastAPI wrapper (:8002) | /image |
差し替え方法は後ろの「LLM のバックエンドを変える」を参照してください。
24GB / 16GB クラスの Mac (MacBook Air M2/M3 など) でも、chat を 9B テキストモデルに差し替えて、画像生成 (mflux ~10GB) と ASR (whisper ~1.5GB) と VL (画像認識) をスキップすれば動きます。Lawsy (法令 RAG) は embeddings + Qdrant + SearXNG + lawsy コンテナが動いていれば軽量プリセットでも完全に動作します。
ホスト側で動かすモデル (合計 ~6GB):
| 役割 | モデル (HF id) | サイズ |
|---|---|---|
| chat | mlx-community/Qwen3.5-9B-MLX-4bit |
~5GB |
| Embedding (lawsy で使用) | cl-nagoya/ruri-v3-310m (既定と同じ) |
~600MB |
| ASR / 画像生成 / VL (画像認識) | スキップ | 0 |
Lawsy を動かす場合は Docker 側にも追加リソースが必要 (法令データ込みで概算):
| コンテナ | 役割 | 概算 RAM | 概算ディスク |
|---|---|---|---|
| qdrant | ベクタ DB (382k 法令条文を格納) | ~1.5GB | ~2GB |
| searxng | メタ検索 | ~100MB | ~100MB |
| lawsy | 法令レポート生成 (Flask) | ~200MB | ~200MB |
| genai-web (web + gateway) | フロント + LLM ゲートウェイ | ~500MB | ~500MB |
24GB Mac で起動中の概算消費は: chat 5GB + embed 0.6GB + Docker 全体 ~2.5GB = ~8GB。OS / Browser / Docker daemon 等を含めても 24GB 内に十分収まります。
通常手順との差分は --small フラグを 2 箇所付けるだけです。
# 1. 上流 + 本リポを clone (通常手順と同じ)
git clone https://github.com/digital-go-jp/genai-web.git ../genai-web
git clone https://github.com/digital-go-jp/genai-ai-api.git ../genai-ai-api
git clone https://github.com/rtoki/genai-local-llm.git
cd genai-local-llm
# 2. quicksetup.sh に --small を渡す。install.sh / apply.sh / docker compose まで一気に走る
bash quicksetup.sh --small
# 3. 起動も --small。MODEL 上書きと --no-image を genai-local.sh が自動で当てる
./genai-local.sh start --small| 機能 | 軽量プリセット |
|---|---|
| チャット / 翻訳 / 文章生成 / ダイアグラム | ✓ |
| lawsy 法令レポート生成 | ✓ |
/transcribe (音声書き起こし) |
✗ (whisper 未 DL) |
/image (画像生成) |
✗ (mflux 未 install) |
| チャットへの画像添付 (画像認識) | ✗ (chat モデルが text-only) |
SERVED_NAME は既定の qwen3.6-35b-a3b のまま据え置き OK (gateway / フロントがその名前で叩くため、中身が 9B でも API 上の名前が一致してれば動きます)。
Docker 側 (アプリ層、2 つの compose):
genai-webcompose:web(Vite + mock) +gateway(OpenAI/Bedrock/Vertex 互換シム)genai-ai-apicompose:qdrant(ベクタ DB) +searxng(メタ検索) +lawsy(法令 RAG)
ホスト側 (推論層、MLX が Metal を直接叩くのでコンテナ化はしていない):
vllm-mlx :8000: chat (qwen3.6-35b-a3b) + embeddings (ruri-v3-310m) + ASR (whisper-large-v3-turbo) + 画像認識 (Qwen3.6-VL) を 1 プロセスで兼任mflux-server.py :8002: 画像生成 (Qwen-Image-2.0、別プロセス)
コンテナ ↔ ホスト間は host.docker.internal:<port> で繋いでいます。
┌─────────────────────────────────────┐
│ Host (Apple Silicon Mac) │
│ vllm-mlx :8000 │
│ ├─ chat LLM (qwen3.6-35b-a3b) │
│ ├─ embeddings (ruri-v3-310m) │
│ └─ ASR (whisper-large-v3-turbo) │
│ mflux-server :8002 (画像生成) │
└─────────────────────────────────────┘
▲ ▲
│ │ host.docker.internal
┌────────────────────────┴─────────────┴────────────────┐
│ Docker (host.docker.internal) │
│ │
│ ┌─ genai-web (compose) ─────────────────────────┐ │
│ │ web :5173 (Vite + mock) │ │
│ │ gateway :8080 (OpenAI 互換 → host LLM) │◄───┼── lawsy が叩く
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌─ genai-ai-api (compose) ──────────────────────┐ │
│ │ qdrant :6333 (vector store) │ │
│ │ searxng :8888 (検索 backend) │ │
│ │ lawsy :8090 (法令レポート生成) │ │
│ └────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
源内 Web (上流 AWS フルスタック) と 源内 AI-API / lawsy (上流 GCP / Vertex / BigQuery) の両方を、本パッチ集でどう置き換えたかの対応表です。
| AWS 本番 | 対応 | ローカル版 |
|---|---|---|
| エッジ (CloudFront / WAF / S3 静的) | 削除 | localhost 直結 (Vite dev サーバが HMR ごと配信) |
| 認証 (Cognito UserPool / Identity Pool / SAML) | 削除 | useAuth ダミーセッション (SystemAdminGroup 権限を固定で返却) |
| API Gateway + Lambda 群 (Authorizer + CRUD + チャット系) | 残存 | Vite plugin /api/* (同プロセス内 Express、local/web/vite-plugin-mock.mjs) |
invokeExApp Lambda + VPC NAT (固定 IP で外部 AI アプリへ) |
残存 | /api/team/exapps/invoke で fetch(exApp.endpoint) 同期。VPC / IP 制限なし |
| DynamoDB (TeamAC / InvokeHistory / Chat、GSI + KMS 暗号化) | 残存 | state.json (Docker volume 永続化、in-memory + JSON)。チャット履歴は保存しない |
| S3 ファイル / SQS / SES (アップロード・非同期キュー・招待メール) | 保留 | 未実装。該当 UI を VITE_APP_HIDDEN_USE_CASES で隠す |
| Bedrock (Converse / Stream / KB) | 置換 | gateway → vllm-mlx (host)。alias 解決で jp.anthropic.claude-... → qwen3.6-35b-a3b にリダイレクト。RAG は Qdrant |
| VPC + NAT Gateway / CloudWatch | 残存 | Docker bridge + docker logs。host.docker.internal でホスト LLM に到達 |
| GCP 本番 | 対応 | ローカル版 |
|---|---|---|
| Vertex AI / Gemini API (LLM 呼出) | 置換 | gemini_config.py を改修し、google-genai SDK の宛先を gateway (host.docker.internal:8080/genai) に向けて OpenAI 互換に翻訳 |
BigQuery + BQML ML.GENERATE_EMBEDDING (法令のベクトル検索) |
置換 | Qdrant + ローカル embedding (ruri-v3-310m)。retrieval_qdrant.py で BigQueryRetriever を差し替え (USE_LOCAL_RETRIEVER=1) |
| Vertex Web Grounding (Google 検索 grounding) | 置換 | SearXNG メタ検索 + Jina Reader。web_grounding_local.py で代替 (USE_LOCAL_GROUNDING=1) |
| Cloud Functions (lawsy のホスト先) | 残存 | Flask 化して Docker container (lawsy:8090)。コード自体は流用 |
| Application Default Credentials (ADC) | 削除 | ローカル切替時は ADC 取得自体をバイパス (gemini_config.py:82 あたりで条件分岐) |
凡例: 残存 = 形を変えて生きている / 削除 = 機能ごとカット / 保留 = UI ごと無効化 / 置換 = 別実装で同等機能を提供
各パッチは下記の上流 commit を base に検証しています (scripts/apply.sh の UPSTREAM_BASE_COMMIT と一致)。これより新しい上流に当てると apply.sh が WARN を出します (適用自体は実行)。
| 上流 repo | base commit | 日付 | 備考 |
|---|---|---|---|
digital-go-jp/genai-web |
f20366a |
2026-04-30 | "Merge pull request #14 from digital-go-jp/update-from-private" |
digital-go-jp/genai-ai-api |
9ff9776 |
2026-05-02 | "Merge pull request #21 from digital-go-jp/update-from-private" |
genai-web/
├── local/ # 新規追加: ローカル LLM スタック (Docker)
│ ├── docker-compose.yml
│ ├── seed/exapps.json
│ └── web/
│ ├── Dockerfile
│ ├── vite-plugin-gateway.mjs # OpenAI/Bedrock/Vertex 互換 gateway
│ └── vite-plugin-mock.mjs # /api/* モック
├── overlay/ # 上流ファイルの上書き版 (修正後フル内容)
│ └── packages/web/...
├── removed-files.txt # 上流から削除するファイル一覧 (AWS Cognito/SAML auth)
└── scripts/apply.sh # 上流チェックアウトに対して overlay + local/ を適用
| 種別 | 内容 |
|---|---|
| 追加 | local/ 一式。Docker Compose + Vite plugin gateway (Node/Express) + mock backend。OpenAI 互換ならどんな upstream にも UPSTREAM_OPENAI_URL で切替可能 |
| 修正 | packages/web/{vite.config.ts, package.json}, src/main.tsx, src/hooks/useAuth.ts, src/lib/{chatApi.ts, fetcher.ts}, src/components/ui/Header.tsx。Vite に local plugins を読み込ませ、AWS Cognito 認証を外し、API 呼び出しをローカル gateway に向ける |
| 削除 | packages/web/src/components/auth/{AuthWithSAML.tsx, AuthWithUserpool.tsx} のクラウド認証コンポーネント (上流から削除する分は removed-files.txt に列挙) |
genai-ai-api/
├── local/ # 新規追加: ローカル LLM 用 Docker Compose スタック
│ ├── docker-compose.yml # qdrant + searxng + lawsy
│ ├── .env.example
│ ├── lawsy/{Dockerfile, serve.py}
│ ├── qdrant/Dockerfile
│ ├── searxng/{Dockerfile, settings.yml}
│ ├── index-from-xml.py # e-Gov XML → Qdrant indexer
│ ├── index-sample-laws.py # サンプル法令を Qdrant に投入
│ └── smoke-test.sh
├── overlay/ # 上流ファイルの上書き版 (修正後フル内容)
│ └── google-cloud/lawsy-custom-bq/modules/api/functions/src/
│ ├── gemini_config.py # 修正: ローカル LLM 接続設定
│ ├── law_report_pipeline.py # 修正: BQ→Qdrant に切替, web grounding をローカル化
│ ├── main.py # 修正: ローカル実行用エントリ
│ ├── retrieval_qdrant.py # 新規: Qdrant retrieval (BQML 代替)
│ └── web_grounding_local.py # 新規: SearXNG ベースの web grounding
└── scripts/apply.sh # 上流チェックアウトに対して overlay + local/ を適用
| 種別 | 内容 |
|---|---|
| 追加 | local/ 一式。Docker Compose で qdrant (ベクタ DB) / searxng (メタ検索) / lawsy (法令調査 Cloud Function 相当) を立ち上げる |
| 修正 | google-cloud/lawsy-custom-bq/.../src/{gemini_config.py, law_report_pipeline.py, main.py}。Gemini API → ローカル LLM gateway に切替、BigQuery 検索 → Qdrant に切替、Vertex Web Grounding → SearXNG に切替 |
| 追加 | google-cloud/lawsy-custom-bq/.../src/{retrieval_qdrant.py, web_grounding_local.py} (上記置き換えのための新規モジュール) |
lawsy は genai-web 側の gateway (host.docker.internal:8080) を経由してローカル LLM に到達するため、ai-api 単体ではチャットが返らない点に注意してください。AWS / Azure / GCP IaC (Terraform / CDK / Bicep) 等は本ポートでは触っていないので、上流のまま残ります (ローカル運用では未使用)。
既定モデルは「既定のローカルモデル」表のとおりです。embeddings 側は ModernBERT 系の ruri-v3-310m を使う都合上、Ollama mainline は使えません (llama.cpp #11282 のとおり ModernBERT 未対応)。
vllm-mlx を別モデル / --served-model-name で起動し直して、local/.env の LLM_MODEL を --served-model-name に揃えれば差し替わります。
# 例: Qwen3-VL-30B-A3B に差し替え
vllm-mlx serve \
mlx-community/Qwen3-VL-30B-A3B-Instruct-8bit \
--served-model-name qwen3-vl-30b \
--embedding-model cl-nagoya/ruri-v3-310m \
--port 8000 --host 0.0.0.0
# genai-web/local/.env
echo "LLM_MODEL=qwen3-vl-30b" >> ../genai-web/local/.envvllm-mlx 以外 (例: vLLM 本家、SGLang、LM Studio) に向けたい場合は local/.env で URL/KEY を上書きするだけです。embeddings を別 upstream にしたいときは UPSTREAM_EMBEDDINGS_URL を分けてください。
UPSTREAM_OPENAI_URL=http://host.docker.internal:<port>/v1
UPSTREAM_OPENAI_KEY=<key>
LLM_MODEL=<served-model-name>
参考: antirez (Redis 作者) による DeepSeek V4 Flash 284B 専用の Metal 推論エンジン antirez/ds4 (~80GB、128GB Mac 向け) も同じ要領で UPSTREAM_OPENAI_URL で接続できます (公式 mlx-lm の DeepSeek V4 サポートは ml-explore/mlx-lm#1233 で議論中)。
上流 (源内 original repo) が更新されたとき:
# 上流側で更新を取り込む
cd ../genai-web # または ../genai-ai-api
git fetch origin && git merge origin/main # パッチ対象ファイルが衝突したら手動 merge
# パッチを再適用 (パッチは上書き、local/ は同期)
/path/to/genai-local-llm/genai-web/scripts/apply.sh . # web 側
/path/to/genai-local-llm/genai-ai-api/scripts/apply.sh . # ai-api 側パッチ対象ファイルに上流変更が当たった場合は、本リポ側 (overlay/...) を手動で更新してから commit してください。scripts/apply.sh の UPSTREAM_BASE_COMMIT が、各パッチを検証した最後の上流 commit です。上流をそれより新しい commit にした状態で apply すると警告が出ます (実行はされます)。
- 本パッチ集は個人がローカル環境で検証する目的で作った非公式の派生物です。デジタル庁および
digital-go-jp/genai-web/digital-go-jp/genai-ai-apiの公式見解・成果物・推奨を代表するものではありません。 - 本番のセキュリティ・運用要件を満たしません。Cognito 認証、KMS 暗号化、VPC NAT、IP allowlist、WAF、CloudWatch 監査ログなど、上流に存在する保護機構の多くを意図的に無効化しています。ローカル PoC / 実装検証用途に限定してください。
- 認証は
useAuthがダミーのSystemAdminGroupセッションを返すため、誰でも全管理 UI に入れます。docker compose upした端末を 外部に公開しないでください (バインドが0.0.0.0になっているサービスがあります)。 - チャット履歴は永続化されず、ページリロードで消えます。
/data/state.json(チーム・AI アプリ定義) のみ Docker volume に保存されます。 - ファイルアップロード (S3) / メール招待 (SES) / 非同期 AI アプリ (SQS) は未実装または UI から隠してあります。画像生成 / 画像認識 / 音声書き起こしは本パッチで実装済みです。
- gateway は OpenAI Streaming 非対応 (擬似 1 チャンク SSE で返却)。LLM 応答は完成してから一気に返ります。長文生成中はブラウザに何も表示されない時間があります。
- モデル選定・gateway の alias 解決・SearXNG への web grounding 置換は作成者個人の判断で行っており、上流の品質・回答内容を保証するものではありません。
- 動作の正確性・完全性・継続性についての保証はありません。
- 修正・追加分は MIT License (LICENSE) です。
*/overlay/内のファイルは上流の MIT License (Copyright (c) Digital Agency of Japan) を継承しています。- 上流原本のライセンス (MIT, CC-BY 4.0 for docs, ASL for some CDK/Lambda files) は上流リポジトリでご確認ください。
上流リポジトリ (digital-go-jp)
- digital-go-jp/genai-web: 源内 Web (React + Vite + AWS CDK)
- digital-go-jp/genai-ai-api: 行政実務 AI アプリ (lawsy ほか)
推論バックエンド
- ml-explore/mlx: Apple 製の Metal/unified memory 向け ML フレームワーク
- waybarrios/vllm-mlx: vLLM スタイルの MLX 推論サーバ (chat + embed + ASR 兼任)
- filipstrand/mflux: MLX 上の diffusion (FLUX / Qwen-Image)
モデル
- mlx-community/Qwen3-VL-32B-Instruct-8bit: 既定の chat / VLM モデル
- cl-nagoya/ruri-v3-310m: embedding モデル (日本語 ModernBERT)
- mlx-community/whisper-large-v3-turbo: ASR モデル
- mlx-community/Qwen-Image-2512-4bit: 画像生成モデル
周辺
- qdrant/qdrant: ベクタ DB
- searxng/searxng: メタ検索エンジン
- huggingface/text-embeddings-inference: embedding 比較に使った参照実装
- OrbStack: Apple Silicon 向け軽量 Docker ランタイム
- llama.cpp Issue #11282: Ollama mainline が ModernBERT 未対応である根拠 (ruri-v3 を Ollama で動かせない理由)

