【Dify・AWS】DifyをAWS上にデプロイし、簡単にチャットボットが作成できて感動した話

# 1.はじめに
どうも、ARIの名古屋支社に勤務している愛知県民こと、
新藏（にいくら）と申します♪
(/・ω・)/

生成AI、盛り上がっていますね。

最近、各生成AIサービスとの連携が簡単にできて、
アプリケーションを作成することができるツールとして**Dify**を知りました。

少し調べると、AWS上にDifyをデプロイして動かすワークショップが見つかったので、
**今回はDifyのデプロイについて**記事にしたいと思います！

https://catalog.us-east-1.prod.workshops.aws/workshops/95a3c231-2064-4a33-9a3d-624b7c11aaa6/ja-JP

最後にも記載するのですが、
自分自身の予想以上に簡単にDifyがデプロイできたので、感動しました♪

チャットボットやDifyについて勉強中の方の参考になれば幸いです。
(*^^)v

# 2.Difyとは
まず初めに、Dify（ディファイ）とは何かについて、一言で言うと、
**AI機能を持つアプリケーション（チャットボット等）を簡単に作成することができるツール**
となります。

Difyにはクラウド版とGitHub上に公開されているコミュニティ版があり、
**今回はOSSとして公開されているコミュニティ版を使用して、
AWS環境にチャットボットを作成しました。**

参考

https://udemy.benesse.co.jp/development/app/dify.html

# 3.構成図
今回の構成図を紹介します。

|![000_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/18e9fc4b-b362-2b99-25e6-6997adb83ac3.png)|
|:--|
出典：https://catalog.us-east-1.prod.workshops.aws/workshops/95a3c231-2064-4a33-9a3d-624b7c11aaa6/ja-JP

構成としてはとてもシンプルで、**EC2上にDifyのコンテナをデプロイし、
Bedrockと連携してチャットボットの回答を作成する**という構成となります。

また、後述のCloudFormationテンプレートを確認すると分かるのですが、
EBSを使用してEC2インスタンスを停止してもデータが飛ばないようにしています。

# 4.いざ、デプロイ
## 4.1.Bedrockの設定
まず初めに、Difyから呼び出すBedrockのモデルを指定します。

4.1.1.マネジメントコンソールにログインし、オレゴンリージョンを選択します。

|![210_Bedrock設定_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/3f512b07-fe2c-2f80-8e63-63343d805e7e.png)|
|:--|

:::note info
今回はオレゴンリージョンですが、東京リージョンでも同じように設定ができます。
ただ、Amazonがアメリカの会社なので、
使えるモデルの種類はアメリカのリージョンの方が多いです
:::

4.1.2.Bedrockの管理画面にて「モデルアクセス」を選択し、「特定のモデルを有効にする」を選択します。

|![220_Bedrock設定_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/68fba073-9d25-4c1e-cd0c-13accb506248.png)|
|:--|

4.1.3.有効にするモデルを選択します。

今回は以下のモデルを選択しました。
* Titan Text Embeddings V2
* Claude 3.5 Haiku
* Claude 3.5 Sonnet V2

|![230_Bedrock設定_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/6e9a6322-0880-5aee-4f03-aff7ae7c6eb2.png)|
|:--|

:::note info
Copilot曰く、それぞれのモデルの特徴は以下とのことでした。
違う点があるかもしれませんが、ご参考までに・・・

* Titan Text Embeddings V2
    *  高精度なテキスト埋め込み: テキストデータの意味を高精度に捉えることができます。
    *  多用途: 検索、分類、クラスタリングなど、さまざまな自然言語処理タスクに適用可能です。
    *  スケーラビリティ: 大規模なデータセットにも対応できるように設計されています。
　
* Claude 3.5 Haiku
        * 高速かつ高性能: 低コストながらも高速で高性能な処理が可能です。
        * コーディング・数学的タスクに強い: プログラミングや数学関連のタスクで優れたパフォーマンスを発揮します。
        * 多言語対応力の強化: 日本語を含む多言語での自然な応答が可能です。
　
* Claude 3.5 Sonnet V2
    * 知性の向上: 前バージョンに比べて、より高度な知的タスクに対応できます。
    * 速度とコストのバランス: 高速な処理とコスト効率の良さを兼ね備えています。
    * 多様な応用: アイデア提案や課題発見など、クリエイティブなタスクにも適しています。
:::

4.1.4.Bedrockを使用する目的等を入力します。

|![240_Bedrock設定_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/83a445ae-c7a1-a36c-46af-50c3734b7eff.png)|
|:--|

以上でBedrockの設定は完了です！

## 4.2.CloudFormationの実行
続きまして、CloudFormationを使用してEC2インスタンスとDifyのコンテナを作成します。

4.2.1.テンプレートをダウンロード
以下のコードブロック、もしくはURLからテンプレートをコピーし、PCに保存します

<details><summary>dify-self-deployment.yml</summary>

```dify-self-deployment.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: Dify CloudFormation Template

Parameters:
  VpcCIDR:
    Type: String
    Default: 192.168.0.0/16
    Description: CIDR block for the VPC

  Subnet1CIDR:
    Type: String
    Default: 192.168.0.0/20
    Description: CIDR block for Subnet 1

  Subnet2CIDR:
    Type: String
    Default: 192.168.16.0/20
    Description: CIDR block for Subnet 2

  AllowedCIDR:
    Type: String
    Default: 0.0.0.0/0
    Description: CIDR block to allow HTTP traffic from

  AmazonLinuxAMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64


Resources:
  DifyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: dify-vpc

  Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref DifyVPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Ref Subnet1CIDR
      Tags:
        - Key: Name
          Value: dify-subnet-1

  Subnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref DifyVPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Ref Subnet2CIDR
      Tags:
        - Key: Name
          Value: dify-subnet-2

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref DifyVPC
      InternetGatewayId: !Ref InternetGateway

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref DifyVPC

  Route:
    Type: AWS::EC2::Route
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  Subnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet1
      RouteTableId: !Ref RouteTable

  Subnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet2
      RouteTableId: !Ref RouteTable

  DifySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP traffic
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref AllowedCIDR
          Description: Allow HTTP traffic from allowed CIDR
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic
      VpcId: !Ref DifyVPC
      Tags:
        - Key: Name
          Value: dify-sg

  DifyWsInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonBedrockFullAccess'
        - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref DifyWsInstanceRole

  DifyWsInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmazonLinuxAMI
      InstanceType: t3.medium
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref DifySecurityGroup
          SubnetId: !Ref Subnet1
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 20
            VolumeType: gp2
            Encrypted: 'true'
            
      Tags:
        - Key: Name
          Value: dify-ws
      IamInstanceProfile: !Ref InstanceProfile
      UserData:
        Fn::Base64: |
            #!/bin/bash
            max_attempts=5
            attempt_num=1
            success=false
            while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
              sudo dnf install -y git docker
              if [ $? -eq 0 ]; then
                echo "dnf install succeeded"
                success=true
              else
                echo "dnf install $attempt_num failed. trying again..."
                sleep 3
                ((attempt_num++))
              fi
            done

            sudo systemctl start docker
            sudo gpasswd -a ec2-user docker
            sudo gpasswd -a ssm-user docker
            sudo chgrp docker /var/run/docker.sock
            sudo service docker restart
            sudo systemctl enable docker
            sudo curl -L "https://github.com/docker/compose/releases/download/v2.28.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
            sudo chmod +x /usr/local/bin/docker-compose
            sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
            cd /opt
            sudo git clone https://github.com/langgenius/dify.git
            cd /opt/dify
            sudo git checkout 0.9.1-fix1
            sudo git pull origin 0.9.1-fix1
            cd /opt/dify/docker
            sudo cp .env.example .env
            docker-compose up -d

Outputs:
  InstancePublicIP:
    Description: Public IP of the EC2 instance
    Value: !GetAtt DifyWsInstance.PublicIp
    Export:
      Name: DifyInstancePublicIP
  InstanceId:
    Description: InstanceId of the EC2 instance
    Value: !Ref DifyWsInstance
    Export:
      Name: DifyInstanceId
```
</details>

https://static.us-east-1.prod.workshops.aws/public/59cb9faa-8f2a-4a1e-8fec-c082ab7aa931/static/resources/dify-self-deployment.yml

4.2.2.テンプレートを修正します。
前手順のyamlファイルをみると、Difyのバージョンが0.9.1で固定になっています。
**最新版のDifyを使いたい場合は、コードを以下の様に修正してください。**


```diff
- sudo git checkout 0.9.1-fix1
- sudo git pull origin 0.9.1-fix1

+ LATEST_TAG=$(curl -s https://api.github.com/repos/langgenius/dify/releases/latest | jq -r .tag_name)
+ sudo git checkout $LATEST_TAG
+ sudo git pull origin $LATEST_TAG
```

次は実際にCloudFormationを動かしていきます！

4.2.3.マネジメントコンソールにログインし、東京リージョンを選択します。

|![005_CFnの実行_リージョン選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/47c3ece3-f596-0096-b9a6-4d1c55c4e72b.png)|
|:--|

4.2.4.CloudFormationの管理画面にて「スタックの作成」を選択し、「新しいリソースを使用」を選択します。

|![010_CFnの実行_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/490899c0-a538-0bbb-d343-c1be1dfde618.png)|
|:--|

4.2.5.「テンプレートファイルのアップロード」を選択し、前の手順でダウンロードしたyamlファイルを選択します。

|![020_CFnの実行_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/795ccb36-3865-5ce9-886e-7eef15c12444.png)|
|:--|


4.2.6.任意のスタック名を入力します。

|![030_CFnの実行_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/29eda387-a59d-6fb2-d5cb-ae5887c40fea.png)|
|:--|


4.2.7.チェックボックスにチェックをします。

|![040_CFnの実行_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/a4365477-8ed6-f39b-e0fc-2531027ada11.png)|
|:--|

4.2.8.全体的な設定を確認し、問題がなければ次に進みます。

|![050_CFnの実行_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/210a557d-f92d-7c44-f448-a547a1fa3736.png)|
|:--|

4.2.9.「リソース」タブを選択し、画面右の更新ボタンを選択するとリソースが作成される様子が確認できます。

|![060_CFnの実行_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/7b17aec7-54c3-94a4-0c6d-ba7996985787.png)|
|:--|

4.2.10.画面左の更新ボタンを押し、「CREATE_COMPLETE」と表示されていればリソースの作成が完了しています。

|![070_CFnの実行_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/12a8aabc-1bec-6787-42e4-c883ba74cbdb.png)|
|:--|

4.2.11.「出力」タブを選択し、「InstancePublicIP」に記載のあるIPアドレスをコピーします。

|![080_CFnの実行_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/56a7d43b-f2ab-7b58-a885-ad6048f6c592.png)|
|:--|

## 4.3.Difyへの接続
さて、CloudFormationを使用してDifyのデプロイができたので、
実際にDifyに接続していきます♪

4.3.1.WEBブラウザを立ち上げ、「http://(前手順でコピーしたIPアドレス)」に接続します。

4.3.2.初期設定の画面が表示されるのでメールアドレス、ユーザ名、パスワードを入力します。

:::note info
入力する値は適当でOKですが、メールアドレスとパスワードはDifyにログインする際、必要になります。
:::

|![110_Difyへのログイン_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/f752b1dc-4ff2-0f39-d9d3-6f1ad4cce574.png)|
|:--|

4.3.2.ログイン画面が表示されるので、前手順で設定したメールアドレスとパスワードを入力します。

|![120_Difyへのログイン_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/7f099e30-6b73-4614-d306-205c4c772ccb.png)|
|:--|

## 4.4.DifyとBedrockとの連携設定
次は、DifyとBedrockを連携させるための設定を実施します。

4.4.1.画面右上のユーザ名を選択し、「設定」を選択します。

|![310_DifyとBedrockの連携_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/1c62e453-b3dc-0aa7-fc1d-73e98ad9be16.png)|
|:--|

4.4.2.「モデルプロバイダー」を選択し、Bedrockの「セットアップ」を選択します。

|![320_DifyとBedrockの連携_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/0a973456-9862-3a61-dafa-d188f0f0960d.png)|
|:--|

4.4.3.「AWS Region」には4.1節でBedrockの設定の際に使用したリージョンを、Avaiable Model Nameにはモデル名を入力します。

Claude 3.5 Sonnetを使用する場合は、
anthropic.claude-3-5-sonnet-20241022-v2:0
となります。

|![330_DifyとBedrockの連携_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/7b552a23-f08b-777d-373b-f61dc7fb7d1a.png)|
|:--|

:::note info
Model Nameで何を入れれば良いか分からない・・・
という場合は、以下の画像の様に生成AI聞くと答えてくれると思います♪

|![335_DifyとBedrockの連携_3.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/aa8fcff2-95c0-c322-cf4c-43a0024efc1e.png)|
|:--|

:::

4.4.4.問題なく設定が出来たら、「モデルの表示」を選択します。

|![340_DifyとBedrockの連携_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/9d34bca4-c005-6e54-d501-6af3faf7be17.png)|
|:--|

4.4.5.モデルプロバイダの画面にて、4.1節で設定したモデル以外を無効化します。

|![350_DifyとBedrockの連携_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/1632fb2b-57eb-66dd-1040-f469bca19d4a.png)|
|:--|

|![360_DifyとBedrockの連携_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/827e074e-4580-9f66-8033-52aec7d3c41c.png)|
|:--|


4.4.6.「システムモデル設定」を選択し、推論モデルと埋め込みモデルを選択します。

自分自身がデプロイした際は推論モデルとして、Claude 3.5 Haikuを、
埋め込みモデルとしてTitan Text Embeddings V2を使用しました。

|![370_DifyとBedrockの連携_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/ea7d052e-1b7c-943c-7825-f5c5ade95c50.png)|
|:--|

:::note info
システム推論モデルと埋め込みモデルについては、
Copilot曰く、以下とのことです。

**システム推論モデル**
システム推論モデルは、ユーザーからの入力に対して応答を生成するためのモデルです。これらのモデルは、自然言語処理（NLP）タスクに特化しており、以下のような用途に使用されます：

* チャットボット：ユーザーとの対話を行うための応答を生成します。
* テキスト生成：文章の生成や補完を行います。
* 質問応答：ユーザーの質問に対して適切な回答を提供します。
　
 
**埋め込みモデル**
埋め込みモデルは、テキストデータを数値ベクトルに変換するためのモデルです。これにより、テキストデータの意味的な類似性を計算したり、検索エンジンの最適化を行ったりすることができます。主な用途は以下の通りです：

* テキスト検索：文書やデータベース内の関連情報を検索します。
* クラスタリング：類似したテキストをグループ化します。
* 分類：テキストデータを特定のカテゴリに分類します。
:::

以上で、Difyの設定は終了です！

## 4.5.チャットボットの作成
最後にチャットボットを作成していきましょう♪

4.5.1.Difyの管理画面から、「最初から作成」を選択し、「チャットフロー」を選択し、任意のアプリ名を入力します。

|![130_チャットボットの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/6bd9eb68-5b9a-5555-3730-0c76711b0b5c.png)|
|:--|

4.5.2.「プレビュー」を選択し、質問をすると回答が返ってきます♪

|![410_チャットボットを動かす.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/fe75e0d7-cff3-ae52-27ec-a36d18ff3e57.png)|
|:--|

# 5.デプロイにかかった時間と料金
最後に補足ですが、Difyのデプロイは約4時間で終わりました。
キャプチャを取得しながら進めていたため、**作業自体は2~3時間**あれば終わると思います！

2日間ほど遊び、**AWSの使用料金は約1ドル**でした！
内訳としてはEC2が7割、Bedrockが約2割、その他が1割くらいでした。

|![510_料金.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/6f0b2078-7b02-fafd-1b54-6c318e73919e.png)|
|:--|

# 6.おわりに
ここまで読んで下さり、ありがとうございます！！！
(^^)

いやはや、こんなにも簡単にチャットボットが作れる時代になるとは・・・驚きました

Difyはまだまだカスタマイズできるので、次回はその内容についての記事、
具体的にはDifyが内部的に持つベクトルDBを使って何かしたいと思います♪
(:3_ヽ)_