将类成员函数作为LangGraph工具时遇TypeError问题求助
问题描述
使用LangChain和LangGraph开发智能体,希望工具仅能在本地目录内进行文件交互,因此将工具集实现为类的成员函数,通过类实例存储本地目录路径。但调用智能体触发工具时出现错误:
TypeError: StructuredTool._run() got multiple values for argument 'self'
错误原因
LangChain的@tool装饰器会将函数转换为StructuredTool对象,当直接装饰类的实例方法时,装饰器无法正确处理方法的self参数(类实例的引用)。调用工具时,会同时传递StructuredTool自身的self和类实例的self,导致参数冲突。
正确实现方式
方案一:手动创建Tool并绑定类实例
取消@tool装饰器,在get_tools方法中手动创建工具对象,将类实例的方法包装进去:
from langchain.tools import tool from pathlib import Path class FileInterface: def __init__(self): self.localdir = Path.cwd() def get_tools(self): return [ # 手动创建工具,绑定当前实例的方法 tool( func=self.read_file, name="read_file", description="Tool for reading file at path, restricted to local directory" ), # 其他工具同理 ] def read_file(self, path: str) -> str: """读取指定路径的文件,仅允许访问本地目录内的文件""" target_path = Path(path).resolve() if not target_path.is_relative_to(self.localdir): raise ValueError(f"文件 {path} 位于允许目录 {self.localdir} 之外") return target_path.read_text()
方案二:继承BaseTool实现自定义工具类
为每个工具创建继承自BaseTool的类,将共享的本地目录作为类属性传入,这种方式扩展性更强:
from langchain_core.tools import BaseTool from langchain_core.pydantic_v1 import BaseModel, Field from pathlib import Path # 定义工具输入参数模型 class ReadFileInput(BaseModel): path: str = Field(description="要读取的文件路径") class ReadFileTool(BaseTool): name = "read_file" description = "读取指定路径的文件,仅允许访问本地目录内的文件" localdir: Path # 共享的本地目录路径 def _run(self, path: str) -> str: target_path = Path(path).resolve() if not target_path.is_relative_to(self.localdir): raise ValueError(f"文件 {path} 位于允许目录 {self.localdir} 之外") return target_path.read_text() class FileInterface: def __init__(self): self.localdir = Path.cwd() def get_tools(self): return [ ReadFileTool(localdir=self.localdir), # 其他工具可类似实现,如write_file等 ]
方案三:使用functools.partial绑定实例
通过functools.partial将类实例绑定到工具方法,避免参数冲突:
from functools import partial from langchain.tools import tool from pathlib import Path class FileInterface: def __init__(self): self.localdir = Path.cwd() def get_tools(self): # 使用partial绑定当前实例到方法 bound_read_file = partial(self.read_file) # 为绑定后的方法添加工具装饰 read_tool = tool(bound_read_file, name="read_file", description="读取本地目录内的文件") return [read_tool] def read_file(self, path: str) -> str: target_path = Path(path).resolve() if not target_path.is_relative_to(self.localdir): raise ValueError(f"文件 {path} 位于允许目录 {self.localdir} 之外") return target_path.read_text()
验证调用
修改后,智能体的初始化和调用代码保持不变即可正常运行:
file_interface = FileInterface() agent = create_agent( model=init_model(...), state_schema=MyState, tools=file_interface.get_tools(), system_prompt="You are a helpful agent that can only interact with files in the local directory", checkpointer=InMemorySaver() ) print(agent.invoke({"messages":[{"role":"human","content":"tell me the contents of file example.txt"}]}, context=context))
内容的提问来源于stack exchange,提问作者FLKR
相关产品推荐
相关产品推荐

