Skip to content

Meta-Tool 模式

Meta-Tool 模式是 Xiuxian 模块的核心设计,LLM 不直接调用业务工具,而是通过 3 个 Meta-Tool 间接调用。

为什么需要 Meta-Tool

直接调用的问题

如果让 LLM 直接调用所有业务工具:

用户消息 → LLM → 直接调用业务工具

存在的问题

  1. 工具数量爆炸:几十个工具全部暴露给 LLM,选择困难
  2. 参数格式不确定:LLM 可能传错参数类型
  3. 复杂任务难编排:多步骤任务需要多次对话
  4. 权限控制分散:每个工具都要处理权限逻辑

Meta-Tool 的解决方案

引入 3 个 Meta-Tool 作为中间层:

用户消息 → LLM → Meta-Tool → 业务工具

优势

问题Meta-Tool 解决方案
工具数量爆炸只暴露 3 个 Meta-Tool,LLM 选择简单
参数格式不确定getToolSchema 返回详细参数格式
复杂任务难编排getSkillGuide 返回完整执行步骤
权限控制分散统一在 executeTool 中处理

三个 Meta-Tool

1. getSkillGuide

获取技能的详细执行指南。

用途:当用户请求匹配某个技能时,获取该技能的完整执行步骤。

java
@Tool(description = "获取技能的详细执行指南,包含每个步骤的工具名称和参数格式")
public String getSkillGuide(@ToolParam(description = "技能ID") String skillId) {
    return capabilityRegistry.getSkillGuide(skillId);
}

返回示例

markdown
# 功法推荐

根据修士的境界、灵根、身份,推荐最适合修炼的功法。

## 执行步骤

### 步骤1:查询修士信息
调用工具:`query_user_detail`

【工具信息】
名称:query_user_detail
描述:查询修士的详细信息
权限要求:峰主

【参数说明】
{
  "type": "object",
  "properties": {
    "userName": {
      "type": "string",
      "description": "修士的道号或姓名"
    }
  }
}

### 步骤2:查询功法列表
...

2. getToolSchema

获取指定工具的详细参数格式。

用途:当 LLM 知道要调用哪个工具,但不确定参数格式时使用。

java
@Tool(description = "获取指定工具的详细参数格式")
public String getToolSchema(@ToolParam(description = "工具名称") String toolName) {
    return capabilityRegistry.getToolSchema(toolName);
}

返回示例

【工具信息】
名称:page_users
描述:分页查询修士列表
权限要求:弟子

【参数说明】
{
  "type": "object",
  "properties": {
    "name": { "type": "string", "description": "姓名(模糊匹配)" },
    "realm": { "type": "string", "description": "境界" },
    "spiritRootType": { "type": "string", "description": "灵根类型" },
    "pageNum": { "type": "integer", "description": "页码,默认1" },
    "pageSize": { "type": "integer", "description": "每页数量,默认20" }
  }
}

3. executeTool

执行指定的工具。

用途:实际调用业务工具并返回结果。

java
@Tool(description = "执行指定的工具")
public String executeTool(
        @ToolParam(description = "工具名称") String toolName,
        @ToolParam(description = "执行参数,JSON对象格式", required = false) Map<String, Object> parameters,
        ToolContext toolContext) {

    // 从 toolContext 获取 AgentContext
    AgentContext ctx = (AgentContext) toolContext.getContext().get(AGENT_CONTEXT_KEY);

    // 设置到 ThreadLocal,供工具类使用
    AgentContextHolder.setContext(ctx);

    try {
        return capabilityRegistry.executeTool(toolName, parameters);
    } finally {
        AgentContextHolder.clear();
    }
}

工作流程

简单查询场景

复杂任务场景

System Prompt 设计

关键要素:

## Meta-Tool(唯一正确的调用方式)

你**只能**通过以下工具来操作:

1. **getSkillGuide** - 获取技能的详细执行指南
2. **getToolSchema** - 获取某个工具的详细参数格式
3. **executeTool** - 执行指定的工具

## 工作流程

1. **分析意图**:判断用户请求是否匹配某个技能
2. **匹配技能**:如果匹配技能,调用 getSkillGuide 获取详细步骤
3. **获取参数**:如果不匹配技能,调用 getToolSchema 获取工具参数格式
4. **执行工具**:调用 executeTool 并传入正确的参数
5. **回复用户**:用修仙世界的语气向用户汇报执行结果

## 严格禁止

- ❌ **禁止直接调用任何列出的工具**
- ❌ 禁止在回复中假设工具执行结果
- ❌ 禁止编造参数值,参数不足时必须询问用户

权限控制

Meta-Tool 模式下,权限控制集中在 executeTool 中:

java
public String executeTool(String toolName, Map<String, Object> parameters) {
    // 1. 获取工具所需权限
    XiuxianRole requiredRole = toolRoles.get(toolName);

    // 2. 获取当前用户权限
    AgentContext ctx = AgentContextHolder.getContext();
    XiuxianRole userRole = ctx.getRole();

    // 3. 校验权限
    if (!hasPermission(userRole, requiredRole)) {
        return "权限不足:需要 " + requiredRole.getName() + " 权限";
    }

    // 4. 执行工具
    return toolCallbacks.get(toolName).call(parameters);
}

最佳实践

1. 技能优先

复杂任务优先匹配技能,让 LLM 按步骤执行,减少出错概率。

2. 参数校验

工具执行前校验参数,返回友好的错误提示:

java
if (!StringUtils.hasText(userName)) {
    return error("请提供修士的道号或姓名");
}

3. 进度通知

通过 AgentContext 发送执行进度,改善用户体验:

java
notifyProgress("正在查阅【%s】的基本信息...", userName);
// 执行查询...
notifyProgress("成功查阅到【%s】的基本信息", user.getName());

4. 结果格式化

使用模板渲染工具输出,保持格式统一:

java
private static final String USER_INFO = """
        ### 【${name}】修士信息

        | 属性 | 值 |
        |------|------|
        | 境界 | ${realm} |
        | 灵根类型 | ${spiritRootType} |
        ...
        """;

return render(USER_INFO, params);

下一步