Open9

Dify 触ってみたメモ

yamatoyamato

Difyとは

DifyはノーコードでLLMを組み込んだアプリケーション(チャットボット、AIエージェントアプリ等)を簡単に作成できる開発ツール。GPTsの超上位互換のような感じ。

複雑なフローが必要なLLMアプリケーションも簡単に素早く作成することができるので、技術検証・PoCに大いに役立ちそう。

yamatoyamato

料金

オープンソースのためローカルにダウンロードして使用すれば無料。
言語モデルを利用する際にAPI料金のみ発生する。

DifyのWebページ上で利用することも可能。ただ、その場合料金が発生してしまう。
一応、無料プランでも200メッセージ/月の制限付きで利用できるが、ローカルで利用すれば完全無料のためローカルをオススメする。

yamatoyamato

ライセンス

商用利用可能なOSSであるが、一部条件があることがライセンスに記載されている。

条件は以下

  • マルチテナントSaaSサービス運営にDifyを用いてはいけない
  • Difyを使用する際に、Difyコンソール内のロゴや著作権情報を削除または変更してはいけない

マルチテナントSaaSサービスかどうかの判断基準は、「ワークスペースが複数あるかどうか」で決まるらしい。
https://note.com/brave_quince241/n/n121312a7f161

yamatoyamato

使える言語モデル

OpenAIのGPTシリーズやAnthropicのClaudeシリーズなど主要なモデルは網羅されている。
各公式サイトでAPIキーを取得し、セットアップ画面でキーを入力することでモデルが利用可能となる。

まずは、比較的料金の安いGPT-4o-mini、無料で利用できるgroqのモデル、一定は無料のGeminiなどのモデルを使うのがおすすめ。

yamatoyamato

アプリケーションタイプ

Difyでは4つのアプリケーションタイプを作成することができる。

それぞれのタイプの説明は以下。

基本的には、チャットボット(chatflow)、ワークフローを使うことになりそう。

yamatoyamato

RAGの準備(知識ベース)

(1)ナビゲーションバー「ナレッジ」で、「ナレッジを作成」をクリックすることでRAG参照ドキュメントをアップロードできる。

(2)ファイルをアップロードする画面となるので、参照させたいドキュメントを選択する。

(3)RAGの設定が表示されるので、お好みの設定を選択する。

チャンク設定

  • 自動:チャンクの長さ、ルールを自動で設定してくれる。初めはこれがおすすめ。
  • カスタム:最大チャンク長、チャンクのオーバーラップなどの手動で設定できる。
    • Microsoftの記事によると最大チャンク長は512、チャンクのオーバーラップは最大チャンク長の25%程度(512トークンであれば128トークン)の精度が良いらしい。

https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid/ba-p/3929167

インデックスモード

  • 高品質:ベクトル検索を行うため高精度。その分コストがかかる。
  • 経済的:Dify用意した無償のインデックス方法が使用される。

検索設定

  • ベクトル検索:ベクトル表現が最も類似するテキストチャンクを検索する
    • 意味的に近い文章を検索するため、直接的な質問でなくても検索可能
    • ただ、LLMが知らない固有名詞や単語が含まれる場合に弱い
  • 全文検索:ユーザーのキーワードベースで検索を行う
    • 正確な照合(固有名詞、番号等)の検索には強いが、意味的な検索には弱い
  • ハイブリット検索(オススメ):全文検索とベクトル検索を同時に実行し、ユーザーのクエリに最適な結果を選択する
    • ベクトル検索、全文検索の欠点を補う方法

※ rerankモデル
rerankモデルとは、複数の情報の中から、生成モデルが最適な回答を生成するために、重要な情報を再選別・再評価するモデル。より精度の高い検索結果を望むことができる。
Cohereのモデルが有名かつ無料で使えるのでオススメ。

yamatoyamato

RAG ChatBot作ってみた

以下の動作をするChatBotを作成してみた。
1時間程度でフローが完成したので、割と簡単にChatBotを作れると思う。

  • GPT-4oに関する質問 → GPT4-oのページを元に、GPT-4o-miniが回答
  • Gemini 1.5に関する質問 → Gemini 1.5の論文(知識ベースにアップロード済み)を元に、Gemini 1.5 Flashが回答
  • Claude 3に関する質問 → Claude 3のページを元に、claude3-haikuが回答
  • その他の質問 → 「すみません、その質問にはお答えできません🤗」と返信

動作確認

Gemini 1.5 proの最大入力トークンについて質問したところ、論文中を適切に参照し、100Mトークンであると回答してくれた。

質問分類器も適切に動作しており、CommandR+について質問しても「答えられません」と返してくれる。

補足情報

PDFの参照(Geminiの論文)はアップロードした知識ベースを用いているが、Web情報の参照にはJinaReaderというツールを用いてWebページの情報を取得している。
JinaReaderはURLやウェブ検索からLLM に適した入力を得られる便利なやつらしい。

DSLファイル出力

作成したフローはDify独自のDSL形式と呼ばれるyaml形式で書き出せるよう。
参考に書き出したDSLを以下に貼っておく。
https://docs.dify.ai/v/ja-jp/guides/application_orchestrate/creating-an-application#dslfairukara

DSL
app:
  description: ''
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: advanced-chat
  name: LLM-Question-RAG
kind: app
version: 0.1.1
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      image:
        enabled: false
        number_limits: 3
        transfer_methods:
        - remote_url
        - local_file
    opening_statement: ''
    retriever_resource:
      enabled: true
    sensitive_word_avoidance:
      configs: []
      enabled: false
      type: ''
    speech_to_text:
      enabled: false
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
      language: ''
      voice: ''
  graph:
    edges:
    - data:
        isInIteration: false
        sourceType: start
        targetType: question-classifier
      id: start-source-1723968002564-target
      source: start
      sourceHandle: source
      target: '1723968002564'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: question-classifier
        targetType: answer
      id: 1723968002564-1-1723968831471-target
      source: '1723968002564'
      sourceHandle: '1'
      target: '1723968831471'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: question-classifier
        targetType: answer
      id: 1723968002564-2-17239688395370-target
      source: '1723968002564'
      sourceHandle: '2'
      target: '17239688395370'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: question-classifier
        targetType: answer
      id: 1723968002564-1723968123909-17239688695710-target
      source: '1723968002564'
      sourceHandle: '1723968123909'
      target: '17239688695710'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: question-classifier
        targetType: answer
      id: 1723968002564-1723968806226-17239688613540-target
      source: '1723968002564'
      sourceHandle: '1723968806226'
      target: '17239688613540'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: answer
        targetType: tool
      id: 1723968831471-source-1723971377306-target
      source: '1723968831471'
      sourceHandle: source
      target: '1723971377306'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: tool
        targetType: llm
      id: 1723971377306-source-1723986187151-target
      source: '1723971377306'
      sourceHandle: source
      target: '1723986187151'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: answer
      id: 1723986187151-source-1723992268204-target
      source: '1723986187151'
      sourceHandle: source
      target: '1723992268204'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: answer
        targetType: knowledge-retrieval
      id: 17239688395370-source-1723995080221-target
      source: '17239688395370'
      sourceHandle: source
      target: '1723995080221'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: knowledge-retrieval
        targetType: llm
      id: 1723995080221-source-1723995105317-target
      source: '1723995080221'
      sourceHandle: source
      target: '1723995105317'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: answer
      id: 1723995105317-source-1723995195840-target
      source: '1723995105317'
      sourceHandle: source
      target: '1723995195840'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: answer
        targetType: tool
      id: 17239688695710-source-17239955048040-target
      source: '17239688695710'
      sourceHandle: source
      target: '17239955048040'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: tool
        targetType: llm
      id: 17239955048040-source-17239955198490-target
      source: '17239955048040'
      sourceHandle: source
      target: '17239955198490'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: answer
      id: 17239955198490-source-17239956005570-target
      source: '17239955198490'
      sourceHandle: source
      target: '17239956005570'
      targetHandle: target
      type: custom
      zIndex: 0
    nodes:
    - data:
        selected: false
        title: START
        type: start
        variables: []
      height: 53
      id: start
      position:
        x: 30
        y: 349
      positionAbsolute:
        x: 30
        y: 349
      type: custom
      width: 244
    - data:
        classes:
        - id: '1'
          name: GPT-4oに関する質問
        - id: '2'
          name: Gemini 1.5に関する質問
        - id: '1723968123909'
          name: Claude 3 Haikuに関する質問
        - id: '1723968806226'
          name: その他の質問
        desc: ''
        instructions: ''
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: gemini-1.5-flash-latest
          provider: google
        query_variable_selector:
        - start
        - sys.query
        selected: false
        title: 質問分類器
        topics: []
        type: question-classifier
      height: 263
      id: '1723968002564'
      position:
        x: 334
        y: 349
      positionAbsolute:
        x: 334
        y: 349
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: 'GPT-4oに関する質問ですね!

          以下質問の回答です。


          ---

          '
        desc: ''
        selected: false
        title: 回答(GPT-4o)
        type: answer
        variables: []
      height: 119
      id: '1723968831471'
      position:
        x: 638
        y: 349
      positionAbsolute:
        x: 638
        y: 349
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: 'Gemini 1.5に関する質問ですね!

          以下質問の回答です。


          ---

          '
        desc: ''
        selected: false
        title: 回答 (Gemini)
        type: answer
        variables: []
      height: 119
      id: '17239688395370'
      position:
        x: 638
        y: 687
      positionAbsolute:
        x: 638
        y: 687
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: すみません、その質問にはお答えできません🤗
        desc: ''
        selected: false
        title: 回答 (その他)
        type: answer
        variables: []
      height: 119
      id: '17239688613540'
      position:
        x: 638
        y: 1108.2857142857142
      positionAbsolute:
        x: 638
        y: 1108.2857142857142
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: 'Claude 3に関する質問ですね!

          以下質問の回答です。


          ---

          '
        desc: ''
        selected: false
        title: 回答 (Claude)
        type: answer
        variables: []
      height: 119
      id: '17239688695710'
      position:
        x: 638
        y: 854.0000000000001
      positionAbsolute:
        x: 638
        y: 854.0000000000001
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        provider_id: jina
        provider_name: jina
        provider_type: builtin
        selected: false
        title: JinaReader
        tool_configurations:
          gather_all_images_at_the_end: 0
          gather_all_links_at_the_end: 0
          image_caption: 0
          max_retries: 3
          no_cache: 0
          proxy_server: null
          summary: 0
          target_selector: null
          wait_for_selector: null
        tool_label: JinaReader
        tool_name: jina_reader
        tool_parameters:
          request_params:
            type: mixed
            value: ''
          url:
            type: mixed
            value: https://openai.com/index/hello-gpt-4o/
        type: tool
      height: 297
      id: '1723971377306'
      position:
        x: 942
        y: 349
      positionAbsolute:
        x: 942
        y: 349
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: true
          variable_selector:
          - '1723971377306'
          - text
        desc: ''
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: gpt-4o-mini
          provider: openai
        prompt_template:
        - id: 4982c67c-d3a9-4ea0-aef3-591cc6dc5a9a
          role: system
          text: 'あなたは世界中で信頼されているQAシステムです。

            事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
        - id: cd454d55-d944-44a4-84f8-83a190b344ae
          role: user
          text: '## コンテキスト

            {{#1723971377306.text#}}


            ## QA

            Q: {{#sys.query#}}

            A: '
        selected: false
        title: LLM
        type: llm
        variables: []
        vision:
          configs:
            detail: high
          enabled: false
      height: 97
      id: '1723986187151'
      position:
        x: 1246
        y: 349
      positionAbsolute:
        x: 1246
        y: 349
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: '{{#1723986187151.text#}}'
        desc: ''
        selected: false
        title: 回答 5
        type: answer
        variables: []
      height: 105
      id: '1723992268204'
      position:
        x: 1550
        y: 349
      positionAbsolute:
        x: 1550
        y: 349
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        dataset_ids:
        - e0afefa8-445e-4a54-b0a3-9d15a6c0816d
        desc: ''
        multiple_retrieval_config:
          reranking_enable: true
          reranking_mode: weighted_score
          top_k: 4
          weights:
            keyword_setting:
              keyword_weight: 0.3
            vector_setting:
              embedding_model_name: text-embedding-3-large
              embedding_provider_name: openai
              vector_weight: 0.7
        query_variable_selector:
        - start
        - sys.query
        retrieval_mode: multiple
        selected: false
        title: 知識取得
        type: knowledge-retrieval
      height: 91
      id: '1723995080221'
      position:
        x: 942
        y: 687
      positionAbsolute:
        x: 942
        y: 687
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: true
          variable_selector:
          - '1723995080221'
          - result
        desc: ''
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: gemini-1.5-flash-latest
          provider: google
        prompt_template:
        - id: 7375c7da-6f26-49e9-a578-aed316441a80
          role: system
          text: 'あなたは世界中で信頼されているQAシステムです。

            事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
        - id: e59edd08-dd59-4cd3-b970-8a313bc4ea99
          role: user
          text: '## コンテキスト

            {{#context#}}


            ## QA

            Q: {{#sys.query#}}

            A: '
        selected: false
        title: LLM 2
        type: llm
        variables: []
        vision:
          configs:
            detail: high
          enabled: false
      height: 97
      id: '1723995105317'
      position:
        x: 1253.142857142857
        y: 687
      positionAbsolute:
        x: 1253.142857142857
        y: 687
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: '{{#1723995105317.text#}}'
        desc: ''
        selected: false
        title: 回答 6
        type: answer
        variables: []
      height: 105
      id: '1723995195840'
      position:
        x: 1550
        y: 687
      positionAbsolute:
        x: 1550
        y: 687
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        provider_id: jina
        provider_name: jina
        provider_type: builtin
        selected: false
        title: JinaReader (1)
        tool_configurations:
          gather_all_images_at_the_end: 0
          gather_all_links_at_the_end: 0
          image_caption: 0
          max_retries: 3
          no_cache: 0
          proxy_server: null
          summary: 0
          target_selector: null
          wait_for_selector: null
        tool_label: JinaReader
        tool_name: jina_reader
        tool_parameters:
          request_params:
            type: mixed
            value: ''
          url:
            type: mixed
            value: https://www.anthropic.com/news/claude-3-family
        type: tool
      height: 297
      id: '17239955048040'
      position:
        x: 942
        y: 854.0000000000001
      positionAbsolute:
        x: 942
        y: 854.0000000000001
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: true
          variable_selector:
          - '1723971377306'
          - text
        desc: ''
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: claude-3-haiku-20240307
          provider: anthropic
        prompt_template:
        - id: 4982c67c-d3a9-4ea0-aef3-591cc6dc5a9a
          role: system
          text: 'あなたは世界中で信頼されているQAシステムです。

            事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
        - id: cd454d55-d944-44a4-84f8-83a190b344ae
          role: user
          text: '## コンテキスト

            {{#17239955048040.text#}}


            ## QA

            Q: {{#sys.query#}}

            A: '
        selected: false
        title: LLM 3
        type: llm
        variables: []
        vision:
          configs:
            detail: high
          enabled: true
      height: 97
      id: '17239955198490'
      position:
        x: 1246
        y: 854.0000000000001
      positionAbsolute:
        x: 1246
        y: 854.0000000000001
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: '{{#17239955198490.text#}}'
        desc: ''
        selected: false
        title: 回答 5 (1)
        type: answer
        variables: []
      height: 105
      id: '17239956005570'
      position:
        x: 1550
        y: 854.0000000000001
      positionAbsolute:
        x: 1550
        y: 854.0000000000001
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    viewport:
      x: 53.64783660411365
      y: -158.8198288148194
      zoom: 0.9267961931891546
yamatoyamato

AWS lightsail上にアプリケーションデプロイ

以下の手順でAWS lightsailにデプロイした。
https://note.com/sangmin/n/nbb4db69784e8

VMの作成

  1. インスタンスイメージはUbuntuを選択。

  2. インスタンスサイズは2GBメモリ、2 vCPUsを選択。3ヶ月のフリートライアルあり。

  3. SSH keyの作成
    Create NewからSSH keyを発行。keyの名前は「dify-key」など何でもOK。
    発行したprivate keyは後ほど使うので、ダウンロード。

  4. Create Instanseでインスタンスを作成

Difyのインストール

  1. インスタンスの作成が完了したら、SSH接続を行う。

  2. Difyインストールのため、SSH先で以下のコマンドを実行。
    コマンド実行には割と時間がかかります。

# Install Docker
curl -fsSL https://get.docker.com -o install-docker.sh
sh install-docker.sh

# Clone the GitHub repository
git clone https://github.com/langgenius/dify.git

# Navigate to the desired directory
cd dify/docker

# Run Docker Compose
sudo docker compose up -d

Difyの起動

デプロイ済みLightsailのhttpアドレスをブラウザに入力することでアクセス可能。
※ httpsではアクセスできないので注意

http://[Public IPv4 address]/install

あとはセットアップを進めていけばOK。

httpsの設定

以下の方法でhttps設定を行えるよう。
https://zenn.dev/shoheiweb/articles/f5627d03019620