news 2026/5/9 3:11:49

[Deep Agents:LangChain的Agent Harness-03]FilesystemMiddleware:赋能Agent读写文件及管理长上下文

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[Deep Agents:LangChain的Agent Harness-03]FilesystemMiddleware:赋能Agent读写文件及管理长上下文

通过“构建抽象的文件系统”我们知道,Deep Agents的文件系统是建立在一个利用BackendProtocol协议抽象的文件系统之上的,使得Agent能够以统一的方式进行文件操作,无论底层存储是本地磁盘、云端S3、数据库还是内存。这种设计不仅提供了极大的灵活性,还使得Agent能够适应不同的应用场景,从而实现更复杂的数据管理和交互能力。这个文件系统是通过FilesystemMiddleware赋予Agent的。

1. 提供文件操作工具集

在Deep Agents框架中,FilesystemMiddleware不仅仅是提供了简单的文件读写工具,更是实现长程任务和上下文工程的核心基础设施。它通过将文件系统抽象为AI的外部工作记忆,解决了大模型在处理复杂任务时因上下文窗口溢出而导致的健忘问题。它赋予Agent存储与计算分离能力,为Agent自动注入了一套标准化的文件操作工具集:

  • ls:列出目录;
  • read_file:读取内容;
  • write_file:写入文件;
  • edit_file:精确编辑内容;
  • glob:模式匹配查找;
  • grep:文本搜索;
  • execute:代码执行(仅限于可作为沙箱的后端存储);

FilesystemMiddleware提供了自动上下文卸载(Context Offloading)的能力。为了防止Agent被海量的工具输出(如超长的搜索结果或日志)淹没,FilesystemMiddleware实现了自动化调度:

  • 阈值触发:默认情况下,当工具调用结果超过20,000个tokens(可配置)时,会自动将结果保存到文件系统中;
  • 引用替换:Agent的对话历史中不会保留原始巨量数据,对应的内容被替换为一个指向文件路径的指针;
  • 动态加载:Agent可以根据需要,通过read_fileoffsetlimit参数实现分页读取,仅获取当前推理所需的片段,从而极大地节省了Context消耗;

与其说FilesystemMiddleware为Agent缔造了一个文件系统,不如说它提供了一套工具集以文件系统的方式来操作各种后端存储。FilesystemMiddleware默认提供了七个工具:lsreadwriteeditglobgrepexecute,它们会调用对应后端的接口来实现文件的列出、读取、写入、编辑、模式匹配和命令执行等功能。通过这些工具,Agent可以以文件系统的方式来访问和操作不同类型的存储,从而实现更复杂的数据管理和交互能力。

如下面的代码片段所示,当我们调用__init__方法创建FilesystemMiddleware实例时,可以利用backend参数来指定一个后端实例,或者直接传入一个后端工厂函数(BackendFactory)来动态创建后端。如果采用StateBackend作为默认后端,文件内容和元数据的FileData对象最终会写入名为files的状态字段中。表示状态Schema类型的state_schema字段返回的类型是FilesystemState,唯一的状态成员files返回的字典模拟存储的文件。

BackendFactory:TypeAlias=Callable[[ToolRuntime],BackendProtocol]BACKEND_TYPES=BackendProtocol|BackendFactoryclassFilesystemMiddleware(AgentMiddleware[FilesystemState,ContextT,ResponseT]):state_schema=FilesystemStatedef__init__(self,*,backend:BACKEND_TYPES|None=None,system_prompt:str|None=None,custom_tool_descriptions:dict[str,str]|None=None,tool_token_limit_before_evict:int|None=20000,max_execute_timeout:int=3600,)->None:self.backend=backendifbackendisnotNoneelse(StateBackend)self._custom_system_prompt=system_prompt self._custom_tool_descriptions=custom_tool_descriptionsor{}self._tool_token_limit_before_evict=tool_token_limit_before_evict self._max_execute_timeout=max_execute_timeout self.tools=[self._create_ls_tool(),self._create_read_file_tool(),self._create_write_file_tool(),self._create_edit_file_tool(),self._create_glob_tool(),self._create_grep_tool(),self._create_execute_tool(),]def_create_ls_tool(self)->BaseTooldef_create_read_file_tool(self)->BaseTooldef_create_write_file_tool(self)->BaseTooldef_create_edit_file_tool(self)->BaseTooldef_create_glob_tool(self)->BaseTooldef_create_grep_tool(self)->BaseTooldef_create_execute_tool(self)->BaseTool

__init__方法中,除了backend参数外,还提供了以下几个可选参数:

  • system_prompt:用于定制文件系统工具的系统提示语,帮助Agent更好地理解工具的用途和使用方法;
  • custom_tool_descriptions:一个字典,用于定制每个工具的描述信息,键是工具名称,值是对应的描述文本;
  • tool_token_limit_before_evict:一个整数,指定在工具调用历史中,当累计的工具调用参数的token数量超过这个限制时,应该开始逐步淘汰旧的工具调用记录,以节省上下文空间;
  • max_execute_timeout:一个整数,指定execute工具的最大执行时间(以秒为单位),防止Agent执行长时间运行的命令导致资源占用;

与其说FilesystemMiddleware为Agent缔造了一个文件系统,不如说它提供了一套工具集以文件系统的方式来操作各种后端存储。它默认提供了七个工具:lsreadwriteeditglobgrepexecute,它们会调用对应后端的接口来实现文件的列出、读取、写入、编辑、模式匹配和命令执行等功能。通过这些工具,Agent可以以文件系统的方式来访问和操作不同类型的存储,从而实现更复杂的数据管理和交互能力。

2. 看看工具描述

AI编程对程序员的自然语言的文字驾驭能力提出了更高的要求,而这是程序最欠缺的能力,也是很多人不屑的地方。对于LLM来说,提示词是唯一的输入,而工具描述是组成提示词的重要部分。即使你将定义工具的函数写得再完美,没有一份清晰、准确、详细的工具描述,LLM也无法正确地理解工具的功能和使用方法,更无法在实际调用中正确地传递参数和处理返回值。工具描述就像是工具的使用说明书,只有当LLM能够正确地理解和使用这些说明书中的信息,才能发挥工具的最大效用。

正如我一贯主张通过阅读知名框架的源码,而不是阅读大量的书籍来学习架构设计一样,我主张通过阅读知名框架中的提示词来学习如何撰写提示词,而不是阅读一本又一本关于提示词工程的书籍。因为框架是高复用率的程序,如果框架本身被验证是成功的,那们证明其设计的提示词具有很高的质量。我们来看一下FilesystemMiddleware的工具描述是如何设计的,也许我们就能从中学到一些撰写工具描述的技巧。

ls

Lists all files in a directory. This is useful for exploring the filesystem and finding the right file to read or edit. You should almost ALWAYS use this tool before using the read_file or edit_file tools.

read_file

Reads a file from the filesystem. Assume this tool is able to read all files. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned. Usage: - By default, it reads up to 100 lines starting from the beginning of the file - **IMPORTANT for large files and codebase exploration**: Use pagination with offset and limit parameters to avoid context overflow - First scan: read_file(path, limit=100) to see file structure - Read more sections: read_file(path, offset=100, limit=200) for next 200 lines - Only omit limit (read full file) when necessary for editing - Specify offset and limit: read_file(path, offset=0, limit=100) reads first 100 lines - Results are returned using cat -n format, with line numbers starting at 1 - Lines longer than 5,000 characters will be split into multiple lines with continuation markers (e.g., 5.1, 5.2, etc.). When you specify a limit, these continuation lines count towards the limit. - You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful. - If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents. - Image files (`.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`) are returned as multimodal image content blocks (see https://docs.langchain.com/oss/python/langchain/messages#multimodal). For image tasks: - Use `read_file(file_path=...)` for `.png/.jpg/.jpeg/.gif/.webp` - Do NOT use `offset`/`limit` for images (pagination is text-only) - If image details were compacted from history, call `read_file` again on the same path - You should ALWAYS make sure a file has been read before editing it.

write_file

Writes to a new file in the filesystem. Usage: - The write_file tool will create the a new file. - Prefer to edit existing files (with the edit_file tool) over creating new ones when possible.

edit_file

Performs exact string replacements in files. Usage: - You must read the file before editing. This tool will error if you attempt an edit without reading the file first. - When editing, preserve the exact indentation (tabs/spaces) from the read output. Never include line number prefixes in old_string or new_string. - ALWAYS prefer editing existing files over creating new ones. - Only use emojis if the user explicitly requests it.

glob

Find files matching a glob pattern. Supports standard glob patterns: `*` (any characters), `**` (any directories), `?` (single character). Returns a list of absolute file paths that match the pattern. Examples: - `**/*.py` - Find all Python files - `*.txt` - Find all text files in root - `/subdir/**/*.md` - Find all markdown files under /subdir - `data_??.csv` - Find files like data_01.csv, data_A1.csv, etc.

grep

Search for a text pattern across files. Searches for literal text (not regex) and returns matching files or content based on output_mode. Special characters like parentheses, brackets, pipes, etc. are treated as literal characters, not regex operators. Examples: - Search all files: `grep(pattern="TODO")` - Search Python files only: `grep(pattern="import", glob="*.py")` - Show matching lines: `grep(pattern="error", output_mode="content")` - Search for code with special chars: `grep(pattern="def __init__(self):")` - Search for a pattern that includes glob special chars: `grep(pattern="data_*.csv", glob="data_*.csv")`

execute

Executes a shell command in an isolated sandbox environment. Usage: Executes a given command in the sandbox environment with proper handling and security measures. Before executing the command, please follow these steps: 1. Directory Verification: - If the command will create new directories or files, first use the ls tool to verify the parent directory exists and is the correct location - For example, before running "mkdir foo/bar", first use ls to check that "foo" exists and is the intended parent directory 2. Command Execution: - Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt") - Examples of proper quoting: - cd "/Users/name/My Documents" (correct) - cd /Users/name/My Documents (incorrect - will fail) - python "/path/with spaces/script.py" (correct) - python /path/with spaces/script.py (incorrect - will fail) - After ensuring proper quoting, execute the command - Capture the output of the command Usage notes: - Commands run in an isolated sandbox environment - Returns combined stdout/stderr output with exit code - If the output is very large, it may be truncated - For long-running commands, use the optional timeout parameter to override the default timeout (e.g., execute(command="make build", timeout=300)) - A timeout of 0 may disable timeouts on backends that support no-timeout execution - VERY IMPORTANT: You MUST avoid using search commands like find and grep. Instead use the grep, glob tools to search. You MUST avoid read tools like cat, head, tail, and use read_file to read files. - When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings) - Use '&&' when commands depend on each other (e.g., "mkdir dir && cd dir") - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of cd Examples: Good examples: - execute(command="pytest /foo/bar/tests") - execute(command="python /path/to/script.py") - execute(command="npm install && npm test") - execute(command="make build", timeout=300) Bad examples (avoid these): - execute(command="cd /foo/bar && pytest tests") # Use absolute path instead - execute(command="cat file.txt") # Use read_file tool instead - execute(command="find . -name '*.py'") # Use glob tool instead - execute(command="grep -r 'pattern' .") # Use grep tool instead Note: This tool is only available if the backend supports execution (SandboxBackendProtocol). If execution is not supported, the tool will return an error message.

3. 指导LLM调用文件操作工具的系统提示词

FilesystemMiddleware__init__方法提供了系统提示词作为使用工具的说明书,提示词的应用是通过重写的wrap_model_call/awrap_model_call方法来实现的。如果tool_token_limit_before_evict不为空,在工具返回内容超过设置的这个阈值时,为了避免上下文过载,FilesystemMiddleware会将其内容以文件形式存储到后端,并返回一个新的工具调用结果,并返回文件的路径。由于tool_token_limit_before_evict参数设置的阈值以Token作为单位,在进行阈值计算的时候会从返回的ToolMessage中提取文本内容,并按照每个字符平均4个token的经验值来估算总的token数量。此项工作是通过重写的wrap_tool_call/awrap_tool_call方法来实现的。

classFilesystemMiddleware(AgentMiddleware[FilesystemState,ContextT,ResponseT]):defwrap_model_call(self,request:ModelRequest[ContextT],handler:Callable[[ModelRequest[ContextT]],ModelResponse[ResponseT]],)->ModelResponse[ResponseT]asyncdefawrap_model_call(self,request:ModelRequest[ContextT],handler:Callable[[ModelRequest[ContextT]],Awaitable[ModelResponse[ResponseT]]],)->ModelResponse[ResponseT]defwrap_tool_call(self,request:ToolCallRequest,handler:Callable[[ToolCallRequest],ToolMessage|Command],)->ToolMessage|Commandasyncdefawrap_tool_call(self,request:ToolCallRequest,handler:Callable[[ToolCallRequest],Awaitable[ToolMessage|Command]],)->ToolMessage|Command

如下所示的是FilesystemMiddleware提供的系统提示词,看看我们能否撰写出同样质量的提示词来指导LLM正确地调用工具:

## Following Conventions - Read files before editing — understand existing content before making changes - Mimic existing style, naming conventions, and patterns ## Filesystem Tools `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep` You have access to a filesystem which you can interact with using these tools. All file paths must start with a /. Follow the tool docs for the available tools, and use pagination (offset/limit) when reading large files. - ls: list files in a directory (requires absolute path) - read_file: read a file from the filesystem - write_file: write to a file in the filesystem - edit_file: edit a file in the filesystem - glob: find files matching a pattern (e.g., "**/*.py") - grep: search for text within files ## Large Tool Results When a tool result is too large, it may be offloaded into the filesystem instead of being returned inline. In those cases, use `read_file` to inspect the saved result in chunks, or use `grep` within `/large_tool_results/` if you need to search across offloaded tool results and do not know the exact file path. Offloaded tool results are stored under `/large_tool_results/<tool_call_id>`.

如果涉及execute工具的使用,还会加上如下这段:

## Execute Tool `execute` You have access to an `execute` tool for running shell commands in a sandboxed environment. Use this tool to run commands, scripts, tests, builds, and other shell operations. - execute: run a shell command in the sandbox (returns output and exit code)

4. 让Agent操作你的本地文件

如下的这个演示程序充分体现了FilesystemMiddleware的功能。我们创建了一个Agent,并在其中注册了FilesystemMiddleware。通过调用Agent来执行一系列文件系统操作,包括创建目录、写入文件、列出目录内容和读取文件内容。Agent通过FilesystemMiddleware提供的工具来与后端存储进行交互,实现了一个完整的文件系统操作流程。

fromlangchain.agentsimportcreate_agentfromdeepagents.middleware.filesystemimportFilesystemMiddlewarefromdeepagents.backends.local_shellimportLocalShellBackendfromlangchain_openaiimportChatOpenAIfromdotenvimportload_dotenvimportasyncio load_dotenv()agent=create_agent(model=ChatOpenAI(model="gpt-5.2-chat"),middleware=[FilesystemMiddleware(backend=LocalShellBackend(virtual_mode=True))],)prompt="""\ Execute following operations: - Remove the `test_dir` directory if it already exists, and then create a new empty one; - Change the current working directory to such a newly created directory; - Create a file called `hello.py` inside it with the content `print("Hello World")`; - Create three files in the newly created directory: `file1.txt`, `file2.txt`, and `file3.txt` with any content of your choice; - List the files of the directory; - Read the content of `hello.py`. """asyncdefmain():result=awaitagent.ainvoke(input={"messages":[{"role":"user","content":prompt}]})result["messages"][-1].pretty_print()asyncio.run(main())

输出:

================================== Ai Message ================================== ✅ All requested operations have been completed. Here are the results step by step: ### 📁 Directory Setup - The directory **`test_dir`** was removed (if it existed) and recreated as a new empty directory. ### 📝 Files Created Inside `test_dir` - **`hello.py`** ```python print("Hello World") ``` - **`file1.txt`** – contains: `content 1` - **`file2.txt`** – contains: `content 2` - **`file3.txt`** – contains: `content 3` ### 📂 Directory Listing The contents of `test_dir` are: ``` file1.txt file2.txt file3.txt hello.py ``` ### 📖 Contents of `hello.py` ``` print("Hello World") ``` If you’d like to run `hello.py`, modify any files, or perform more filesystem operations, just let me know!
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 3:06:31

GEO优化监控工具哪家适合企业品牌监测

随着生成式AI技术的快速普及&#xff0c;用户的搜索行为与信息获取路径正在发生显著的范式迁移。根据中国互联网络信息中心发布的《生成式人工智能应用发展报告&#xff08;2025&#xff09;》显示&#xff0c;截至2025年10月&#xff0c;我国生成式AI用户规模已达5.15亿。这一…

作者头像 李华
网站建设 2026/5/9 3:01:32

OpenClaw三层记忆系统:为AI助手构建可检索的长期知识库

1. 项目概述如果你和我一样&#xff0c;长期与各种AI助手打交道&#xff0c;无论是编程、写作还是日常任务规划&#xff0c;最头疼的问题之一就是“记忆”。每次对话都像是一次全新的邂逅&#xff0c;助手记不住你昨天提到的项目细节&#xff0c;也忘了上周讨论过的技术方案。这…

作者头像 李华
网站建设 2026/5/9 2:44:32

【第4章:信息系统架构】:系统集成项目管理工程师默写本

1. 信息系统架构是指体现信息系统相关的组件、关系以及系统的设计和演化原则的基本概念或特性。2. 架构的本质是决策&#xff0c;是在权衡方向、结构、关系以及原则各方面因素后进行的决策。3. 信息系统架构设计原则包括&#xff1a;坚持以人为本、坚持创新引领、坚持问题导向、…

作者头像 李华
网站建设 2026/5/9 2:39:58

第二篇:深入量化——Tushare数据处理与策略开发实战

在上一篇中&#xff0c;我们学会了如何用Tushare获取单只股票的数据并做基础可视化。但量化分析的核心&#xff0c;在于从数据中找规律、把规律变成策略、然后用数据验证策略的可行性。 今天我们更进一步&#xff0c;学习多股票数据获取、指标计算、策略构建和数据存储——这将…

作者头像 李华
网站建设 2026/5/9 2:38:56

天线设计:从基础原理到工程实践的全方位解析

1. 天线设计的魅力&#xff1a;从简单使命到无限创意天线&#xff0c;这个在射频信号链中首当其冲&#xff08;或最后出场&#xff09;的元件&#xff0c;其设计领域所展现出的活力与创造性思维&#xff0c;总是让我这个老工程师感到惊叹。我说的不是那种用于克服信号衰落、多径…

作者头像 李华