記事の概要
ローカルにDIfyを構築すると、docker上に構築することになり、ローカルにファイルを保存したり、ローカルのファイルを読み込むのがめんどうな構成となっている。
特にLLMを使ったアプリ開発などでは、Dify上で実行できるプログラムにはかなり制限があるため、ローカルにファイルを出力したくなる。
そこで、ローカルへのファイル出力と読込をpythonのライブラリであるflaskを利用して実現する。
ただし、Dify自体に外部へのファイル出力の機能が実装されるのも時間の問題と思うので、一時的な手順と考えている。
全体的な構成は以下の構成となっている。
ローカル側の設定
pythonがインストールされているWindowsのPC上であれば、以下のコマンドをコマンドプロンプトで実行すれば、ローカルPC側の構築は完了する想定。
mkdir dify_external_folder
cd dify_external_folder
chcp 65001
echo # -*- coding: utf-8 -*- > app.py
echo from flask import Flask, request, jsonify, send_file >> app.py
echo import os >> app.py
echo. >> app.py
echo app = Flask(__name__) >> app.py
echo. >> app.py
echo @app.route('/upload', methods=['POST']) >> app.py
echo def upload_file(): >> app.py
echo ^# JSONデータの取得 >> app.py
echo data = request.json >> app.py
echo. >> app.py
echo ^# フォルダ名とファイル名の取得 >> app.py
echo folder_name = data.get('folder_name') >> app.py
echo file_name = data.get('file_name') >> app.py
echo file_content = data.get('file_content') >> app.py
echo. >> app.py
echo ^# フォルダ名とファイル名が存在するかチェック >> app.py
echo if not folder_name or not file_name: >> app.py
echo return jsonify({'error': 'Folder name and file name are required'}), 400 >> app.py
echo. >> app.py
echo ^# 出力先のディレクトリを作成(存在しない場合) >> app.py
echo output_dir = os.path.join('output', folder_name) >> app.py
echo if not os.path.exists(output_dir): >> app.py
echo os.makedirs(output_dir) >> app.py
echo. >> app.py
echo ^# ファイル内容を指定されたディレクトリに保存 >> app.py
echo file_path = os.path.join(output_dir, file_name) >> app.py
echo try: >> app.py
echo with open(file_path, 'w', encoding='utf-8') as f: >> app.py
echo f.write(file_content) >> app.py
echo except Exception as e: >> app.py
echo return jsonify({'error': str(e)}), 500 >> app.py
echo. >> app.py
echo return jsonify({'message': 'File saved successfully', 'path': file_path}), 200 >> app.py
echo.>> app.py
echo @app.route('/retrieve', methods=['GET']) >> app.py
echo def retrieve_file(): >> app.py
echo ^# JSONデータの取得 >> app.py
echo data = request.json >> app.py
echo. >> app.py
echo ^# クエリパラメータからフォルダ名とファイル名を取得 >> app.py
echo folder_name = data.get('folder_name') >> app.py
echo file_name = data.get('file_name') >> app.py
echo. >> app.py
echo ^# フォルダ名とファイル名が存在するかチェック >> app.py
echo if not folder_name or not file_name: >> app.py
echo return jsonify({'error': 'Folder name and file name are required'}), 400 >> app.py
echo. >> app.py
echo ^# ファイルパスを構築 >> app.py
echo file_path = os.path.join('output', folder_name, file_name) >> app.py
echo. >> app.py
echo ^# ファイルが存在するかチェック >> app.py
echo if not os.path.exists(file_path): >> app.py
echo return jsonify({'error': 'File not found'}), 404 >> app.py
echo. >> app.py
echo ^# ファイルを送信 >> app.py
echo return send_file(file_path, as_attachment=True) >> app.py
echo.>> app.py
echo if __name__ == '__main__': >> app.py
echo app.run(host='0.0.0.0', port=5000, debug=True) >> app.py
mkdir output
python -m venv venv
venv\Scripts\activate
pip install flask
python app.py
実行すると、以下のようなファイルとフォルダが作成される。
project/
├── app.py
├── output/ # 出力ファイルを保存するフォルダ
└── venv/ # 仮想環境
また、上記のコマンドを実行すると「python app.py」このコマンドでflaskが起動しており、以下のようなログが出力される。
この状態であれば、DifyからのHTTPリクエストを受け付けている状態となる。
(venv) C:\XXXXXXXXXXXXXXXX\project>python app.py
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.XXX.XXX:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 805-336-953
(再起動後などに)再度、flaskを起動する場合は、以下のコマンドを実行する。
venv\Scripts\activate # pythonの仮想環境もactivateが必要な場合のみ
python app.py
構築用のコマンドを実行すれば、自動で作成されている想定だが、念のためpythonファイルを記載しておく。
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify, send_file
import os
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_file():
# JSONデータの取得
data = request.json
# フォルダ名とファイル名の取得
folder_name = data.get('folder_name')
file_name = data.get('file_name')
file_content = data.get('file_content')
# フォルダ名とファイル名が存在するかチェック
if not folder_name or not file_name:
return jsonify({'error': 'Folder name and file name are required'}), 400
# 出力先のディレクトリを作成(存在しない場合)
output_dir = os.path.join('output', folder_name)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# ファイル内容を指定されたディレクトリに保存
file_path = os.path.join(output_dir, file_name)
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(file_content)
except Exception as e:
return jsonify({'error': str(e)}), 500
return jsonify({'message': 'File saved successfully', 'path': file_path}), 200
@app.route('/retrieve', methods=['GET'])
def retrieve_file():
# JSONデータの取得
data = request.json
# クエリパラメータからフォルダ名とファイル名を取得
folder_name = data.get('folder_name')
file_name = data.get('file_name')
# フォルダ名とファイル名が存在するかチェック
if not folder_name or not file_name:
return jsonify({'error': 'Folder name and file name are required'}), 400
# ファイルパスを構築
file_path = os.path.join('output', folder_name, file_name)
# ファイルが存在するかチェック
if not os.path.exists(file_path):
return jsonify({'error': 'File not found'}), 404
# ファイルを送信
return send_file(file_path, as_attachment=True)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Dify側の設定
フロー自体に意味はないが、Dify側の設定のイメージを持つために以下のフローを構築する。
「開始」ノード
「input_text」にはLLMへの質問事項を入力する。
「LLM」ノード
LLMの出力である必要はないが、基本的にLLMの出力をファイル出力する想定で作成している。
日本語で出力してください。
入力がどのような形式でも日本語のみで出力してください。
「ユーザーの質問」:の質問について回答してください。
「ユーザーの質問」:{{#1715394478158.input_text#}}
「HTTPリクエスト(upload)」ノード
LLMの出力をローカルに保存する。
「192.168.XXX.XXX」にはipconfigなどで「IPv4 アドレス」を入力する。
{
"folder_name": "test_folder",
"file_name": "text.txt",
"file_content": "{{#1716114640530.text#}}"
}
「HTTPリクエスト(retrieve)」ノード
ローカルからファイル内のデータを取得する。
「192.168.XXX.XXX」にはipconfigなどで「IPv4 アドレス」を入力する。
{
"folder_name": "test_folder",
"file_name": "text.txt"
}
「終了」ノード
ローカルに保存したファイルの内容を出力する。
結果
以下のように、各種アウトプットが想定通り出力されている。