Meta-Tool 模式
Meta-Tool 模式是 Xiuxian 模块的核心设计,LLM 不直接调用业务工具,而是通过 3 个 Meta-Tool 间接调用。
为什么需要 Meta-Tool
直接调用的问题
如果让 LLM 直接调用所有业务工具:
用户消息 → LLM → 直接调用业务工具存在的问题:
- 工具数量爆炸:几十个工具全部暴露给 LLM,选择困难
- 参数格式不确定:LLM 可能传错参数类型
- 复杂任务难编排:多步骤任务需要多次对话
- 权限控制分散:每个工具都要处理权限逻辑
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);