maybe daily dev notes

私の開発日誌

Difyのコード実行機能で任意のPythonライブラリを使う

LLMアプリ開発プラットフォームのDifyでは、ワークフローのコードブロックでPythonコードを実行できます。 この記事では、このコード内でboto3やnumpyなど任意のライブラリを呼び出す方法をまとめます。セルフホストのDify向けです。

Difyコードブロックの例

Difyのコード実行の仕組み

前提知識として、Difyのコード実行の仕組みを簡単におさらいします。

Difyでは、PythonやNode.jsのコードをDify Sandboxという独自のサンドボックス内で実行します。

Dify、特にSaaS版では、ユーザーがDifyのサーバー上で悪意のあるコードを実行する可能性があるため、こうしたセキュリティ対策が必要となります。対策がない場合、例えばDifyのサーバーから重要な情報を窃取したり、Difyサーバーのネットワーク・AWS IAM権限 (あれば) を悪用したりといったリスクが考えられます。

そうしたリスクへの対策として開発されたのがDify Sandboxで、このサンドボックス内ではいくつかの制限が課されています。例えば本記事に関連する部分では、以下の制限があります:

より詳細は、こちらのブログをご覧ください。

dify.ai

一方で、上記の対策があるために、多くのPythonライブラリはそのままでは動作しません。動かすには、いくつかのワークアラウンドが必要です。

それでは、本題の任意のPythonライブラリを使う方法を紹介します。

任意のPythonライブラリを使う方法

ステップバイステップで説明します。セルフホスト版のDifyを想定しています。なお、下の方に色々すっ飛ばして楽する手順もあります。

1. requirements.txtに必要なライブラリを追加

Dify Sandboxコンテナでは、/dependencies/python-requirements.txt にファイルを配置することで、追加のPythonライブラリをインストール可能です。

requirements.txtの書き方はこちらにあります: Requirements File Format

基本的にはライブラリの名前を並べれば良いです:

# requirements.txt

# ライブラリの名前をそのまま書けばOK
boto3

# バージョン固定したい場合はこう
numpy == 2.1.0

このファイルをsandboxコンテナに含めるには、例えばカスタムのDockerfileを作るのが楽でしょう:

FROM langgenius/dify-sandbox
COPY ./requirements.txt /dependencies/python-requirements.txt

2. 必要なシステムコールを許可する

上記だけでデプロイしてコードを実行した場合、多くの場合 operation not permitted というエラーが表示されると思います。これは、ライブラリが必要とするシステムコールがDify Sandboxに許可されていないことを意味します。

この問題に対する正攻法は、ライブラリが必要とするシステムコールを特定し、そのシステムコールのリスクを理解したうえでリスクを許容できるのであれば、そのシステムコールホワイトリストに追加することです。詳細な手順はこちらのFAQに書かれています (2024/8現在)。

しかし、その作業は面倒で、とにかく制限を取っ払いたいだけだという場合もあるでしょう。そのようなときは、すべてのシステムコールの番号を許可リストに追加することができます。

すべての番号はどう網羅できるでしょうか?こちらのStackoverflowの回答を見ると、アーキテクチャLinuxバージョンにより差はあるものの、現在はおよそ400〜500個弱のシステムコールがあり、0から連番を振られているようです。Dify Sandboxでは存在しないシステムコールを指定しても問題ないので、雑に500番まで許可すれば良いでしょう。

Dify Sandboxのホワイトリストを変更するための最も簡単な方法は、環境変数 ALLOWED_SYSCALLS を利用することです。この変数はカンマ区切りのシステムコール番号をリストを期待するので、ALLOWED_SYSCALLS=0,1,2,3,...,499,500と渡します。

AWS CDKを使えば、下記のように簡単に書くことが出来ますね。

  environment: {
    ALLOWED_SYSCALLS: Array(500).fill(0).map((_, i) => i).join(',')
  }

3. 必要なshared libraryをサンドボックス内にコピーする

2でより多くのライブラリは動作するようになるはずですが、一部のライブラリではまだ以下のようなエラーが発生することがあります。

ImportError: libexpat.so.1: cannot open shared object file

libxxx.so ファイルが存在しないというエラーです。先述の通りDify Sandboxのサンドボックスではrootディレクトリが変更され、/var/sandox/sandbox-python 以下のディレクトリがrootとなります 。この新しいroot配下に必要なshared library (soファイル) が存在しない場合、上記のエラーが発生します。

こちらの問題の正攻法は、必要なファイルを特定して、config.yamlPYTHON_LIB_PATH 環境変数でそのファイルのパスを指定することです。詳細な手順は同じくこちらのFAQに書かれています。指定されたパスは、初期化時に本来のrootから /var/sandox/sandbox-python にコピーされます。

こちらもファイルを一つ一つ指定するのは面倒なこともあるでしょう。そのような場合は、ディレクトリ単位で指定できます。Dify Sandboxコンテナの場合、多くの shared library は /usr/lib/x86_64-linux-gnu ディレクトリにあるようです。

環境変数を使うとデフォルトのパスが上書きされてしまうため、それらを含めるように環境変数を指定しましょう。(例: PYTHON_LIB_PATH="/usr/local/lib/python3.10,/usr/lib/python3.10,/usr/lib/python3,/usr/lib/x86_64-linux-gnu,certs/ca-certificates.crt,/etc/nsswitch.conf,/etc/hosts,/etc/resolv.conf,/run/systemd/resolve/stub-resolv.conf,/run/resolvconf/reslvconf/resolv.conf")

いくつかライブラリを試した限りでは、上記のディレクトリを加えるだけでもエラーはなくなりました。もちろんこれだけでは不足している場合もあると思われるので、そのときは都度必要なディレクトリ・ファイルを追加してください。

1〜3まで実施すると、boto3やnumpyなどは(軽く確認した限り)無事動くようになりました。

簡単に設定する

上記は少し大変、そもそもDifyのセルフホスト自体が大変ですね。

私の公開している dify-self-hosted-on-aws プロジェクトでは、DifyをAWS上にセルフホストした上で、上記の設定が簡単にできます。

github.com

このプロジェクトを使えば、必要な設定箇所は以下の2点のみです:

1. bin/cdk.tsallowAnySyscalls を追加

new DifyOnAwsStack(app, 'DifyOnAwsStack', {
  ...
  difySandboxImageTag: 'main',
  allowAnySyscalls: true,   // これを追加!
});

2. sandbox-python-requirements.txtPythonライブラリを追加

lib/constructs/dify-services/docker/sandbox-python-requirements.txt に必要なPythonライブラリを追加します。

これでデプロイすれば、上記の設定が完了した状態になります。

なお、python_lib_path については api.ts で設定しています。こちらも適宜追加してください。

そもそも制限を回避して良いですか?

冒頭でDify Sandboxの意義を説明しましたが、上記のワークアラウンドを適用することで、一部のセキュリティ対策が事実上無効化されてしまうことになります。これは許容できるでしょうか?

いつものように、答えはケースバイケースとなります。本来Dify Sandboxが防ぎたいリスクは悪意のあるコードを実行される点にあるので、それを考慮しなくて良いケースでは大きなリスクはないと考えることもできるかもしれません。例えばDifyを自分専用で使う場合や、信頼できる社内メンバーのみに提供する場合などです。

Dify自体はマルチテナントのSaaSを提供しているため、悪意あるコードを実行されるリスクは必ず対処する必要があるのでしょう。

まとめ

セルフホスト版のDifyのコード実行機能で任意のPythonライブラリを利用する方法を紹介しました。

記事の途中で紹介した dify-on-aws-cdk については、先日のJAWS CDK支部でも話す機会をいただけたので、ぜひご覧ください!

speakerdeck.com

最後に今月のもなちゃんです。