Eino: ToolsNode & Tool Guide

Introduction

Tool in the Eino framework is defined as “external capabilities that ChatModel can choose to call”, including local functions, MCP server tools, etc.

ToolsNode is the designated “Tool executor” in the Eino framework. Whether inside a Graph or in an Agent, tool execution is performed via ToolsNode:

// 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)

Configure ToolsNode with a list of tools and supporting policies:

// 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
}

With this, ToolsNode “can execute configured tools” and gains extensibility such as execution ordering, error handling, argument processing, and middleware extensions.

How does ToolsNode “decide” which Tool to execute? It doesn’t decide - it executes according to the incoming *schema.Message:

// 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) generates the []ToolCall to be called (including ToolName, Argument, etc.) and places them in a *schema.Message to pass to ToolsNode. ToolsNode actually executes a call for each ToolCall.

If ExecuteSequentially is configured, ToolsNode will execute tools in the order they appear in []ToolCall.

The result of each ToolCall execution is wrapped into a *schema.Message as part of the ToolsNode output.

Tool Definition

Interface Definition

The Tool component provides two types of interfaces: Standard Tool Interface and Enhanced Tool Interface.

Code location: eino/components/tool/interface.go

Standard Tool Interface

Standard tool interfaces return string type results:

// Basic tool interface, provides tool information
type BaseTool interface {
    Info(ctx context.Context) (*schema.ToolInfo, error)
}

// Invokable tool interface, supports synchronous calls
type InvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
}

// Tool interface supporting streaming output
type StreamableTool interface {
    BaseTool
    StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)
}

Enhanced Tool Interface

Enhanced tool interfaces support returning structured multimodal results (*schema.ToolResult), which can contain text, images, audio, video, and files:

// EnhancedInvokableTool is a tool interface that supports returning structured multimodal results
// Unlike InvokableTool which returns a string, this interface returns *schema.ToolResult
// which can contain text, images, audio, video, and files
type EnhancedInvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.ToolResult, error)
}

// EnhancedStreamableTool is a streaming tool interface that supports returning structured multimodal results
// Provides a streaming reader for incremental access to multimodal content
type EnhancedStreamableTool interface {
    BaseTool
    StreamableRun(ctx context.Context, toolArgument *schema.ToolArgument, opts ...Option) (*schema.StreamReader[*schema.ToolResult], error)
}

Enhanced Tool Data Structures

Code location: eino/schema/message.go

ToolArgument - Tool Input Parameters

// ToolArgument contains the input information for a tool call
type ToolArgument struct {
    // TextArgument contains the tool call parameters in JSON format
    TextArgument string
}

ToolResult - Tool Output Result

// ToolResult represents the structured multimodal output of tool execution
// Used when a tool needs to return more than just a simple string,
// such as images, files, or other structured data
type ToolResult struct {
    // Parts contains multimodal output parts. Each part can be a different type of content,
    // such as text, images, or files
    Parts []ToolOutputPart `json:"parts,omitempty"`
}

ToolOutputPart - Output Content Part

// ToolPartType defines the content type of a tool output part
type ToolPartType string

const (
    ToolPartTypeText  ToolPartType = "text"   // Text
    ToolPartTypeImage ToolPartType = "image"  // Image
    ToolPartTypeAudio ToolPartType = "audio"  // Audio
    ToolPartTypeVideo ToolPartType = "video"  // Video
    ToolPartTypeFile  ToolPartType = "file"   // File
)

// ToolOutputPart represents a part of the tool execution output
type ToolOutputPart struct {
    Type  ToolPartType     `json:"type"`            // Content type
    Text  string           `json:"text,omitempty"`  // Text content
    Image *ToolOutputImage `json:"image,omitempty"` // Image content
    Audio *ToolOutputAudio `json:"audio,omitempty"` // Audio content
    Video *ToolOutputVideo `json:"video,omitempty"` // Video content
    File  *ToolOutputFile  `json:"file,omitempty"`  // File content
    Extra map[string]any   `json:"extra,omitempty"` // Extra information
}

// Multimedia content structs, all containing URL or Base64 data and MIME type information
type ToolOutputImage struct { MessagePartCommon }
type ToolOutputAudio struct { MessagePartCommon }
type ToolOutputVideo struct { MessagePartCommon }
type ToolOutputFile  struct { MessagePartCommon }

Method Descriptions

Info Method

  • Purpose: Get tool description information
  • Parameters:
    • ctx: Context object
  • Returns:
    • *schema.ToolInfo: Tool description information
    • error: Errors during information retrieval

InvokableRun Method (Standard Tool)

  • Purpose: Synchronously execute the tool
  • Parameters:
    • ctx: Context object for passing request-level information and Callback Manager
    • argumentsInJSON: JSON format parameter string
    • opts: Tool execution options
  • Returns:
    • string: Execution result
    • error: Errors during execution

InvokableRun Method (Enhanced Tool)

  • Purpose: Synchronously execute the tool, returning multimodal results
  • Parameters:
    • ctx: Context object
    • toolArgument: *schema.ToolArgument containing JSON format parameters
    • opts: Tool execution options
  • Returns:
    • *schema.ToolResult: Execution result containing multimodal content
    • error: Errors during execution

StreamableRun Method (Standard Tool)

  • Purpose: Execute tool in streaming mode
  • Parameters:
    • ctx: Context object
    • argumentsInJSON: JSON format parameter string
    • opts: Tool execution options
  • Returns:
    • *schema.StreamReader[string]: Streaming execution result
    • error: Errors during execution

StreamableRun Method (Enhanced Tool)

  • Purpose: Execute tool in streaming mode, returning multimodal result stream
  • Parameters:
    • ctx: Context object
    • toolArgument: *schema.ToolArgument containing JSON format parameters
    • opts: Tool execution options
  • Returns:
    • *schema.StreamReader[*schema.ToolResult]: Streaming multimodal execution result
    • error: Errors during execution

ToolInfo Struct

Code location: eino/schema/tool.go

type ToolInfo struct {
    // Unique tool name that clearly expresses its purpose
    Name string
    // Used to tell the model how/when/why to use this tool
    // Can include brief examples in the description
    Desc string
    // Definition of parameters the tool accepts
    // Can be described in two ways:
    // 1. Using ParameterInfo: schema.NewParamsOneOfByParams(params)
    // 2. Using OpenAPIV3: schema.NewParamsOneOfByOpenAPIV3(openAPIV3)
    *ParamsOneOf
}

Common Options

The Tool component uses ToolOption to define optional parameters. ToolsNode has no abstracted common options. Each specific implementation can define its own specific Options, wrapped into the unified ToolOption type using the WrapToolImplSpecificOptFn function.

Usage

Standard Tool Usage

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

// Create tools node
toolsNode := compose.NewToolsNode([]tool.Tool{
    searchTool,    // Search tool
    weatherTool,   // Weather query tool
    calculatorTool, // Calculator tool
})

// Mock LLM output as input
input := &schema.Message{
    Role: schema.Assistant,
    ToolCalls: []schema.ToolCall{
       {
          Function: schema.FunctionCall{
             Name:      "weather",
             Arguments: `{"city": "Shenzhen", "date": "tomorrow"}`,
          },
       },
    },
}

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

Enhanced Tool Usage

Enhanced tools are suitable for scenarios that need to return multimodal content, such as images, audio, video, or files.

Method 1: Use InferEnhancedTool for Automatic Inference

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

// Define input parameter struct
type ImageSearchInput struct {
    Query string `json:"query" jsonschema:"description=Search keyword"`
}

// Create enhanced tool
imageSearchTool, err := utils.InferEnhancedTool(
    "image_search",
    "Search and return related images",
    func(ctx context.Context, input *ImageSearchInput) (*schema.ToolResult, error) {
        // Execute image search logic...
        imageURL := "https://example.com/image.png"
        
        return &schema.ToolResult{
            Parts: []schema.ToolOutputPart{
                {Type: schema.ToolPartTypeText, Text: "Found the following images:"},
                {
                    Type: schema.ToolPartTypeImage,
                    Image: &schema.ToolOutputImage{
                        MessagePartCommon: schema.MessagePartCommon{
                            URL: &imageURL,
                        },
                    },
                },
            },
        }, nil
    },
)

Method 2: Use NewEnhancedTool for Manual Creation

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"`
}

// Manually define ToolInfo
toolInfo := &schema.ToolInfo{
    Name: "file_generator",
    Desc: "Generate and return a file",
    ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
        "file_name": {Type: "string", Desc: "File name"},
        "content":   {Type: "string", Desc: "File content"},
    }),
}

// Create enhanced tool
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: "File generated: " + input.FileName},
                {
                    Type: schema.ToolPartTypeFile,
                    File: &schema.ToolOutputFile{
                        MessagePartCommon: schema.MessagePartCommon{
                            URL:      &fileURL,
                            MIMEType: "text/plain",
                        },
                    },
                },
            },
        }, nil
    },
)

Method 3: Implement EnhancedInvokableTool Interface

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) {
    // Parse arguments
    // toolArgument.TextArgument contains JSON format parameters
    
    // Execute tool logic...
    
    return &schema.ToolResult{
        Parts: []schema.ToolOutputPart{
            {Type: schema.ToolPartTypeText, Text: "Execution result"},
        },
    }, nil
}

Enhanced Streaming Tool

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

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

// Create enhanced streaming tool
streamTool, err := utils.InferEnhancedStreamTool(
    "stream_search",
    "Streaming search tool",
    func(ctx context.Context, input *StreamInput) (*schema.StreamReader[*schema.ToolResult], error) {
        results := []*schema.ToolResult{
            {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Searching..."}}},
            {Parts: []schema.ToolOutputPart{{Type: schema.ToolPartTypeText, Text: "Found results"}}},
        }
        return schema.StreamReaderFromArray(results), nil
    },
)

Usage in Orchestration

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

// Create tools node
toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
       Tools: []tool.BaseTool{
          searchTool,     // Search tool
          weatherTool,    // Weather query tool
          calculatorTool, // Calculator tool
       },
    })

// In Chain
chain := compose.NewChain[*schema.Message, []*schema.Message]()
chain.AppendToolsNode(toolsNode)

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

Note: When a tool implements both standard and enhanced interfaces, ToolsNode will prioritize using the enhanced interface.

Option Mechanism

Custom Tools can implement specific Options as needed:

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

// Define Option struct
type MyToolOptions struct {
    Timeout time.Duration
    MaxRetries int
    RetryInterval time.Duration
}

// Define Option function
func WithTimeout(timeout time.Duration) tool.Option {
    return tool.WrapImplSpecificOptFn(func(o *MyToolOptions) {
        o.Timeout = timeout
    })
}

Middleware Mechanism

ToolsNode supports intercepting and enhancing tool calls through Middleware. There are four types of Middleware:

// compose/tool_node.go

// ToolMiddleware combines middleware hooks for invokable and streamable tool calls
type ToolMiddleware struct {
    // Invokable is for non-streaming standard tool calls
    Invokable InvokableToolMiddleware
    
    // Streamable is for streaming standard tool calls
    Streamable StreamableToolMiddleware
    
    // EnhancedInvokable is for non-streaming enhanced tool calls
    EnhancedInvokable EnhancedInvokableToolMiddleware
    
    // EnhancedStreamable is for streaming enhanced tool calls
    EnhancedStreamable EnhancedStreamableToolMiddleware
}

Middleware Usage Example

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

// Create ToolsNode with Middleware
toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
    Tools: []tool.BaseTool{myEnhancedTool},
    ToolCallMiddlewares: []compose.ToolMiddleware{
        {
            // Standard tool middleware
            Invokable: func(next compose.InvokableToolEndpoint) compose.InvokableToolEndpoint {
                return func(ctx context.Context, input *compose.ToolInput) (*compose.ToolOutput, error) {
                    fmt.Printf("Calling standard tool: %s\n", input.Name)
                    return next(ctx, input)
                }
            },
            // Enhanced tool middleware
            EnhancedInvokable: func(next compose.EnhancedInvokableToolEndpoint) compose.EnhancedInvokableToolEndpoint {
                return func(ctx context.Context, input *compose.ToolInput) (*compose.EnhancedInvokableToolOutput, error) {
                    fmt.Printf("Calling enhanced tool: %s\n", input.Name)
                    output, err := next(ctx, input)
                    if err != nil {
                        return nil, err
                    }
                    fmt.Printf("Enhanced tool returned %d content parts\n", len(output.Result.Parts))
                    return output, nil
                }
            },
        },
    },
})

Option and Callback Usage

Callback Usage Example

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"
)

// Create callback handler
handler := &callbackHelper.ToolCallbackHandler{
    OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context {
       fmt.Printf("Starting tool execution, arguments: %s\n", input.ArgumentsInJSON)
       return ctx
    },
    OnEnd: func(ctx context.Context, info *callbacks.RunInfo, output *tool.CallbackOutput) context.Context {
       fmt.Printf("Tool execution completed, result: %s\n", output.Response)
       return ctx
    },
    OnEndWithStreamOutput: func(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[*tool.CallbackOutput]) context.Context {
       fmt.Println("Tool starting streaming output")
       go func() {
          defer output.Close()

          for {
             chunk, err := output.Recv()
             if errors.Is(err, io.EOF) {
                return
             }
             if err != nil {
                return
             }
             fmt.Printf("Received streaming output: %s\n", chunk.Response)
          }
       }()
       return ctx
    },
}

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

// Use at runtime
runnable, err := chain.Compile()
if err != nil {
    return err
}
result, err := runnable.Invoke(ctx, input, compose.WithCallbacks(helper))

How to Get ToolCallID

In the tool function body or tool callback handler, you can use compose.GetToolCallID(ctx) to get the current Tool’s ToolCallID.

Existing Implementations

  1. Google Search Tool: Tool implementation based on Google search Tool - Googlesearch
  2. DuckDuckGo Search Tool: Tool implementation based on DuckDuckGo search Tool - DuckDuckGoSearch
  3. MCP: Use MCP server as tool Tool - MCP

v0.5.x → v0.6.x

Based on the following two considerations:

  1. Major model vendor APIs and MCP Tool protocols specify using JSONSchema to describe tool input/output schemas.
  2. The getkin/kin-openapi@v0.118.0 referenced by Eino has security issues, and secure versions of kin-openapi have incompatible updates.

Eino has removed all OpenAPI schema 3.0 related definitions and methods, switching to JSONSchema 2020-12. For specific removed and added definitions and methods, see https://github.com/cloudwego/eino/discussions/397.

After upgrading, some eino-ext modules may error with “undefined: schema.NewParamsOneOfByOpenAPIV3” - upgrade the erroring eino-ext modules to the latest version.

If schema migration is complex, you can use the helper tool methods provided in https://github.com/cloudwego/eino/discussions/397.

v0.6.x New Enhanced Tools

New EnhancedInvokableTool and EnhancedStreamableTool interfaces have been added, supporting structured multimodal results.

Main Changes:

  1. New Tool Interfaces:
  • EnhancedInvokableTool: Receives *schema.ToolArgument, returns *schema.ToolResult
  • EnhancedStreamableTool: Receives *schema.ToolArgument, returns *schema.StreamReader[*schema.ToolResult]
  1. New Tool Helper Functions (components/tool/utils/):
  • InferEnhancedTool: Automatically infer and create enhanced tool from function
  • InferEnhancedStreamTool: Automatically infer and create enhanced streaming tool from function
  • NewEnhancedTool: Manually create enhanced tool
  • NewEnhancedStreamTool: Manually create enhanced streaming tool
  1. New Data Structures (schema/message.go):
  • ToolPartType: Tool output content type enum (text, image, audio, video, file)
  • ToolArgument: Tool input parameter struct
  • ToolResult: Tool multimodal output result struct
  • ToolOutputPart: Tool output content part
  • ToolOutputImage/Audio/Video/File: Various multimedia output structs
  1. ToolsNode Enhancements:
  • Added EnhancedInvokableToolMiddleware and EnhancedStreamableToolMiddleware
  • Supports mixed use of enhanced tools and standard tools
  • When a tool implements both interfaces, the enhanced interface is prioritized
  1. Callback Enhancements:
  • CallbackOutput added ToolOutput *schema.ToolResult field for enhanced tool multimodal output

Use Cases:

Enhanced tools are suitable for scenarios that need to return rich media content, such as:

  • Image search tools returning found images
  • File generation tools returning generated files
  • Audio/video processing tools returning processed media files
  • Multimodal AI Agent scenarios

Last modified March 2, 2026: feat: sync en files (c14c5a55)