跳到主要內容
Open In ColabOpen on GitHub

如何將執行階段值傳遞給工具

📦相容性
本指南中的程式碼需要 langchain-core>=0.2.21。請確保您已安裝正確的套件。

您可能需要將值綁定到 工具,這些值僅在執行階段才知道。例如,工具邏輯可能需要使用發出請求的使用者 ID。

大多數情況下,這些值不應由 LLM 控制。實際上,允許 LLM 控制使用者 ID 可能會導致安全風險。

相反,LLM 應僅控制工具的參數,這些參數旨在由 LLM 控制,而其他參數(例如使用者 ID)應由應用程式邏輯固定。

本操作指南向您展示如何防止模型產生某些工具參數並在執行階段直接注入它們。

搭配 LangGraph 使用

如果您正在使用 LangGraph,請參考此操作指南,其中展示了如何建立一個代理程式來追蹤給定使用者最喜歡的寵物。

我們可以將它們綁定到聊天模型,如下所示

pip install -qU "langchain[openai]"
import getpass
import os

if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")

from langchain.chat_models import init_chat_model

llm = init_chat_model("gpt-4o-mini", model_provider="openai")

從模型隱藏參數

我們可以使用 InjectedToolArg 注釋來標記我們工具的某些參數,例如 user_id,表示它們是在執行階段注入的,這意味著它們不應由模型產生

from typing import List

from langchain_core.tools import InjectedToolArg, tool
from typing_extensions import Annotated

user_to_pets = {}


@tool(parse_docstring=True)
def update_favorite_pets(
pets: List[str], user_id: Annotated[str, InjectedToolArg]
) -> None:
"""Add the list of favorite pets.

Args:
pets: List of favorite pets to set.
user_id: User's ID.
"""
user_to_pets[user_id] = pets


@tool(parse_docstring=True)
def delete_favorite_pets(user_id: Annotated[str, InjectedToolArg]) -> None:
"""Delete the list of favorite pets.

Args:
user_id: User's ID.
"""
if user_id in user_to_pets:
del user_to_pets[user_id]


@tool(parse_docstring=True)
def list_favorite_pets(user_id: Annotated[str, InjectedToolArg]) -> None:
"""List favorite pets if any.

Args:
user_id: User's ID.
"""
return user_to_pets.get(user_id, [])
API 參考:InjectedToolArg | tool

如果我們查看這些工具的輸入架構,我們將看到 user_id 仍然列出

update_favorite_pets.get_input_schema().schema()
{'description': 'Add the list of favorite pets.',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'},
'user_id': {'description': "User's ID.",
'title': 'User Id',
'type': 'string'}},
'required': ['pets', 'user_id'],
'title': 'update_favorite_petsSchema',
'type': 'object'}

但是,如果我們查看工具調用架構(這是傳遞給模型以進行工具調用的內容),則 user_id 已被移除

update_favorite_pets.tool_call_schema.schema()
{'description': 'Add the list of favorite pets.',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'}},
'required': ['pets'],
'title': 'update_favorite_pets',
'type': 'object'}

因此,當我們調用我們的工具時,我們需要傳入 user_id

user_id = "123"
update_favorite_pets.invoke({"pets": ["lizard", "dog"], "user_id": user_id})
print(user_to_pets)
print(list_favorite_pets.invoke({"user_id": user_id}))
{'123': ['lizard', 'dog']}
['lizard', 'dog']

但是當模型調用該工具時,不會產生 user_id 參數

tools = [
update_favorite_pets,
delete_favorite_pets,
list_favorite_pets,
]
llm_with_tools = llm.bind_tools(tools)
ai_msg = llm_with_tools.invoke("my favorite animals are cats and parrots")
ai_msg.tool_calls
[{'name': 'update_favorite_pets',
'args': {'pets': ['cats', 'parrots']},
'id': 'call_pZ6XVREGh1L0BBSsiGIf1xVm',
'type': 'tool_call'}]

在執行階段注入參數

如果我們想使用模型產生的工具調用實際執行我們的工具,我們需要自行注入 user_id

from copy import deepcopy

from langchain_core.runnables import chain


@chain
def inject_user_id(ai_msg):
tool_calls = []
for tool_call in ai_msg.tool_calls:
tool_call_copy = deepcopy(tool_call)
tool_call_copy["args"]["user_id"] = user_id
tool_calls.append(tool_call_copy)
return tool_calls


inject_user_id.invoke(ai_msg)
API 參考:chain
[{'name': 'update_favorite_pets',
'args': {'pets': ['cats', 'parrots'], 'user_id': '123'},
'id': 'call_pZ6XVREGh1L0BBSsiGIf1xVm',
'type': 'tool_call'}]

現在我們可以將我們的模型、注入程式碼和實際工具鏈接在一起,以建立一個工具執行鏈

tool_map = {tool.name: tool for tool in tools}


@chain
def tool_router(tool_call):
return tool_map[tool_call["name"]]


chain = llm_with_tools | inject_user_id | tool_router.map()
chain.invoke("my favorite animals are cats and parrots")
[ToolMessage(content='null', name='update_favorite_pets', tool_call_id='call_oYCD0THSedHTbwNAY3NW6uUj')]

查看 user_to_pets 字典,我們可以發現它已更新為包含貓和鸚鵡

user_to_pets
{'123': ['cats', 'parrots']}

註解參數的其他方式

以下是一些註解我們的工具參數的其他方式

from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field


class UpdateFavoritePetsSchema(BaseModel):
"""Update list of favorite pets"""

pets: List[str] = Field(..., description="List of favorite pets to set.")
user_id: Annotated[str, InjectedToolArg] = Field(..., description="User's ID.")


@tool(args_schema=UpdateFavoritePetsSchema)
def update_favorite_pets(pets, user_id):
user_to_pets[user_id] = pets


update_favorite_pets.get_input_schema().schema()
API 參考:BaseTool
{'description': 'Update list of favorite pets',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'},
'user_id': {'description': "User's ID.",
'title': 'User Id',
'type': 'string'}},
'required': ['pets', 'user_id'],
'title': 'UpdateFavoritePetsSchema',
'type': 'object'}
update_favorite_pets.tool_call_schema.schema()
{'description': 'Update list of favorite pets',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'}},
'required': ['pets'],
'title': 'update_favorite_pets',
'type': 'object'}
from typing import Optional, Type


class UpdateFavoritePets(BaseTool):
name: str = "update_favorite_pets"
description: str = "Update list of favorite pets"
args_schema: Optional[Type[BaseModel]] = UpdateFavoritePetsSchema

def _run(self, pets, user_id):
user_to_pets[user_id] = pets


UpdateFavoritePets().get_input_schema().schema()
{'description': 'Update list of favorite pets',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'},
'user_id': {'description': "User's ID.",
'title': 'User Id',
'type': 'string'}},
'required': ['pets', 'user_id'],
'title': 'UpdateFavoritePetsSchema',
'type': 'object'}
UpdateFavoritePets().tool_call_schema.schema()
{'description': 'Update list of favorite pets',
'properties': {'pets': {'description': 'List of favorite pets to set.',
'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'}},
'required': ['pets'],
'title': 'update_favorite_pets',
'type': 'object'}
class UpdateFavoritePets2(BaseTool):
name: str = "update_favorite_pets"
description: str = "Update list of favorite pets"

def _run(self, pets: List[str], user_id: Annotated[str, InjectedToolArg]) -> None:
user_to_pets[user_id] = pets


UpdateFavoritePets2().get_input_schema().schema()
{'description': 'Use the tool.\n\nAdd run_manager: Optional[CallbackManagerForToolRun] = None\nto child implementations to enable tracing.',
'properties': {'pets': {'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'},
'user_id': {'title': 'User Id', 'type': 'string'}},
'required': ['pets', 'user_id'],
'title': 'update_favorite_petsSchema',
'type': 'object'}
UpdateFavoritePets2().tool_call_schema.schema()
{'description': 'Update list of favorite pets',
'properties': {'pets': {'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'}},
'required': ['pets'],
'title': 'update_favorite_pets',
'type': 'object'}

此頁面是否有幫助?