【Eino 框架入门】Tool 与文件系统访问:给 Agent 装上"手"

6 阅读3分钟

【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)
}

继承关系BaseToolInvokableTool / StreamableTool

参数是 JSON 字符串,返回也是字符串,简单粗暴。

ChatModelAgent vs DeepAgent

前面用的是 ChatModelAgent,要访问文件系统得换成 DeepAgent

能力ChatModelAgentDeepAgent
多轮对话
手动加 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 可能需要:

  1. 调用 glob 找到所有 .go 文件
  2. 对每个文件调用 grep 搜索 TODO
  3. 整理结果回复用户

这就是多轮迭代。设置 MaxIteration: 50 表示最多循环 50 轮。

运行

cd ./cmd/ch04
go run .

# 试着问:
# you> 列出当前目录的文件
# you> 读取 go.mod 文件
# you> 搜索所有包含 "func main" 的文件

小结

概念一句话解释
ToolAgent 的"手脚",让它能执行实际操作
Backend文件系统操作的统一接口
LocalBackendBackend 的本地实现,操作你电脑的文件
DeepAgent高级 Agent,传入 Backend 就自动有文件操作能力

核心思想:Agent 只管"思考"和"决策",Tool 负责"执行"。解耦清晰,扩展方便。