langchain_core.tools.InjectedToolArg
是 LangChain 库中用于工具调用参数注入的注解类,隶属于 langchain_core.tools
模块。它主要用于标记工具函数中的参数,表示这些参数应由运行时上下文(如代理或链)自动注入,而不是由用户输入或模型直接提供。InjectedToolArg
是一个类型注解工具,通常结合 Annotated
类型(来自 Python 的 typing
模块)使用,增强工具函数的灵活性和可维护性。
langchain_core.tools.InjectedToolArg
的详细介绍
langchain_core.tools.InjectedToolArg
是 LangChain 框架中的一个重要功能,旨在增强工具参数的灵活性和安全性,特别是在处理运行时注入值时。本文将详细探讨其定义、使用方法、运行时注入机制以及相关需求。
1. 背景与定义
LangChain 是一个用于构建基于大语言模型(LLM)应用程序的框架,支持从提示管理到工具调用等功能。在工具调用场景中,有时需要将某些参数(如用户 ID)在运行时注入,而非由模型生成,以避免模型错误处理敏感信息或降低安全性。InjectedToolArg
正是为此设计的标记工具,允许开发者在工具定义中明确某些参数为运行时注入。
2. 使用方法
使用 InjectedToolArg
需要在工具定义中通过 Annotated
类型标注参数。以下是两种主要的使用方式:
2.1 使用 @tool
装饰器
直接在函数定义中标注参数。例如:
from langchain_core.tools import tool, InjectedToolArg
from typing import Annotated, List
@tool
def update_favorite_pets(pets: List[str], user_id: Annotated[str, InjectedToolArg]) -> None:
"""更新用户的收藏宠物。"""
# 实现
在这里,user_id
被标记为注入参数,模型只需生成 pets
参数。
2.2 使用 Pydantic 模型
通过 args_schema
定义工具参数,也可以使用 InjectedToolArg
。例如:
from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field
class UpdateFavoritePetsSchema(BaseModel):
"""更新收藏宠物列表"""
pets: List[str] = Field(..., description="要设置的收藏宠物列表。")
user_id: Annotated[str, InjectedToolArg] = Field(..., description="用户 ID。")
@tool(args_schema=UpdateFavoritePetsSchema)
def update_favorite_pets(pets, user_id):
user_to_pets[user_id] = pets
这展示了 InjectedToolArg
的灵活性,支持多种工具定义方式。
3. 对模式的影响
InjectedToolArg
对工具的输入和调用模式有显著影响:
- 输入模式 (
get_input_schema().schema()
):包括所有参数,包括标记为注入的参数。 - 工具调用模式 (
tool_call_schema.schema()
):排除标记为注入的参数,确保模型只生成非注入参数。
例如,在上述 update_favorite_pets
示例中,get_input_schema().schema()
会显示 pets
和 user_id
,但 tool_call_schema.schema()
只包含 pets
,user_id
被移除,模型无需生成它。
4. 运行时注入机制
运行时注入通常通过链式操作实现,修改工具调用以添加注入参数。以下是一个示例:
from langchain_core.runnables import chain
@chain
def inject_user_id(input):
tool_call = input["tool_call"]
user_id = input["user_id"] # 假设 user_id 在输入中可用
tool_call_copy = tool_call.copy()
tool_call_copy["args"]["user_id"] = user_id
return {"tool_call": tool_call_copy}
此链可与工具执行组合,确保在调用工具前正确添加 user_id
。例如,模型生成的工具调用可能如下:
{
"name": "update_favorite_pets",
"args": {
"pets": ["dog", "cat"]
}
}
通过链式操作,user_id
被注入后变为:
{
"name": "update_favorite_pets",
"args": {
"pets": ["dog", "cat"],
"user_id": "12345"
}
}
这确保模型不处理敏感信息,安全性得到提升。
5. 完整代码示例
import getpass
import os
from langchain.chat_models import init_chat_model
from typing import List
from langchain_core.tools import InjectedToolArg, tool
from typing_extensions import Annotated
from copy import deepcopy
from langchain_core.runnables import chain
# 导入和设置 API 密钥
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")
llm = init_chat_model("gpt-4o-mini", model_provider="openai")
user_to_pets = {}
# 使用 InjectedToolArg 注解来标记工具的某些参数
@tool(parse_docstring=True)
def update_favorite_pets(
pets: List[str], user_id: Annotated[str, InjectedToolArg]
) -> None:
"""添加最喜欢的宠物列表。
Args:
pets: 要设置的最喜欢的宠物列表。
user_id: 用户的 ID。
"""
user_to_pets[user_id] = pets
@tool(parse_docstring=True)
def delete_favorite_pets(user_id: Annotated[str, InjectedToolArg]) -> None:
"""删除最喜欢的宠物列表。
Args:
user_id: 用户的 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:
"""列出最喜欢的宠物(如果有)。
Args:
user_id: 用户的 ID。
"""
return user_to_pets.get(user_id, [])
# 查看这些工具的输入模式,会发现 user_id 仍然列在
print(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 已被移除
print(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")
print(ai_msg.tool_calls)
"""
输出:
[{'name': 'update_favorite_pets',
'args': {'pets': ['cats', 'parrots']},
'id': 'call_pZ6XVREGh1L0BBSsiGIf1xVm',
'type': 'tool_call'}]
"""
# 在运行时注入参数:如果我们想使用模型生成的工具调用来实际执行工具,需要自己注入 user_id:
@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
print(inject_user_id.invoke(ai_msg))
"""
输出:
[{'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()
response = chain.invoke("my favorite animals are cats and parrots")
print(response)
"""
输出:
[ToolMessage(content='null', name='update_favorite_pets', tool_call_id='call_oYCD0THSedHTbwNAY3NW6uUj')]
"""
# 查看 `user_to_pets` 字典,可以看到它已更新为包含cats和parrots:
print(user_to_pets)
"""
输出:
{'123': ['cats', 'parrots']}
"""
以下是注解工具参数的几种替代方式:
方法 1
from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field
class UpdateFavoritePetsSchema(BaseModel):
"""更新最喜欢的宠物列表"""
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()
输出:
{
"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"
}
方法 2
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"
}
方法 3
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"
}
6. 需求与注意事项
使用 InjectedToolArg
需要满足以下条件:
- 版本要求:需要
langchain-core>=0.2.21
,可通过pip install -qU "langchain[openai]"
安装。 - 安装:确保环境配置正确,特别是在使用 OpenAI 模型时。
7. 实际应用与社区反馈
InjectedToolArg
广泛用于代理和工具调用场景,特别是在不暴露给 LLM 的情况下传递运行时参数。它在 2024 年 8 月被添加到 LangChain 中,解决了许多开发者的需求。社区反馈表明其功能强大,但开发者应注意版本兼容性问题,并通过链式操作实现注入。
8. 总结
InjectedToolArg
是 LangChain 中处理运行时注入参数的重要工具,特别适合安全性和灵活性要求高的场景。开发者应确保使用最新版本,并通过链式操作实现注入。