第十三章 工具分组:workspace/tools.json + toolFilter,按角色配置化限定工具
"我们希望"客服 agent"只能查订单和物流,"财务 agent"只能用支付相关工具。1.x 时代我们在 Java 端写一堆条件判断;2.0 用
workspace/tools.json+toolFilter把这套规则配置化:新增一个角色只需要在 JSON 里加一段,不需要改 Java 代码。"本章你将学到:如何写
workspace/tools.json、如何用toolFilter限定工具集、如何让 subagent 继承主 agent 的工具 / 屏蔽危险工具。
13.1 工具分组的两种方式
2.0 提供两套"工具分组"机制,可以单独用,也可以组合:
| 方式 | 配置位置 | 适用场景 |
|---|---|---|
Java 端 Toolkit |
HarnessAgent.builder().toolkit(toolkit) |
代码定义工具,编译即固定 |
Java 端 toolsConfig |
HarnessAgent.builder().toolsConfig(config) |
程序化 allow/deny,无需 JSON 文件 |
workspace tools.json |
workspace/tools.json |
运行时可改,多环境差异化 |
生产建议:基础工具集放 Java 端(
Toolkit),角色级过滤放toolsConfig(Java 端)或tools.json(文件端)。
13.2 第一个 tools.json
{
"tools": [
{
"name": "query_order",
"class": "demo.tools.OrderTool",
"description": "查订单"
},
{
"name": "query_logistics",
"class": "demo.tools.LogisticsTool",
"description": "查物流"
},
{
"name": "refund_order",
"class": "demo.tools.RefundTool",
"description": "退款"
}
]
}
HarnessAgent.builder().workspace(...) 启动时会自动扫描 workspace/tools.json 并把工具注册进 agent。但不传 classpath 时默认关闭——需要显式开:
HarnessAgent agent = HarnessAgent.builder()
...
.workspace(Path.of("./workspace"))
.loadWorkspaceTools(true) // 2.0 新增
.build();
工具类的 class 必须在 classpath 里能找到(通过反射
Class.forName(...)实例化),所以demo.tools.OrderTool这种业务类要保证在 agent 进程的 classpath 上。
13.3 角色级 toolFilter:allow / deny
一个 tools.json 可以同时定义工具列表和可见性规则:
{
"tools": [
{
"name": "query_order", "class": "demo.tools.OrderTool" },
{
"name": "query_logistics", "class": "demo.tools.LogisticsTool" },
{
"name": "refund_order", "class": "demo.tools.RefundTool" }
],
"toolFilter": {
"allow": ["query_order", "query_logistics"],
"deny": ["refund_order"]
}
}
行为:
allow白名单模式:只放行名字精确匹配的工具;其他一律不可见deny黑名单模式:名字匹配的工具被移除;其他都可见allow和deny同时存在:deny 优先(先 allow 再 deny)- 注意:使用精确工具名匹配,不支持通配符
HarnessAgent 启动时按 toolFilter 把工具列表过滤后再交给 LLM——LLM 看不到的工具连描述都不会出现在 prompt 里,避免"模型想调却调不到"。
13.4 给 subagent 单独配置 tools.json
每个 subagent 可以在 workspace/subagents/<id>.md 里声明自己的工具集:
# customer_service.md
id: customer_service
description: 客服 agent;只能查订单和物流,不能退款
toolsFile: customer_service.tools.json
sysPrompt: |
你是客服,只能查订单和物流,不能直接退款。
如需退款,转人工。
workspace/subagents/customer_service.tools.json:
{
"tools": [
{
"name": "query_order", "class": "demo.tools.OrderTool" },
{
"name": "query_logistics", "class": "demo.tools.LogisticsTool" }
],
"toolFilter": {
"deny": ["refund_order"]
}
}
13.5 在 Java 端过滤
不写 JSON 也行——直接用 ToolsConfig 对象:
ToolsConfig config = new ToolsConfig();
config.setAllow(List.of("query_order", "query_logistics"));
config.setDeny(List.of("refund_order"));
HarnessAgent agent = HarnessAgent.builder()
...
.toolsConfig(config)
.build();
ToolsConfig 与 tools.json 里的 toolFilter 字段语义完全一致。
13.6 工具权限的"四层防线"
生产环境推荐工具分组 + Permission 双保险:
第 1 层:工具注册(class 必须在 classpath)
第 2 层:toolFilter(allow / deny 精确匹配)
第 3 层:Permission rule(运行时再校验一次)
第 4 层:Middleware onModelCall / onActing(埋点 / 限流)
例:想让"退款"工具运行时必须经过人审批:
PermissionContextState perms = PermissionContextState.builder()
.mode(PermissionMode.ACCEPT_EDITS)
.addAskRule("refund_order",
new PermissionRule("refund_order", null, PermissionBehavior.ASK, "userSettings"))
.build();
HarnessAgent agent = HarnessAgent.builder()
.permissionContext(perms)
.build();
即使 LLM 想调 refund_order,Permission 也会拦下来问用户。
13.7 完整可运行示例
这个例子在演示什么?
我们注册 3 个工具:
query_order(查订单)、query_logistics(查物流)、refund_order(退款)。然后创建两个 agent:
- 客服 agent:
deny屏蔽refund_order→ 只能查订单和物流,不能退款- 财务 agent:
allow只放行refund_order→ 只能退款,看不到查询工具同一套工具,两个角色两个视野。新增角色只需要改
ToolsConfig,不改 Java 代码。
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.harness.agent.tools.ToolsConfig;
import io.agentscope.harness.HarnessAgent;
import java.util.List;
public class Chapter13_ToolFilter {
// 业务工具类:@Tool 注解标记方法名即工具名
public static class OrderTools {
@Tool(name = "query_order", description = "查询订单状态和详情")
public String queryOrder(String orderId) {
return "订单 " + orderId + ":已发货。";
}
}
public static class LogisticsTools {
@Tool(name = "query_logistics", description = "查询物流轨迹")
public String queryLogistics(String trackingId) {
return "物流 " + trackingId + ":快件在北京分拣中心。";
}
}
public static class RefundTools {
@Tool(name = "refund_order", description = "处理订单退款")
public String refundOrder(String orderId) {
return "订单 " + orderId + " 退款已处理。";
}
}
public static void main(String[] args) {
// 1. 全部工具注册到同一个 Toolkit
Toolkit baseToolkit = new Toolkit();
baseToolkit.registerTool(new OrderTools());
baseToolkit.registerTool(new LogisticsTools());
baseToolkit.registerTool(new RefundTools());
// 2. 客服 agent:deny "refund_order" — 看不到退款工具
ToolsConfig csConfig = new ToolsConfig();
csConfig.setDeny(List.of("refund_order"));
HarnessAgent customerService = HarnessAgent.builder()
.name("customer_service")
.sysPrompt("你是客服,只能查订单和物流。不能退款,如需退款请转人工。")
.model(model())
.workspace(Path.of("./workspace"))
.toolkit(baseToolkit)
.toolsConfig(csConfig)
.build();
// 3. 财务 agent:allow "refund_order" — 只能看到退款工具
ToolsConfig finConfig = new ToolsConfig();
finConfig.setAllow(List.of("refund_order"));
HarnessAgent finance = HarnessAgent.builder()
.name("finance")
.sysPrompt("你是财务,只处理退款。")
.model(model())
.workspace(Path.of("./workspace"))
.toolkit(baseToolkit)
.toolsConfig(finConfig)
.build();
// 问客服"订单 123 的状态"——能答
// 问客服"退款"——会说"请转人工"
// 问财务"订单 123 的状态"——看不到 query 工具
// 问财务"退款"——正常处理
}
}
关键:
ToolsConfig的allow/deny在HarnessAgent.build()里被ToolFilter.apply()执行——从Toolkit中物理删除对应工具。LLM 的 prompt 里根本不会出现被屏蔽工具的描述,不存在"知道但不能用"的半吊子状态。
13.8 本章小结
workspace/tools.json让工具配置离开 Java 代码,运行时可改。toolFilter用allow/deny+ 精确工具名限定角色可调工具。- subagent 可声明自己的
toolsFile单独配工具集。 - 工具权限推荐四层防线:注册 → toolFilter → Permission → Middleware。