Eino: AgenticToolsNode&Tool 使用说明[Beta]
基本介绍
Tool 在 eino 框架中的定义是“AgenticModel 能够选择调用的外部能力”,包括本地函数,MCP server tool 等。
AgenticToolsNode 是 eino 框架指定的“Tool 执行器”,执行工具的方法定义如下:
代码位置: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 与 ToolsNode 复用同一套配置,用法相同,如配置执行时序、异常处理、入参处理、middleware 扩展等。
代码位置: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
}
AgenticToolsNode 如何“决策”应该执行哪个 Tool?它不决策,而是依据输入的 *schema.AgenticMessage 来执行。AgenticModel 生成要调用的 FunctionToolCall(包含 ToolName,Argument 等),放到 *schema.AgenticMessage 中传给 AgenticToolsNode。AgenticToolsNode 针对每个 FunctionToolCall 实际执行一次调用。
如果配置了 ExecuteSequentially,则 AgenticToolsNode 会按照 []*ContentBlock 中的先后顺序来执行工具。
每个 FunctionToolCall 调用完成后的结果,又会封装为 *schema.AgenticMessage,作为 AgenticToolsNode 输出的一部分。
// 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 定义
接口定义
Tool 组件提供了三个层次的接口:
代码位置: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 方法
- 功能:获取工具的描述信息
- 参数:
- ctx:上下文对象
- 返回值:
*schema.ToolInfo:工具的描述信息- error:获取信息过程中的错误
InvokableRun 方法
- 功能:同步执行工具
- 参数:
- ctx:上下文对象,用于传递请求级别的信息,同时也用于传递 Callback Manager
argumentsInJSON:JSON 格式的参数字符串- opts:工具执行的选项
- 返回值:
- string:执行结果
- error:执行过程中的错误
StreamableRun 方法
- 功能:以流式方式执行工具
- 参数:
- ctx:上下文对象,用于传递请求级别的信息,同时也用于传递 Callback Manager
argumentsInJSON:JSON 格式的参数字符串- opts:工具执行的选项
- 返回值:
*schema.StreamReader[string]:流式执行结果- error:执行过程中的错误
ToolInfo 结构体
代码位置:https://github.com/cloudwego/eino/components/tool/interface.go
type ToolInfo struct {
// 工具的唯一名称,用于清晰地表达其用途
Name string
// 用于告诉模型如何/何时/为什么使用这个工具
// 可以在描述中包含少量示例
Desc string
// 工具接受的参数定义
// 可以通过两种方式描述:
// 1. 使用 ParameterInfo:schema.NewParamsOneOfByParams(params)
// 2. 使用 JSONSchema:schema.NewParamsOneOfByJSONSchema(jsonschema)
*ParamsOneOf
}
公共 Option
Tool 组件使用 ToolOption 来定义可选参数, AgenticToolsNode 没有抽象公共的 option。每个具体的实现可以定义自己的特定 Option,通过 WrapToolImplSpecificOptFn 函数包装成统一的 ToolOption 类型。
使用方式
ToolsNode 通常不会被单独使用,一般用于编排之中接在 AgenticModel 之后。
import (
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
)
// 创建工具节点
toolsNode := compose.NewAgenticToolsNode([]tool.Tool{
searchTool, // 搜索工具
weatherTool, // 天气查询工具
calculatorTool, // 计算器工具
})
// Mock LLM 输出作为输入
input := &schema.AgenticMessage{
Role: schema.AgenticRoleTypeAssistant,
ContentBlocks: []*schema.ContentBlock{
{
Type: schema.ContentBlockTypeFunctionToolCall,
FunctionToolCall: &schema.FunctionToolCall{
CallID: "1",
Name: "get_weather",
Arguments: `{"city": "深圳", "date": "tomorrow"}`,
},
},
},
}
toolMessages, err := toolsNode.Invoke(ctx, input)
在编排中使用
import (
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
)
// 创建工具节点
toolsNode := compose.NewAgenticToolsNode([]tool.Tool{
searchTool, // 搜索工具
weatherTool, // 天气查询工具
calculatorTool, // 计算器工具
})
// 在 Chain 中使用
chain := compose.NewChain[*schema.AgenticMessage, []*schema.AgenticMessage]()
chain.AppendAgenticToolsNode(toolsNode)
// graph 中
graph := compose.NewGraph[*schema.AgenticMessage, []*schema.AgenticMessage]()
graph.AddAgenticToolsNode(toolsNode)
Option 机制
自定义 Tool 可根据自己需要实现特定的 Option:
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
})
}
Option 和 Callback 使用
Callback 使用示例
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。
已有实现
- Google Search Tool: 基于 Google 搜索的工具实现 Tool - Googlesearch
- duckduckgo search tool: 基于 duckduckgo 搜索的工具实现 Tool - DuckDuckGoSearch
- MCP: 把 mcp server 作为 toolEino Tool - MCP
工具实现方式
工具的实现方式有多种,可以参考如下方式:
- 基于 HTTP API 的 tool 实现: 如何使用 openapi 创建 tool/function call ?
- 基于 gRPC 的 tool 实现: 如何使用 proto3 创建 tool/function call ?
- 基于 thrift 的 tool 实现: 如何使用 thrift idl 创建 tool/function call ?
- 基于本地函数的工具实现: 如何创建一个 tool ?
- ……