跳转至

Tool 工具使用文档

Tool 工具系统是 tRPC-Agent-Go 框架的核心组件,为 Agent 提供了与外部服务和功能交互的能力。框架支持多种工具类型,包括函数工具和基于 MCP(Model Context Protocol)标准的外部工具集成。

概述

🎯 核心特性

  • 🔧 多类型工具:支持函数工具(Function Tools)和 MCP 标准工具
  • 🌊 流式响应:支持实时流式响应和普通响应两种模式
  • ⚡ 并行执行:工具调用支持并行执行以提升性能
  • 🔄 MCP 协议:完整支持 STDIO、SSE、Streamable HTTP 三种传输方式
  • 🛠️ 配置支持:提供配置选项和过滤器支持

核心概念

🔧 Tool(工具)

Tool 是单个功能的抽象,实现 tool.Tool 接口。每个 Tool 提供特定的能力,如数学计算、搜索、时间查询等。

1
2
3
4
5
6
7
8
type Tool interface {
    Declaration() *Declaration  // 返回工具元数据
}

type CallableTool interface {
    Call(ctx context.Context, jsonArgs []byte) (any, error)
    Tool
}

📦 ToolSet(工具集)

ToolSet 是一组相关工具的集合,实现 tool.ToolSet 接口。ToolSet 负责管理工具的生命周期、连接和资源清理。

1
2
3
4
type ToolSet interface {
    Tools(context.Context) []CallableTool  // 返回工具列表
    Close() error                          // 资源清理
}

Tool 与 ToolSet 的关系:

  • 一个 Tool = 一个具体功能(如计算器)
  • 一个 ToolSet = 一组相关的 Tool(如MCP服务器提供的所有工具)
  • Agent 可以同时使用多个 Tool 和多个 ToolSet

🌊 流式工具支持

框架支持流式工具,提供实时响应能力:

// 流式工具接口
type StreamableTool interface {
    StreamableCall(ctx context.Context, jsonArgs []byte) (*StreamReader, error)
    Tool
}

// 流式数据单元
type StreamChunk struct {
    Content  any      `json:"content"`
    Metadata Metadata `json:"metadata,omitempty"`
}

流式工具特点:

  • 🚀 实时响应:数据逐步返回,无需等待完整结果
  • 📊 大数据处理:适用于日志查询、数据分析等场景
  • 用户体验:提供即时反馈和进度显示

工具类型说明

工具类型 定义 集成方式
Function Tools 直接调用 Go 函数实现的工具 Tool 接口,进程内调用
Agent Tool (AgentTool) 将任意 Agent 包装为可调用工具 Tool 接口,支持流式内部转发
DuckDuckGo Tool 基于 DuckDuckGo API 的搜索工具 Tool 接口,HTTP API
MCP ToolSet 基于 MCP 协议的外部工具集 ToolSet 接口,支持多种传输方式

📖 相关文档:Agent 间协作相关的 Agent Tool 和 Transfer Tool 请参考 多 Agent 系统文档

Function Tools 函数工具

Function Tools 通过 Go 函数直接实现工具逻辑,是最简单直接的工具类型。

基本用法

import "trpc.group/trpc-go/trpc-agent-go/tool/function"

// 1. 定义工具函数
func calculator(ctx context.Context, req struct {
    Operation string  `json:"operation"`
    A         float64 `json:"a"`
    B         float64 `json:"b"`
}) (map[string]interface{}, error) {
    switch req.Operation {
    case "add":
        return map[string]interface{}{"result": req.A + req.B}, nil
    case "multiply":
        return map[string]interface{}{"result": req.A * req.B}, nil
    default:
        return nil, fmt.Errorf("unsupported operation: %s", req.Operation)
    }
}

// 2. 创建工具
calculatorTool := function.NewFunctionTool(
    calculator,
    function.WithName("calculator"),
    function.WithDescription("执行数学运算"),
)

// 3. 集成到 Agent
agent := llmagent.New("math-assistant",
    llmagent.WithModel(model),
    llmagent.WithTools([]tool.Tool{calculatorTool}))

流式工具示例

// 1. 定义输入输出结构
type weatherInput struct {
    Location string `json:"location"`
}

type weatherOutput struct {
    Weather string `json:"weather"`
}

// 2. 实现流式工具函数
func getStreamableWeather(input weatherInput) *tool.StreamReader {
    stream := tool.NewStream(10)
    go func() {
        defer stream.Writer.Close()

        // 模拟逐步返回天气数据
        result := "Sunny, 25°C in " + input.Location
        for i := 0; i < len(result); i++ {
            chunk := tool.StreamChunk{
                Content: weatherOutput{
                    Weather: result[i : i+1],
                },
                Metadata: tool.Metadata{CreatedAt: time.Now()},
            }

            if closed := stream.Writer.Send(chunk, nil); closed {
                break
            }
            time.Sleep(10 * time.Millisecond) // 模拟延迟
        }
    }()

    return stream.Reader
}

// 3. 创建流式工具
weatherStreamTool := function.NewStreamableFunctionTool[weatherInput, weatherOutput](
    getStreamableWeather,
    function.WithName("get_weather_stream"),
    function.WithDescription("流式获取天气信息"),
)

// 4. 使用流式工具
reader, err := weatherStreamTool.StreamableCall(ctx, jsonArgs)
if err != nil {
    return err
}

// 接收流式数据
for {
    chunk, err := reader.Recv()
    if err == io.EOF {
        break // 流结束
    }
    if err != nil {
        return err
    }

    // 处理每个数据块
    fmt.Printf("收到数据: %v\n", chunk.Content)
}
reader.Close()

内置工具类型

DuckDuckGo 搜索工具

DuckDuckGo 工具基于 DuckDuckGo Instant Answer API,提供事实性、百科类信息搜索功能。

基础用法

1
2
3
4
5
6
7
8
9
import "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo"

// 创建 DuckDuckGo 搜索工具
searchTool := duckduckgo.NewTool()

// 集成到 Agent
searchAgent := llmagent.New("search-assistant",
    llmagent.WithModel(model),
    llmagent.WithTools([]tool.Tool{searchTool}))

高级配置

import (
    "net/http"
    "time"
    "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo"
)

// 自定义配置
searchTool := duckduckgo.NewTool(
    duckduckgo.WithBaseURL("https://api.duckduckgo.com"),
    duckduckgo.WithUserAgent("my-app/1.0"),
    duckduckgo.WithHTTPClient(&http.Client{
        Timeout: 15 * time.Second,
    }),
)

MCP Tools 协议工具

MCP(Model Context Protocol)是一个开放协议,标准化了应用程序向 LLM 提供上下文的方式。MCP 工具基于 JSON-RPC 2.0 协议,为 Agent 提供了与外部服务的标准化集成能力。

MCP ToolSet 特点:

  • 🔗 统一接口:所有 MCP 工具都通过 mcp.NewMCPToolSet() 创建
  • 🚀 多种传输:支持 STDIO、SSE、Streamable HTTP 三种传输方式
  • 🔧 工具过滤:支持包含/排除特定工具

基本用法

import "trpc.group/trpc-go/trpc-agent-go/tool/mcp"

// 创建 MCP 工具集(以 STDIO 为例)
mcpToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "stdio",           // 传输方式
        Command:   "go",              // 执行命令
        Args:      []string{"run", "./stdio_server/main.go"},
        Timeout:   10 * time.Second,
    },
    mcp.WithToolFilter(mcp.NewIncludeFilter("echo", "add")), // 可选:工具过滤
)

// 集成到 Agent
agent := llmagent.New("mcp-assistant",
    llmagent.WithModel(model),
    llmagent.WithToolSets([]tool.ToolSet{mcpToolSet}))

传输方式配置

MCP ToolSet 通过 Transport 字段支持三种传输方式:

1. STDIO 传输

通过标准输入输出与外部进程通信,适用于本地脚本和命令行工具。

1
2
3
4
5
6
7
8
mcpToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "stdio",
        Command:   "python",
        Args:      []string{"-m", "my_mcp_server"},
        Timeout:   10 * time.Second,
    },
)

2. SSE 传输

使用 Server-Sent Events 进行通信,支持实时数据推送和流式响应。

mcpToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "sse",
        ServerURL: "http://localhost:8080/sse",
        Timeout:   10 * time.Second,
        Headers: map[string]string{
            "Authorization": "Bearer your-token",
        },
    },
)

3. Streamable HTTP 传输

使用标准 HTTP 协议进行通信,支持普通HTTP和流式响应。

1
2
3
4
5
6
7
mcpToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "streamable_http",  // 注意:使用完整名称
        ServerURL: "http://localhost:3000/mcp",
        Timeout:   10 * time.Second,
    },
)

会话重连支持

MCP ToolSet 支持自动会话重连,当服务器重启或会话过期时自动恢复连接。

1
2
3
4
5
6
7
8
9
// SSE/Streamable HTTP 传输支持会话重连
sseToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "sse",
        ServerURL: "http://localhost:8080/sse",
        Timeout:   10 * time.Second,
    },
    mcp.WithSessionReconnect(3), // 启用会话重连,最多尝试3次
)

重连特性:

  • 🔄 自动重连:检测到连接断开或会话过期时自动重建会话
  • 🎯 独立重试:每次工具调用独立计数,不会因早期失败影响后续调用
  • 🛡️ 保守策略:仅针对明确的连接/会话错误触发重连,避免配置错误导致的无限循环

Agent 工具 (AgentTool)

AgentTool 允许把一个现有的 Agent 以工具的形式暴露给上层 Agent 使用。相比普通函数工具,AgentTool 的优势在于:

  • ✅ 复用:将复杂 Agent 能力作为标准工具复用
  • 🌊 流式:可选择将子 Agent 的流式事件“内联”转发到父流程
  • 🧭 控制:通过选项控制是否跳过工具后的总结补全、是否进行内部转发

基本用法

import (
    "trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
    "trpc.group/trpc-go/trpc-agent-go/model"
    "trpc.group/trpc-go/trpc-agent-go/tool"
    agenttool "trpc.group/trpc-go/trpc-agent-go/tool/agent"
)

// 1) 定义一个可复用的子 Agent(可配置为流式)
mathAgent := llmagent.New(
    "math-specialist",
    llmagent.WithModel(modelInstance),
    llmagent.WithInstruction("你是数学专家..."),
    llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}),
)

// 2) 包装为 Agent 工具
mathTool := agenttool.NewTool(
    mathAgent,
    agenttool.WithSkipSummarization(true), // 可选:工具响应后跳过外层模型总结
    agenttool.WithStreamInner(true),       // 开启:把子 Agent 的流式事件转发给父流程
)

// 3) 在父 Agent 中使用该工具
parent := llmagent.New(
    "assistant",
    llmagent.WithModel(modelInstance),
    llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}),
    llmagent.WithTools([]tool.Tool{mathTool}),
)

流式内部转发详解

WithStreamInner(true) 时,AgentTool 会把子 Agent 在运行时产生的事件直接转发到父流程的事件流中:

  • 转发的事件本质是子 Agent 里的 event.Event,包含增量内容(choice.Delta.Content
  • 为避免重复,子 Agent 在结束时产生的“完整大段内容”不会再次作为转发事件打印;但会被聚合到最终 tool.response 的内容里,供下一次 LLM 调用作为工具消息使用
  • UI 层建议:展示“转发的子 Agent 增量内容”,但默认不重复打印最终聚合的 tool.response 内容(除非用于调试)

示例:仅在需要时显示工具片段,避免重复输出

if ev.Response != nil && ev.Object == model.ObjectTypeToolResponse {
    // 工具响应(包含聚合后的内容),默认不打印,避免和子 Agent 转发的内容重复
    // ...仅在调试或需要展示工具细节时再打印
}

// 子 Agent 转发的流式增量(作者不是父 Agent)
if ev.Author != parentName && len(ev.Choices) > 0 {
    if delta := ev.Choices[0].Delta.Content; delta != "" {
        fmt.Print(delta)
    }
}

选项说明

  • WithSkipSummarization(bool):
    • false(默认):允许在工具结果后继续一次 LLM 调用进行总结/回答
    • true:外层 Flow 在 tool.response 后直接结束本轮(不再额外总结)
  • WithStreamInner(bool):
    • true:把子 Agent 的事件直接转发到父流程(强烈建议父/子 Agent 都开启 GenerationConfig{Stream: true}
    • false:按“仅可调用工具”处理,不做内部事件转发

注意事项

  • 事件完成信号:工具响应事件会被标记 RequiresCompletion=true,Runner 会自动发送完成信号,无需手工处理
  • 内容去重:如果已转发子 Agent 的增量内容,默认不要再把最终 tool.response 的聚合内容打印出来
  • 模型兼容性:一些模型要求工具调用后必须跟随工具消息,AgentTool 已自动填充聚合后的工具内容满足此要求

工具集成与使用

创建 Agent 与工具集成

import (
    "trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
    "trpc.group/trpc-go/trpc-agent-go/tool/function"
    "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo"
    "trpc.group/trpc-go/trpc-agent-go/tool/mcp"
)

// 创建函数工具
calculatorTool := function.NewFunctionTool(calculator,
    function.WithName("calculator"),
    function.WithDescription("执行基础数学运算"))

timeTool := function.NewFunctionTool(getCurrentTime,
    function.WithName("current_time"), 
    function.WithDescription("获取当前时间"))

// 创建内置工具
searchTool := duckduckgo.NewTool()

// 创建 MCP 工具集(不同传输方式的示例)
stdioToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "stdio",
        Command:   "python",
        Args:      []string{"-m", "my_mcp_server"},
        Timeout:   10 * time.Second,
    },
)

sseToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "sse",
        ServerURL: "http://localhost:8080/sse",
        Timeout:   10 * time.Second,
    },
)

streamableToolSet := mcp.NewMCPToolSet(
    mcp.ConnectionConfig{
        Transport: "streamable_http",
        ServerURL: "http://localhost:3000/mcp",
        Timeout:   10 * time.Second,
    },
)

// 创建 Agent 并集成所有工具
agent := llmagent.New("ai-assistant",
    llmagent.WithModel(model),
    llmagent.WithInstruction("你是一个有帮助的AI助手,可以使用多种工具协助用户"),
    // 添加单个工具(Tool 接口)
    llmagent.WithTools([]tool.Tool{
        calculatorTool, timeTool, searchTool,
    }),
    // 添加工具集(ToolSet 接口)
    llmagent.WithToolSets([]tool.ToolSet{stdioToolSet, sseToolSet, streamableToolSet}),
)

工具过滤器

// 包含过滤器:只使用指定工具
includeFilter := mcp.NewIncludeFilter("get_weather", "get_news", "calculator")

// 排除过滤器:排除指定工具
excludeFilter := mcp.NewExcludeFilter("deprecated_tool", "slow_tool")

// 组合过滤器
combinedToolSet := mcp.NewMCPToolSet(
    connectionConfig,
    mcp.WithToolFilter(includeFilter),
)

并行工具执行

1
2
3
4
5
6
7
// 启用并行工具执行(可选,用于性能优化)
agent := llmagent.New("ai-assistant",
    llmagent.WithModel(model),
    llmagent.WithTools(tools),
    llmagent.WithToolSets(toolSets),
    llmagent.WithEnableParallelTools(true), // 启用并行执行
)

并行执行效果:

# 并行执行(启用时)
Tool 1: get_weather     [====] 50ms
Tool 2: get_population  [====] 50ms  
Tool 3: get_time       [====] 50ms
总时间: ~50ms(同时执行)

# 串行执行(默认)
Tool 1: get_weather     [====] 50ms
Tool 2: get_population       [====] 50ms
Tool 3: get_time                  [====] 50ms  
总时间: ~150ms(依次执行)

快速开始

环境准备

# 设置 API 密钥
export OPENAI_API_KEY="your-api-key"

简单示例

package main

import (
    "context"
    "fmt"

    "trpc.group/trpc-go/trpc-agent-go/runner"
    "trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
    "trpc.group/trpc-go/trpc-agent-go/model/openai"
    "trpc.group/trpc-go/trpc-agent-go/model"
    "trpc.group/trpc-go/trpc-agent-go/tool/function"
)

func main() {
    // 1. 创建简单工具
    calculatorTool := function.NewFunctionTool(
        func(ctx context.Context, req struct {
            Operation string  `json:"operation"`
            A         float64 `json:"a"`
            B         float64 `json:"b"`
        }) (map[string]interface{}, error) {
            var result float64
            switch req.Operation {
            case "add":
                result = req.A + req.B
            case "multiply":
                result = req.A * req.B
            default:
                return nil, fmt.Errorf("unsupported operation")
            }
            return map[string]interface{}{"result": result}, nil
        },
        function.WithName("calculator"),
        function.WithDescription("简单计算器"),
    )

    // 2. 创建模型和 Agent
    llmModel := openai.New("DeepSeek-V3-Online-64K")
    agent := llmagent.New("calculator-assistant",
        llmagent.WithModel(llmModel),
        llmagent.WithInstruction("你是一个数学助手"),
        llmagent.WithTools([]tool.Tool{calculatorTool}),
        llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}), // 启用流式输出
    )

    // 3. 创建 Runner 并执行
    r := runner.NewRunner("math-app", agent)

    ctx := context.Background()
    userMessage := model.NewUserMessage("请计算 25 乘以 4")

    eventChan, err := r.Run(ctx, "user1", "session1", userMessage)
    if err != nil {
        panic(err)
    }

    // 4. 处理响应
    for event := range eventChan {
        if event.Error != nil {
            fmt.Printf("错误: %s\n", event.Error.Message)
            continue
        }

        // 显示工具调用
        if len(event.Response.Choices) > 0 && len(event.Response.Choices[0].Message.ToolCalls) > 0 {
            for _, toolCall := range event.Response.Choices[0].Message.ToolCalls {
                fmt.Printf("🔧 调用工具: %s\n", toolCall.Function.Name)
                fmt.Printf("   参数: %s\n", string(toolCall.Function.Arguments))
            }
        }

        // 显示流式内容
        if len(event.Response.Choices) > 0 {
            fmt.Print(event.Response.Choices[0].Delta.Content)
        }

        if event.Done {
            break
        }
    }
}

运行示例

# 进入工具示例目录
cd examples/tool
go run .

# 进入 MCP 工具示例目录  
cd examples/mcp_tool

# 启动外部服务器
cd streamalbe_server && go run main.go &

# 运行主程序
go run main.go -model="deepseek-chat"

总结

Tool 工具系统为 tRPC-Agent-Go 提供了丰富的扩展能力,支持函数工具、DuckDuckGo 搜索工具和 MCP 协议工具。