ジェネラティブエージェンツの大嶋です。
運営している勉強会コミュニティStudyCoで「【LangChainゆる勉強会#16】LangGraph Functional APIをさわってみる」というイベントを開催しました。
アーカイブ動画はこちらです。
今回はLangGraphの新しい実装方法「Functional API」をさわっていきました。
LangGraph Functional APIについてのLangChain公式ブログはこちらです。
公式ドキュメントはこちらです。
今回のポイント
LangGraph Functional APIの概要
LangGraph Functional APIは、LangGraphの新しい実装方法です。
今までのStateGraphを使った実装方法(Graph API)と比較して、Functional APIでは通常のPythonのコードのようにワークフローを実装できます。
langgraph v0.2.74のリリース時にFunctional APIのドキュメントから「Beta」の記述が削除されたので、今後はLangGraphの主要な実装方法の1つになっていくかもしれません。
https://github.com/langchain-ai/langgraph/releases/tag/0.2.74
LangGraph Functional APIの動作のポイント
LangGraph Functional API、ひいてはLangGraphの動作のポイントは、Checkpointerにあると考えています。
以下は、LangGraph Functional APIでgenerate_random_numberというタスクをワークフローに入れた例です。
import random import uuid from langgraph.checkpoint.memory import MemorySaver from langgraph.func import entrypoint, task from langgraph.types import Command, interrupt @task def generate_random_number() -> int: return random.randint(0, 100) @entrypoint(checkpointer=MemorySaver()) def workflow(topic: str) -> dict: random_number = generate_random_number().result() is_approved = interrupt({"random_number": random_number}) return { "is_approved": is_approved, "random_number": random_number, }
ワークフローでは「generate_random_number」というタスクを呼び出しています。
@entrypoint(checkpointer=MemorySaver()) def workflow(topic: str) -> dict: random_number = generate_random_number().result() is_approved = interrupt({"random_number": random_number}) return { "is_approved": is_approved, "random_number": random_number, }
Functional APIの公式ドキュメントにも記載されていますが、ワークフローの関数内でランダム性のある処理を行うのはNGです。 以下のようなコードにしてしまうと、interrupt(Human-in-the-Loop)の前後でrandom_numberの値が再計算され、異なる値になってしまう可能性があるわけです。
@entrypoint(checkpointer=MemorySaver()) def workflow(topic: str) -> dict: random_number = random.randint(0, 100) is_approved = interrupt({"random_number": random_number}) return { "is_approved": is_approved, "random_number": random_number, }
LangGraph Functional APIでは、@entrypointが付けられたワークフローをinterrupt後に再実行すると、@taskをつけた各関数の結果はCheckpointerから得るのだと思われます。
これはHuman-in-the-Loopやエラー時に続きから実行する処理を組み込むためです。 そこで、LLMの呼び出しなどは@taskをつけた関数内で実施する必要がある、ということになります。
Functional APIのサンプルコード
LangGraphの公式ドキュメントには、Functional APIのサンプルコードもいくつか追加されています。
たとえば以下は、Functional APIでFunction callingを繰り返すエージェント(LangGraphで言うReActエージェント)を実装する例です。
他にもFunctional APIでHandsoff型のマルチエージェントを実装する例も公開されています。
Functional APIを使うと、Graph APIよりも通常のプログラミングらしくワークフローを構築できることがわかります。
SubGraphのようなワークフローがうまく動かない例
最後に余談ですが、LangGraphのGraph APIでは、SubGraph中でのinterruptがうまく動作しないという問題があります。
Functional APIもSubGraphのようなことができるのですが、やはりinterruptがうまく動作しないようでした。
@entrypoint() def sub_workflow(topic: str) -> dict: : is_approved = interrupt( : @entrypoint(checkpointer=MemorySaver()) def workflow(topic: str) -> dict: : sub_workflow_result = sub_workflow.invoke({"topic": topic}) :
現状interruptは親のワークフローのみで使うのが良さそうです。
おわりに
以上、LangGraph Functional APIをさわってみました。
次回として、LangGraphの「Prebuilt Agents」をさわる会も開催予定です。
LangChain / LangGraph、その他のテーマで引き続き勉強会を開催していきます。 もしも「こんな話が聞きたい」というテーマがあれば、ぜひお声がけください!