Eino: AgenticToolsNode & Tool User Guide [Beta]

Introduction

In the eino framework, a Tool is defined as “an external capability that an AgenticModel can choose to invoke”, including local functions, MCP server tools, etc.

AgenticToolsNode is the designated “Tool executor” in the eino framework. The methods for executing tools are defined as follows:

Code location: https://github.com/cloudwego/eino/tree/main/compose/agentic_tools_node.go

func (a *AgenticToolsNode) Invoke(ctx context.Context, input *schema.AgenticMessage, opts ...ToolsNodeOption) ([]*schema.AgenticMessage, error) {}

func (a *AgenticToolsNode) Stream(ctx context.Context, input *schema.AgenticMessage,
    opts ...ToolsNodeOption) (*schema.StreamReader[[]*schema.AgenticMessage], error) {}

AgenticToolsNode and ToolsNode share the same configuration and usage, such as configuring execution sequence, exception handling, input processing, middleware extensions, etc.

Code location: https://github.com/cloudwego/eino/tree/main/compose/tool_node.go

type ToolsNodeConfig struct {
    // Tools specify the list of tools can be called which are BaseTool but must implement InvokableTool or StreamableTool.
    Tools []tool.BaseTool

    // UnknownToolsHandler handles tool calls for non-existent tools when LLM hallucinates.
    // This field is optional. When not set, calling a non-existent tool will result in an error.
    // When provided, if the LLM attempts to call a tool that doesn't exist in the Tools list,
    // this handler will be invoked instead of returning an error, allowing graceful handling of hallucinated tools.
    // Parameters:
    //   - ctx: The context for the tool call
    //   - name: The name of the non-existent tool
    //   - input: The tool call input generated by llm
    // Returns:
    //   - string: The response to be returned as if the tool was executed
    //   - error: Any error that occurred during handling
    UnknownToolsHandler func(ctx context.Context, name, input string) (string, error)

    // ExecuteSequentially determines whether tool calls should be executed sequentially (in order) or in parallel.
    // When set to true, tool calls will be executed one after another in the order they appear in the input message.
    // When set to false (default), tool calls will be executed in parallel.
    ExecuteSequentially bool

    // ToolArgumentsHandler allows handling of tool arguments before execution.
    // When provided, this function will be called for each tool call to process the arguments.
    // Parameters:
    //   - ctx: The context for the tool call
    //   - name: The name of the tool being called
    //   - arguments: The original arguments string for the tool
    // Returns:
    //   - string: The processed arguments string to be used for tool execution
    //   - error: Any error that occurred during preprocessing
    ToolArgumentsHandler func(ctx context.Context, name, arguments string) (string, error)

    // ToolCallMiddlewares configures middleware for tool calls.
    // Each element can contain Invokable and/or Streamable middleware.
    // Invokable middleware only applies to tools implementing InvokableTool interface.
    // Streamable middleware only applies to tools implementing StreamableTool interface.
    ToolCallMiddlewares []ToolMiddleware
}

How does AgenticToolsNode “decide” which Tool to execute? It doesn’t make decisions; instead, it executes based on the input *schema.AgenticMessage. The AgenticModel generates FunctionToolCalls to be invoked (containing ToolName, Argument, etc.) and places them in *schema.AgenticMessage to pass to AgenticToolsNode. AgenticToolsNode then actually executes each FunctionToolCall.

If ExecuteSequentially is configured, AgenticToolsNode will execute tools in the order they appear in []*ContentBlock.

After each FunctionToolCall execution completes, the result is wrapped as *schema.AgenticMessage and becomes part of the AgenticToolsNode output.

// https://github.com/cloudwego/eino/tree/main/schema/agentic_message.go

type AgenticMessage struct {
    // role should be 'assistant' for tool call message
    Role AgenticRoleType

    // ContentBlocks is the list of content blocks.
    ContentBlocks []*ContentBlock

     // other fields...
}

type ContentBlock struct {
    Type ContentBlockType

    // FunctionToolCall contains the invocation details for a user-defined tool.
    FunctionToolCall *FunctionToolCall

    // FunctionToolResult contains the result returned from a user-defined tool call.
    FunctionToolResult *FunctionToolResult

    // other fields...
}

// FunctionToolCall is the function call in a message.
// It's used in assistant message.
type FunctionToolCall struct {
    // CallID is the unique identifier for the tool call.
    CallID string

    // Name specifies the function tool invoked.
    Name string

    // Arguments is the JSON string arguments for the function tool call.
    Arguments string
}

// FunctionToolResult is the function call result in a message.
// It's used in user message.
type FunctionToolResult struct {
    // CallID is the unique identifier for the tool call.
    CallID string

    // Name specifies the function tool invoked.
    Name string

    // Result is the function tool result returned by the user
    Result string
}

Tool Definition

Interface Definition

The Tool component provides three levels of interfaces:

Code location: https://github.com/cloudwego/eino/components/tool/interface.go

// BaseTool get tool info for ChatModel intent recognition.
type BaseTool interface {
    Info(ctx context.Context) (*schema.ToolInfo, error)
}

// InvokableTool the tool for ChatModel intent recognition and ToolsNode execution.
type InvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
}

// StreamableTool the stream tool for ChatModel intent recognition and ToolsNode execution.
type StreamableTool interface {
    BaseTool
    StreamableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (*schema.StreamReader[string], error)
}

Info Method

  • Function: Get the tool’s description information
  • Parameters:
    • ctx: Context object
  • Return values:
    • *schema.ToolInfo: Tool description information
    • error: Error during information retrieval

InvokableRun Method

  • Function: Execute the tool synchronously
  • Parameters:
    • ctx: Context object, used for passing request-level information and also for passing the Callback Manager
    • argumentsInJSON: JSON-formatted argument string
    • opts: Tool execution options
  • Return values:
    • string: Execution result
    • error: Error during execution

StreamableRun Method

  • Function: Execute the tool in streaming mode
  • Parameters:
    • ctx: Context object, used for passing request-level information and also for passing the Callback Manager
    • argumentsInJSON: JSON-formatted argument string
    • opts: Tool execution options
  • Return values:
    • *schema.StreamReader[string]: Streaming execution result
    • error: Error during execution

ToolInfo Struct

Code location: https://github.com/cloudwego/eino/components/tool/interface.go

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

Common Options

The Tool component uses ToolOption to define optional parameters. AgenticToolsNode does not abstract common options. Each specific implementation can define its own specific Options, which are wrapped into the unified ToolOption type through the WrapToolImplSpecificOptFn function.

Usage

ToolsNode is typically not used alone; it is generally used in orchestration after an AgenticModel.

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

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

// Mock LLM output as input
input := &schema.AgenticMessage{
    Role: schema.AgenticRoleTypeAssistant,
    ContentBlocks: []*schema.ContentBlock{
       {
          Type: schema.ContentBlockTypeFunctionToolCall,
          FunctionToolCall: &schema.FunctionToolCall{
             CallID:    "1",
             Name:      "get_weather",
             Arguments: `{"city": "Shenzhen", "date": "tomorrow"}`,
          },
       },
    },
}

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

Using in Orchestration

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

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

// Use in Chain
chain := compose.NewChain[*schema.AgenticMessage, []*schema.AgenticMessage]()
chain.AppendAgenticToolsNode(toolsNode)

// In graph
graph := compose.NewGraph[*schema.AgenticMessage, []*schema.AgenticMessage]()
graph.AddAgenticToolsNode(toolsNode)

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

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 and tool callback handler, you can use the compose.GetToolCallID(ctx) function to get the ToolCallID of the current Tool.

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 a tool Eino Tool - MCP

Tool Implementation Methods

There are multiple ways to implement tools. You can refer to the following approaches:


Last modified January 20, 2026: feat(eino): sync En docs with zh docs (9da8ff724c)