やってみた! IM-LogicDesigner 拡張で Ollama とつないでみた

はじめに

最近、ローカルで動作する大規模言語モデル「Ollama」に出会いました。

ChatGPT のような AI を自分の PC で動かせるという点にとても興味を持ち、さっそく試してみることに。

一方で、普段触っている「IM-LogicDesigner」との連携ができたら、もっと面白いことができるのでは?と思い立ち、今回のチャレンジが始まりました。

私はまだ intra-mart 開発を学び始めたばかりの初心者ですが、だからこそ「初心者でもここまでできる!」という視点で、試行錯誤の過程を記録しておきたいと思いました。

この記事では、Ollama と IM-LogicDesigner を連携させるまでの流れを、環境構築から実装、つまずいたポイントまで、できるだけ丁寧に紹介していきます。

この記事はこんな人におすすめ

  • intra-mart 環境で Ollama を使ってみたいけど、何から始めればいいかわからない人
  • IM-LogicDesigner 拡張プログラミングに興味がある人

そもそも Ollama とは?

Ollama は、ローカル環境で大規模言語モデル(LLM)を動かせるツールです。

ChatGPT のような AI を、クラウドに頼らず 自分の PC やサーバー上で動かすことができます
そのため、インターネット接続がなくても使えたり、データを外部に送らずに済むので セキュリティやプライバシーの面でも安心です。

また、Java 向けに Ollama4j というライブラリも提供されています。

Ollama4j は、Java から Ollama の API を簡単に扱えるライブラリで、Java ベースのアプリケーションや拡張機能から Ollama を手軽に呼び出すことができます。

そもそも IM-LogicDesigner 拡張プログラミングとは?

IM-LogicDesigner 拡張プログラミングは、文字通り IM-LogicDesigner に独自のタスクや機能を追加するための方法です。

今回は 拡張プログラミングを使って Ollama 連携用のフロー要素(タスク)を追加、Ollama4j を使って Ollama の API を呼び出し、LLMの応答を取得する処理を組み込みました。

環境構築

今回の連携では、Docker コンテナ上で Ollama を動かし、IM-LogicDesigner 独自タスクから Ollama4j を使って API 連携する構成にしました。

Maven などのビルドツールは使用せず、必要最小限の JAR ファイル(Ollama4j に関連するもの)を手動で lib フォルダに追加する方法を採用しています。
これは学習目的での検証であるため、シンプルで扱いやすい構成を目指しました。

なぜこの構成にしたのか?

今回の技術検証では、業務アプリに自然言語処理(NLP)を組み込むことを目指して、以下の3つのポイントを意識して構成を考えました。

  1. Docker を使って開発環境をシンプルに

    • 毎回開発環境を整えるのは手間がかかるので、Dockerを使って環境をまとめて管理できるようにしました。これにより、依存関係のトラブルを減らし、いつでも同じ状態で作業を始められることを意識しました。
  2. ローカルで動く LLM(大規模言語モデル)を効率よく試すため

    • 今後、ローカルで動作する LLM(たとえば Ollama など)をいくつか試していきたいと考えました。新しい LLM やモデルが登場したときにもすぐに切り替えて検証できるよう、環境の切り替えがしやすい構成を意識しました。
  3. Java から簡単に LLM を使えるようにしたかった

    • IM-LogicDesigner 拡張プログラミングとの親和性などを考慮した結果、LLM を Java から簡単に呼び出せる方法が必要と考えました。そこで、Ollama4j という Java 用の SDK を使うことで、API の扱いをシンプルすることを意識しました。
なぜ Ollama4j を選んだのか?

いくつかの方法を検討した中で、Ollama4j は最も手軽に Java から Ollama を扱えると感じました。
HTTP リクエストを自分で書く必要がなく、コードもすっきりします。ドキュメントも比較的わかりやすく、試しながら使うにはちょうど良い選択肢でした。

参考情報

Ollama4j 以外にも、以下のような選択肢がありました。

  • langchain4j-ollama

    • LangChain の Java 実装と組み合わせて使えるライブラリで、より複雑な処理や連携を柔軟に構成したい場合に適しています。
  • Apache HttpClient を使った直接連携

    • SDK を使わずに HTTP リクエストを自前で実装する方法です。柔軟性は高いものの、その分実装コストが増します。

Docker Compose のインストール

まずは 利用する OS に沿った手順で Docker と Docker Compose のインストールから始めました。

Docker のインストールについては、ネットに沢山情報が載っているので割愛します。

インストール後、以下のコマンドを実行して、正しくインストールできたか確認しました。

docker version
docker compose version

Ollama コンテナの準備、起動

docker-compose.yml ファイルを作成しました:

services:
  # Ollama: LLMエンジン
  ollama:
    container_name: ollama
    image: ollama/ollama:latest
    volumes:
      # モデルや設定ファイルの永続化
      - ./runtime/ollama:/root/.ollama
      - ./config/models:/data/models
    environment:
      - TZ=Asia/Tokyo
    ports:
      - "11434:11434"
    networks:
      - public

networks:
  public:

この構成でコンテナを起動しました:

docker compose up -d

ブラウザで http://<ホスト名>:11434 にアクセスし、Ollama に正常に接続できることを確認しました。

Ollama is running

モデルのデプロイ

ollama コンテナ内でチャットと埋め込みモデルをデプロイするため、以下の手順を実行しました。

  1. ollama コンテナ内に入る:
docker exec -it ollama /bin/bash
  1. チャット用モデルをデプロイ:
ollama pull gemma3
  1. 埋め込み用モデルをデプロイ:
ollama pull all-minilm

これでモデルがローカルにダウンロードされ、使用可能になりました。


ちなみに、Ollama では他にもさまざまなモデルが提供されています。

たとえば、文章生成やチャットに強いモデルとしては llama3 や mistral が人気です。llama3 は高精度な応答が得られる一方で、mistral は軽量で高速な処理が特徴です。

また、埋め込み(ベクトル化)に特化したモデルとしては、今回使った all-minilm のほかに、より高精度な mxbai-embed-large というモデルもあります。こちらは意味理解の精度が高く、類似文書検索などに向いています。

e Builder モジュールプロジェクトの作成

開発環境として、今回は以下の構成を使用しました。

なぜ IM-Copilot を含めたのか?

IM-Copilot には、チャット用の画面やUIコンポーネント(アシスタントUI)が用意されています。
今回はその画面を活用し、クライアントサイドの実装を省略しつつ、表示の確認を行うことにしました。

Ollama4j に関するライブラリの入手、配置

Ollama4j に関するライブラリを入手、e Builder モジュールプロジェクトに配置しました。

  1. GitHubリポジトリ Releases ページにアクセス:

  2. 最新の JAR ファイルをダウンロード

  3. ダウンロードした JAR ファイルをモジュール・プロジェクトの lib フォルダに配置

  4. POM.xmlの依存関係を参考に、その他に必要なライブラリを確認

  5. Maven Central や MVNRepository から 「jackson-datatype-jsr310」の JAR ファイルをダウンロード

  6. ダウンロードした JAR ファイルをモジュール・プロジェクトの lib フォルダに配置

e Builder モジュール・プロジェクトへの Jar ファイル配置イメージ
└─OllamaClientSample
    └─src
        └─main
            ├─conf
            │  ├─message
            │  ├─routing-jssp-config
            │  └─routing-service-config
            ├─generated
            ├─java
            ├─jssp
            │  └─src
            ├─plugin
            ├─public
            ├─resources
            ├─schema
            ├─storage
            │  ├─public
            │  └─system
            └─webapp
                └─WEB-INF
                    └─lib
                            jackson-datatype-jsr310-2.17.1.jar
                            ollama4j-1.0.100.jar

接続確認

接続確認用のクラスを作成、Ollama API に接続できるか確認しました。

import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
import io.github.ollama4j.models.chat.OllamaChatRequest;
import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;

public class OllamaPoke {

    static String BASE_URL = "http://192.168.56.117:11434/";
    static String PROMPT = "こんにちは";

    public static void main(String[] args) throws Exception {

        // Ollama APIクライアントを指定したベースURLで初期化
        OllamaAPI ollamaAPI = new OllamaAPI(BASE_URL);

        // デバッグ用に詳細ログを出力(初期化時の確認用)
        ollamaAPI.setVerbose(true);

        // リクエストのタイムアウト時間を設定(秒単位)
        ollamaAPI.setRequestTimeoutSeconds(300);

        // 通常運用用に詳細ログを無効化
        ollamaAPI.setVerbose(false);

        // 使用するモデルを指定してチャットリクエストビルダーを取得
        OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance("gemma3:latest");

        // ユーザーからのメッセージを含むチャットリクエストを構築
        OllamaChatRequest chatRequest = builder.withMessage(OllamaChatMessageRole.USER, PROMPT).build();

        // チャットリクエストをストリーミングで送信し、応答トークンを逐次出力
        ollamaAPI.chatStreaming(chatRequest, token -> System.out.print(token.getMessage().getContent()));
    }
}

モデルからの応答がコンソールに表示されました!

cookbook260738_03_en

もしうまくいかない場合は、以下をチェック:

  • Dockerコンテナが起動しているか
  • ポート 11434 が開いているか
  • Java側で正しいURLを指定しているか

ここまでで環境が整いました!

これで、「Ollama」と「e Builder モジュール・プロジェクト」の連携準備が完了しました。

次のセクションでは、実際にどのように連携処理を実装したかをご紹介します。

Ollama チャットタスク、埋め込みタスクの作成

以下を参考に Ollama 連携用のタスクを作成しました。


作成したタスクのソースは、以下からダウンロードできます。
cookbook260738_ollama_ld_task_source_sample.zip
なお、これは intra-mart Accel Platform 2025 Spring 上で動作確認したソースです。
上記環境以外では、正常に動作しない場合があります。

モジュール・プロジェクトへのソースファイル配置イメージ
└─OllamaClientSample
    └─src
        └─main
            ├─conf
            │  ├─message
            │  ├─routing-jssp-config
            │  └─routing-service-config
            ├─generated
            ├─java
            │  │  OllamaPoke.java
            │  │
            │  └─org
            │      └─example
            │          └─logicdesigner
            │              └─element
            │                  └─ollama
            │                      │  OllamaCategory.java
            │                      │  OllamaPackageFactory.java
            │                      │
            │                      ├─chat
            │                      │      ChatChunkMessage.java
            │                      │      ChatContents.java
            │                      │      ChatMessage.java
            │                      │      ChatParameter.java
            │                      │      ChatResult.java
            │                      │      ChatTask.java
            │                      │      ChatTaskMetadata.java
            │                      │
            │                      └─embed
            │                              EmbeddingParameter.java
            │                              EmbeddingResult.java
            │                              EmbeddingTask.java
            │                              EmbeddingTaskMetadata.java
            │
            ├─jssp
            │  └─src
            ├─plugin
            ├─public
            ├─resources
            │  └─META-INF
            │      └─services
            │              jp.co.intra_mart.system.logic.factory.ElementScanPackageFactory
            │
            ├─schema
            ├─storage
            │  ├─public
            │  └─system
            └─webapp
                └─WEB-INF
                    └─lib
                            jackson-datatype-jsr310-2.17.1.jar
                            ollama4j-1.0.100.jar

Ollama チャットタスク

はじめに Ollama チャットタスクを作成、動作確認しました。

package org.example.logicdesigner.element.ollama.chat;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.example.logicdesigner.element.ollama.OllamaCategory;

import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.models.chat.OllamaChatMessage;
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
import io.github.ollama4j.models.chat.OllamaChatRequest;
import io.github.ollama4j.models.chat.OllamaChatResult;
import jp.co.intra_mart.common.aid.jdk.java.lang.StringUtil;
import jp.co.intra_mart.foundation.copilot.action.chat.ChunkChatMessage;
import jp.co.intra_mart.foundation.copilot.action.chat.ChunkChatMessageDelta;
import jp.co.intra_mart.foundation.logic.FlowElementDataHandler;
import jp.co.intra_mart.foundation.logic.annotation.LogicFlowElement;
import jp.co.intra_mart.foundation.logic.element.ElementContext;
import jp.co.intra_mart.foundation.logic.element.Task;
import jp.co.intra_mart.foundation.logic.exception.FlowExecutionException;
import jp.co.intra_mart.foundation.logic.exception.LogicServiceRuntimeException;

/**
 * フロー要素クラス.
 */
@LogicFlowElement(id = "ollama_chat_task", category = OllamaCategory.class, index = 100)
public class ChatTask extends Task<ChatTaskMetadata, ChatParameter, ChatResult> {

    public ChatTask(ElementContext context) {
        super(context);
    }

    @Override
    public ChatResult execute(ChatParameter parameter) throws FlowExecutionException {

        try {
            // Ollama APIクライアントを初期化(ベースURLは外部から指定)
            OllamaAPI ollamaAPI = new OllamaAPI(parameter.getBaseUrl());

            // リクエストのタイムアウト時間を設定(秒単位)
            ollamaAPI.setRequestTimeoutSeconds(parameter.getTimeoutSeconds());

            // ログ有効化
            ollamaAPI.setVerbose(true);

            // ユーザーからのメッセージリストを Ollama 用に変換
            List<OllamaChatMessage> messageList = new ArrayList<>();

            for (ChatMessage msg : parameter.getMessages()) {
                OllamaChatMessage message = new OllamaChatMessage();

                // ロールの変換
                String roleStr = msg.getRole().toLowerCase();
                OllamaChatMessageRole role;
                switch (roleStr) {
                case "user":
                    role = OllamaChatMessageRole.USER;
                    break;
                case "system":
                    role = OllamaChatMessageRole.SYSTEM;
                    break;
                case "assistant":
                    role = OllamaChatMessageRole.ASSISTANT;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown role: " + msg.getRole());
                }
                message.setRole(role);

                // メッセージ内容の設定(contents[0].text または content のいずれか一方にメッセージが存在する場合を想定)
                if (msg.getContents() != null && msg.getContents().length > 0) {
                    // 複数の ChatContents が存在する場合、それぞれの text を改行で結合
                    StringBuilder contentBuilder = new StringBuilder();
                    for (ChatContents content : msg.getContents()) {
                        if (content != null && content.getText() != null) {
                            contentBuilder.append(content.getText()).append("\n");
                        }
                    }
                    // 結合された内容が存在する場合のみ content に設定(末尾の改行は trim で除去)
                    if (contentBuilder.length() > 0) {
                        message.setContent(contentBuilder.toString().trim());
                    }
                } else if (msg.getContent() != null && !msg.getContent().isEmpty()) {
                    // contents が存在しない場合は、単一の content を使用
                    message.setContent(msg.getContent());
                }

                messageList.add(message);
            }
            // チャットリクエストを作成し、メッセージを設定
            OllamaChatRequest chatRequest = new OllamaChatRequest();
            chatRequest.setMessages(messageList);

            // 使用するAIモデル名を設定(例: "llama3", "mistral", など)
            String modelName = parameter.getModelName();
            if (modelName == null || modelName.isEmpty()) {
                throw new IllegalArgumentException("モデル名を指定する必要があります。");
            }
            chatRequest.setModel(modelName);

            if (parameter.isStreaming()) {
                // ストリーミングチャット処理
                return executeStreamingChat(ollamaAPI, chatRequest, parameter);
            } else {
                // 非ストリーミングチャット処理
                return executeNonStreamingChat(ollamaAPI, chatRequest, parameter);
            }

        } catch (final Exception e) {
            throw new LogicServiceRuntimeException(e);
        }
    }

    /**
     * 非ストリーミングチャットを実行し、応答を一括取得.
     */
    private ChatResult executeNonStreamingChat(OllamaAPI ollamaAPI, OllamaChatRequest chatRequest,
            ChatParameter parameter) throws Exception {

        // チャットAPIを呼び出して応答を取得
        OllamaChatResult chatResult = ollamaAPI.chat(chatRequest);

        // 応答メッセージの内容を抽出
        String content = chatResult.getResponseModel().getMessage().getContent();

        // メッセージ履歴と応答から結果を生成
        return ChatResult.from(parameter.getMessages(), content);
    }

    /**
     * ストリーミングチャットを実行し、トークンごとに アシスタント UIへ逐次送信.
     */
    private ChatResult executeStreamingChat(OllamaAPI ollamaAPI, OllamaChatRequest chatRequest, ChatParameter parameter)
            throws Exception {

        // アシスタント UI連携用のハンドラを取得
        final FlowElementDataHandler handler = context.getLogicSession().getFlowElementDataHandler();

        // チャンクメッセージの初期化
        final ChatChunkMessage chunkMessage = new ChatChunkMessage(null, "");
        final String id = UUID.randomUUID().toString();

        // ストリーミングAPIの呼び出し
        ollamaAPI.chatStreaming(chatRequest, token -> {
            try {
                // チャンクメッセージの構築
                ChunkChatMessage chunk = new ChunkChatMessage();
                ChunkChatMessageDelta delta = new ChunkChatMessageDelta();

                delta.setRole(token.getMessage().getRole().getRoleName());
                delta.setContent(token.getMessage().getContent());

                chunk.setDelta(delta);
                chunk.setFinishReason(token.getDoneReason());
                chunk.setId(id);

                // チャンクメッセージデータの設定
                setChunkMessageData(chunk, chunkMessage);

                // ハンドラが設定されている場合は実行(ストリーミングを利用する場合は ロジックフローアシスタントとして実行される必要があります。)
                if (handler != null) {
                    // チャンクを渡すことで、アシスタント UI と連携 されます。
                    handler.execute("im_chat", chunk);
                }
            } catch (final FlowExecutionException e) {
                throw new LogicServiceRuntimeException(e);
            }
        });

        // メッセージ履歴と蓄積された応答から結果を生成
        return ChatResult.from(parameter.getMessages(), chunkMessage.getContent());
    }

    /**
     * チャンクデータを ChatChunkMessage に設定.
     */
    private void setChunkMessageData(final ChunkChatMessage chunk, final ChatChunkMessage message) {
        if (chunk.getDelta() != null) {
            // ロール名が空でない場合、メッセージに設定する
            if (!StringUtil.isBlank(chunk.getDelta().getRole())) {
                message.setRole(chunk.getDelta().getRole());
            }
            // コンテンツが空でない場合、既存のコンテンツに追加する
            if (!StringUtil.isBlank(chunk.getDelta().getContent())) {
                message.setContent(message.getContent() + chunk.getDelta().getContent());
            }
        }
    }
}

実行結果

動かした結果、期待通りの結果を得られました。

cookbook260738_07_en

Ollama 埋め込みタスク

次に Ollama 埋め込みタスクを作成、動作確認しました。

package org.example.logicdesigner.element.ollama.embed;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.example.logicdesigner.element.ollama.OllamaCategory;

import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
import jp.co.intra_mart.foundation.logic.annotation.LogicFlowElement;
import jp.co.intra_mart.foundation.logic.element.ElementContext;
import jp.co.intra_mart.foundation.logic.element.Task;
import jp.co.intra_mart.foundation.logic.exception.FlowExecutionException;
import jp.co.intra_mart.foundation.logic.exception.LogicServiceRuntimeException;

/**
 * フロー要素クラス.
 */
@LogicFlowElement(id = "ollama_embedding_task", category = OllamaCategory.class, index = 100)
public class EmbeddingTask extends Task<EmbeddingTaskMetadata, EmbeddingParameter, EmbeddingResult> {

    public EmbeddingTask(ElementContext context) {
        super(context);
    }

    @Override
    public EmbeddingResult execute(EmbeddingParameter parameter) throws FlowExecutionException {

        try {
            // Ollama APIクライアントを初期化(ベースURLは外部から指定)
            OllamaAPI ollamaAPI = new OllamaAPI(parameter.getBaseUrl());

            // ログ有効化
            ollamaAPI.setVerbose(true);

            // リクエストのタイムアウト時間を設定(秒単位)
            ollamaAPI.setRequestTimeoutSeconds(parameter.getTimeoutSeconds());

            // 埋め込みベクトルを取得
            OllamaEmbedResponseModel response = ollamaAPI.embed(parameter.getModelName(),
                    Arrays.asList(parameter.getInput()));

            // 結果オブジェクトを作成
            EmbeddingResult result = new EmbeddingResult();

            // 埋め込みベクトルが取得できた場合の処理
            if (response != null && response.getEmbeddings() != null && !response.getEmbeddings().isEmpty()) {
                // Double型のリストをFloat型に変換
                List<Double> doubleList = response.getEmbeddings().get(0);
                List<Float> floatList = doubleList.stream().map(Double::floatValue).collect(Collectors.toList());

                // 1536次元に後ゼロ埋め(不足分をゼロで補う)
                List<Float> paddedList = this.padEmbedding(floatList, 1536);

                // 結果に埋め込みベクトルをセット
                result.setEmbeddings(paddedList);
            }

            return result;
        } catch (final Exception e) {
            throw new LogicServiceRuntimeException(e);
        }
    }

    /**
     * 指定された次元数に後ゼロ埋めして返すメソッド
     *
     * @param originalList 元の埋め込みベクトル(Float型のリスト)
     * @param targetDim 目標とする次元数(例:1536)
     * @return 後ろにゼロを追加して指定次元に揃えたベクトル
     */
    private List<Float> padEmbedding(List<Float> originalList, int targetDim) {
        if (originalList == null) {
            throw new IllegalArgumentException("originalList must not be null");
        }

        int originalSize = originalList.size();

        if (originalSize > targetDim) {
            throw new IllegalArgumentException("元のベクトルの次元数が目標次元を超えています: " + originalSize + " > " + targetDim);
        }

        List<Float> paddedList = new ArrayList<>(originalList);

        for (int i = 0; i < targetDim - originalSize; i++) {
            paddedList.add(0f); // float型のゼロ
        }

        return paddedList;
    }
}

実行結果

動かした結果、期待通りの結果を得られました。

cookbook260738_05_en

アシスタントUIへの表示

最後に、IM-Copilot ロジックフローアシスタントで使用している「チャットタスク」と「埋め込みタスク」を、今回作成したタスクに置き換えて動かしてみました。

ロジックフローアシスタントとは

ロジックフローアシスタントは、IM-LogicDesigner を用いて構築されたアシスタント機能です。
このアシスタントは、ストレージ内のファイルを情報源として、ユーザーの質問に自動で回答することができます。

詳細は、「IM-Copilot 利用ガイド」 を参照してください。

ロジックフローアシスタントの「チャットタスク」と「埋め込みタスク」の置き換え

ベクトル情報構築(sample-rag-assistant-vector-build)

タスク変更

IM-Copilot の埋め込みタスクを、今回作成した埋め込みタスクに変更しました。(1タスク変更)

cookbook260738_55_en

サンプルRAGアシスタント(sample-rag-assistant)

タスク変更

IM-Copilot の埋め込みタスクを、今回作成した埋め込みタスクに変更しました。(1タスク変更)

また、IM-Copilot のチャットタスクを、今回作成したチャットタスクに変更しました。(3タスク変更)

cookbook260738_65_en

実行結果

動かした結果、期待通りの結果を得られました。

cookbook260738_16_2_en

試してわかったこと

ストリーミング応答の実装と確認

IM-LogicDesigner に Ollama 連携を組み込み、アシスタント UI 上で実際にチャットができることを確認しました。

試してわかった課題

Ollama はローカル環境で LLM を動かせる点が魅力ですが、実際に使ってみると以下のような課題も見えてきました。

  • 応答速度が全体的に遅め

    • モデルの処理に時間がかかることがあり、体感的に遅さを感じる場面が多いと感じました。
  • PCのスペックに大きく依存する

    • VRAM やメモリが少ない環境では、モデルの読み込みに時間がかかったり、応答がさらに遅くなる傾向を感じました。
  • 長文や複雑なプロンプトへの対応に限界がある

    • ChatGPT などのクラウドベースの LLM と比べると、応答の精度や安定性に差があり、特に複雑な問いに対しては弱さを感じました。

高スペック構成との比較

上記課題感から、以下のハードスペックを切り替えながら、応答速度を比較してみました。

検証方法
  • 検証モデル
    • Gemma2-2b-jpn(日本語最適化モデル)
  • プロンプト
    • 同一プロンプトを各構成で複数回実行(短い質問)
比較環境1
  • m5.large
    • vCPU: 2
    • メモリ: 8GB
    • 結果:
      • 1回目:28.01秒
      • 2回目:2.95秒(キャッシュが効いている可能性あり)
      • 質問変更:7.45秒
比較環境2
  • r5.xlarge
    • vCPU: 4
    • メモリ: 32GB
    • 結果:
      • 1回目:25.15秒
      • 2回目:1.64秒
      • 質問変更:3.96秒
比較環境3
  • g5.xlarge
    • vCPU: 4
    • メモリ: 16GB
    • GPU: NVIDIA A10G Tensor Core GPU ×1
    • 結果:
      • 1回目:24.32秒
      • 2回目:1.05秒
      • 質問変更:2.49秒

今回は深掘りしませんでしたが、GPU周りの設定や最適化(例:CUDAバージョンやバッチサイズ調整)によって、さらなる高速化が期待できそうです。

まとめ

今回、Ollama を使ってローカル環境で LLM を動かし、実際にチャットができることを確認できたのは大きな収穫でした。一方で、現時点では応答速度や処理能力に課題があり、用途によっては「まだ実用には厳しい」と感じる場面もありました。

特に、初回の応答が遅い点や、長文・複雑なプロンプトへの対応力の弱さは、クラウド LLM と比べると明確な差があります。これは、モデルのロードや初期化に時間がかかるローカル LLM の特性によるもので、ハードウェアスペックの影響も大きいです。

とはいえ、制約を理解した上で使えば、ローカル LLM ならではのメリットも確かにあります。プライバシーの確保やネットワーク依存の排除など、クラウドにはない利点もあります。

ただし、現状では高性能なGPUや十分なメモリを備えた環境が必要であり、それを用意できるのは限られた一部のユーザーにとどまるのも事実です。誰もが気軽に使える段階ではないからこそ、今後のモデルの軽量化やハードウェアの進化に期待したいところです。

ローカル LLM はまだ発展途上の技術ですが、目的に応じた使い分けを意識することで、十分に価値ある選択肢になり得ると感じました。