ローカルLLMが応答するLineBOTを作ってみる


この記事では、個人のUbuntu 22.04 上にローカルなLLMサーバを用意し、LINE上のチャットボットとして動かすことに成功したので、その全体的な構築手順や得られた知見を書き残します。
最終的なシステム構成は以下のような形。

```mermaid
flowchart TD
  subgraph Localhost [Ubuntu 22.04]
    NgrokClient[ngrok Client]
    FlaskServer[Flask Web Server]
    subgraph DifyContainer [Dify Container]
      DifyAPI[Dify API]
    end
    subgraph OpenWebUIContainer [Open WebUI Container]
      OpenWebUI[Open WebUI]
      Oltama[Oltama LLM Server]
    end
  end

  subgraph External2 [ngrok]
    NgrokServer[ngrok Server]
  end

  subgraph External [LINE Official Account Manager]
    LineBot[自作LINE Botアカウント]
  end

  subgraph UserGroupChat [LINEのグループチャット]
    User1[End User 1]
    User2[End User 2]
    User3[End User 3]
  end

  %% 矢印設定
  FlaskServer <--> NgrokClient
  FlaskServer <--> DifyContainer
  DifyAPI <--> OpenWebUI
  OpenWebUI <--> Oltama
  NgrokClient <--> NgrokServer
  NgrokServer <--> LineBot
  LineBot <--> UserGroupChat
```

## 目次

### 1. 環境構築の準備
- 必要なソフトウェアとツールのインストール
    -  おおよそ、以下のインストールが必要なはずです。
    （いろいろ試し過ぎて結局何が最低限必要なのかよくわからん…）
        - Docker
        - python
        - NVIDIAのGPUドライバ
        - NVIDIA Container Toolkit
　
- Dockerの基本的な設定

    - 非rootで実行できるようにする
        - ユーザーをDockerグループに追加しておきましょう。デフォルトではrootでしかコンテナを実行できないので不便（というか不安）です。
    - コンテナデータの格納先パスを変更する
        - /etc/docker/daemon.jsonの中でdata-rootにコンテナデータの格納先にしたいパスを書いた上でDockerサービスを再起動しましょう。そうでないと/var/lib/dockerとかいうとんでもねぇ場所にコンテナのデータが溜まってしまいます。
        （企業だろうが個人だろうがデータ用ドライブに配置したいでしょ普通…なんでこんなシステム用の領域に保管しようとするんだ）

### [2. Open WebUIの導入と利用](https://qiita.com/taro_yamada_dewanokami/items/19326b535c3f40482696)
詳細はリンク先に書き連ねるとして、端的にいうと以下です。
- インストールがめちゃくちゃ楽（Dockerコンテナだから）
- 更新がめちゃくちゃ楽（watchtowerにより、１コマンド実行すれば適当に更新できる）
- とんでもなく充実したUI（モデルの動作確認とか性能確認がめっちゃ楽）
- マルチモーダルLLM、つまり画像認識できるモデルに画像を渡すこともできる
- RAGもできるらしい（まだ触ってない）
- OpenAI互換APIとして動くのでLLMサーバとして利用できる

### [3. Difyの導入とモデルプロバイダ設定](https://qiita.com/taro_yamada_dewanokami/items/da9e1a12b648971ea2cd)
- DockerコンテナでのDifyのセットアップ
- LLMサーバとしてのOpen WebUIを、Difyにモデルプロバイダとして登録する
- 接続時の注意点と設定方法
    - ローカルLLMサーバを使う場合はDify側で`host.docker.internal`の使用必須
    - `ufw`によるポート開放をしておく必要もあり

### [4. Difyでのワークフロー作成とAPI化及びハマりポイント](https://qiita.com/taro_yamada_dewanokami/items/0676563c8848ef2838c6)
- 複数のLLMを活用したワークフローの作成
- 作ったワークフローの動作確認
- Difyで作ったワークフローをAPIとして利用可能な状態にする
    - 公開する
    - ワークフローの更新を反映する
    - ワークフローの「終了」のブロックで出力変数を指定していないので結果が帰ってこない
- Difyワークフローのいいところ
    - メトリクス情報が見られる
    - ワークフローの各ステップの細かい処理内容（特にLLM系ブロックの応答の全文）が見えるので、非常にデバッグしやすい
    - ワークフローのログにおいても各ステップの処理内容は全て参照できるので、APIとして利用しているときでも細かい追跡調査が可能

### 5. LINE Botのアカウント＆Webサーバ作成、およびngrokでのトンネリング
- まずはオウム返しBOTから始めるのがよい。以下の参考記事が全て。
    - [参考記事](https://qiita.com/ayakaintheclouds/items/6515a329d7cce94f8358#flask%E3%81%A7line-bot%E3%82%92%E4%BD%9C%E6%88%90)
        - 上記記事に以下の要素が含まれている
            - LINE DevelopersでのBotアカウント作成
            - Flaskサーバーの構築とWebhookの設定
            - ngrokでBotをLINEサーバと接続

### [6. 本編：LINE BotにLLMサーバのAPIを叩かせて応答メッセージを作成させる](https://qiita.com/taro_yamada_dewanokami/items/f9684def6af4ae7504ce)
- BOTの本体（頭脳をLLMとするなら胴体と言うべきか）はflaskで書いたウェブサーバ
    - このウェブサーバがLineのコールバックから受け取ったユーザーのメッセージを、然るべき形に加工したり追加の情報を添えたりしてDify APIに送信し、その返答をLineサーバに返却する
- Dify APIを利用してLLMとユーザーの会話を実現（Dify側のワークフロー内でLLMサーバのAPIが叩かれている）

### [7. 実際の動作確認と課題](リンク先8)　※作成中
- Botとの対話イメージ
- Difyで作ったワークフロー利用時の、トークン消費量の計測
- 発生した課題とその解決策
- プロンプト次第でLLMの振る舞いや見かけ上の賢さがかなり変わる。システムプロンプトの影響はかなり大きい