---
Description: ""
date: "2026-03-03"
lastmod: ""
tags: []
title: ToolsNode&Tool 使用说明
weight: 9
---

## **基本介绍**

`Tool` 在 eino 框架中的定义是“ChatModel 能够选择调用的外部能力”，包括本地函数，MCP server tool 等。

`ToolsNode` 是 eino 框架指定的”Tool 执行器“，无论是 Graph 内还是 Agent 中，Tool 的执行都要通过 ToolsNode：

```go
// compose/tool_node.go

// run tools using `Invoke`
func (tn *ToolsNode) Invoke(ctx context.Context, input *schema.Message,
    opts ...ToolsNodeOption) ([]*schema.Message, error)
    
// run tools using `Stream`
func (tn *ToolsNode) Stream(ctx context.Context, input *schema.Message,
    opts ...ToolsNodeOption) (*schema.StreamReader[[]*schema.Message], error)
```

给 ToolsNode 配置一个 Tool 列表以及一些配套策略：

```go
// compose/tool_node.go

type ToolsNodeConfig struct {
    Tools []tool.BaseTool

    UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)

    ExecuteSequentially bool

    ToolArgumentsHandler func(ctx context.Context, name, arguments string) (string, error)

    ToolCallMiddlewares []ToolMiddleware
}
```

这样 ToolsNode 就“能够执行配置的 Tool”，并获得一些扩展能力，如执行时序、异常处理、入参处理、middleware 扩展等。

ToolsNode 如何“决策”应该执行哪个 Tool？它不决策，而是依据输入的 `*schema.Message` 来执行：

```go
// schema/message.go

type Message struct {
    // role should be 'assistant' for tool call message
    Role RoleType `json:"role"`

    // here each `ToolCall` is generated by ChatModel and to be executed by ToolsNode
    ToolCalls []ToolCall `json:"tool_calls,omitempty"`
    
    // other fields...
}

// ToolCall is the tool call in a message.
// It's used in Assistant Message when there are tool calls should be made.
type ToolCall struct {
    // Index is used when there are multiple tool calls in a message.
    // In stream mode, it's used to identify the chunk of the tool call for merging.
    Index *int `json:"index,omitempty"`
    // ID is the id of the tool call, it can be used to identify the specific tool call.
    ID string `json:"id"`
    // Type is the type of the tool call, default is "function".
    Type string `json:"type"`
    // Function is the function call to be made.
    Function FunctionCall `json:"function"`

    // Extra is used to store extra information for the tool call.
    Extra map[string]any `json:"extra,omitempty"`
}

// FunctionCall is the function call in a message.
// It's used in Assistant Message.
type FunctionCall struct {
    // Name is the name of the function to call, it can be used to identify the specific function.
    Name string `json:"name,omitempty"`
    // Arguments is the arguments to call the function with, in JSON format.
    Arguments string `json:"arguments,omitempty"`
}
```

ChatModel(LLM) 生成要调用的 []ToolCall（包含 ToolName，Argument 等），放到 *schema.Message 中传给 ToolsNode。ToolsNode 针对每个 ToolCall 实际执行一次调用。

如果配置了 ExecuteSequentially，则 ToolsNode 会按照 []ToolCall 中的先后顺序来执行工具。

每个 ToolCall 调用完成后的结果，又会封装为 *schema.Message，作为 ToolsNode 输出的一部分。

## Tool 定义

### **接口定义**

Tool 组件提供了两类接口：**标准工具接口**和**增强型工具接口**。

> 代码位置：eino/components/tool/interface.go

#### **标准工具接口**

标准工具接口返回字符串类型的结果：

```go
// 基础工具接口，提供工具信息
type BaseTool interface {
    Info(ctx context.Context) (*schema.ToolInfo, error)
}

// 可调用的工具接口，支持同步调用
type InvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
}

// 支持流式输出的工具接口
type StreamableTool interface {
    BaseTool
    StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)
}
```

#### **增强型工具接口（Enhanced Tool）**

增强型工具接口支持返回结构化的多模态结果（`*schema.ToolResult`），可以包含文本、图片、音频、视频和文件等多种类型的内容：

```go
// EnhancedInvokableTool 是支持返回结构化多模态结果的工具接口
// 与返回字符串的 InvokableTool 不同，此接口返回 *schema.ToolResult
// 可以包含文本、图片、音频、视频和文件
type EnhancedInvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error)
}

// EnhancedStreamableTool 是支持返回结构化多模态结果的流式工具接口
// 提供流式读取器以逐步访问多模态内容
type EnhancedStreamableTool interface {
    BaseTool
    StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error)
}
```

### **增强型工具相关数据结构**

> 代码位置：eino/schema/message.go

#### **ToolArgument - 工具输入参数**

```go
// ToolArgument 包含工具调用的输入信息
type ToolArgument struct {
    // TextArgument 包含 JSON 格式的工具调用参数
    TextArgument string
}
```

#### **ToolResult - 工具输出结果**

```go
// ToolResult 表示工具执行的结构化多模态输出
// 当工具需要返回不仅仅是简单字符串时使用，
// 例如图片、文件或其他结构化数据
type ToolResult struct {
    // Parts 包含多模态输出部分。每个部分可以是不同类型的内容，
    // 如文本、图片或文件
    Parts []ToolOutputPart `json:"parts,omitempty"`
}
```

#### **ToolOutputPart - 输出内容部分**

```go
// ToolPartType 定义工具输出部分的内容类型
type ToolPartType string

const (
    ToolPartTypeText  ToolPartType = "text"   // 文本
    ToolPartTypeImage ToolPartType = "image"  // 图片
    ToolPartTypeAudio ToolPartType = "audio"  // 音频
    ToolPartTypeVideo ToolPartType = "video"  // 视频
    ToolPartTypeFile  ToolPartType = "file"   // 文件
)

// ToolOutputPart 表示工具执行输出的一部分
type ToolOutputPart struct {
    Type  ToolPartType     `json:"type"`            // 内容类型
    Text  string           `json:"text,omitempty"`  // 文本内容
    Image *ToolOutputImage `json:"image,omitempty"` // 图片内容
    Audio *ToolOutputAudio `json:"audio,omitempty"` // 音频内容
    Video *ToolOutputVideo `json:"video,omitempty"` // 视频内容
    File  *ToolOutputFile  `json:"file,omitempty"`  // 文件内容
    Extra map[string]any   `json:"extra,omitempty"` // 扩展信息
}

// 多媒体内容结构体，都包含 URL 或 Base64 数据以及 MIME 类型信息
type ToolOutputImage struct { MessagePartCommon }
type ToolOutputAudio struct { MessagePartCommon }
type ToolOutputVideo struct { MessagePartCommon }
type ToolOutputFile  struct { MessagePartCommon }
```

### **方法说明**

#### **Info 方法**

- 功能：获取工具的描述信息
- 参数：
  - ctx：上下文对象
- 返回值：
  - `*schema.ToolInfo`：工具的描述信息
  - error：获取信息过程中的错误

#### **InvokableRun 方法（标准工具）**

- 功能：同步执行工具
- 参数：
  - ctx：上下文对象，用于传递请求级别的信息，同时也用于传递 Callback Manager
  - `argumentsInJSON`：JSON 格式的参数字符串
  - opts：工具执行的选项
- 返回值：
  - string：执行结果
  - error：执行过程中的错误

#### **InvokableRun 方法（增强型工具）**

- 功能：同步执行工具，返回多模态结果
- 参数：
  - ctx：上下文对象
  - `toolArgument`：包含 JSON 格式参数的 `*schema.ToolArgument`
  - opts：工具执行的选项
- 返回值：
  - `*schema.ToolResult`：包含多模态内容的执行结果
  - error：执行过程中的错误

#### **StreamableRun 方法（标准工具）**

- 功能：以流式方式执行工具
- 参数：
  - ctx：上下文对象
  - `argumentsInJSON`：JSON 格式的参数字符串
  - opts：工具执行的选项
- 返回值：
  - `*schema.StreamReader[string]`：流式执行结果
  - error：执行过程中的错误

#### **StreamableRun 方法（增强型工具）**

- 功能：以流式方式执行工具，返回多模态结果流
- 参数：
  - ctx：上下文对象
  - `toolArgument`：包含 JSON 格式参数的 `*schema.ToolArgument`
  - opts：工具执行的选项
- 返回值：
  - `*schema.StreamReader[*schema.ToolResult]`：流式多模态执行结果
  - error：执行过程中的错误

### **ToolInfo 结构体**

> 代码位置：eino/schema/tool.go

```go
type ToolInfo struct {
    // 工具的唯一名称，用于清晰地表达其用途
    Name string
    // 用于告诉模型如何/何时/为什么使用这个工具
    // 可以在描述中包含少量示例
    Desc string
    // 工具接受的参数定义
    // 可以通过两种方式描述：
    // 1. 使用 ParameterInfo：schema.NewParamsOneOfByParams(params)
    // 2. 使用 OpenAPIV3：schema.NewParamsOneOfByOpenAPIV3(openAPIV3)
    *ParamsOneOf
}
```

### **公共 Option**

Tool 组件使用 ToolOption 来定义可选参数， ToolsNode 没有抽象公共的 option。每个具体的实现可以定义自己的特定 Option，通过 WrapToolImplSpecificOptFn 函数包装成统一的 ToolOption 类型。

## **使用方式**

### **标准工具使用**

```go
import (
    "github.com/cloudwego/eino/components/tool"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
)

// 创建工具节点
toolsNode := compose.NewToolNode([]tool.Tool{
    searchTool,    // 搜索工具
    weatherTool,   // 天气查询工具
    calculatorTool, // 计算器工具
})

// Mock LLM 输出作为输入
input := &schema.Message{
    Role: schema.Assistant,
    ToolCalls: []schema.ToolCall{
       {
          Function: schema.FunctionCall{
             Name:      "weather",
             Arguments: `{"city": "深圳", "date": "tomorrow"}`,
          },
       },
    },
}

toolMessages, err := toolsNode.Invoke(ctx, input)
```

### **增强型工具使用**

增强型工具适用于需要返回多模态内容的场景，如返回图片、音频、视频或文件等。

#### **方式一：使用 InferEnhancedTool 自动推断**

```go
import (
    "context"
    
    "github.com/cloudwego/eino/components/tool"
    "github.com/cloudwego/eino/components/tool/utils"
    "github.com/cloudwego/eino/schema"
)

// 定义输入参数结构体
type ImageSearchInput struct {
    Query string `json:"query" jsonschema:"description=搜索关键词"`
}

// 创建增强型工具
imageSearchTool, err := utils.InferEnhancedTool(
    "image_search",
    "搜索并返回相关图片",
    func(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) {
        // 执行图片搜索逻辑...
        imageURL := "https://example.com/image.png"
        
        return &schema.ToolResult{
            Parts: []schema.ToolOutputPart{
                {Type: schema.ToolPartTypeText, Text: "找到以下图片："},
                {
                    Type: schema.ToolPartTypeImage,
                    Image: &schema.ToolOutputImage{
                        MessagePartCommon: schema.MessagePartCommon{
                            URL: &imageURL,
                        },
                    },
                },
            },
        }, nil
    },
)
```

#### **方式二：使用 NewEnhancedTool 手动创建**

```go
import (
    "context"
    
    "github.com/cloudwego/eino/components/tool/utils"
    "github.com/cloudwego/eino/schema"
)

type FileGeneratorInput struct {
    FileName string `json:"file_name"`
    Content  string `json:"content"`
}

// 手动定义 ToolInfo
toolInfo := &schema.ToolInfo{
    Name: "file_generator",
    Desc: "生成并返回文件",
    ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
        "file_name": {Type: "string", Desc: "文件名"},
        "content":   {Type: "string", Desc: "文件内容"},
    }),
}

// 创建增强型工具
fileGenTool := utils.NewEnhancedTool[*FileGeneratorInput](
    toolInfo,
    func(ctx context.Context, input *FileGeneratorInput) (*schema.ToolResult, error) {
        fileURL := "https://example.com/files/" + input.FileName
        
        return &schema.ToolResult{
            Parts: []schema.ToolOutputPart{
                {Type: schema.ToolPartTypeText, Text: "文件已生成：" + input.FileName},
                {
                    Type: schema.ToolPartTypeFile,
                    File: &schema.ToolOutputFile{
                        MessagePartCommon: schema.MessagePartCommon{
                            URL:      &fileURL,
                            MIMEType: "text/plain",
                        },
                    },
                },
            },
        }, nil
    },
)
```

#### **方式三：实现 EnhancedInvokableTool 接口**

```go
import (
    "context"
    
    "github.com/cloudwego/eino/components/tool"
    "github.com/cloudwego/eino/schema"
)

type MyEnhancedTool struct {
    info *schema.ToolInfo
}

func (t *MyEnhancedTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
    return t.info, nil
}

func (t *MyEnhancedTool) InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...tool.Option) (*schema.ToolResult, error) {
    // 解析参数
    // toolArgument.TextArgument 包含 JSON 格式的参数
    
    // 执行工具逻辑...
    
    return &schema.ToolResult{
        Parts: []schema.ToolOutputPart{
            {Type: schema.ToolPartTypeText, Text: "执行结果"},
        },
    }, nil
}
```

#### **增强型流式工具**

```go
import (
    "context"
    
    "github.com/cloudwego/eino/components/tool/utils"
    "github.com/cloudwego/eino/schema"
)

type StreamInput struct {
    Query string `json:"query"`
}

// 创建增强型流式工具
streamTool, err := utils.InferEnhancedStreamTool(
    "stream_search",
    "流式搜索工具",
    func(ctx context.Context, input *StreamInput) (*schema.StreamReader[*schema.ToolResult], error) {
        results := []*schema.ToolResult{
            {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "搜索中..."}}},
            {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "找到结果"}}},
        }
        return schema.StreamReaderFromArray(results), nil
    },
)
```

### **在编排中使用**

```go
import (
    "github.com/cloudwego/eino/components/tool"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
)

// 创建工具节点
toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
       Tools: []tool.BaseTool{
          searchTool,     // 搜索工具
          weatherTool,    // 天气查询工具
          calculatorTool, // 计算器工具
       },
    })

// 在 Chain 中使用
chain := compose.NewChain[*schema.Message, []*schema.Message]()
chain.AppendToolsNode(toolsNode)

// graph 中
graph := compose.NewGraph[*schema.Message, []*schema.Message]()
graph.AddToolsNode("tools", toolsNode)
```

> **注意**：当工具同时实现了标准接口和增强型接口时，ToolsNode 会优先使用增强型接口。

## **Option 机制**

自定义 Tool 可根据自己需要实现特定的 Option：

```go
import "github.com/cloudwego/eino/components/tool"

// 定义 Option 结构体
type MyToolOptions struct {
    Timeout time.Duration
    MaxRetries int
    RetryInterval time.Duration
}

// 定义 Option 函数
func WithTimeout(timeout time.Duration) tool.Option {
    return tool.WrapImplSpecificOptFn(func(o *MyToolOptions) {
        o.Timeout = timeout
    })
}
```

## **Middleware 机制**

ToolsNode 支持通过 Middleware 对工具调用进行拦截和增强。Middleware 分为四种类型：

```go
// compose/tool_node.go

// ToolMiddleware 组合了 invokable 和 streamable 工具调用的中间件钩子
type ToolMiddleware struct {
    // Invokable 用于非流式标准工具调用
    Invokable InvokableToolMiddleware
    
    // Streamable 用于流式标准工具调用
    Streamable StreamableToolMiddleware
    
    // EnhancedInvokable 用于非流式增强型工具调用
    EnhancedInvokable EnhancedInvokableToolMiddleware
    
    // EnhancedStreamable 用于流式增强型工具调用
    EnhancedStreamable EnhancedStreamableToolMiddleware
}
```

### **Middleware 使用示例**

```go
import (
    "context"
    "fmt"
    
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
)

// 创建带 Middleware 的 ToolsNode
toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
    Tools: []tool.BaseTool{myEnhancedTool},
    ToolCallMiddlewares: []compose.ToolMiddleware{
        {
            // 标准工具中间件
            Invokable: func(next compose.InvokableToolEndpoint) compose.InvokableToolEndpoint {
                return func(ctx context.Context, input *compose.ToolInput) (*compose.ToolOutput, error) {
                    fmt.Printf("调用标准工具: %s\n", input.Name)
                    return next(ctx, input)
                }
            },
            // 增强型工具中间件
            EnhancedInvokable: func(next compose.EnhancedInvokableToolEndpoint) compose.EnhancedInvokableToolEndpoint {
                return func(ctx context.Context, input *compose.ToolInput) (*compose.EnhancedInvokableToolOutput, error) {
                    fmt.Printf("调用增强型工具: %s\n", input.Name)
                    output, err := next(ctx, input)
                    if err != nil {
                        return nil, err
                    }
                    fmt.Printf("增强型工具返回 %d 个内容部分\n", len(output.Result.Parts))
                    return output, nil
                }
            },
        },
    },
})
```

## **Option 和 Callback 使用**

### **Callback 使用示例**

```go
import (
    "context"

    callbackHelper "github.com/cloudwego/eino/utils/callbacks"
    "github.com/cloudwego/eino/callbacks"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/components/tool"
)

// 创建 callback handler
handler := &callbackHelper.ToolCallbackHandler{
    OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context {
       fmt.Printf("开始执行工具，参数: %s\n", input.ArgumentsInJSON)
       return ctx
    },
    OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context {
       fmt.Printf("工具执行完成，结果: %s\n", output.Response)
       return ctx
    },
    OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*tool.CallbackOutput]) context.Context {
       fmt.Println("工具开始流式输出")
       go func() {
          defer output.Close()

          for {
             chunk, err := output.Recv()
             if errors.Is(err, io.EOF) {
                return
             }
             if err != nil {
                return
             }
             fmt.Printf("收到流式输出: %s\n", chunk.Response)
          }
       }()
       return ctx
    },
}

// 使用 callback handler
helper := callbackHelper.NewHandlerHelper().
    Tool(handler).
    Handler()
 
/*** compose a chain
* chain := NewChain
* chain.appendxxx().
*       appendxxx().
*       ...
*/

// 在运行时使用
runnable, err := chain.Compile()
if err != nil {
    return err
}
result, err := runnable.Invoke(ctx, input, compose.WithCallbacks(helper))
```

## 如何获取 ToolCallID

在 tool 函数体、tool callback handler 中，都可以通过 `compose.GetToolCallID(ctx)` 函数获取当前 Tool 的 ToolCallID。

## **已有实现**

1. Google Search Tool: 基于 Google 搜索的工具实现 [Tool - Googlesearch](/zh/docs/eino/ecosystem_integration/tool/tool_googlesearch)
2. duckduckgo search tool: 基于 duckduckgo 搜索的工具实现 [Tool - DuckDuckGoSearch](/zh/docs/eino/ecosystem_integration/tool/tool_duckduckgo_search)
3. MCP: 把 mcp server 作为 tool[Tool - MCP](/zh/docs/eino/ecosystem_integration/tool/tool_mcp)

### v0.5.x->0.6.x

鉴于以下两点考虑：

1. 各大模型厂商 API、MCP Tool 协议约定使用 JSONSchema 来描述工具 input/output schema。
2. Eino 引用的 getkin/kin-openapi@v0.118.0 有安全问题，且 kin-openapi 安全版本有不兼容更新。

Eino 移除了 OpenAPI schema 3.0 相关的所有定义与方法，转为使用 JSONSchema 2020-12。具体移除及增加的定义与方法详见 [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397) 。

升级后，部分 eino-ext module 可能报错“_undefined: schema.NewParamsOneOfByOpenAPIV3_”等问题，升级报错的 eino-ext module 到最新版本即可。

如果 schema 改造比较复杂，可以使用  [https://github.com/cloudwego/eino/discussions/397](https://github.com/cloudwego/eino/discussions/397) 中提供的工具方法辅助转换。

### **v0.6.x 新增增强型工具（Enhanced Tool）**

新增 `EnhancedInvokableTool` 和 `EnhancedStreamableTool` 接口，支持返回结构化的多模态结果。

**主要变更：**

1. **新增工具接口**：

- `EnhancedInvokableTool`：接收 `*schema.ToolArgument`，返回 `*schema.ToolResult`
- `EnhancedStreamableTool`：接收 `*schema.ToolArgument`，返回 `*schema.StreamReader[*schema.ToolResult]`

1. **新增工具辅助函数**（`components/tool/utils/`）：

- `InferEnhancedTool`：从函数自动推断创建增强型工具
- `InferEnhancedStreamTool`：从函数自动推断创建增强型流式工具
- `NewEnhancedTool`：手动创建增强型工具
- `NewEnhancedStreamTool`：手动创建增强型流式工具

1. **新增数据结构**（`schema/message.go`）：

- `ToolPartType`：工具输出内容类型枚举（text、image、audio、video、file）
- `ToolArgument`：工具输入参数结构体
- `ToolResult`：工具多模态输出结果结构体
- `ToolOutputPart`：工具输出内容部分
- `ToolOutputImage/Audio/Video/File`：各类多媒体输出结构体

1. **ToolsNode 增强**：

- 新增 `EnhancedInvokableToolMiddleware` 和 `EnhancedStreamableToolMiddleware`
- 支持增强型工具和标准工具混合使用
- 当工具同时实现两种接口时，优先使用增强型接口

1. **Callback 增强**：

- `CallbackOutput` 新增 `ToolOutput *schema.ToolResult` 字段，用于增强型工具的多模态输出

**使用场景：**

增强型工具适用于需要返回富媒体内容的场景，例如：

- 图片搜索工具返回搜索到的图片
- 文件生成工具返回生成的文件
- 音视频处理工具返回处理后的媒体文件
- 多模态 AI Agent 场景
