【AgentScope Java新手村系列】(3)工具系统

简介: 工具系统 — @Tool/@ToolParam 注解将 Java 方法注册为 Agent 能力,自主决定调用时机,支持同步/异步返回。

第三章 工具系统与 @Tool 注解:让 Agent 自主决定调用哪个 Java 方法

"没有工具的 Agent 只能'纸上谈兵'。本章演示如何用 @Tool 注解把任意 Java 方法注册为 Agent 可调用的能力,Agent 在 ReAct 循环中自主决定何时调用。"

3.1 什么是工具

工具(Tool)是 Agent 与外部世界交互的能力。通过给 Agent 配备工具,它可以:

  • 查询数据库
  • 调用 API
  • 执行计算
  • 读写文件
  • 搜索网络

Agent 在推理过程中会自主决定是否需要调用工具,以及调用哪个工具。

3.2 @Tool 注解

使用 @Tool 注解将 Java 方法注册为 Agent 可调用的工具:

import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;

public class MyTools {
   

    @Tool(name = "get_current_time", description = "获取指定时区的当前时间")
    public String getCurrentTime(
            @ToolParam(name = "timezone", description = "时区名称,例如 'Asia/Shanghai'")
            String timezone) {
   
        // 实现逻辑
        return "Current time in " + timezone + ": 2024-01-01 12:00:00";
    }
}

关键点:

  • @Toolname 属性是工具的唯一标识,Agent 调用时使用此名称
  • @Tooldescription 属性描述工具的功能,Agent 根据此描述决定何时调用
  • @ToolParam 标注在方法参数上,描述参数的含义
  • 方法的返回值会作为工具的执行结果返回给 Agent

2.0 完全兼容 1.x 的 @Tool / @ToolParam 注解与 Toolkit.registerTool(...) 注册方式。HarnessAgent 在工作区模式下还支持通过 workspace/tools.json 声明 MCP server 和工具白名单——详见第十五章。

3.3 注册工具

创建 Toolkit 实例,将工具对象注册进去:

Toolkit toolkit = new Toolkit();
toolkit.registerTool(new MyTools());

然后把 Toolkit 传给 Agent:

import io.agentscope.core.ReActAgent;
import io.agentscope.core.tool.Toolkit;

// 纯 ReActAgent 场景
ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .sysPrompt("你是一个可以使用工具的助手。")
        .model(model)
        .toolkit(toolkit)
        .build();

// 或 HarnessAgent 场景
HarnessAgent agent = HarnessAgent.builder()
        .name("Assistant")
        .sysPrompt("你是一个可以使用工具的助手。")
        .model(model)
        .workspace(Path.of("./workspace"))
        .toolkit(toolkit)               // 工具集可以和工作区并存
        .build();

3.4 完整示例:带工具的 Agent

package com.example;

import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.formatter.openai.OpenAIChatFormatter;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.model.OpenAIChatModel;
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;
import io.agentscope.core.tool.Toolkit;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class ToolCallingExample {
   

    public static void main(String[] args) {
   
        String apiKey = System.getenv("DEEPSEEK_API_KEY");

        // 创建工具集并注册工具
        Toolkit toolkit = new Toolkit();
        toolkit.registerTool(new SimpleTools());

        ReActAgent agent = ReActAgent.builder()
                .name("ToolAgent")
                .sysPrompt(
                        "你是一个可以使用工具的助手。"
                        + "在需要时使用工具来准确回答问题。"
                        + "每次使用工具时请解释你在做什么。")
                .model(OpenAIChatModel.builder()
                        .apiKey(apiKey)
                        .modelName("deepseek-reasoner")
                        .baseUrl("https://api.deepseek.com")
                        .stream(true)
                        .formatter(new OpenAIChatFormatter())
                        .build())
                .toolkit(toolkit)
                .build();

        // 测试工具调用
        String reply = agent.call(new UserMessage("现在北京几点了?"), RuntimeContext.empty())
                .block()
                .getTextContent();
        System.out.println(reply);
    }

    /**
     * 工具类:每个带 @Tool 注解的方法都会被注册为一个工具
     */
    public static class SimpleTools {
   

        @Tool(name = "get_current_time",
              description = "获取指定时区的当前时间")
        public String getCurrentTime(
                @ToolParam(name = "timezone",
                           description = "时区名称,例如 'Asia/Shanghai'、'America/New_York'")
                String timezone) {
   
            try {
   
                ZoneId zoneId = ZoneId.of(timezone);
                LocalDateTime now = LocalDateTime.now(zoneId);
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                return String.format("Current time in %s: %s", timezone, now.format(formatter));
            } catch (Exception e) {
   
                return "Error: Invalid timezone. Try 'Asia/Shanghai' or 'America/New_York'";
            }
        }

        @Tool(name = "calculate",
              description = "计算简单的数学表达式")
        public String calculate(
                @ToolParam(name = "expression",
                           description = "要计算的数学表达式,例如 '123 + 456'、'10 * 20'")
                String expression) {
   
            try {
   
                expression = expression.replaceAll("\\s+", "");
                double result;
                if (expression.contains("+")) {
   
                    String[] parts = expression.split("\\+");
                    result = Double.parseDouble(parts[0]) + Double.parseDouble(parts[1]);
                } else if (expression.contains("-")) {
   
                    String[] parts = expression.split("-");
                    result = Double.parseDouble(parts[0]) - Double.parseDouble(parts[1]);
                } else if (expression.contains("*")) {
   
                    String[] parts = expression.split("\\*");
                    result = Double.parseDouble(parts[0]) * Double.parseDouble(parts[1]);
                } else if (expression.contains("/")) {
   
                    String[] parts = expression.split("/");
                    result = Double.parseDouble(parts[0]) / Double.parseDouble(parts[1]);
                } else {
   
                    return "Error: Unsupported operation. Use +, -, *, or /";
                }
                return String.format("%s = %.2f", expression, result);
            } catch (Exception e) {
   
                return "Error: Invalid expression. Example: '123 + 456'";
            }
        }

        @Tool(name = "search",
              description = "在网络上搜索信息")
        public String search(
                @ToolParam(name = "query", description = "搜索关键词")
                String query) {
   
            // 这里模拟搜索结果,实际项目中可以接入真实搜索 API
            return "Search results for '" + query + "':\n"
                    + "1. Result about " + query + "\n"
                    + "2. More information on " + query;
        }
    }
}

运行后,向 Agent 提问"现在北京几点了?",Agent 会:

  1. 推理:需要获取北京时间
  2. 决定调用 get_current_time 工具,参数为 Asia/Shanghai
  3. 执行工具,获取结果
  4. 将结果整理后返回给用户

3.5 工具的返回类型

工具方法的返回值会自动转换为字符串传给 Agent。支持以下返回类型:

// 1. 直接返回字符串
@Tool(name = "echo")
public String echo(String input) {
   
    return "Echo: " + input;
}

// 2. 返回对象(自动序列化为 JSON)
@Tool(name = "get_user")
public Map<String, Object> getUser(String userId) {
   
    return Map.of("id", userId, "name", "Alice", "age", 30);
}

// 3. 返回 void(Agent 会收到空结果)
@Tool(name = "log")
public void log(String message) {
   
    System.out.println("[LOG] " + message);
}

// 4. 异步返回 Mono<ToolResultBlock>
@Tool(name = "async_task")
public Mono<ToolResultBlock> asyncTask(String input) {
   
    return Mono.fromCallable(() -> ToolResultBlock.text("Async result: " + input));
}

3.6 工具分组

当工具较多时,可以使用工具分组(Tool Group)来管理。2.0 继续支持 1.x 的元工具机制:

Toolkit toolkit = new Toolkit();

// 注册工具到不同分组
toolkit.registration()
        .tool(new WeatherTools())
        .group("weather")
        .apply();

toolkit.registration()
        .tool(new MathTools())
        .group("math")
        .apply();

// 创建工具分组(默认全部激活)
toolkit.createToolGroup("weather", "天气工具", true);
toolkit.createToolGroup("math", "数学工具", true);

// 注册元工具,让 Agent 可以自主切换工具组
toolkit.registerMetaTool();

注册元工具后,Agent 会获得一个 reset_equipped_tools 工具,可以自主激活/停用工具组。这在工具数量很多时非常有用,可以减少发送给 LLM 的工具描述数量。

3.7 子 Agent 作为工具(1.x 风格)

1.x 的 toolkit.registration().subAgent(...) 在 2.0 仍可用(兼容层),但新代码请用子 agent 系统:把子 agent spec 写到 workspace/subagents/<id>.md,主 agent 就能在推理时通过 agent_spawn 委派——这是 Harness 的内置能力,不需要"把 agent 当工具注册"。详见第七章。

下面是 1.x 风格的兼容写法(不推荐新代码使用):

// 1.x:把专家 Agent 注册为主 Agent 的工具
Toolkit mainToolkit = new Toolkit();
mainToolkit.registration()
        .subAgent(() -> expertAgent)
        .apply();

下面是 2.0 推荐写法:

<!-- workspace/subagents/data-analyst.md -->
---
description: 数据分析专家。当用户要做统计分析、可视化、数据清洗时使用。
model: openai:gpt-4o-mini
---

你是一个数据分析专家。请按以下流程工作:
1. 先用 read_file / grep_files 收集数据
2. 做必要的统计与可视化
3. 给出业务结论

主 agent 在推理时直接 agent_spawn agent_id="data-analyst" task="...",框架自动加载并执行子 agent,结果以 TOOL_RESULT 块回给主 agent。

3.8 工具描述的重要性

工具的 namedescription 是 Agent 决定是否调用工具的唯一依据。好的描述应该:

  1. 明确功能边界:说明工具能做什么,不能做什么
  2. 包含使用场景:什么时候应该调用这个工具
  3. 参数说明清晰:每个参数的含义、格式、取值范围

反面示例:

@Tool(name = "do_stuff", description = "做事情")
public String doStuff(String input) {
    ... }

正面示例:

@Tool(name = "get_weather",
      description = "获取指定城市的当前天气信息。"
              + "返回温度、湿度和天气状况。"
              + "当用户询问某个地点的天气时使用此工具。")
public String getWeather(
        @ToolParam(name = "city",
                   description = "城市英文名称,例如 'Beijing'、'New York'")
        String city) {
    ... }

3.9 工具执行配置

可以为工具执行配置超时和重试:

import io.agentscope.core.model.ExecutionConfig;

ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .model(model)
        .toolkit(toolkit)
        .toolExecutionConfig(ExecutionConfig.builder()
                .timeout(Duration.ofSeconds(30))
                .maxRetries(2)
                .build())
        .build();

3.10 2.0 增量:工具在 Harness 中的可见性

HarnessAgent 默认会把 Toolkit 里的所有工具暴露给 LLM——和 1.x 行为一致。如果你想做工具粒度的允许/拒绝,2.0 多了两条更精细的路径:

  1. workspace/tools.json:声明 MCP server + 工具粒度白名单/黑名单(推荐)
  2. Middleware:在 onActing / onModelCall 钩子里改写工具列表
// workspace/tools.json
{
   
  "mcpServers": [
    {
   
      "name": "github",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
    "GITHUB_TOKEN": "${env:GITHUB_TOKEN}" },
      "enabled": true
    }
  ],
  "toolFilter": {
   
    "mode": "allowlist",
    "tools": ["read_file", "write_file", "grep_files", "github__*"]
  }
}

RC2 新增ToolCallParam.builder(original) 可以从已有参数创建副本并覆盖特定字段:

ToolCallParam param = ToolCallParam.builder(original)
        .input(newInput)
        .build();

这在 Middleware 或 Hook 中需要拦截并修改工具调用参数时非常有用——不用从头构建整个 ToolCallParam

3.11 实际应用场景

工具系统可以实现各种能力:

场景 工具示例
信息查询 天气查询、股票查询、知识库搜索
数据处理 数据库查询、文件读写、格式转换
外部交互 发送邮件、创建日程、调用第三方 API
计算推理 数学计算、统计分析、数据可视化
系统操作 创建文件、执行命令、管理进程

在实际项目中,工具通常是与业务系统集成的桥梁。Agent 通过工具获取实时数据、执行操作,从而完成复杂的任务。

目录
相关文章
|
21小时前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
7507 32
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
21小时前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
643 142
|
21小时前
|
人工智能 缓存 自然语言处理
阿里Qwen3.7-Max评测:Agent能力显著提升,耗时与调用成本大幅下降
阿里云百炼推出面向智能体的旗舰大模型Qwen3.7-Max,具备长周期自主执行能力,显著提升编程、办公自动化等复杂任务处理水平;支持MCP集成与多框架兼容,并以限时5折+100万Tokens免费试用大幅降低使用门槛,助力企业高效落地AI应用。在阿里云百炼平台快速体验:https://t.aliyun.com/U/fPVHqY
|
21小时前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
1262 2
|
21小时前
|
人工智能 弹性计算 运维
阿里云发布堡垒机智能运维Agent,运维交互进入自然语言新时代
支持自然语言运维,提升效率与安全双保障。
1168 1
|
21小时前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1316 4
|
21小时前
|
人工智能 运维 JavaScript
阿里云Qoder CN(原通义灵码)全解析 产品形态、版本划分与技术适配说明
在AI辅助开发与智能办公工具持续普及的当下,阿里云旗下原通义灵码正式更名为Qoder CN,同时延伸出QoderWork CN、Qoder CN CLI、Qoder CN Mobile等多款配套产品,形成覆盖代码开发、日常办公、终端交互、移动端使用的完整工具矩阵。Qoder CN核心定位为AI智能编码助手,深度适配主流代码编辑器、集成开发环境以及终端场景;QoderWork CN则偏向桌面端综合办公辅助,二者面向不同使用场景,划分了多个版本档位,搭配差异化资源配额、功能权限与计费规则,同时兼容多款主流大模型。
395 4
|
21小时前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
344 1
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
21小时前
|
存储 安全 Java
AgentScope Java 2.0:打造分布式、企业级智能体底座
AgentScope 2.0 面向分布式部署、稳定运行、权限安全等企业级需求全面升级,打造支持多租户隔离与长期稳定运行的企业级智能体底座。
|
21小时前
|
人工智能 运维 API
2026年阿里云百炼通义千问Qwen3.7-plus深度介绍 功能特性、使用优势及618大促订阅方案指南
大模型技术的普及,让AI能力逐步融入个人办公、内容创作、代码编写、企业运营、教育培训等各类场景。不同定位的模型对应不同使用需求,旗舰级模型性能强劲但使用成本偏高,轻量化模型价格低廉却难以胜任复杂任务,而介于两者之间的中端主力模型,凭借均衡的能力、亲民的定价、广泛的场景适配性,成为绝大多数个人用户、小型团队、中小企业的首选。
462 1

热门文章

最新文章