技術 約18分で読めます

open-notebookをDockerもクラウドAPIも使わずM1 Maxで動かしてqwen3.6:35bに自分の記事を読ませた

いけさん目次

open-notebook はGoogleのNotebookLMのOSS実装。PDFやWeb記事を取り込んでチャット・要約・ポッドキャスト生成までできる。触れ込みは “A private, multi-model, 100% local, full-featured alternative to Notebook LM”

ただしREADMEに書かれた標準の導入手順はDocker Compose前提で、しかも最初に勧められるのはOpenAIやAnthropicのAPIキー登録。“100% local”と言いつつ、そのままだとDockerに縛られるし、プロバイダAPI使った瞬間にデータはサーバーに出ていく。

なので手元のM1 Max 64GBで、

  • Dockerを使わない(SurrealDBをネイティブで動かす)
  • クラウドAPIキーを1本も登録しない(Ollamaのみ)

の条件で動かした。その上で自分が朝書いた Qwen3.6-27B Dense vs 35B-A3B MoE比較記事 を食わせて、チャットLLM自身(qwen3.6:35b)に自分のベンチマーク結果を答えさせてみた。

環境

項目
マシンM1 Max 64GB(統合メモリ)
OSmacOS Darwin 25.3.0
open-notebookv1.8.5
SurrealDB2.6.5(ネイティブ)
Ollama0.20.6
Chat Modelqwen3.6:35b(23GB、4bit GGUF、MoE)
Embedding Modelbge-m3:latest(1.2GB、多言語対応)
FrontendNext.js 16.2.3(:3000)
Backend APIFastAPI(:5055)
Workersurreal-commands-worker

4プロセス常駐するので、素直にtmuxの4ペインで起動した。

構成図

open-notebookはバックエンドがPython、フロントがNext.js、DBがSurrealDB、そこに非同期タスクを捌くWorkerが別プロセスで居る。普段はdocker-compose.ymlが全部面倒を見てくれるが、自前で立てるときはこのブロック図を頭に入れておくと楽。

flowchart LR
  Browser[ブラウザ] -->|:3000| Frontend[Next.js frontend]
  Frontend -->|/api/* proxy| Backend[FastAPI backend :5055]
  Backend -->|ws :8000/rpc| DB[(SurrealDB v2)]
  Backend -->|command enqueue| DB
  Worker[surreal-commands-worker] -->|poll| DB
  Worker -->|chat/embed| Ollama[Ollama :11434]
  Backend -->|chat/embed| Ollama

Workerが居るのは、ソース取り込みの「本文取得→チャンク分割→埋め込み生成→要約生成」が長時間かかる処理だから。APIはそれを同期で待たずにコマンドIDだけ返す。

Dockerなしで立ち上げる

open-notebookのDocker依存は実質SurrealDBだけ。SurrealDB自体はRust製シングルバイナリなのでネイティブで動く。

1. SurrealDB v2を入れる

brewに surrealdb/tap/surreal があるが、ここに入るのは最新のv3系。open-notebookはv2系を前提にしているので、公式インストーラでv2を明示する。

curl -sSf https://install.surrealdb.com | sh -s -- --version v2.6.5

~/.surrealdb/surreal に入る。PATHに足すかそのまま呼ぶ。

2. open-notebook本体

cd ~/projects
git clone https://github.com/lfnovo/open-notebook.git
cd open-notebook
uv sync
uv pip install python-magic
brew install libmagic  # python-magicのバックエンド

python-magicはファイルタイプ判定ライブラリで、バックエンドにlibmagicの共有ライブラリが要る。brewで入れないとimport時にfailed to find libmagicで落ちる。

3. .env設定

暗号化キーだけ強いものに差し替える。API KeyはUI経由で後から登録するので、.envには書かない。

cp .env.example .env
# OPEN_NOTEBOOK_ENCRYPTION_KEYを強い値に
sed -i '' "s/^OPEN_NOTEBOOK_ENCRYPTION_KEY=.*/OPEN_NOTEBOOK_ENCRYPTION_KEY=$(openssl rand -hex 32)/" .env
# SURREAL_URLをdocker内の名前解決からlocalhostに差し替え
sed -i '' 's|^SURREAL_URL=.*|SURREAL_URL=ws://localhost:8000/rpc|' .env

OPEN_NOTEBOOK_ENCRYPTION_KEYはDBに保存するAPIキーをFernet(AES-128-CBC + HMAC-SHA256)で暗号化するのに使う鍵。ここを弱くすると平文露出のリスクがあるので32バイトのランダムHEXにしておく。

4. Frontendの依存

cd frontend && npm install

5. tmuxで4プロセス起動

tmux new-session -d -s opennb -c ~/projects/open-notebook

# Pane 0: SurrealDB
tmux send-keys -t opennb:0 'export PATH=$HOME/.surrealdb:$PATH && \
  surreal start --log info --user root --pass root \
  rocksdb:./surreal_data/db.rocksdb' Enter

# Pane 1: API
tmux split-window -t opennb:0 -v
tmux send-keys -t opennb:0.1 'uv run --env-file .env run_api.py' Enter

# Pane 2: Frontend
tmux split-window -t opennb:0.1 -h -c ~/projects/open-notebook/frontend
tmux send-keys -t opennb:0.2 'npm run dev' Enter

# Pane 3: Worker
tmux split-window -t opennb:0 -h
tmux send-keys -t opennb:0.3 'uv run --env-file .env surreal-commands-worker \
  --import-modules commands' Enter

各ポート。

プロセスポート
SurrealDB8000
FastAPI5055
Next.js3000
Worker(DBのLIVE queryで待つだけ、ポート無し)

ブラウザでhttp://localhost:3000を開けば起動完了。フロントが/api/*:5055にプロキシしてくれるので、普段は:3000しか触らない。

安全性の下調べ

APIキーを預ける前に、リポジトリの挙動をざっと確認した。

観点結果
外部送信先LLMプロバイダ以外への通信なし。requests.posthttpx.postfetch全部grepしたが、著者サーバーへのtelemetryは無し
APIキーの取り扱いFernetで暗号化してDBに保存。ログ出力で鍵を漏らす箇所なし
任意コード実行evalexecsubprocessos.system使用なし
フロントdangerouslySetInnerHTMLは静的テーマスクリプトのみ。XSSリスク低
依存著者自作のパッケージ5個(esperantocontent-coreai-prompterpodcast-creatorsurreal-commands)はPyPI公式で、GitHubで追える。普通のOSSリスク範囲

ローカルだけで使う分には問題なさそうだった。公開デプロイするならOPEN_NOTEBOOK_PASSWORDも設定する必要がある。

Ollama接続

最初の画面は /notebooks。左サイドバーの Manage → Models からAPIキー管理に飛ぶ。

open-notebookのAPIキー設定画面。OpenAI・Anthropic・Ollama等18プロバイダが並び、デフォルトでは全部Not configured

18プロバイダ並んでいるうちの Ollama セクションで Add Configuration → 名前適当(Local Ollama)、API Keyは空欄、Base URLはデフォルト(http://localhost:11434)で保存。

次に Models ボタンからモデル発見UIを開く。ollama list で見える全モデルがチェックボックスで選べるので、

  • Model Type: Languageqwen3.6:35b をチェック → Add
  • Model Type: Embeddingbge-m3:latest をチェック → Add

手持ちのOllamaモデルにEmbeddingが無かったのでbge-m3だけpullした。bge-m3はBGE系の多言語埋め込みモデルで日本語対応、1024次元。open-notebookはLanguage用とEmbedding用を分けて登録するので、vision/text LLMだけ持っていてもRAGは動かない。

最後に上部の Default Model AssignmentsAuto-assign Defaults を押すと、登録済みモデルが自動でチャット・埋め込み・要約・Toolsの各用途に振り分けられる。

Ollama登録後。Chat/Transformation/Tools/Large Contextがqwen3.6:35b、Embeddingがbge-m3:latestに自動割当

TTS/STTはOllama非対応なので空欄。ポッドキャスト機能はTTS必須なので諦める。

ノートブックに記事を食わせる

Notebooks → New Notebook で空のノートブックを作ると3ペイン構成。左が Sources、中央が Notes、右が Chat with Notebook。右下にチャットで使うモデル名が表示されていて、今はちゃんと qwen3.6:35b が当たっている。

ノートブックの3ペイン。Sources / Notes / Chatで、Chatの下にqwen3.6:35bの表示

Add Source → Add URL に自分のQwen3.6比較記事のURLを貼り付けた。

https://lilting.ch/articles/qwen36-27b-dense-vs-35b-moe-mlx-ollama

Workerログの動きを追うと、処理の流れが見える。

Processing source with 1 notebooks
Submitting embed_source job for source source:wi9kimb151g4g0h1y0o0
Created 20 chunks for source (sizes: min=47, max=644, avg=404 chars)
Successfully embedded source: 20 chunks in 2.59s
Successfully created insight source_insight:zo13w52zo7ly3gfpg6rn in 0.07s
Successfully embedded insight in 0.41s

20チャンクをbge-m3で埋め込むのに2.59秒。そのあとqwen3.6:35bで要約(insight)が走る。ここが一番時間かかる。

ollama ps で両モデルのVRAM占有を見ると、

NAME             SIZE    PROCESSOR    CONTEXT    UNTIL
qwen3.6:35b      30 GB   100% GPU     128000     4 minutes from now
bge-m3:latest    1.3 GB  100% GPU     8192       3 minutes from now

合計31GBほど。M1 Max 64GBでまだ余裕がある。UNTILはkeep-aliveで、両方ともGPU常駐のまま次の問い合わせを待つ。

qwen3.6:35bに自分のベンチマークを答えさせる

チャットペインに質問を投げた。

このソース記事について、Qwen3.6-27B DenseとQwen3.6-35B-A3B MoEのどちらがM1 Maxで高速でしたか?ベンチマーク結果の数字も教えてください。

チャット応答。qwen3.6:35bが自身のベンチマーク記事を読んで、27B Denseが11 tok/s、35B-A3Bが54 tok/s (MLX)等の正しい数値とMoEのスパース活性化の説明を返した

返ってきた答え。

M1 Max環境では、Qwen3.6-35B-A3B MoE の方が高速でした。提供されたベンチマーク結果は以下の通りです:

  • Qwen3.6-27B Dense: 約 11 tok/s (MLX環境)
  • Qwen3.6-35B-A3B MoE: 約 54 tok/s (MLX環境)/ 約 27 tok/s (Ollama GGUF環境)

総パラメータ数(35B)がDenseモデル(27B)を上回っているにもかかわらず、MoEアーキテクチャのスパース活性化により演算コストが抑えられており、M1 Maxのようなメモリ帯域幅がボトルネックになりやすいApple Siliconでも高速に動作しています[1]。また、推論ランタイム比較ではMLXがOllama GGUFに対して約2倍の速度差を示しており、MoEモデルをMLXで実行した場合の最適化効果が特に顕著です。

References: [1] - source:wi9kimb151g4g0h1y0o0

数字はソース記事(元記事のベンチマーク表)と完全一致。スパース活性化の解説もソース中の記述を踏まえていて、Apple Siliconのメモリ帯域ボトルネックの話まで拾っている。最後にちゃんとsource IDの形式でcitationが付く。

つまり qwen3.6:35b 自身が、自分のベンチマーク記事を読んで、自分のパフォーマンスを正しく説明したことになる。モデルは学習時点で自分の存在を知らないはずなので、RAG経由で現在の記事を引っ張って答えた形。

APIキー一切なし、ネットワークパケットはOllamaのlocalhost:11434内で完結。プロバイダにも著者サーバーにもデータは出ていない。

PDFも食える

URL以外に Upload File タブからファイル直投入もできる。試しに手元のPDF(当方の同人誌データ)を上げたら、本文テキストをちゃんと抽出した。

ソース一覧画面。PDFファイルと以前のURLソースが並ぶ

コンテンツタブを開くと、PDF内のテキストが取り出されてURL取り込みと同じ扱いになっている。そのあとEmbeddingとinsight生成も同じフローで走る。

PDFソースの詳細画面。コンテンツタブにPDFから抽出した本文テキストが表示されている

PDFパーサは content-core に同梱されている。追加セットアップは要らない。

ファイルアップロード時にNext.jsプロキシが大きめのリクエストを蹴らないように、フロント側には proxyClientMaxBodySize: "100mb" が設定されている。大型PDFや動画ファイルを食わせる場合はここの値を調整する。

ここまでで分かったセットアップの勘所

器として動かすために掴んでおくべきポイントはこのあたり。

項目所感
Docker依存の実体SurrealDBだけ。ネイティブv2を入れれば丸ごと剥がれる。open-notebook本体はPython+Node+JavaScriptの普通のWebアプリ
tmux 4ペイン運用慣れの問題。起動スクリプトにしてしまえばDocker Composeより起動が速い(イメージpullが無い)
Embedding専用モデル必ず要る。VLMやテキスト生成LLMで埋め込みAPIを叩くことは技術的には可能だが、RAG用の距離計算に使えない。bge-m3の1.2GBは誤差
初回のinsight生成遅い。qwen3.6:35bのロードで数十秒食う。2回目以降はkeep-aliveで常駐するので早い
ポッドキャスト生成機能諦め。OllamaはTTS非対応。完全ローカルを優先するならこの機能は捨てるしかない

ここまではopen-notebookというアプリの話。ここから先は載せるモデル側の話になる。

qwen3.6:35bはチャット応答で詰まる

PDFを食わせたあと、素朴に「このPDFの内容を簡単に説明して」と投げたら応答が返ってこなくなった。二度投げ直したがどちらもスピナーが回り続けるだけ。

切り分けのため、open-notebookを経由しないで直接Ollama APIを叩いた。

curl -sS -m 180 http://localhost:11434/api/chat -d '{
  "model":"qwen3.6:35b",
  "messages":[{"role":"user","content":"1+1は?"}],
  "stream":false,"options":{"num_predict":500}
}'
# curl: (28) Operation timed out after 180003 milliseconds with 0 bytes received

3分経っても0バイト返らない。Ollama経由でも同じく沈黙。

runner プロセスの CPU/メモリを時系列で測った。

経過時間Runner CPURunner RAM状態
t=5s41.6%45.8%モデルをVRAMにロード中
t=15s0%45.8%ロード完了、以降沈黙
t=45s0%45.8%沈黙継続
t=105s0%45.9%沈黙継続、タイムアウトへ

CPUは0%だがMacのファンは轟音で回り続けている。Apple SiliconのGPU使用率はpsには出ないが、症状からしてMetal側で生成カーネルが完了通知を返せずにスピン中とみられる。API側のPythonプロセスは__ulock_wait(mutex待ち)でスタックしていた。

対照実験として同じOllamaで gemma3:4b を叩いたら、

response: '1 + 1 = 2\n'
eval: 9 tokens, 0.1 s
load: 2.6 s
total: 3.0 s

3秒で返ってきた。Ollama自体は生きていて、qwen3.6:35b固有の詰まり

発生条件

決定的な再現手順は特定しきれていないが、当たっている体感はこのあたり。

  • PDF insight生成(内部で160秒の長い生成)を走らせた直後に発生
  • 一度詰まるとOllama.appを完全に再起動しても復帰しない(qwen3.6:35bのみ、gemma3:4bは即応)
  • モデルをkeep_alive:0でアンロード→再ロードしても直らない

同じqwen3.6:35bを今朝のOllamaベンチ記事では普通に27 tok/sで動かしていたので、短いプロンプト単発なら問題ない。PDF級の長文insight生成を挟むとMetal/Ollama側の状態が壊れるのか、何か別のトリガがあるのかは不明。

対処

素朴な対処はチャット用モデルを差し替えること。open-notebookはチャット・要約・Tools・埋め込みを別モデルに割り当てられるので、

  • Chat Model: gemma3:12b(応答即時、Ollamaで安定)
  • Transformation Model(insight生成): qwen3.6:35bのままでOK(バックグラウンドなので時間が掛かっても許容)
  • Embedding Model: bge-m3 据え置き

と役割分担すれば、体感レスポンスが別物になる。open-notebookの Settings → Models → Default Model Assignments で Chat Model だけ差し替えれば済む。

差し替え後の再テスト

open-notebookの /api/models/defaults を叩いてChat Modelだけgemma3:12bに差し替えた(Transformation Modelはqwen3.6:35bのまま)。その上で同じPDF(ChikaraMeshi_0802_New.pdf、当方の同人誌データ)に対し「このPDFの内容を簡単に説明して」を投げ直した。

gemma3:12bからの応答。PDFの内容を「力飯(ちからめし)」という牛丼チェーン店をテーマにした文化人類学的な考察と認識し、ライフサイクル・メニュー・評価基準をinsightへの引用付きでまとめている

返ってきた応答。

このPDF [source:n6xfz2tkhjddcrrxdggg] は、東京の「力飯(ちからめし)」という牛丼チェーン店をテーマにした文化人類学的な考察です。

以下に内容をまとめます。

  • 力飯とは: 焼き立て牛丼を提供する短命なチェーン店で、現代の肉食文化と丼料理の進化を象徴する現象です。[insight:1dtfth4oe3nhafa6m1xn]
  • ライフサイクル: 急速な拡大、品質の多様化、企業買収、そして業態転換というサイクルを辿りました。[insight:1dtfth4oe3nhafa6m1xn]
  • メニュー: 鉄格子で焼く「バラボク」と鉄板で焼く「コマメク」という2種類の焼き方があり、それぞれ異なるタレが使われます。[insight:1dtfth4oe3nhafa6m1xn]
  • 評価基準: 肉の質感、タレの浸透度、米の品質、サービスの速さ、立地などを評価する独自の指標が用いられています。[insight:1dtfth4oe3nhafa6m1xn]
  • 文化的な意味: インターネットミームの起源、食い倒れツアー、そして消滅の危機に瀕した文化遺産としての記録という側面を持っています。[insight:1dtfth4oe3nhafa6m1xn]
  • 記録の時点: 2015年7月31日時点のデータに基づいています。[insight:1dtfth4oe3nhafa6m1xn]

このドキュメントは、力飯を単なる飲食店としてではなく、一過性の文化現象として捉え、記録しようとする試みです。[insight:1dtfth4oe3nhafa6m1xn]

qwen3.6:35bで3分沈黙だったのに対し、gemma3:12bは体感数秒で返り切った。ただ読み比べるとPDFの中身と答えの対応が微妙なので、実際の本文と照合してみた。

gemma3:12bの回答精度を本文と照合

使ったPDFは2015年のC88で出したyakigyu 2015年8月号で、当方が東京チカラめしを追いかけて出した飲食系同人誌。Boothメロンブックスで今でも買える、という唐突な宣伝も挟みつつ、本題に戻る。

gemma3の主張PDF本文の記述判定
「2種類の焼き方がある」「焼き牛丼には2種類ある?従来型と新型(新焼き牛丼)」✅ 合致
「2015年7月31日時点のデータ」「2015年7月31日現在のものです」✅ 合致
「企業買収」「業態転換」「マックは2015年3月25日に神戸らんぷ亭を買収」「株式会社チカラめしの全店舗が業態転換」⚠️ 語彙は拾えているが主客がズレて、チカラめし自身の経緯として並列化されている
「急速な拡大、品質の多様化」店舗展開の年表・店ごとの差異記述あり✅ 拾えている
「鉄格子で焼く『バラボク』」「鉄板で焼く『コマメク』」「バラ肉を網焼き」「コマ肉を鉄板(フライパン)で調理」🚨 造語化。バラ肉→バラボク、コマ肉→コマメク、網焼き→鉄格子
力飯(ちからめし)」本文は「チカラめし」「東京チカラめし」のみ🚨 ファイル名ChikaraMeshiからの逆翻訳っぽい
「文化人類学的な考察」「インターネットミームの起源」「消滅の危機に瀕した文化遺産」該当記述なし🚨 フレーム丸ごと創作

事実は拾えていて具体数値や日付は合う。だが固有名詞を勝手に造語化する(「バラ肉」→「バラボク」、「網焼き」→「鉄格子」)のと、無かった大げさな位置付けを付与する癖が目立つ。幻覚というより造語症と言ったほうが正確で、元の単語を原形のまま出せば足りる場面で必ずひとひねり加えてくる。

この挙動が出ると検索・引用用途には使えない。PDFの中身をgrep代わりに取り出したいのに、固有名詞が造語に置き換わっていたら、その答えから元の原稿を検索できない。

他のモデルも並べて比較

gemma3:12bだけで片付けるのは中途半端なので、手元のOllamaに入っている日本語チャット候補を順番に同じPDFへぶつけた。全部同じ質問「このPDFの内容を簡単に説明して」を新規チャットセッションから投げる形。

open-notebookのChat Modelは /api/models/defaults を叩けばAPIから切り替えられる。UIの選択肢が長いモデル名で潰れた場合も、

curl -sS -X PUT http://localhost:5055/api/models/defaults \
  -H "Content-Type: application/json" \
  -d '{"default_chat_model":"model:<id>"}'

既存セッションは chat_session:xxxx 単位で model_override が固着しているので、こちらも直接書き換えた。

curl -sS -X PUT http://localhost:5055/api/chat/sessions/<sid> \
  -H "Content-Type: application/json" \
  -d '{"model_override":"model:<id>"}'

並べた結果

モデルサイズ応答情報量共通造語(バラボク/鉄格子)新規造語細部の拾い
qwen3.6:35b23GB MoE🚨 ハング
gemma3:12b8.1GB両方 🚨買収/業態転換は主客ズレで拾う
gemma3:4b3.3GBバラボクのみ✅ 消失・鉄格子🚨タレの醤油/味噌を拾う
gemma4:e4b9.6GB同上gemma3:4bと一字一句同じ出力
Qwen3-Swallow-30B-A3B(日本語LLM整理18GB MoE両方 🚨🚨「焼肉丼」店舗の30/60点スコアを抽出、関東/関西/中国地方まで推論
qwen3.5-abliterated(abliterated解説17GB最多両方 🚨🚨「マッコ社」米のブレンド・器の規格不統一まで拾う
qwen3.5:35b23GB MoE最多両方 🚨🚨「焼き牛肉の丼」abliterated とほぼ同文、番号リスト表記
LLM-jp-4-32B-A3B-thinking(ROCmベンチ、mmnga-o GGUF)21GB MoE🚨 出力破綻
LLM-jp-4-32B-A3B-thinking(alfredplpl GGUF)18GB MoE (MXFP4)🚨 出力破綻

観察

  • 共通造語の発生源はおそらく insight 側。バラボク・コマメク・鉄格子がgemma・Swallow・qwen3.5系のすべてに出る。insight(PDF取り込み時にqwen3.6:35bで生成した要約)に一度紛れ込んだ表現が、各チャットモデルの入力として共有されているため、下流モデルが何であれ同じ造語を吐いてしまう、という仮説が最も整合する
  • 各モデル固有の新規造語もある。Swallowは「焼肉丼」、qwen3.5-abliteratedは「マッコ社」(株式会社マックの誤記)、qwen3.5:35bは「焼き牛肉の丼」。造語は insight だけが原因ではなく、モデル側でも追加される
  • Qwen3.5系(Swallow・abliterated・素3.5)は拾える情報量が多い。30/60の総合評価点、米ブレンドの記述、器の規格不統一、FC 初店舗といった insight に出てこない細部まで引っ張ってくる。BGE-m3 のベクトル検索で source チャンク側に直接ヒットしている
  • Gemmaは安全寄りで拾える情報量が少ない。小さい 4B/e4B は逆に「バラボク」造語を避ける傾向もあり、情報量と造語リスクはトレードオフになっている
  • gemma3:4b と gemma4:e4b が一字一句同じ出力を返した。insight が同じ 433 トークンで、温度が同じなら、世代違いでも同型の出力に収束する(Gemmaファミリーの日本語トークナイズ挙動が世代間で近いことを示唆)
  • LLM-jp-4 は現状 Ollama から叩けない。mmnga-o と alfredplpl の別コンバート両方で、/api/chat を叩くと \x01 \x08 などの制御文字混じりの壊れたトークン列が返ってくる。/api/generate に素のプロンプトを投げれば正常に日本語を返すので、モデル本体は生きている。GGUFに格納されているchat template(もしくは特殊トークン定義)が Ollama の llama.cpp と噛み合っていない。生成速度自体は 67 tok/s 前後で出ているだけに惜しい

何を選ぶか

速度・精度・安定性のバランスで選ぶとこうなる。

用途選択
とにかく応答が欲しい、内容はゆるくていいgemma3:12b
日本語の細部を拾いたい、大まかなまとめでは済まないQwen3-Swallow-30B-A3Bqwen3.5:35b
最新の Qwen を使いたいqwen3.6:35b が動く条件を探る(次の Ollama アップデート待ち?)
純国産を試したいLLM-jp-4 の Ollama 対応待ち(もしくは MLX から直接叩く)

造語症はどのモデルでも残った。insight を使わない純ベクトル検索モードや、insight 生成側のモデルを変える方向で詰めれば改善の余地はありそうだが、そこは次回の宿題。今回の記事では「完全ローカルで動かすこと自体は現実的、ただしモデル選びが実用の分水嶺」というところまでが結論になる。

なおモデル入れ替えの勘所は過去に散々ぶつけていて、

このあたりと並べて読むと「どのモデルが何に向くか」「なぜ固有名詞を言い換えたがるか」の手触りが立体的になる。open-notebook自体はほぼ過不足なく動いていて、ボトルネックは載せるモデルに移った、というのが今回の手応え。これで手元のM1 Maxが、データを一切外に出さずに自分の記事・メモ・PDFを問い合わせできるマシンにはなった。