FileSystem Backend
💡 Package: github.com/cloudwego/eino/adk/filesystem
Background and Goals
In AI Agent scenarios, the agent often needs to interact with a filesystem: reading file content, searching code, editing configs, executing commands, and so on. However, different runtime environments access the filesystem very differently:
- Local development: operate on the local filesystem directly, works out of the box
- Cloud sandbox: operate on an isolated sandbox filesystem via remote APIs, requires authentication and networking
- Testing: use an in-memory simulated filesystem without real disk I/O
- Custom storage: integrate with OSS, databases, or other non-traditional “filesystems”
If each environment implements its own set of file operations, middleware and agent code become tightly coupled to the underlying storage implementation, making reuse and testing difficult.
To address this, Eino ADK defines the filesystem.Backend interface as a unified filesystem operation protocol. Its design goals are:
- Decouple storage from business logic: middleware depends only on the Backend interface and does not care whether the underlying implementation is local disk, a remote sandbox, or an in-memory mock
- Pluggable replacement: by switching Backend implementations, the same agent can run in different environments without changing any business code
- Testability: a built-in
InMemoryBackendmakes it easy to simulate filesystem behavior in unit tests - Extensibility: all methods use struct parameters, so adding new fields in the future won’t break compatibility for existing implementations
Backend Interface
type Backend interface {
// List files and directories under the given path
LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error)
// Read file content, supports line-based pagination (offset + limit)
Read(ctx context.Context, req *ReadRequest) (*FileContent, error)
// Search for matches of pattern under the given path and return the match list
GrepRaw(ctx context.Context, req *GrepRequest) ([]GrepMatch, error)
// Find matching files by glob pattern and base path
GlobInfo(ctx context.Context, req *GlobInfoRequest) ([]FileInfo, error)
// Write or create a file
Write(ctx context.Context, req *WriteRequest) error
// Replace string content in a file
Edit(ctx context.Context, req *EditRequest) error
}
Extension Interfaces
Besides the core file operations, a Backend can optionally implement shell command execution:
// Shell provides synchronous command execution
type Shell interface {
Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error)
}
// StreamingShell provides streaming command execution for long-running commands
type StreamingShell interface {
ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error)
}
When a Backend implements Shell or StreamingShell, the FileSystem middleware additionally registers the execute tool so the agent can run shell commands.
Core Data Types
| Type | Description |
FileInfo | File/directory info: path, isDir, size, modified time |
FileContent | File content with line number information |
GrepMatch | Search match: content, path, line number |
ReadRequest | Read request: path, offset (1-based line), limit (line count) |
GrepRequest | Search request: pattern (regex), path, glob filter, file type filters, etc. |
WriteRequest | Write request: path, content |
EditRequest | Edit request: path, old string, new string, replace all |
ExecuteRequest | Command request: command string, background flag |
ExecuteResponse | Command result: stdout/stderr, exit code, truncated flag |
Built-in Implementation: InMemoryBackend
InMemoryBackend is a built-in Backend implementation that stores files in an in-memory map, mainly used for:
- Unit tests: test agent and middleware file operations without a real filesystem
- Lightweight scenarios: temporary file operations without persistence
- Large tool result offloading: the FileSystem middleware’s large tool result offloading feature uses InMemoryBackend by default
import "github.com/cloudwego/eino/adk/filesystem"
ctx := context.Background()
backend := filesystem.NewInMemoryBackend()
// Write file
err := backend.Write(ctx, &filesystem.WriteRequest{
FilePath: "/example/test.txt",
Content: "Hello, World!\nLine 2\nLine 3",
})
// Read file (paginated)
content, err := backend.Read(ctx, &filesystem.ReadRequest{
FilePath: "/example/test.txt",
Offset: 1,
Limit: 10,
})
// List directory
files, err := backend.LsInfo(ctx, &filesystem.LsInfoRequest{
Path: "/example",
})
// Search content (regex supported)
matches, err := backend.GrepRaw(ctx, &filesystem.GrepRequest{
Pattern: "Hello",
Path: "/example",
})
// Edit file
err = backend.Edit(ctx, &filesystem.EditRequest{
FilePath: "/example/test.txt",
OldString: "Hello",
NewString: "Hi",
ReplaceAll: false,
})
Features:
- Thread-safe (based on
sync.RWMutex) - GrepRaw supports regex, case-insensitive, context lines, and other advanced options
- GrepRaw uses parallel processing internally (up to 10 workers)
External Implementations
The following Backend implementations live in the eino-ext repository:
- Local Backend — a local filesystem implementation that operates on the host disk with zero configuration
- Ark Agentkit Sandbox Backend — a Volcengine Agentkit remote sandbox implementation that executes file operations in an isolated cloud environment
Implementation Comparison
| Feature | InMemory | Local | Agentkit Sandbox |
| Execution model | In-memory | Local direct | Remote sandbox |
| Network dependency | No | No | Yes |
| Configuration complexity | Zero config | Zero config | Credentials required |
| Persistence | No | Yes | Yes |
| Shell support | No | Yes (including streaming) | Yes |
| Use cases | Tests/temporary | Development/local | Multi-tenant/production |
Custom Implementations
To integrate custom storage (e.g. OSS, databases), you only need to implement the Backend interface:
type MyBackend struct {
// ...
}
func (b *MyBackend) LsInfo(ctx context.Context, req *filesystem.LsInfoRequest) ([]filesystem.FileInfo, error) {
// Custom implementation
}
func (b *MyBackend) Read(ctx context.Context, req *filesystem.ReadRequest) (*filesystem.FileContent, error) {
// Custom implementation
}
// ... implement the remaining methods
If you also need command execution, implement Shell or StreamingShell as well.