😎

[LangGraph] 自律的にプログラムを実行するLLM Agentを作るための60行スクリプト

2024/04/28に公開

60行スクリプトシリーズ第二弾。前回はこちら
この記事はMultiAgent作成フレームワーク「LangGraph」に興味がある人が手っ取り早く動かしてみることを目標にした記事です。

この記事を見てできること

  • 指定のお題に対して、関数で定義したツール実行(Web検索など)をする。
  • 実行結果を元に答えを返す。答えがわかるまでツール実行を繰り返す。

このワークフローの拡張性も意識して、組み込みのワークフローは利用しない方針で作成しています。

まずはセットアップ

poetry init
poetry add langgraph@0.0.39 langchain-openai@0.1.1 langchain@0.1.14

※ Tavilyを利用するのでTAVILY_API_KEYを取得しておく

プログラムと解説

# 必要なライブラリをインポート
import argparse
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import END, MessageGraph
from langgraph.prebuilt.tool_node import ToolNode

# 条件付きエッジ
def should_continue(messages):
    last_message = messages[-1]
    if last_message.tool_calls:
        return "action"
    return END


# 利用するツールとモデルを定義
# 今回はLangChainが提供するTavilyを用いた検索ツールを利用
tools = [TavilySearchResults(max_results=1)]
model = ChatOpenAI(model="gpt-3.5-turbo")


# ワークフローを定義
# app = create_tool_calling_executor(model), tools) を利用する場合は不要
workflow = MessageGraph()
workflow.add_node("agent", model.bind_tools(tools))
workflow.add_node("action", ToolNode(tools))
workflow.add_edge("action", "agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.set_entry_point("agent")
memory = SqliteSaver.from_conn_string(":memory:")
app = workflow.compile(checkpointer=memory)


# CLIの引数を定義
parser = argparse.ArgumentParser(description="Run a language graph")
parser.add_argument("question", type=str, help="The question to ask the graph", nargs="?")
args = parser.parse_args()


# 実行
question = args.question
thread = {"configurable": {"thread_id": "4"}}
for step in app.stream(question, thread):  # type: ignore
    node, message = next(iter(step.items()))
    if message:
        if isinstance(message, list) and isinstance(message[-1], BaseMessage):
            message[-1].pretty_print()
        elif isinstance(message, BaseMessage):
            message.pretty_print()
            if isinstance(message, AIMessage) and len(message.tool_calls) > 0:
                print(message.tool_calls)

実行結果

$ export OPENAI_API_KEY=sk-xxxxxx
$ export TAVILY_API_KEY=tvly-xxxx
$ python langgraph_base.py "2024/01/01の東京の天気は?"
================================== Ai Message ==================================


[{'name': 'tavily_search_results_json', 'args': {'query': '2024/01/01 Tokyo weather'}, 'id': 'call_CnpHrC6hvoWAY2nx0bRhkIJd'}]
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://www.weatherapi.com/", "content": "{'location': {'name': 'Tokyo', 'region': 'Tokyo', 'country': 'Japan', 'lat': 35.69, 'lon': 139.69, 'tz_id': 'Asia/Tokyo', 'localtime_epoch': 1714273330, 'localtime': '2024-04-28 12:02'}, 'current': {'last_updated_epoch': 1714273200, 'last_updated': '2024-04-28 12:00', 'temp_c': 23.0, 'temp_f': 73.4, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, 'wind_mph': 8.1, 'wind_kph': 13.0, 'wind_degree': 150, 'wind_dir': 'SSE', 'pressure_mb': 1014.0, 'pressure_in': 29.94, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 69, 'cloud': 0, 'feelslike_c': 24.7, 'feelslike_f': 76.4, 'vis_km': 10.0, 'vis_miles': 6.0, 'uv': 6.0, 'gust_mph': 12.5, 'gust_kph': 20.2}}"}]
================================== Ai Message ==================================

2024年1月1日の東京の天気は晴れです。気温は23度で、風速は8.1 mphです。UV指数は6.0で、湿度は69%です。詳細な情報は[こちら](https://www.weatherapi.com/)をご覧ください。

独自のツールを追加する

@toolで関数を追加するだけで、ツールとして利用できるようになります。
tool内でエージェントを呼び出せばMulti Agentを実現します。
一部のタスクに特化したLLMを呼び出したり、Visionモデルなどコストの高いモデルと安いモデルを組み合わせたりするのに便利です。

from langchain_core.tools import tool

@tool
def exponentiate(x: float, y: float) -> float:
    """Raise 'x' to the 'y'."""
    return x**y

...

tools = [TavilySearchResults(max_results=1), exponentiate]
$ python langgraph_base.py "2の3乗は?"
================================== Ai Message ==================================


[{'name': 'exponentiate', 'args': {'x': 2, 'y': 3}, 'id': 'call_1CMEwIueMxvMDd2jn0rvezy5'}]
================================= Tool Message =================================
Name: exponentiate

8.0
================================== Ai Message ==================================

2の3乗は8です。

参考

今回作ったソースコードのフルバージョン
https://github.com/HikaruEgashira/python-workspaces/blob/main/langchain/langgraph_base.py

ClaudeもTool Callingに対応してるのですが、ドキュメントのやり方で成功しなかったのでOpenAIを利用しています。

Discussion