8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BedrockのTool useをLangChainツールでいい感じにする

Posted at

例えば、以下のような4つのツールがあるとします。

def add(a: int, b: int) -> int:
    """二つの数値を足し算します。"""
    return a + b


def subtract(a: int, b: int) -> int:
    """一つ目の数値から二つ目の数値を引き算します。"""
    return a - b


def multiply(a: int, b: int) -> int:
    """二つの数値を掛け算します。"""
    return a * b


def divide(a: int, b: int) -> float:
    """一つ目の数値を二つ目の数値で割り算します。結果は小数点数(float)で返します。"""
    if b == 0:
        raise ValueError("0での除算はできません。")
    return a / b

これらのツールを使用するには、Converse APIのtoolConfigパラメーターにツールの定義を指定します。

Converse API呼び出し
response = client.converse(
    modelId=model_id,
    system=[{"text": system_prompt}],
    messages=messages,
    toolConfig={
        "tools": [
            {
                "toolSpec": {
                    "name": "add",
                    "description": "二つの数値を足し算します。",
                    "inputSchema": {
                        "json": {
                            "description": "二つの数値を足し算します。",
                            "properties": {
                                "a": {"title": "A", "type": "integer"},
                                "b": {"title": "B", "type": "integer"},
                            },
                            "required": ["a", "b"],
                            "title": "add",
                            "type": "object",
                        }
                    },
                }
            },
            {
                "toolSpec": {
                    "name": "subtract",
                    "description": "一つ目の数値から二つ目の数値を引き算します。",
                    "inputSchema": {
                        "json": {
                            "description": "一つ目の数値から二つ目の数値を引き算します。",
                            "properties": {
                                "a": {"title": "A", "type": "integer"},
                                "b": {"title": "B", "type": "integer"},
                            },
                            "required": ["a", "b"],
                            "title": "subtract",
                            "type": "object",
                        }
                    },
                }
            },
            {
                "toolSpec": {
                    "name": "multiply",
                    "description": "二つの数値を掛け算します。",
                    "inputSchema": {
                        "json": {
                            "description": "二つの数値を掛け算します。",
                            "properties": {
                                "a": {"title": "A", "type": "integer"},
                                "b": {"title": "B", "type": "integer"},
                            },
                            "required": ["a", "b"],
                            "title": "multiply",
                            "type": "object",
                        }
                    },
                }
            },
            {
                "toolSpec": {
                    "name": "divide",
                    "description": "一つ目の数値を二つ目の数値で割り算します。結果は小数点数(float)で返します。",
                    "inputSchema": {
                        "json": {
                            "description": "一つ目の数値を二つ目の数値で割り算します。結果は小数点数(float)で返します。",
                            "properties": {
                                "a": {"title": "A", "type": "integer"},
                                "b": {"title": "B", "type": "integer"},
                            },
                            "required": ["a", "b"],
                            "title": "divide",
                            "type": "object",
                        }
                    },
                }
            },
        ],
        "toolChoice": {"auto": {}},
    },
)

えーっと、、

めんどくさい!!!

関数のパラメーターが変わったら、JSONも変えないといけないので、面倒です。

ということで、少しでも簡単にツールを指定する方法として、LangChainのツールを使用する方法を紹介します。

無理にConverse APIを使わないで全部LangChainでやったらいいじゃないか ってのは、今回は禁句です

ツールをLangChainのツールとして定義する

LangChainのツールは@toolデコレーターをつけるだけです。

from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """二つの数値を足し算します。"""
    return a + b


@tool
def subtract(a: int, b: int) -> int:
    """一つ目の数値から二つ目の数値を引き算します。"""
    return a - b


@tool
def multiply(a: int, b: int) -> int:
    """二つの数値を掛け算します。"""
    return a * b


@tool
def divide(a: int, b: int) -> float:
    """一つ目の数値を二つ目の数値で割り算します。結果は小数点数(float)で返します。"""
    if b == 0:
        raise ValueError("0での除算はできません。")
    return a / b

LangChainツールはinvokeメソッドで呼び出します。

add.invoke(input={"a": 1, "b": 2})

@toolデコレーターを使わずに関数をLangChainツールとする方法もあります。

from langchain_core.tools import StructuredTool

divide_from_func = StructuredTool.from_function(func=divide)

将来的にLambda上で動作させたい場合など、できるだけ依存ライブラリーを減らしたい場合があると思います。
こちらの方法だと元の関数をいじらないので、langchain_core(とその依存ライブラリー)を剥がしやすくなると思います。

LangChainツールとして定義したツールは、パラメーターの構造をJSONスキーマとして出力できます。

add.args_schema.model_json_schema()
{
    "description": "二つの数値を足し算します。",
    "properties": {
        "a": {
            "title": "A",
            "type": "integer"
        },
        "b": {
            "title": "B",
            "type": "integer"
        }
    },
    "required": [
        "a",
        "b"
    ],
    "title": "add",
    "type": "object"
}

これを使ってtoolConfigパラメーターを生成する便利関数を作ります。

def tool_to_spec(tool: StructuredTool):
    name = tool.name
    description = tool.description
    json_schema = tool.args_schema.model_json_schema()

    return {
        "toolSpec": {
            "name": name,
            "description": description,
            "inputSchema": {"json": json_schema},
        }
    }

これにより、Converse APIの呼び出しがこうなります

tools = [add, subtract, multiply, divide]

response = client.converse(
    modelId=model_id,
    system=[{"text": system_prompt}],
    messages=messages,
    toolConfig={
        "tools": [tool_to_spec(tool) for tool in tools],
        "toolChoice": {"auto": {}},
    },
)

いえーい。シンプル!

副産物(?)として、様々な既存のLangChainツールをConverse APIで呼び出せます!

from langchain_community.tools import TavilySearchResults

search_tool = TavilySearchResults()

tools = [search_tool]

response = client.converse(
    modelId=model_id,
    system=[{"text": system_prompt}],
    messages=messages,
    toolConfig={
        "tools": [tool_to_spec(tool) for tool in tools],
        "toolChoice": {"auto": {}},
    },
)

ちなみにConverse APIのresponseから、Toolを呼び出すまでのコードはこんな感じになると思います

for c in response["output"]["message"]["content"]:

    if "toolUse" in c:
        tool_use = c["toolUse"]

        tool_use_id = tool_use["toolUseId"]
        name = tool_use["name"]
        input = tool_use["input"]

        tools_dict = {tool.name: tool for tool in tools}
        tool_result = tools_dict[name].invoke(input=input)
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?