DifyとLINEボットサーバーを連携してみる その2 Chatflow編

# はじめに

[前回](https://qiita.com/bathtimefish/items/d48761702255c090b4f8)の続きです。前回のDifyアプリはテキストジェネレーターでしたが、今回はChatflowでLINE Botを作成してみます。

# Chatflowアプリを作成する

ダッシュボードから`最初から作成`をクリックします
![スクリーンショット 2024-06-24 9.57.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/04f9b952-a10e-0592-6a39-61d2f5327d84.png)

タイプ`チャットボット`、オーケストレーション方法`Chatflow [BETA]`を選択して適当な名前をつけて`作成`ボタンをクリックします
![スクリーンショット 2024-06-24 9.58.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/537a5488-e001-bd2a-29d3-9c0546883fd9.png)

アプリが作成されると下記のようにワークフローを編集する画面が表示されます
![スクリーンショット 2024-06-24 10.01.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/73aec241-9432-f785-0300-3a9deeff929f.png)

すでに作成されているワークフローでもチャットを開始できますが、今回はRAGを試してみようと思い、[こちらの記事](https://note.com/npaka/n/nca849f1a23f1)を参考にワークフローを編集してみました。ナレッジは記事の`ドキュメントの準備`を参考に[マンガペディアのソードアート・オンライン](https://mangapedia.com/%E3%82%BD%E3%83%BC%E3%83%89%E3%82%A2%E3%83%BC%E3%83%88%E3%83%BB%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3-d85wisn5t)のドキュメントを作成しました

![スクリーンショット 2024-06-24 10.09.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/df3c3697-2a71-95c9-7f68-8b3939eaa992.png)

`デバッグとプレビュー`で動作を確認してみます

![スクリーンショット 2024-06-24 10.10.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/d30894ba-57ce-3aad-d401-3ff87452ec5f.png)

動作を確認できたら、`公開する`ボタンをクリックしてアプリを公開状態にします

# APIを叩いてみる

APIリファレンスを参考にcurlでAPIを叩いてみます。ENTER-YOUR-SECRET-KEYは先程控えたAPIシークレットキーに差し替えてください。APIパスがテキストジェネレーターのときは`completion-messages`でしたが、チャットボットの場合は`chat-messages`となります

```shell
curl -X POST 'https://api.dify.ai/v1/chat-messages' \
--header 'Authorization: Bearer ENTER-YOUR-SECRET-KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
    "inputs": {},
    "query": "アインクラッドとは？",
    "response_mode": "blocking",
    "user": "abc-123"
}'|jq .
```

回答が返ってきました。チャットボットの場合は`query`にLINEメッセージを入力してリクエストしてレスポンスのanswerをリプライするようです

```shell
{
  "event": "message",
  "task_id": "3f6c385c-593d-4d20-a0ea-2a95503ad2d2",
  "id": "d6ae0243-c556-4886-ae66-02ebc162c5a2",
  "message_id": "d6ae0243-c556-4886-ae66-02ebc162c5a2",
  "conversation_id": "12eabc7e-7b9f-40ca-8f13-bc202e3371a1",
  "mode": "advanced-chat",
  "answer": "アインクラッドは、『ソードアート・オンライン』の舞台となる架空の城です。この城は100の階層に分かれており、各階層にはフィールドや迷宮区、フロアボスが存在します。プレイヤーたちは階層をクリアすることで上の階層に進むことができます。また、各階層には転移門があり、クリアした階層へ簡単に移動することができます。#ナレッジ\n\nナレッジによると、浮遊城アインクラッドは非常に広大で、最初の階層だけでも直径が10kmに及ぶほどの規模を持っています。",
  "metadata": {
    "usage": {
      "prompt_tokens": 715,
      "prompt_unit_price": "0.001",
      "prompt_price_unit": "0.001",
      "prompt_price": "0.0007150",
      "completion_tokens": 229,
      "completion_unit_price": "0.002",
      "completion_price_unit": "0.001",
      "completion_price": "0.0004580",
      "total_tokens": 944,
      "total_price": "0.0011730",
      "currency": "USD",
      "latency": 3.527997043915093
    }
  },
  "created_at": 1719190372
}
```

# LINE Botサーバーをカスタマイズする

前回のコードをチャットボットのAPI向けにカスタマイズします。次のAPIトークンの値は適宜差し替えてください。

- `YOUR_CHANNEL_ACCESS_TOKEN`: LINEチャンネルアクセストークン
- `YOUR_CHANNEL_SECRET`: LINEチャンネルシークレット
- `ENTER-YOUR-SECRET-KEY`: Dify APIシークレットキー

下記のように`routes/api/messaging.ts`を更新します。

```messaging.ts
import type { Handlers, FreshContext } from "$fresh/server.ts";
import { messagingApi, MessageEvent } from "npm:@line/bot-sdk@9.2.2";
import type { ClientConfig, TextEventMessage } from "npm:@line/bot-sdk@9.2.2";

declare interface DifyChatMessageResponse {
  event: string;
  task_id: string;
  id: string;
  message_id: string;
  conversation_id: string;
  mode: string;
  answer: string;
  metadata: {
    usage: {
      prompt_tokens: number;
      prompt_unit_price: string;
      prompt_price_unit: string;
      prompt_price: string;
      completion_tokens: number;
      completion_unit_price: string;
      completion_price_unit: string;
      completion_price: string;
      total_tokens: number;
      total_price: string;
      currency: string;
      latency: number;
    }
  },
  created_at: number; 
}

const config: ClientConfig = {
    channelAccessToken: "YOUR_CHANNEL_ACCESS_TOKEN",
    channelSecret: "YOUR_CHANNEL_SECRET",
};
const client = new messagingApi.MessagingApiClient(config);

export const handler: Handlers =  {
  async POST(_req: Request, _ctx: FreshContext): Promise<Response> {
    const body = await _req.json(); 
    const event: MessageEvent = body.events[0];
    const textMessage = event.message as TextEventMessage;
    console.log(textMessage.text);
    // request to Dify API
    const requestData = {
      inputs: {},
      query: textMessage.text,
      response_mode: "blocking",
      user: "line-bot",
    };
    const resp = await fetch("https://api.dify.ai/v1/chat-messages", {
      method: "POST",
      headers: {
        "Authorization": "Bearer ENTER-YOUR-SECRET-KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(requestData),
    });
    const res = await resp.json() as DifyChatMessageResponse;
    await client.replyMessage({
      replyToken: event.replyToken,
      messages: [
        {
          type: "text",
          text: res.answer,
        },
      ],
    });
    return new Response(null, { status: 204 });
  }
};
```

# 使ってみる

実行します

```shell
deno task start

Task start deno run -A --watch=static/,routes/ dev.ts
Watcher Process started.
The manifest has been generated for 6 routes and 1 islands.

 🍋 Fresh ready 
    Local: http://localhost:8000/
```

正常に実行できました

![Screenshot_20240624-104737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/e3e7a1db-2a79-df43-64c8-d5b1421079cb.png)

# おわりに

今回はDifyでRAGを用いたチャットアプリを作成し、LINE Bot化してみました。これを使うと手軽にAI LINE Botが作成できそうですね。なによりDifyでAIアプリ側をノーコードでアップデートしていく運用ができることが魅力だと感じました。