如何将运行时值传递给工具
📦Compatibility
The code in this guide requires
langchain-core>=0.2.21. Please ensure you have the correct packages installed.您可能需要将仅在运行时知晓的值绑定到 工具 (tool)。例如,工具逻辑可能需要使用发出请求的用户的 ID。
大多数情况下,这些值不应由 LLM 控制。实际上,允许 LLM 控制用户 ID 可能会带来安全风险。
相反,LLM 应只控制工具中旨在由 LLM 控制的参数,而其他参数(如用户 ID)应由应用程序逻辑固定。
本指南将展示如何阻止模型生成某些工具参数,并在运行时直接注入这些参数。
与 LangGraph 一起使用
如果您正在使用 LangGraph,请参考本指南,它展示了如何创建一个代理来跟踪给定用户最喜欢的宠物。
我们可以将它们绑定到聊天模型,如下所示:
Select chat model:
pip install -qU "langchain[google-genai]"
import getpass
import os
if not os.environ.get("GOOGLE_API_KEY"):
os.environ["GOOGLE_API_KEY"] = getpass.getpass("Enter API key for Google Gemini: ")
from langchain.chat_models import init_chat_model
llm = init_chat_model("gemini-2.0-flash", model_provider="google_genai")
隐藏参数
我们可以使用 InjectedToolArg 注解来标记 Tool 中的某些参数,例如 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 Reference:InjectedToolArg | tool
如果我们查看这些工具的输入 schema,会发现 user_id 仍然被列了出来:
update_favorite_pets.get_input_schema().model_json_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.model_json_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 Reference: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().model_json_schema()
API Reference: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.model_json_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().model_json_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.model_json_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().model_json_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.model_json_schema()
{'description': 'Update list of favorite pets',
'properties': {'pets': {'items': {'type': 'string'},
'title': 'Pets',
'type': 'array'}},
'required': ['pets'],
'title': 'update_favorite_pets',
'type': 'object'}