【Eino 框架入门】Tool 与文件系统访问:给 Agent 装上"手"
前几章的 Agent 只能聊天,这篇让它能真正干活:读文件、搜代码、列目录、执行命令。
为什么需要 Tool?
Agent 本身只会"说话",要让它能执行实际操作,就得给它配工具。
打个比方:
- Agent = 智能助手(能理解指令,但没手没脚)
- Tool = 工具箱(文件操作、网络请求、数据库查询等)
没有 Tool 的 Agent:
用户:帮我读取 main.go 的内容
Agent:抱歉,我只能聊天,没法访问文件系统 😅
有了 Tool 之后:
用户:帮我读取 main.go 的内容
Agent:好的,让我调用 read_file 工具...
[tool call] read_file({"file_path": "main.go"})
[tool result] package main\n\nfunc main() {...}
Agent:main.go 的内容是这样的...
Tool 接口长啥样?
// 基础接口:告诉模型"我是谁、能干啥"
type BaseTool interface {
Info(ctx context.Context) (*schema.ToolInfo, error)
}
// 可执行的工具:真正干活的
type InvokableTool interface {
BaseTool
InvokableRun(ctx context.Context, args string, opts ...Option) (string, error)
}
// 流式工具:边干边汇报进度
type StreamableTool interface {
BaseTool
StreamableRun(ctx context.Context, args string) (*schema.StreamReader[string], error)
}
继承关系:BaseTool → InvokableTool / StreamableTool
参数是 JSON 字符串,返回也是字符串,简单粗暴。
ChatModelAgent vs DeepAgent
前面用的是 ChatModelAgent,要访问文件系统得换成 DeepAgent:
| 能力 | ChatModelAgent | DeepAgent |
|---|---|---|
| 多轮对话 | ✅ | ✅ |
| 手动加 Tool | ✅ | ✅ |
| 文件系统访问 | ❌ 要自己造轮子 | ✅ 传入 Backend 就行 |
| 命令执行 | ❌ | ✅ 传入 StreamingShell |
| 内置任务管理 | ❌ | ✅ 自动有 write_todos |
| 子 Agent | ❌ | ✅ |
选择建议:
- 纯聊天 →
ChatModelAgent - 要访问文件/执行命令 →
DeepAgent
Backend 接口
Backend 是"文件系统操作"的抽象接口:
type Backend interface {
LsInfo(ctx, req) ([]FileInfo, error) // 列目录
Read(ctx, req) (*FileContent, error) // 读文件
Write(ctx, req) error // 写文件
Edit(ctx, req) error // 编辑文件
GrepRaw(ctx, req) ([]GrepMatch, error) // 搜索内容
GlobInfo(ctx, req) ([]FileInfo, error) // 按模式找文件
}
LocalBackend 是本地实现,直接操作你电脑上的文件:
import localbk "github.com/cloudwego/eino-ext/adk/backend/local"
backend, _ := localbk.NewBackend(ctx, &localbk.Config{})
核心代码
// 1. 创建本地文件系统后端
backend, _ := localbk.NewBackend(ctx, &localbk.Config{})
// 2. 创建 DeepAgent,传入 Backend
agent, _ := deep.New(ctx, &deep.Config{
Name: "Ch04ToolAgent",
ChatModel: cm,
Instruction: instruction,
Backend: backend, // 文件系统能力
StreamingShell: backend, // 命令执行能力
MaxIteration: 50,
})
关键点:只要把 Backend 传进去,DeepAgent 会自动注册这些工具:
read_file- 读文件write_file- 写文件edit_file- 编辑文件glob- 查找文件grep- 搜索内容execute- 执行 shell 命令
不用你一个一个手动注册!
Tool 调用流程
用户:列出当前目录的文件
↓
Agent 分析意图:"哦,要用 glob 工具"
↓
生成 Tool Call:{"pattern": "*"}
↓
执行 Tool:glob("*")
↓
返回结果:{"files": ["main.go", "go.mod", ...]}
↓
Agent 生成回复:"找到 5 个文件..."
Agent 会自动判断:这个问题需要工具吗?需要就调用,不需要就直接回答。
事件流的变化
有 Tool 后,事件流多了两种消息:
Tool Call(模型决定调用工具):
for _, tc := range mv.Message.ToolCalls {
fmt.Printf("[tool call] %s(%s)\n", tc.Function.Name, tc.Function.Arguments)
}
Tool Result(工具执行结果):
if mv.Role == schema.Tool {
fmt.Printf("[tool result] %s\n", content)
}
完整消息序列:
[Assistant] 我来帮你看看...
[tool call] read_file({"file_path": "/path/main.go"})
[tool result] {"content": "package main..."}
[Assistant] main.go 的内容是...
MaxIteration 是什么?
MaxIteration 控制工具调用循环的最大次数。
比如用户问"找出所有 TODO 并整理",Agent 可能需要:
- 调用
glob找到所有.go文件 - 对每个文件调用
grep搜索 TODO - 整理结果回复用户
这就是多轮迭代。设置 MaxIteration: 50 表示最多循环 50 轮。
运行
cd ./cmd/ch04
go run .
# 试着问:
# you> 列出当前目录的文件
# you> 读取 go.mod 文件
# you> 搜索所有包含 "func main" 的文件
小结
| 概念 | 一句话解释 |
|---|---|
| Tool | Agent 的"手脚",让它能执行实际操作 |
| Backend | 文件系统操作的统一接口 |
| LocalBackend | Backend 的本地实现,操作你电脑的文件 |
| DeepAgent | 高级 Agent,传入 Backend 就自动有文件操作能力 |
核心思想:Agent 只管"思考"和"决策",Tool 负责"执行"。解耦清晰,扩展方便。