Dify でファイル添付できない問題の解決策を考えた

概要

Dify のワークフローでファイル添付できない問題の解決策を考えた。 ただし、適用できるのは以下のケースで、非常に限定的である。

  1. ファイル形式は CSV
  2. ファイルの内容は機密情報ではない
  3. CSV ファイルの内容はプロンプト内で用いる変数のセットである

3 について補足しておく。 同じ形のプロンプトであるが、一部だけ変数のように扱い、複数のパターンでプロンプトを実行したいケースがある。 例えば以下のようなものである。

以下の[背景]、[条件]において、発生しうる現象を教えて下さい。

# 背景

${背景}

# 条件

- ${条件 1}
- ${条件 2}

${背景}${条件 1}${条件 2}を変数と表現した。 そして、1 プロンプトに含めたい複数の変数を、変数のセットと表現した。

今回作成したフローは、以下のとおりである。

  1. Dropbox に CSV ファイルを保存する
  2. 1 を HTTP リクエストで取得する
  3. 2 で取得した内容はテキストであるので、これを変数セットのリストに変換する
  4. イテレーションの中で、1 セットずつプロンプトを実行する
  5. 4 での出力を一つにまとめる
  6. 5 を最終的な出力とする

背景

同じ形のプロンプトであるが、一部だけ変数のように扱い、複数のパターンでプロンプトを実行したいケースがある。 そのときに、変数のセットをまとめた CSV を添付して、変数だけ異なる複数のプロンプトを一度に実行できると便利である。 しかし、Dify のワークフローでは、開始時にファイルを添付することができないため、この方法が使えない。 これができると以下の利点がある。

  • 内部では複数回に分けてプロンプトを実行することになるので、トークンの出力制限を受けづらくなる
  • 変数のセットが多いときは CSV の方が作りやすく、管理しやすい

以上の背景があり、自分で用意した CSV ファイルの内容を、プロンプトに入れられるフローを作ることにした。

おこなったこと

以下のような CSV ファイルが Dropbox に保存されている。

key1key2
val1-1val1-2
val2-1val2-2

プロンプトのテンプレートを準備したうえでこの CSV を取得し、1 回目に実行するプロンプトに 1 行目の変数のセットを、 2 回目に実行するプロンプトに 2 行目の変数のセットを埋め込み、それぞれ実行する。

作ったフローは以下のとおり。

DSL ファイルは こちら

それぞれ簡単に説明する。

開始

ここでは Dropbox 上のファイルの URL のみを指定する。 プロンプトは後述する MAKE-PROMPT で指定している。 変数を入れるため、テンプレートにする必要があったのでそうしているが、 開始時にプロンプトを入れないのは直感に反しているため、気に入っていない。

URL 文字列修正

Dropbox 上でファイルの URL をコピーすると ”https://www.dropbox.com/” のように、ドメインの前に “www” がついているが、 これだと HTML を取得してしまう。 生の CSV を取得するためには ”https://dl.dropbox.com” のように “www” の部分を “dl” に変える必要がある。 ここでは、その変更処理を Python で行っている。 コードは以下のとおり。

def main(dropbox_url):
    return {
        "result": dropbox_url \
        .replace("https://www.dropbox.com", "https://dl.dropbox.com") \
        .replace("&dl=0", "&dl=1")
    }

HTTP リクエスト

HTTP リクエストを実行し、CSV を取得する。

TXT2DICT

HTTP リクエストを実行すると、文字列が返される。 文字列を List[Dict[str, Any]] にする処理を行う。 コードは以下のとおり。

from typing import Any, Dict, List


def main(csv_text: str) -> List[Dict[str, Any]]:
    lines = csv_text.strip().split('\n')
    # 最初の行をキーとして使用
    keys = lines[0].split(',')
    # 各行に対して辞書を作成
    dict_list = []
    for line in lines[1:]:
        values = line.split(',')
        entry = {keys[i]: values[i] for i in range(len(keys))}
        dict_list.append(entry)
    return {
        "result": dict_list
    }

EXECUTE-PROMPTS

TXT2DICT で作った List をイテレーションで扱う。

MAKE-PROMPT

プロンプトのテンプレートを作ってある。 辞書型が与えられるので Jinja テンプレートで埋め込んでいく。 テンプレートは以下の通り。

[変数]を[出力形式]で出力してください。

# 変数

{% for key, value in ojb.items() %}

- {{ key }}: {{ value }}

{% endfor %}

# 出力形式

=====
[key]: [value]

LLM

MAKE-PROMPT で作ったプロンプトを実行する。

SUMMERIZE-OUTPUT

LLM で得られた出力結果を一つにまとめる。 テンプレートは以下の通り。

{% for output in arg1 %}

===== プロンプト {{ loop.index }} =====

{{ output }}

{% endfor %}

終了

SUMMERIZE-OUTPUT で作った文を出力する。

あとがき

思いがけず、Dify のワークフローの様々な機能に触れることができたのは良かった。 これから Dify を使いこなしていきたい。