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
- 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 a tool Eino Tool - MCP
Tool Implementation Methods
There are multiple ways to implement tools. You can refer to the following approaches:
- HTTP API-based tool implementation: How to create a tool/function call using OpenAPI?
- gRPC-based tool implementation: How to create a tool/function call using proto3?
- Thrift-based tool implementation: How to create a tool/function call using thrift IDL?
- Local function-based tool implementation: How to create a tool?
- ……