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.ToolArgumentcontaining 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.ToolArgumentcontaining 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
- Google Search Tool: Tool implementation based on Google search Tool - Googlesearch
- DuckDuckGo Search Tool: Tool implementation based on DuckDuckGo search Tool - DuckDuckGoSearch
- MCP: Use MCP server as tool Tool - MCP
v0.5.x → v0.6.x
Based on the following two considerations:
- Major model vendor APIs and MCP Tool protocols specify using JSONSchema to describe tool input/output schemas.
- 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:
- New Tool Interfaces:
EnhancedInvokableTool: Receives*schema.ToolArgument, returns*schema.ToolResultEnhancedStreamableTool: Receives*schema.ToolArgument, returns*schema.StreamReader[*schema.ToolResult]
- New Tool Helper Functions (
components/tool/utils/):
InferEnhancedTool: Automatically infer and create enhanced tool from functionInferEnhancedStreamTool: Automatically infer and create enhanced streaming tool from functionNewEnhancedTool: Manually create enhanced toolNewEnhancedStreamTool: Manually create enhanced streaming tool
- New Data Structures (
schema/message.go):
ToolPartType: Tool output content type enum (text, image, audio, video, file)ToolArgument: Tool input parameter structToolResult: Tool multimodal output result structToolOutputPart: Tool output content partToolOutputImage/Audio/Video/File: Various multimedia output structs
- ToolsNode Enhancements:
- Added
EnhancedInvokableToolMiddlewareandEnhancedStreamableToolMiddleware - Supports mixed use of enhanced tools and standard tools
- When a tool implements both interfaces, the enhanced interface is prioritized
- Callback Enhancements:
CallbackOutputaddedToolOutput *schema.ToolResultfield 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