Building a simple file manipulation tool allows an LLM agent to interact with its environment. This exercise demonstrates how to enable an LLM agent to read from and write to text files, a fundamental capability for many tasks. The primary focus is on creating two Python functions: one for reading file content and another for writing content to a file. These basic tools form the building blocks for more sophisticated file management operations.Our goal is to create two distinct functions:read_file(filepath: str): This function will take a file path as input and return the content of the file as a string.write_file(filepath: str, content: str): This function will take a file path and a string of content as input, write the content to the specified file, and confirm success.For both functions, error handling is important, as file operations can fail for various reasons (e.g., file not found, permission issues). The feedback provided to the LLM in case of an error should be clear and informative.Implementing the read_file ToolLet's start by implementing the read_file function. This function needs to open a file in read mode, extract its content, and handle potential errors like the file not existing.import os def read_file(filepath: str) -> str: """ Reads the content of a specified text file. Args: filepath: The path to the file to be read. Returns: The content of the file as a string, or an error message if an issue occurs. """ try: # Basic security: Prevent directory traversal for this simple example # In a real system, use an allowlist of directories or more path validation. if ".." in filepath: return "Error: Relative paths with '..' are not allowed." # For simplicity, we assume files are in a specific 'agent_files' subdirectory. # In a production environment, this path would be configurable and secured. base_directory = "agent_files" safe_filepath = os.path.join(base_directory, os.path.basename(filepath)) # Create the base directory if it doesn't exist for this example 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)}" In this function:We include a very basic check to prevent trivial directory traversal attacks using ".." in the filepath. A production system would require much more sophisticated path validation and sandboxing.We define a base_directory (e.g., agent_files) where the agent is allowed to read files from. This helps contain file operations. os.path.basename(filepath) is used to ensure only the filename part is considered, preventing the agent from specifying paths outside the intended directory.We use a try-except block to catch FileNotFoundError specifically, providing a tailored message.A general except Exception catches other potential I/O errors, returning a generic error message that includes the original exception.Files are opened with utf-8 encoding, a common standard for text files.When this tool is invoked by an LLM, if the file agent_files/example.txt exists and contains "Hello World", a call to read_file("example.txt") would return "Hello World". If the file doesn't exist, it would return "Error: File not found at 'example.txt' (resolved to 'agent_files/example.txt')."Implementing the write_file ToolNext, let's implement the write_file function. This tool will allow the agent to save text to a file. Writing files carries more risk than reading, so careful consideration of permissions and target locations is even more significant in a real application.import os def write_file(filepath: str, content: str) -> str: """ Writes the given content to a specified text file. If the file exists, it will be overwritten. Args: filepath: The path to the file to be written. content: The text content to write to the file. Returns: A success message or an error message if an issue occurs. """ try: # Basic security: Prevent directory traversal if ".." in filepath: return "Error: Relative paths with '..' are not allowed for writing." # Confine writes to a specific subdirectory base_directory = "agent_files" safe_filepath = os.path.join(base_directory, os.path.basename(filepath)) # Create the base directory if it doesn't exist 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)}" Important points for write_file:Similar to read_file, it includes basic path safety by using os.path.basename and a base_directory.The file is opened in write mode ('w'), which means if the file already exists, it will be overwritten. For a more advanced tool, you might consider adding an append mode or a parameter to control overwrite behavior.A try-except block handles potential IOError or other exceptions during the write operation.A success message includes the resolved path to avoid ambiguity.If an agent decides to use this tool with filepath="report.txt" and content="This is the agent's report.", the tool will attempt to create or overwrite agent_files/report.txt with the given content.Tool Specifications for the LLMFor an LLM agent to use these Python functions, it needs clear descriptions of what each tool does, what parameters it expects, and what it returns. Here’s how you might define these for an agent framework:Tool 1: Read FileName: read_fileDescription: "Reads the entire content of a specified text file located within the 'agent_files' directory. Use this to retrieve information stored in files."Input Parameters:filepath (string, required): "The name of the file to read (e.g., 'notes.txt'). Do not include directory paths like '../' or '/'; all files are relative to a pre-configured 'agent_files' directory."Output:(string): "The content of the file, or an error message if the file cannot be read."Tool 2: Write FileName: write_fileDescription: "Writes or overwrites a text file with the provided content within the 'agent_files' directory. Use this to save information or create new text files."Input Parameters:filepath (string, required): "The name of the file to write to (e.g., 'summary.txt'). Do not include directory paths like '../' or '/'; all files are relative to a pre-configured 'agent_files' directory. If the file exists, it will be overwritten."content (string, required): "The text content to write into the file."Output:(string): "A confirmation message upon successful write, or an error message if the file cannot be written."These descriptions are designed to guide the LLM in using the tools correctly, including hints about the file system structure (the agent_files directory) and potential outcomes (like overwriting files).Testing Your File Manipulation ToolTo test these tools:Direct Python Testing: Call the functions directly from a Python script.Create a dummy file in an agent_files subdirectory (e.g., agent_files/my_test_file.txt) with some content.Call read_file("my_test_file.txt") and verify the output.Test read_file with a non-existent filename to check error handling.Call write_file("new_test_file.txt", "Hello from the agent!"). Check if agent_files/new_test_file.txt is created with the correct content.Test write_file by trying to write to a restricted path (if you were to implement more complex path validation) or simulate a permissions error if possible (though this is harder to unit test without manipulating actual file permissions).Agent Integration Testing: While full agent integration is covered later, you can mentally walk through how an agent might use these.If an agent is asked to "Summarize the document 'project_update.txt' and save the summary to 'summary_v1.txt'," it should first call read_file("project_update.txt"), then process the content, and finally call write_file("summary_v1.txt", "...").Consider how the agent would react to an error message from read_file (e.g., if project_update.txt doesn't exist).Security MeasuresThe file tools we've built are illustrative. For any application, especially if the LLM can determine filenames or content, security is of utmost importance:Path Validation and Allowlisting: The os.path.basename and base_directory approach is a very basic first step. Production systems must use strict allowlists for readable/writable directories and potentially for filenames or file extensions. Never allow arbitrary path construction from LLM outputs without sanitization and validation.Permissions: The process running the Python code (and thus the agent's tools) should have the minimum necessary file system permissions. It should not run as a privileged user.Sandboxing: For maximum security, especially if executing LLM-generated filenames or dealing with potentially untrusted content, file operations should occur within a sandboxed environment (e.g., a Docker container with restricted volume mounts, or using OS-level sandboxing features).Resource Limits: Implement checks to prevent agents from reading or writing excessively large files, which could lead to denial-of-service.Input Sanitization: If the LLM generates content to be written, ensure it's sanitized if that content might be interpreted by another system later (e.g., writing code or scripts).These simple file manipulation tools, when properly secured and described, significantly expand an agent's ability to interact with persistent storage and manage information. As you build more complex agents, you might extend these tools to handle directories, list files, check for existence, or append to files, always keeping security at the forefront of your design.