「Difyソースコードリーディング #6 - DSLのYAMLファイルの扱いを読み解く」を開催しました #もくもくDify

ジェネラティブエージェンツの大嶋です。

もくもくDifyで「Difyソースコードリーディング #6 - DSLYAMLファイルの扱いを読み解く」という勉強会を開催しました。

dify-mokumoku.connpass.com

アーカイブ動画はこちらです。

youtube.com

Difyのソースコードは以下です。

github.com

今回も 戸塚さん と一緒に話しながらコードを読んでいきました!

今回のポイント

DifyにはアプリをDSLと呼ばれるYAMLファイルでexport・importする機能があります。

参考:https://docs.dify.ai/ja-jp/guides/application-orchestrate/creating-an-application

今回はDSLYAMLファイルの内容がどのように作られるのかを読み解いていきました。

YAMLファイルのexportのAPI

まず最初に、YAMLファイルをexportするAPIを見つけました。

api.add_resource(AppExportApi, "/apps/<uuid:app_id>/export")

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/api/controllers/console/app/app.py#L369C1-L370C1

その処理では、AppDslServiceのexport_dslが使われていました。

        return {"data": AppDslService.export_dsl(app_model=app_model, include_secret=args["include_secret"])}

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/api/controllers/console/app/app.py#L245

AppDslServiceのexport_dsl

AppDslServiceのexport_dslを読んでいくと、dictをYAMLに変換して返していることが分かりました。

    def export_dsl(cls, app_model: App, include_secret: bool = False) -> str:
            :
        export_data = {
            "version": current_dsl_version,
            :
        }
            :
        return yaml.dump(export_data, allow_unicode=True)

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/api/services/app_dsl_service.py#L159

Workflowクラス(workflowsテーブル)

さらに読み進めていくと、DBのworkflowsテーブルに対応するWorkflowクラスに、このクラスのデータ(つまりworkflowsテーブルのレコード)をdictに変換する処理が実装されており、これが使われていることが分かりました。

    def to_dict(self, *, include_secret: bool = False) -> Mapping[str, Any]:

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/api/models/workflow.py#L269

workflowsテーブルにはgraphやfeaturesといったカラムがあり、ここにJSON形式でワークフローのグラフなどが保存されているようです。

    graph: Mapped[str] = db.Column(db.Text)
    features: Mapped[str] = db.Column(db.Text)

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/api/models/workflow.py#L122C1-L124C1

そのJSONが具体的にどのように構成されているのかを確認しようと読み進めたところ、バックエンドではなくフロントエンドで定義されていることに辿り着きました。

(おそらくですが、バックエンドでのバリデーションなどはなさそうでした)

フロントエンドのNode・Edgeのtype

フロントエンドには、CommonNodeTypeやCommonEdgeTypeといったtypeが定義されていました。

export type CommonNodeType<T = {}> = {

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/web/app/components/workflow/types.ts#L43C1-L44C1

export type CommonEdgeType = {

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/web/app/components/workflow/types.ts#L69C1-L70C1

各ノードは、CommonNodeTypeを拡張して用意されていました。 たとえばLLMのノードのtypeは以下のようになっています。

export type LLMNodeType = CommonNodeType & {
  model: ModelConfig
  prompt_template: PromptItem[] | PromptItem
    :

https://github.com/langgenius/dify/blob/4373777871b33daa13ae55c52a06f8e76764cbfc/web/app/components/workflow/nodes/llm/types.ts#L4

React Flow

ワークフローのフロントエンドは、上記のtypeを使いつつ、React Flowというライブラリを使って実装されていることが分かりました。

reactflow.dev

次回のご案内

以上、DSLYAMLファイルがどのように作られるのか、雰囲気を読み解いてみました。

今まではバックエンドのソースコードリーディングが多かったので、今回フロントエンドも少し扱えたのはよかったです。

次回は「ビルドやリリース時のバージョン番号付けなどを読み解く」というテーマです。 ご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

また、水曜日にもDifyの活用についてのもくもく会があります。 こちらもご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

「AIエージェントキャッチアップ #3 - The AI Scientist」を開催しました

ジェネラティブエージェンツの大嶋です。

「AIエージェントキャッチアップ #3 - The AI Scientist」という勉強会を開催しました。

generative-agents.connpass.com

アーカイブ動画はこちらです。

youtube.com

The AI Scientist

今回は、Sakana AI社のThe AI Scientistを動かそうと挑戦したり、ソースコードを読んだりしてみました。

The AI ScientistのGItHubリポジトリはこちらです。

github.com

今回のポイント

The AI Scientistの概要

The AI Scientistは、研究・論文の作成を自動化するAIエージェントです。

コードを生成して実行することで、機械学習の研究の実験を行い、その結果を論文にすることができます。

The AI Scientistで生成された論文の例がいくつかGitHubで公開されています。

https://github.com/SakanaAI/AI-Scientist/tree/main/example_papers

研究のアイデア生成・実験・論文生成に加えて、LLMによる論文のレビューもできるようです。

動くか試したところ...

READMEを読みながら動かそうとしてみましたが、結論として、今回はうまく動かすことができませんでした。

Google Colab上で動くか試したところ、Semantic Scholar APIのレートリミットで処理がなかなか先に進まず、断念しました。

Semantic Scholar APIは、APIキーを払い出すことでレートリミットが緩和されるようです。 The AI Scientistを動かそうと挑戦する方は、まずSemantic Scholar APIAPIキーを取得するとよさそうです。

ちなみに、Dockerで動かす手順についても挑戦しようとしましたが、docker buildがエラーになり、こちらも断念しました。

処理の流れ

The AI Scientistを動かす際は、launch_scientist.py というファイルを実行することになります。

ここには主な処理の流れが実装されていて、このコードを読むとおおよそ以下のように処理が進むことが分かります。

  1. generate_ideas(アイデアの生成)
  2. check_idea_novelty(新規性のチェック)
  3. perform_experiments(実験)
  4. perform_writeup(論文の生成)
  5. perform_review(レビュー)
  6. perform_improvement(論文の改善)

各処理のソースコード

上記の各処理のソースコードai_scientist ディレクトリに置かれています。 プロンプトもこの中に含まれています。

このディレクトリに含まれるプロンプト・ソースコードは全部で2000行程度で、想像していたよりも短いコードで実装されていて驚きました。

実験のためのコード生成・実行には Aider が使われていました。 この部分でAiderを使用していることで、The AI Scientist自体は比較的コンパクトに実装できた、ということだと思います。

Aiderは比較的有名かつ今回の件で気になったので、そのうち勉強会のテーマとして扱うかもしれません。

次回のご案内

The AI Scientistはとても話題になっていたので、今回扱うことができてよかったです。

次回は「AIエージェントキャッチアップ #4 - OpenHands (旧OpenDevin)」ということで、OpenHands (旧OpenDevin) を動かしてみます!

github.com

generative-agents.connpass.com

ご興味・お時間ある方はぜひご参加ください!

また、その次の回以降のテーマも募集しているので、気になるエージェントのOSSなどあれば教えてください!

勉強会「【LangChainゆる勉強会#13】LangChain v0.3やその周辺のアップデートをチェック」を開催しました #StudyCo

ジェネラティブエージェンツの大嶋です。

運営している勉強会コミュニティStudyCoで「【LangChainゆる勉強会#12】LangGraphの最新ドキュメントを全体的にざっと読む」というイベントを開催しました。

studyco.connpass.com

アーカイブ動画はこちらです。

youtube.com

今回は「LangChain v0.3やその周辺のアップデートをチェック」というテーマで、LangChainの公式ドキュメントや公式ブログを参照しながら、LangChain v0.3やその周辺のアップデートをチェックしていきました。

LangChain v0.3での破壊的変更

公式ドキュメントに書かれている通りですが、LangChain v0.3での主な変更は、LangChainの内部でPydantic v1が使われていた箇所がPydantic v2に変更されたことです。

from langchain_core.pydantic_v1 import BaseModel などとしていた箇所がある場合、from pydantic import BaseModel のように変更することになります。

その他、Pydantic v1特有の機能を使っている場合は、Pydantic v2へのマイグレーションが必要です。

詳細は公式ドキュメントを参照してください。

python.langchain.com

LangChain v0.2の間のアップデート

LangChain v0.3がリリースされるタイミングでの変更は上記のPydanticの件が中心ですが、それ以外にもLangChain v0.2の間にはさまざまなアップデートが入っています。

v0.2の間の主要なアップデートについても、LangChain v0.3についての公式ドキュメントとブログで解説されています。

以下、いくつか抜粋して簡単に紹介していきます。

Tool関連のインターフェースの強化

Tool関連のインターフェースの強化として、以下のようなアップデートがありました。

  • llm.bind_tools でツールを設定
  • runnable.as_tool でRunnableをツールに変換
  • ツールの引数がRunnableConfigに対応
  • ツールの戻り値にartifactを追加可能

(Tool関連のアップデートは他にもいくつかあります)

詳細は以下のブログ記事にまとまっています。

blog.langchain.dev

init_chat_model

Chat modelの共有の初期化方法として、init_chat_modelという関数が追加されました。

たとえば以下のようにして、ChatOpenAIのインスタンスを得ることができます。

from langchain.chat_models import init_chat_model

gpt_4o = init_chat_model("gpt-4o", model_provider="openai", temperature=0)

python.langchain.com

Rate limiter

モデルのレートリミットを設定するRate limiterが追加されました。

たとえば以下のようにして利用可能です。

from langchain_core.rate_limiters import InMemoryRateLimiter
from langchain_openai import ChatOpenAI

rate_limiter = InMemoryRateLimiter(
    requests_per_second=1,
    check_every_n_seconds=0.1,
    max_bucket_size=10,
)

model = ChatOpenAI(model="gpt-4o-mini", rate_limiter=rate_limiter)

python.langchain.com

Message関連のユーティリティ

SystemMessage、HumanMessage、AIMessageなどのMessage関連のユーティリティとして、以下の3つが追加されました。

  • trim_messages: messagesを、最大トークン数などでトリミング
  • filter_messages: messagesから、HumanMessageだけといったフィルタリング
  • merge_message_runs: messagesでHuman・Humanのように同じ種別が連続している場合に1つにマージ

たとえばAnthropicのClaudeはHumanMessageが連続しているとエラーとなりますが、そのようなケースでmerge_message_runsが役立つことになります。

python.langchain.com

python.langchain.com

python.langchain.com

カスタムイベントのサポート

adispatch_custom_eventを使用することで、astream_eventsやCallbackにカスタムイベントを追加できるようになりました。

python.langchain.com

レガシーChainの非推奨化

LCEL以前のレガシーChainがいくつか追加で非推奨になりました。

これはたまたま先日見つけたのですが... LLMChainのような以前からdeprecatedだったChainはv0.3で廃止予定だったのですが、v1.0での廃止に変更されているようです。

@deprecated(
    since="0.1.17",
    alternative="RunnableSequence, e.g., `prompt | llm`",
    removal="1.0",
)
class LLMChain(Chain):

引用元:https://github.com/langchain-ai/langchain/blob/9404e7af9d8768a7e884dae3e9fafb712eaf4e99/libs/langchain/langchain/chains/llm.py#L41

おわりに

以上、LangChain v0.3についてと、v0.2の間のアップデートを紹介しました。

とくにv0.2の間のアップデートは、地味に便利なものが多く、この機会に改めて確認できてよかったです。

LangChain / LangGraph、その他のテーマで引き続き勉強会を開催していきます。 もしも「こんな話が聞きたい」というテーマがあれば、ぜひお声がけください!

「Difyソースコードリーディング #5 - ナレッジベース・RAGの実装を読み解く」を開催しました #もくもくDify

ジェネラティブエージェンツの大嶋です。

もくもくDifyで「Difyソースコードリーディング #5 - ナレッジベース・RAGの実装を読み解く」というイベントを開催しました。

dify-mokumoku.connpass.com

アーカイブ動画はこちらです。

youtube.com

Difyのソースコードは以下です。

github.com

今回も 戸塚さん と一緒に話しながらコードを読んでいきました!

今回のポイント

DifyのRAGの実装の概要

DifyではRAG関係の重要なコードは api/core/rag ディレクトリに置かれているようでした。

api/core/rag以下のディレクトリの概要は、以下のようになっていました。

  • cleaner ... 前処理
  • data_post_processor ... 後処理(リランクとリオーダーの呼び出し)
  • datasource ... Weaviateなどのベクトルデータベースのインテグレーション
  • docstore ... RDBにデータを読み書きする機能
  • extractor ... CSV、PDF、PPTXなどのファイルからのテキストの抽出
  • index_processor ... インデクシングの処理
  • models ... Document型の定義
  • rerank ... リランクの実装
  • retrieval ... Retrievalの処理
  • splitter ... チャンク化の処理

IndexingRunner

Difyのナレッジベースの機能でインデクシングを実行したときは、indexing_runner.py のIndexingRunnerクラスが使われるようでした。

IndexingRunnerからextractorやdatasourceなどが呼び出されて処理が進むようです。

インデクシングの際の処理の流れを把握したい場合は、IndexingRunnerクラスがヒントになりそうです。

ファイルからのテキスト抽出処理

extract_processor.py には、以下のように拡張子に応じてテキストの抽出処理を切り替えるよう実装されていました。

                    if file_extension in {".xlsx", ".xls"}:
                        extractor = ExcelExtractor(file_path)
                    elif file_extension == ".pdf":
                        extractor = PdfExtractor(file_path)

このあたりを改造すると、PDFやPPTXといったファイルからのテキスト抽出処理を変えてみるといったこともできそうです。

次回のご案内

次回は「DSLYAMLファイルの扱いを読み解く」というテーマです。 ご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

また、水曜日にもDifyの活用についてのもくもく会があります。 こちらもご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

「AIエージェントキャッチアップ #2 - crewAI」を開催しました

ジェネラティブエージェンツの大嶋です。

「AIエージェントキャッチアップ #2 - crewAI」という勉強会を開催しました。

generative-agents.connpass.com

アーカイブ動画はこちらです。

youtube.com

crewAI

今回は、crewAIについて、ドキュメントを読んだり、実際に動かしてみたりしてキャッチアップしていきました。

crewAIのGItHubリポジトリはこちらです。

github.com

公式ドキュメントはこちらです。

docs.crewai.com

最初にGitHubに掲載されているGetting Startedの手順を試したところ、すぐに動きました!

今回のポイント

crewAIの概要

crewAIは、Pythonパッケージとして提供されている、OSSのマルチエージェントフレームワークです。

以下のコードは、crewAIのリポジトリREADME の例から、重要な箇所を抜粋したものです。

from crewai import Agent, Task, Crew, Process

researcher = Agent(
  role='Senior Research Analyst',
    :
)
writer = Agent(
  role='Tech Content Strategist',
    :
)

task1 = Task(
    :
)

task2 = Task(
    :
)

crew = Crew(
  agents=[researcher, writer],
  tasks=[task1, task2],
  verbose=True,
  process = Process.sequential
)

result = crew.kickoff()

print("######################")
print(result)

複数のAgent・複数のTaskを定義して、「Crew」としてまとめて実行する、ということになります。

AgentやTaskの詳細はYAMLファイルで定義することもできるようでした。

参考:https://docs.crewai.com/getting-started/Start-a-New-CrewAI-Project-Template-Method/#example-defining-agents-and-tasks

crewAIの特徴

crewAIの README には、以下のように書かれています。

While Autogen does good in creating conversational agents capable of working together, it lacks an inherent concept of process.

AutoGenには「プロセス」という概念が不足しており、逆にcrewAIには「プロセス」という概念があることが特徴だと言いたいのではないかと思います。

「プロセス」については、タスクを処理する流れのようなもののようで、

  • Sequential:順々に処理する
  • Hierarchical:マネージャーのエージェントを使って階層的に処理する
  • Consensual Process(計画中):エージェントが共同で意思決定する

という選択肢があるとのことです。

docs.crewai.com

このプロセスの選択のように、crewAIが提供する基本的な処理に対して、ある程度のカスタマイズができるようになっているようでした。

まとめ

crewAIもAutoGenと同じく、マルチエージェントの汎用フレームワークというよりは、特定のユースケースに特化したフレームワークでした。 当たり前かもしれませんが、crewAIのユースケースにハマる状況であれば素早く実装でき、逆にカスタマイズが多い状況にはフィットしないという考え方になると思います。

ちなみに、waitlistに登録されたため実際にさわることはできませんでしたが、crewAI公式からcrewAI+というサービスが提供されており、crewAIのアプリケーションをデプロイできるようです。

www.crewai.com

次回のご案内

crewAIもよく耳にするフレームワークだったので、この機会にキャッチアップできてよかったです。

次回は「AIエージェントキャッチアップ #3 - The AI Scientist」ということで、Sakana AI社のThe AI Scientistを動かしてみます!

github.com

generative-agents.connpass.com

ご興味・お時間ある方はぜひご参加ください!

また、その次の回以降のテーマも募集しているので、気になるエージェントのOSSなどあれば教えてください!

「Difyソースコードリーディング #4 - モデルやツールのYAMLファイルの扱いを読み解く」を開催しました #もくもくDify

ジェネラティブエージェンツの大嶋です。

もくもくDifyで「Difyソースコードリーディング #4 - モデルやツールのYAMLファイルの扱いを読み解く」というイベントを開催しました。

dify-mokumoku.connpass.com

アーカイブ動画はこちらです。

youtube.com

Difyのソースコードは以下です。

github.com

今回も 戸塚さん と一緒に話しながらコードを読んでいきました!

今回のポイント

Difyに組み込みのツールを追加する方法

今回まずは、Difyに組み込みのツールを追加する処理がどのように実装されているのかを探していきました。

Difyに組み込みのツールを追加する方法は、api/core/tools/README.mdapi/core/tools/docs ディレクトリのドキュメントに書かれていました。

現状は英語と中国語のドキュメントのみですが、戸塚さんが日本語版のプルリクエストを出してくれているそうです。

プルリクエスト:https://github.com/langgenius/dify/pull/8469

組み込みツールの追加の例

組み込みツールの追加の例として、Discordのツールを追加するプルリクエストも参考に見ていきました。

プルリクエスト:https://github.com/langgenius/dify/pull/7852

そのツール特有の処理を行うクラスを実装し、ツールの名前やパラメータをYAMLファイルに記述するだけで、組み込みのツールが追加されるようです。

YAMlファイルの構造の定義

ツールの名前やパラメータを記述するYAMLファイルの構造は、tool_entities.py というファイルに定義されていました。

たとえばToolParameterというクラスが以下のように定義されています。

class ToolParameter(BaseModel):
        :
    name: str = Field(..., description="The name of the parameter")
    label: I18nObject = Field(..., description="The label presented to the user")
    human_description: Optional[I18nObject] = Field(None, description="The description presented to the user")

ツールの読み込みの実装

ツールを追加する際、そのツール用のPythonファイルとYAMLファイルだけ記述すればいいということは、どこかでツールの実装を動的にimportしているはずです。

builtin_tool_provider.py を見ると、以下のようにツールのモジュール名からサブクラスをロードしている箇所がありました。

            assistant_tool_class = load_single_subclass_from_source(
                module_name=f"core.tools.provider.builtin.{provider}.tools.{tool_name}",
                script_path=path.join(
                    path.dirname(path.realpath(__file__)), "builtin", provider, "tools", f"{tool_name}.py"
                ),
                parent_type=BuiltinTool,
            )

load_single_subclass_from_sourceの実装は module_import_helper.py にあり、

以下のように動的にモジュールを読み込んでいることがわかりました。

        module = importlib.util.module_from_spec(spec)

importlib.util.module_from_specはPythonの標準ライブラリの関数で、モジュールの動的なimportなどに使えるようです。 この関数は知らなかったので勉強になりました。

参考:https://docs.python.org/ja/3/library/importlib.html#importlib.util.module_from_spec

組み込みのモデルについて

その後、組み込みのモデルについても見ていきました。

基本的に同じような構造になっていて、モデルを追加する際に記述するYAMLファイルの構造は model_entities.py で定義されており、組み込みモデルの動的なimportは model_provider.py で実装されているようでした。

モデルやツールを追加しやすいよう動的に読み込んでいるしくみは、なかなか面白かったです!

次回のご案内

次回は「ナレッジベース・RAGの実装を読み解く」というテーマです。 ご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

また、水曜日にもDifyの活用についてのもくもく会があります。 こちらもご興味ある方はぜひ気軽にご参加ください!

dify-mokumoku.connpass.com

「AIエージェントキャッチアップ #1 - AutoGen / AutoGen Studio」を開催しました

ジェネラティブエージェンツの大嶋です。

「AIエージェントキャッチアップ #1 - AutoGen / AutoGen Studio」という勉強会を開催しました。

generative-agents.connpass.com

アーカイブ動画はこちらです。

youtube.com

AutoGen / AutoGen Studio

今回は、AutoGen / AutoGen Studioについて、ドキュメントを読んだり、実際に動かしてみたりしてキャッチアップしていきました。

AutoGenはこちらです。

github.com

AutoGen Studioはこちらです。

github.com

どちらも公式ドキュメントに記載されている手順ですぐに動きました!

フレームワークによってはなかなか動作しないこともあるので、まず動いてよかったです。

今回のポイント

AutoGen

AutoGenのGitHubリポジトリには、以下のように書かれています。

Multi-Agent Conversation Framework

この言葉の通り、AutoGenはマルチエージェントの「会話」に特化したフレームワークでした。

実装としては、Python版と.NET版があるようです。

ConversableAgentやその子クラスであるAssistantAgent・UserProxyAgentといったクラスでエージェントが実装されており、これらにシステムプロンプトなどの設定をほどこした上で会話させるということでした。

各エージェントは、コードの実行環境やHuman-in-the-Loopといった設定をON/OFFすることもできれば、Function callingの関数を設定したり、自作したコードで機能拡張することもできるようでした。

上にも書いたように、AutoGenは、マルチエージェントの汎用フレームワークというよりは、「会話」に特化したフレームワークです。 そのため、「マルチエージェントに議論させたい」といったAutoGenのユースケースにハマる状況であれば、かなり素早くに実装できそうでした。

AutoGen Studio

AutoGenのGUIである、AutoGen Studioにもふれました。

AutoGenのコードを書くときのように、AssistantAgentやUserProxyAgentクラスに対応する設定をGUIで入力したりしていく、というものでした。

「グループチャット」という種類のエージェントを作成することで、3種類のエージェントに会話させるところまで試すことができました。

AutoGen Studioは、認証機能などもまだなさそうで、少なくとも現時点では個人の手元で動かすぐらいの使い方になりそうです。

ちなみに、画面上のログアウトボタンを押すと、「Please implement your own logout logic」と表示されます。

次回のご案内

AutoGenはかなり有名で前々からさわらなくてはと思っていたので、この機会にキャッチアップできてよかったです。

次回は「AIエージェントキャッチアップ #2 - crewAI」ということで、crewAIにふれてみます。

generative-agents.connpass.com

ご興味・お時間ある方はぜひご参加ください!

また、その次の回以降のテーマも募集しているので、気になるエージェントのOSSなどあれば教えてください!