构建一个简单的文件操作工具,能够让大型语言模型(LLM)代理与环境进行交互。这个练习将展示LLM代理如何读写文本文件,这是许多任务所需的主要功能。我们将重点创建两个Python函数:一个用于读取文件内容,另一个用于写入内容到文件。尽管这些工具很基础,但它们构成了更复杂文件管理操作的组成部分。我们的目标是创建两个不同的函数:read_file(filepath: str): 这个函数将接受文件路径作为输入,并以字符串形式返回文件内容。write_file(filepath: str, content: str): 这个函数将接受文件路径和字符串内容作为输入,将内容写入指定文件,并确认操作成功。对于这两个函数,错误处理很重要,因为文件操作可能因多种原因失败(例如,文件未找到、权限问题)。在出现错误时,提供给大型语言模型的反馈应该清晰且提供足够信息。实现 read_file 工具让我们开始实现 read_file 函数。这个函数需要以读取模式打开文件,获取其内容,并处理文件不存在等潜在错误。import os def read_file(filepath: str) -> str: """ 读取指定文本文件的内容。 参数: filepath: 要读取文件的路径。 返回: 文件内容(字符串形式),如果发生问题则返回错误信息。 """ try: # 基本安全措施:在此简单示例中阻止目录遍历 # 在实际系统中,应使用目录白名单或更严格的路径验证。 if ".." in filepath: return "Error: Relative paths with '..' are not allowed." # 为简化起见,我们假设文件位于特定的 'agent_files' 子目录中。 # 在生产环境中,此路径应可配置并有安全防护。 base_directory = "agent_files" safe_filepath = os.path.join(base_directory, os.path.basename(filepath)) # 如果此示例的基目录不存在,则创建它 if not os.path.exists(base_directory): os.makedirs(base_directory) with open(safe_filepath, 'r', encoding='utf-8') as f: content = f.read() return content except FileNotFoundError: return f"Error: File not found at '{filepath}' (resolved to '{safe_filepath}')." except Exception as e: return f"Error reading file '{filepath}': {str(e)}" 在这个函数中:我们包含了一个非常基础的检查,通过在 filepath 中使用 ".." 来阻止简单的目录遍历攻击。生产系统将需要更复杂的路径验证和沙箱机制。我们定义了一个 base_directory(例如 agent_files),代理被允许从该目录读取文件。这有助于限制文件操作。使用 os.path.basename(filepath) 是为了确保只考虑文件名部分,从而防止代理指定意图目录之外的路径。我们使用 try-except 块来专门捕获 FileNotFoundError,并提供定制化的消息。一个通用的 except Exception 捕获其他潜在的 I/O 错误,返回包含原始异常的通用错误消息。文件以 utf-8 编码打开,这是文本文件的常见标准。当大型语言模型调用此工具时,如果文件 agent_files/example.txt 存在且包含“Hello World”,则调用 read_file("example.txt") 将返回 "Hello World"。如果文件不存在,它将返回 "Error: File not found at 'example.txt' (resolved to 'agent_files/example.txt')."实现 write_file 工具接下来,我们来实现 write_file 函数。这个工具将允许代理将文本保存到文件中。写入文件比读取文件带来更大的风险,因此在实际应用中,对权限和目标位置的仔细考量更为重要。import os def write_file(filepath: str, content: str) -> str: """ 将给定内容写入指定的文本文件。 如果文件存在,将被覆盖。 参数: filepath: 要写入文件的路径。 content: 要写入文件的文本内容。 返回: 成功消息,如果发生问题则返回错误信息。 """ try: # 基本安全措施:阻止目录遍历 if ".." in filepath: return "Error: Relative paths with '..' are not allowed for writing." # 将写入操作限制在特定子目录中 base_directory = "agent_files" safe_filepath = os.path.join(base_directory, os.path.basename(filepath)) # 如果基目录不存在,则创建它 if not os.path.exists(base_directory): os.makedirs(base_directory) with open(safe_filepath, 'w', encoding='utf-8') as f: f.write(content) return f"Successfully wrote content to '{filepath}' (resolved to '{safe_filepath}')." except Exception as e: return f"Error writing file '{filepath}': {str(e)}" write_file 的要点:类似于 read_file,它通过使用 os.path.basename 和 base_directory 包含了基本的路径安全性。文件以写入模式('w')打开,这意味着如果文件已存在,它将被覆盖。对于一个更高级的工具,你可能需要考虑添加一个 append 模式或一个参数来控制覆盖行为。一个 try-except 块处理写入操作期间可能出现的 IOError 或其他异常。成功消息中包含已解析的路径,以避免歧义。如果一个代理决定使用此工具,其中 filepath="report.txt" 且 content="This is the agent's report.", 该工具将尝试使用给定内容创建或覆盖 agent_files/report.txt。大型语言模型的工具说明大型语言模型代理要使用这些Python函数,需要清楚的说明每个工具的功能、预期的参数以及返回的内容。以下是你可能为一个代理框架定义这些信息的方式:工具 1: 读取文件名称: read_file描述: "读取位于 'agent_files' 目录中指定文本文件的全部内容。使用此功能来获取文件中存储的信息。"输入参数:filepath (字符串,必需): "要读取的文件名(例如,'notes.txt')。不要包含 '../' 或 '/' 等目录路径;所有文件都相对于预配置的 'agent_files' 目录。"输出:(字符串): "文件内容,如果文件无法读取则为错误消息。"工具 2: 写入文件名称: write_file描述: "在 'agent_files' 目录中用提供的内容写入或覆盖文本文件。使用此功能来保存信息或创建新的文本文件。"输入参数:filepath (字符串,必需): "要写入的文件名(例如,'summary.txt')。不要包含 '../' 或 '/' 等目录路径;所有文件都相对于预配置的 'agent_files' 目录。如果文件存在,它将被覆盖。"content (字符串,必需): "要写入文件的文本内容。"输出:(字符串): "写入成功时的确认消息,或文件无法写入时的错误消息。"这些说明旨在指导大型语言模型正确使用这些工具,其中包括有关文件系统结构(agent_files 目录)和潜在结果(如覆盖文件)的提示。测试你的文件操作工具要测试这些工具:直接Python测试: 直接从Python脚本调用这些函数。在 agent_files 子目录中创建一个示例文件(例如 agent_files/my_test_file.txt),并包含一些内容。调用 read_file("my_test_file.txt") 并验证输出。使用不存在的文件名测试 read_file 以检查错误处理。调用 write_file("new_test_file.txt", "Hello from the agent!")。检查 agent_files/new_test_file.txt 是否以正确内容创建。通过尝试写入受限路径(如果你要实现更复杂的路径验证)或在可能的情况下模拟权限错误来测试 write_file(尽管在不修改实际文件权限的情况下,这更难进行单元测试)。代理集成测试: 尽管完整的代理集成将在后面介绍,但你可以设想代理如何使用这些工具。如果要求代理“总结文档 'project_update.txt' 并将摘要保存到 'summary_v1.txt'”,它应该首先调用 read_file("project_update.txt"),然后处理内容,最后调用 write_file("summary_v1.txt", "...")。思考代理将如何响应来自 read_file 的错误消息(例如,如果 project_update.txt 不存在)。重要的安全考量我们构建的文件工具仅作示例。对于任何应用,特别是当大型语言模型可以确定文件名或内容时,安全是头等大事:路径验证和白名单: 使用 os.path.basename 和 base_directory 的方法只是一个非常基础的第一步。生产系统必须对可读/可写目录以及可能的文件名或文件扩展名使用严格的白名单。绝不允许在未经过净化和验证的情况下,从大型语言模型输出构建任意路径。权限: 运行Python代码(以及代理工具)的进程应具有最低必要的文件系统权限。它不应以特权用户身份运行。沙箱: 为了最大程度的安全性,特别是当执行大型语言模型生成的文件名或处理可能不受信任的内容时,文件操作应在沙箱环境中进行(例如,带有受限卷挂载的Docker容器,或使用操作系统级别的沙箱功能)。资源限制: 实施检查以防止代理读取或写入过大的文件,这可能导致拒绝服务。输入净化: 如果大型语言模型生成要写入的内容,请确保对其进行净化,以防该内容稍后可能被其他系统解释(例如,写入代码或脚本)。这些简单的文件操作工具,如果得到适当的安全保护和说明,将显著增强代理与持久存储交互和管理信息的能力。在构建更复杂的代理时,你可能会扩展这些工具以处理目录、列出文件、检查文件是否存在或向文件追加内容,同时始终将安全性放在设计的首位。