aptpod Tech Blog

株式会社アプトポッドのテクノロジーブログです

オンデバイスでLLM+RAGを動かす〜Ollama + Open WebUI + Open Notebook

こんにちは、ソリューションアーキテクトの尾澤です。

この記事は「aptpod Advent Calendar 2025」の12月9日の記事です。

皆さんは ChatGPT や Gemini のような「LLMチャットツール」をどのように活用していますか?PDF資料を分析するためにNotebook LMのような「RAGツール」を活用する場面もあるでしょう。とても便利で、すでに仕事道具として手放せないという人も多いのではないでしょうか?

本記事では、これらのツールをSaaSに頼らずオンデバイスで動かせるように、必要な構成と手順を紹介していきます。

なぜオンデバイスなのか

オンデバイスでAIを動かすメリットは、大きく以下の3つが考えられます。

情報漏洩リスクの回避

SaaSは手軽で便利である一方、データが外部に送信されることが前提です。顧客資料・非公開の内部資料・個人情報などの機密データを「外部に出せない」という要件は珍しくありません。

また、今年2025年7月には、OpenAIのサム・アルトマンCEOが「ChatGPTには守秘義務がなく法的請求があれば会話内容が開示されるため、心の闇や秘密をすべてさらけ出すのはやめたほうがいい」と警告しています。

オンデバイスであれば、そもそもデータが外部に送信されることがないため、安心してAIを使えます。

外部依存の回避

SaaSを利用することは、サービス提供者によるモデル変更・料金改定・レートリミットなどに依存することにもなります。

オンデバイスであれば、そういった外部要因に影響されることなく、UIやツールチェーンを変更せずにモデルを入れ替えながらワークフローを維持できます。

オフライン・エッジ用途への転用

レイテンシ要件が厳しいユースケースや、ネットワーク帯域が弱い環境など、システム構成にSaaSを組み込めないことがあります。

オンデバイスであれば、場所を選ばずにAIを持ち込めます。

デメリット

最大のデメリットとして「SaaS独自のプロプライエタリなモデル」が使えないという点があります。

しかしその一方で、オープンなモデルが豊富なバリエーションで公開されています。 例えば、今年2025年8月にOpenAIが公開した「GPT-OSS」は、SaaSで提供していた「o3-mini」と同等の推論能力をベンチマークで示しており、こうした優れたモデルも次々と現れています。

このようなモデルを用途に応じて選定し、場合によっては追加でチューニングを行うことで、目的を実現することが十分に可能です。

オンデバイスAIの分野では、モデルの選択が重要なテーマになってきます。

動作環境

この記事では GPU を搭載した Windows 環境を前提に手順を紹介します。GPUの有無によって処理速度とユーザ体験が大きく変わるため、実用的な体験ができる環境で解説を進めます。

また、拡張性や可搬性を考慮して、すべてのコンポーネントをDockerコンテナで実行します。後述する docker-compose.yml はそのまま使えるようになっています。

本記事で使用した環境

  • Windows 11
  • CPU: Intel Core i7-14700F (2.10 GHz)
  • GPU: NVIDIA GeForce RTX4070 Ti SUPER (16GB)
  • RAM: 16GB
  • Docker Desktop + NVIDIA Container Toolkit

実は、このGPU環境は、小学生の息子がマイクラのMODやらコマンドやらに興味を持ち始めたのをいいことに、それを口実にして家庭内稟議を通して購入したゲーミングPCです。 本当は私自身が、都市開発シミュレーションゲーム「Cities Skylines II」をやりたくて、虎視眈々と狙っていたのです。 この度、このマシンがゲーム以外の分野で活躍できることに興奮しております。

LLMチャットを動かす

それでは早速、LLMチャットを動かしていきましょう。

以下のGitリポジトリをダウンロードしてください。 https://github.com/shumaijp/local-llm-chat-docker-compose

services:
  ollama:
    # image: ollama/ollama:latest
    image: ollama/ollama:0.13.1
    container_name: ollama-chat
    ports:
      - "11434:11434"
    volumes:
      - ollama-data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  open-webui:
    # image: ghcr.io/open-webui/open-webui:main
    image: ghcr.io/open-webui/open-webui:v0.6.41
    container_name: open-webui
    ports:
      - "3030:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    volumes:
      - open-webui-data:/app/backend/data
    depends_on:
      - ollama

  searxng:
    # image: searxng/searxng:latest
    image: searxng/searxng:2025.12.7-9d3ec9a2a
    container_name: searxng
    ports:
      - "8080:8080"
    volumes:
      - ./config/searxng:/etc/searxng:rw

volumes:
  ollama-data:
    name: shared-ollama-data
  open-webui-data:

モデルエンジン

まずは、LLMの実行環境について説明します。

Ollamaとは

Ollamaは「LLM版のDocker」と表現すると分かりやすいかもしれません。Dockerがコンテナを対象とするように、OllamaはLLMを対象として、実行エンジン・CLIツール・公開リポジトリを提供します。

LLMのモデル入手

OllamaのリポジトリにあるLLMは以下のURLで確認できます。 https://ollama.com/library

Ollama Library

それぞれのモデルには「8b」「20b」のようなタグが表記されています。タグにある数字はパラメータの数を示しており、AIモデルの脳細胞の数のようなものです。パラメータが多いほど賢く知識が豊富でアウトプットの質が高い反面、メモリやGPUなどのリソースをより多く消費します。

使用するときは「モデル名:タグ名」という形式で指定します。

今回はまず、軽量でポピュラーな「gemma2:2b」を使ってみます。コンテナを起動してCLIツールを使ってダウンロードしてみましょう。

docker compose up -d
docker exec ollama-chat ollama pull gemma2:2b
動作確認

Ollamaコンテナが正しく動いているかCLIでAPIを叩いて確認してみましょう。

curl http://localhost:11434/api/generate \
    -d '{"model": "gemma2:2b", "prompt": "Hello"}'

以下のように、レスポンスが返ってくれば正常に動いています。

{"model":"gemma2:2b","created_at":"2025-12-06T06:52:59.907909427Z","response":"Hello","done":false}
{"model":"gemma2:2b","created_at":"2025-12-06T06:52:59.980637427Z","response":"!","done":false}
{"model":"gemma2:2b","created_at":"2025-12-06T06:53:00.07509926Z","response":" 👋","done":false}
....

チャットUI

つぎに、UIについて説明します。

Open WebUIとは

Open WebUIは、LLMを「ChatGPTっぽい快適なUIで使えるようにする」ためのWebアプリです。

LLMが「バックエンド」、Open WebUIが「フロントエンド」という役割分担になっています。

Open WebUIの環境変数(OLLAMA_BASE_URL)でOllamaをバックエンドに指定することで、チャット画面の裏側でLLMへのAPIリクエストが走る仕組みになっています。

実際に使ってみる

コンテナが起動している状態で、 http://localhost:3030 にブラウザでアクセスしてみてください。

初回アクセス時は、ユーザ登録画面が表示されます。アカウントを作成してログインすると、下図のような ChatGPT ライクな画面が表示されます。

使用モデルが「gemma2:2b」になっていることを確認して、チャットを開始してみましょう。

gemma2:2bの結果

違うモデルを使ってみる

動作確認のために軽量な「gemma」を使いましたが、他にもMicrosoftの「phi」、Metaの「llama」、Alibabaの「Qwen」など多くのLLMが公開されており、GGUF(GPT-Generated Unified Format)というファイルフォーマット規格によって、これらを同じプラットフォーム上で切り替えて使えるようになっています。

試しにOpenAIの「gpt-oss:20b」をダウンロードして使ってみましょう。

docker exec ollama-chat ollama pull gpt-oss:20b

ダウンロード済みのモデルは以下のコマンドで確認できます。

% docker exec ollama-chat ollama list
NAME                        ID              SIZE      MODIFIED
gpt-oss:20b                 17052f91a42e    13 GB     3 minutes ago
gemma2:2b                   8ccf136fdd52    1.6 GB    4 hours ago

モデルを「gpt-oss:20b」に切り替えて、先ほどと同じプロンプトを投げてみましょう。

gpt-oss:20bの結果

このように、モデルによって推論結果が大きく変わってきます。

検索機能をつける

ChatGPTなどのLLMチャットが、もともと学習していない情報をウェブ検索で補って回答してくれるようになってから、利便性がグッとあがりましたよね。

オンデバイスでも、インターネットに出られる環境であれば同じことが可能です。

ユーザアイコンをクリックしたメニューから「設定」→「管理者設定」に進んで「ウェブ検索」を有効にし、「ウェブ検索エンジン」を指定します。APIキーを用意すれば、GoogleやPerplexityなどのSaaSも使えますが、今回は情報漏洩リスクの回避もひとつのテーマなので、メタ検索エンジンである「SearXNG」を使ってみましょう。すでに docker-compose.yml に定義してあるコンテナのそのエンドポイント「http://searxng:8080」を指定して、設定を保存してください。

ウェブ検索の設定

設定が終わったらプロンプトに戻ります。プロンプトの「Integration」のアイコンから「ウェブ検索」を有効にして、インターネットを参照しないとわからないようなことを尋ねてみましょう。

ウェブ検索ありの結果

ウェブから情報を取得して回答してくれました。メタ検索エンジンの SearXNG を介しているので、Google等に直接ユーザのIPアドレスやクエリが送信されることはありません。

コンテナを終了する

使い終わったらコンテナを終了させておいてください。

docker compose down

本記事では、ユースケースごとの分かりやすさと学習のしやすさのため、LLMチャット用とRAGツール用でDocker Compose環境をそれぞれ分けて紹介しています。

このあと説明するRAGツールでもOllamaコンテナを使用しますが、両方のユースケースを同時に起動するとポートが衝突するため、それぞれ別タイミングで起動する前提としています。

RAGツールを動かす

チャットの次は、Notebook LMのようなRAGツールを動かしてみましょう。

RAGツールとは

ChatGPTやGeminiは使ったことあるけど、Notebook LMは使ったことないという人もいるかもしれません。

RAGツールは、特定のウェブページやファイルを読み込ませることで、LLMに対象ドメインを与えて使う仕組みです。 LLMチャットツールが「世界中の知識をもとに答えるAI」であるのに対して、RAGツールは「渡された資料を読んで答えるAI」です。

Googleの「Notebook LM」は、この代表的なツールです。

Open Notebookとは

Notebook LMのようなRAG体験をオンデバイスで再現できるのが 今回使用する「Open Notebook」です。
https://www.open-notebook.ai/

Open WebUIと同じように、裏側でLLMへのAPIリクエストが走る仕組みになっています。

以下のGitリポジトリをダウンロードしてください。 https://github.com/shumaijp/local-opennotebook-docker-compose

services:

  ollama:
    # image: ollama/ollama:latest
    image: ollama/ollama:0.13.1
    container_name: ollama-rag
    ports:
      - "11434:11434"
    volumes:
      - ollama-data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  open_notebook:
    # image: lfnovo/open_notebook:v1-latest-single
    image: lfnovo/open_notebook:1.2.3-single
    container_name: open-notebook
    ports:
      - "8502:8502"  # Web UI
      - "5055:5055"  # API
    environment:
      - API_URL=http://127.0.0.1:5055
      - OLLAMA_API_BASE=http://ollama:11434
      # Database connection (required)
      - SURREAL_URL=ws://localhost:8000/rpc
      - SURREAL_USER=root
      - SURREAL_PASSWORD=root
      - SURREAL_NAMESPACE=open_notebook
      - SURREAL_DATABASE=production
    volumes:
      - notebook-data:/app/data
      - surreal-data:/mydata
    depends_on:
      - ollama

volumes:
  ollama-data:
    name: shared-ollama-data
  notebook-data:
  surreal-data:

なお、上記で使用しているイメージ(lfnovo/open_notebook:1.2.3-single)は、Open Notebookを構成するコンポーネントがすべて入っているオールインワン・イメージです。

中身は「フロントエンド」と「バックエンド(RAG)」と「データベース(SurrealDB)」で構成されており、バックエンドとデータベースだけ立ててAPI経由で活用する、という使い方も可能です。

初期設定

RAGを実現するためには、LLMの他に「埋め込みモデル(Embedding Model)」という別の役割のモデルが必要です。

埋め込みモデルとは

埋め込みモデルとは、テキストや画像などのデータを数値のベクトルに変換するモデルのことで、RAGが与えられた情報を解釈するために必要なものです。

意味的に近いデータがベクトル空間上でも近くに配置されることによって、データの類似性を理解できるようになるというものです。

埋め込みモデルの入手

それでは、コンテナを起動して埋め込みモデルをダウンロードしていきましょう。

今回は速度や精度のバランスが最も優れていると言われている「mxbai-embed-large」を使用します。

docker compose up -d
docker exec ollama-rag ollama pull mxbai-embed-large

なお、今回のコンテナ構成では、Ollama用にダウンロードしたモデルは「ollama-data」という名前付きボリュームに格納されるようになっています。これにより、RAGツール用の「ollama-rag」コンテナと、先ほどのチャット用の「ollama-chat」コンテナは、ダウンロードしたモデルを共有できるようになっています。

それでは、 http://localhost:8502 にブラウザでアクセスしてみてください。

Open Notebook

モデルの設定

Open WebUIと違ってモデルを使うための初期設定が必要です。

左ペインの「MANAGE」→「Models」から「Model Management」の画面を開きます。

まず、「API Providers」のセクションで「Ollama」が有効になっていることを確認してください。

Open Notebookの設定画面

「Default Model Assignments」のセクションで、Open Notebookが使用する様々なモデルを指定しますが、そのためにモデルをあらかじめ登録しておく必要があります。

「Language Models」のセクションで、ダウンロード済みの「gpt-oss:20b」を言語モデルに登録しましょう。「+ Add Model」をクリックし、Providerを「Ollama」、Model Nameに「gpt-oss:20b」を入力して「Add Model」してください。

Language Modelの登録

「Embedding Models」のセクションで、ダウンロード済みの「mxbai-embed-large」を埋め込みモデルに登録しましょう。同じく画面下の「+ Add Model」をクリックし、Providerを「Ollama」、Model Nameに「mxbai-embed-large」を入力して「Add Model」してください。

Embedding Modelの登録

「Default Model Assignments」のセクションに戻ると、登録したモデルを指定できるようになっているので、必須マークがついている Chat Model と Transformation Model に「gpt-oss:20b」を、Embedding Model に「mxbai-embed-large」を設定してください。

モデル設定

実際に使ってみる

それでは使ってみましょう。 本記事ではデモとして、MQTTのRFCドキュメントを分析対象にしてみます。

まず「+ New Notebook」からNotebookを作ります。

Notebookの作成

つぎに「+ Add Source」→「+ Add New Source」から、参照する資料をURL・ファイル・プレーンテキストのいずれかで追加します。 今回は、RFCのURL「https://www.rfc-editor.org/rfc/rfc9431.html」を指定します。

ソースの追加

登録したSourceのベクトル解析が終わったら質問してみましょう。

あえて「MQTTの」とは言わずに「QoS 0/1/2 の違いを要約して」と入力してみると、ちゃんとRFCを読んで「MQTTのQoS」について回答してくれました。

RAGの結果

一見あいまいな質問ですが、ChatGPTのように話題がズレることなく、与えられた情報の範囲内で答えを探してくれます。

このように、推論の前提となる情報源が「指定した資料」に限定されるため、一般的なLLMチャットにありがちなハルシネーションが起こりにくいのが、RAGの特徴です。

Macユーザの場合

本記事で解説したツールは、Apple Silicon Mac でも問題なく動作します。

手順もほぼ同じで、docker-compose.yml をGitリポジトリに用意した docker-compose-cpu.yml に読み替えるだけで動きます。

# GPU環境の場合
docker compose up -d
# 非GPU環境の場合
docker compose -f docker-compose-cpu.yml up -d

ただし、macOSではApple SiliconのGPUをDockerから使えないため、同じモデルでも推論速度は大きく低下します。

OllamaをDockerではなくシステム上で動かすことで、Apple SiliconのGPUが活かされある程度のパフォーマンスは期待できます。

brew install ollama
ollama serve

しかし、実際に普段業務で使用している M4 MacBook Pro でも試してみましたが、Metalサポートが進んでいるとはいえ、CUDA対応GPUに最適化されたモデルではやはり性能差は歴然です。

「まず手元の環境でLLMに触ってみたい」場合にはMacでも十分ですが、「日常的に生産性ツールとして使いたい」場合は、GPU環境のほうが圧倒的に快適です。

以下は「GPU Windows + Ollama on Docker」と「M4 Mac + Ollama on Docker」と「M4 Mac + Ollama on System」の3つの環境で同じプロンプトの性能を比較した表です。

環境 gemma2:2b gpt-oss:20b 推論リソース
GPU Windows + Ollama on Docker 6.24 tokens/sec 2.11 tokens/sec CUDA/GPU
M4 Mac + Ollama on System 1.60 tokens/sec 0.68 tokens/sec Metal/GPU
M4 Mac + Ollama on Docker 0.45 tokens/sec 0.13 tokens/sec CPU

Apple Siliconの方向性

ただし、この結果は決して「Applie Siliconが遅い」という話ではありません。

Apple Siliconはそもそも「AIをオンデバイスで高速かつ省電力で動かすこと」をターゲットに設計されています。

そのため、Macユーザが本来とるべきアプローチは、NVIDIAスタックで学習したモデルをそのまま動かすことではなく、それをCore ML 形式に最適化して macOS / iOS / visionOS などで動かすことです。

ですので、前述の比較表は優劣を比較するためのものではなく、あくまで参考値として見ていただけると幸いです。

すでに、Meta・Mistral・Alibaba などの主要モデルが Core ML 版を公開しており、Apple Silicon 上での AI 活用は来年以降もさらに実用化が進んでいきそうだと感じています。

本記事では「NVIDIAスタックで学習されたモデルを動かす」という前提のため Core ML 最適化には触れていませんが、こちらの動向も把握しておくと良いと思います。

まとめ

LLMやRAGをオンデバイスで動かすことで、機密データをクラウドに出さずにAIの恩恵を受けられるだけでなく、それをそのままエッジに持ち込めるというメリットがあります。

これは産業IoTにおいても重要なテーマで、エッジ環境における「セキュリティ」「レイテンシ」「ネットワーク」などの要件を、AIにも適用できるということになります。

今回紹介した構成はデスクトップでの検証用途ですが、アプリケーション層とモデル層がAPI分離されているため、ゲートウェイやMEC環境への移植も可能です。

もし「こんなことをAIでできないか?」というアイデアが浮かんだ時、今回構築した環境がその入口になれば嬉しいです。