FileSystem Backend

💡Package: github.com/cloudwego/eino/adk/filesystem

背景与目的

AI Agent 需要与文件系统交互(读取、搜索、编辑、执行命令),但不同运行环境的访问方式差异很大:本地磁盘、远程沙箱、内存模拟、对象存储等。若每种环境单独实现文件操作逻辑,会导致 Middleware/Agent 代码与底层存储耦合。

filesystem.Backend 接口解决这一问题——作为统一文件系统操作协议

  1. 解耦存储与业务 — Middleware 只依赖接口,不关心底层实现
  2. 可插拔替换 — 切换 Backend 即可在不同环境运行,无需修改业务代码
  3. 易于测试 — 内置 InMemoryBackend,无需真实磁盘 I/O
  4. 向前兼容 — 所有方法使用结构体参数,新增字段不破坏已有实现

Backend 接口

type Backend interface {
    LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error)
    Read(ctx context.Context, req *ReadRequest) (*FileContent, error)
    GrepRaw(ctx context.Context, req *GrepRequest) ([]GrepMatch, error)
    GlobInfo(ctx context.Context, req *GlobInfoRequest) ([]FileInfo, error)
    Write(ctx context.Context, req *WriteRequest) error
    Edit(ctx context.Context, req *EditRequest) error
}
方法功能返回
LsInfo
列出指定路径下的文件和目录信息
[]FileInfo
Read
读取文件内容,支持按行分页(offset + limit)
*FileContent
GrepRaw
在文件中搜索匹配 pattern 的内容
[]GrepMatch
GlobInfo
根据 glob pattern 查找匹配文件
[]FileInfo
Write
写入或创建文件
error
Edit
替换文件中的字符串内容
error

扩展接口

Shell / StreamingShell

Backend 可选择性实现命令执行能力。当 Backend 同时实现 ShellStreamingShell 时,Filesystem Middleware 会额外注册 execute 工具。两者互斥,不可同时配置。

type Shell interface {
    Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error)
}

type StreamingShell interface {
    ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error)
}

MultiModalReader

可选扩展接口,支持多模态文件读取(图片、PDF 等),返回结构化的 MultiFileContent

type MultiModalReader interface {
    MultiModalRead(ctx context.Context, req *MultiModalReadRequest) (*MultiFileContent, error)
}

当 Backend 实现此接口且 Middleware 配置 UseMultiModalRead = true 时,read_file 工具将使用多模态读取。

核心数据类型

请求类型

类型字段说明
LsInfoRequest
Path string
要列出的目录路径
ReadRequest
FilePath string
Offset int
Limit int
文件路径;起始行号(1-based,<1 视为 1);最大读取行数(0=全部)
MultiModalReadRequest
嵌入
ReadRequest
Pages string
继承 ReadRequest 所有字段;Pages 指定 PDF 页码范围(如 "1-5"、"3")
GrepRequest
Pattern string
Path string
Glob string
FileType string
CaseInsensitive bool
EnableMultiline bool
AfterLines int
BeforeLines int
正则搜索模式(ripgrep 语法);搜索目录;glob 文件过滤;文件类型过滤(如 "go"、"py");忽略大小写;启用多行匹配;匹配后显示 N 行;匹配前显示 N 行
GlobInfoRequest
Pattern string
Path string
glob 表达式(支持
*
**
?
[abc]
);搜索起始目录
WriteRequest
FilePath string
Content string
目标文件路径;写入内容
EditRequest
FilePath string
OldString string
NewString string
ReplaceAll bool
文件路径;被替换的精确字符串(非空);替换后的字符串;false 时要求 OldString 在文件中仅出现一次
ExecuteRequest
Command string
RunInBackendGround bool
要执行的命令字符串;是否后台运行

响应类型

类型字段说明
FileInfo
Path string
IsDir bool
Size int64
ModifiedAt string
文件/目录路径;是否为目录;文件大小(字节);最后修改时间(ISO 8601 格式)
FileContent
Content string
文件的纯文本内容
MultiFileContent
*FileContent
Parts []FileContentPart
嵌入 FileContent;多模态输出部分。Parts 与 FileContent 互斥:Parts 非空时 FileContent 被忽略
FileContentPart
Type FileContentPartType
MIMEType string
Data []byte
内容类型(
"image"
"pdf"
);MIME 类型(如 "image/png");原始二进制数据
GrepMatch
Content string
Path string
Line int
匹配的行内容;文件路径;1-based 行号
ExecuteResponse
Output string
ExitCode *int
Truncated bool
命令输出内容;退出码(指针,可能为 nil);输出是否被截断

常量

type FileContentPartType string

const (
    FileContentPartTypeImage FileContentPartType = "image"
    FileContentPartTypePDF   FileContentPartType = "pdf"
)

内置实现:InMemoryBackend

InMemoryBackend 将文件存储在内存 map 中,主要用于:

  • 单元测试 — 无需真实文件系统即可测试 Agent/Middleware 的文件操作逻辑
  • 轻量场景 — 不需要持久化的临时文件操作
  • 工具结果卸载 — Filesystem Middleware 的大型工具结果卸载功能默认使用 InMemoryBackend

构造函数

func NewInMemoryBackend() *InMemoryBackend

零参数构造,返回空的内存文件系统。

使用示例

backend := filesystem.NewInMemoryBackend()
ctx := context.Background()

// 写入
_ = backend.Write(ctx, &filesystem.WriteRequest{
    FilePath: "/example/test.txt",
    Content:  "Hello, World!\nLine 2\nLine 3",
})

// 读取(分页)
content, _ := backend.Read(ctx, &filesystem.ReadRequest{
    FilePath: "/example/test.txt",
    Offset:   1,
    Limit:    10,
})

// 列目录
files, _ := backend.LsInfo(ctx, &filesystem.LsInfoRequest{Path: "/example"})

// 搜索(正则)
matches, _ := backend.GrepRaw(ctx, &filesystem.GrepRequest{
    Pattern:         "Hello",
    Path:            "/example",
    CaseInsensitive: true,
})

// 编辑
_ = backend.Edit(ctx, &filesystem.EditRequest{
    FilePath:   "/example/test.txt",
    OldString:  "Hello",
    NewString:  "Hi",
    ReplaceAll: false,
})

实现特性

  • 线程安全 — 基于 sync.RWMutex,读操作使用读锁,写操作使用写锁
  • GrepRaw 并行处理 — 多文件搜索时最多启动 10 个 worker 并行匹配
  • 正则支持 — 支持完整正则、大小写不敏感 ((?i) 前缀)、多行模式
  • 上下文行 — GrepRaw 支持 BeforeLines/AfterLines 显示匹配行前后的上下文
  • Glob 匹配 — 使用 doublestar 库支持 ** 递归匹配
  • FileType 映射 — 内置 70+ 种文件类型到扩展名的映射表(go、py、ts、rust 等)
  • 不实现 Shell — InMemoryBackend 不实现 Shell/StreamingShell 接口

外部实现

以下 Backend 实现位于 eino-ext 仓库:

  • Local Backend (github.com/cloudwego/eino-ext/adk/backend/local) — 本地文件系统实现,直接操作本机磁盘
  • Ark Agentkit Sandbox (github.com/cloudwego/eino-ext/adk/backend/agentkit) — 火山引擎 Agentkit 远程沙箱实现

实现对比

特性InMemoryLocalAgentkit Sandbox
执行模型内存本地直接远程沙箱
网络依赖需要
配置复杂度零配置零配置需要凭证
持久化
Shell 支持Shell + StreamingShellShell
MultiModalReader视实现而定视实现而定
适用场景测试 / 临时存储开发 / 本地环境多租户 / 生产环境

自定义实现

实现 Backend 接口即可对接自定义存储。如需命令执行,额外实现 ShellStreamingShell;如需多模态读取,实现 MultiModalReader

type MyBackend struct { /* ... */ }

func (b *MyBackend) LsInfo(ctx context.Context, req *filesystem.LsInfoRequest) ([]filesystem.FileInfo, error) {
    // 自定义实现
}

func (b *MyBackend) Read(ctx context.Context, req *filesystem.ReadRequest) (*filesystem.FileContent, error) {
    // 自定义实现
}

func (b *MyBackend) GrepRaw(ctx context.Context, req *filesystem.GrepRequest) ([]filesystem.GrepMatch, error) {
    // 自定义实现
}

func (b *MyBackend) GlobInfo(ctx context.Context, req *filesystem.GlobInfoRequest) ([]filesystem.FileInfo, error) {
    // 自定义实现
}

func (b *MyBackend) Write(ctx context.Context, req *filesystem.WriteRequest) error {
    // 自定义实现
}

func (b *MyBackend) Edit(ctx context.Context, req *filesystem.EditRequest) error {
    // 自定义实现
}

// 可选:实现 Shell
func (b *MyBackend) Execute(ctx context.Context, input *filesystem.ExecuteRequest) (*filesystem.ExecuteResponse, error) {
    // 自定义实现
}

// 可选:实现 MultiModalReader
func (b *MyBackend) MultiModalRead(ctx context.Context, req *filesystem.MultiModalReadRequest) (*filesystem.MultiFileContent, error) {
    // 自定义实现
}