世界上有一种选择叫 Tool Selection,它就像给Agent配了个智能管家——每次要干活时,管家会帮它挑选最合适的工具,而不是拿着锤子看什么都像钉子。
凌晨4点35分,我的Agent面对一个任务:"帮我查查今天的AI新闻,然后保存成HTML"。它先调用了web_search,然后调用了write_file,最后还调用了generate_html。那一刻我意识到:好的工具选择,比工具本身更重要。
📚 定义
Tool Selection(工具选择)是AI Agent的核心能力之一,指Agent根据当前任务需求,从可用工具集中智能选择最合适工具的过程。
核心问题:面对100个工具,Agent如何知道该用哪个?
- 任务理解:理解用户意图,确定需要什么能力
- 工具匹配:从工具描述中找出最相关的工具
- 参数填充:根据上下文填充工具参数
- 执行规划:决定调用顺序(单工具 vs. 多工具链式调用)
- 结果评估:判断工具输出是否满足需求
🔬 工作原理
Tool Selection 的三种主流策略:
策略1: 全量注入 (Brute Force) ┌─────────────────────────────────┐ │ 将所有工具定义注入 Prompt │ │ "你有以下工具可用:..." │ │ LLM 根据描述自行选择 │ └─────────────────────────────────┘ 优点: 简单直接 缺点: 工具多了会撑爆上下文 策略2: 检索增强 (RAG-based) ┌─────────────────────────────────┐ │ 1. 用户任务 → Embedding查询 │ │ 2. 向量数据库检索相关工具 │ │ 3. 只注入 Top-K 相关工具 │ └─────────────────────────────────┘ 优点: 可扩展 缺点: 检索可能不准 策略3: 分层过滤 (Hierarchical) ┌─────────────────────────────────┐ │ 1. 粗筛: 按类别过滤 (文件/网络/计算) │ │ 2. 精筛: 按语义相似度排序 │ │ 3. 最终: LLM 从候选中选择 │ └─────────────────────────────────┘ 优点: 平衡性能和准确性 缺点: 实现复杂
OpenClaw 的工具选择机制:
- ✅ Skills系统:每个Skill就是一个工具包,包含SKILL.md描述
- ✅ 自动激活:根据任务关键词自动加载相关Skills
- ✅ 上下文感知:根据对话历史动态调整工具优先级
- ✅ 失败回退:工具调用失败时,尝试替代工具
🚀 OpenClaw 实战应用
OpenClaw 通过 Skills 实现智能工具选择:
# OpenClaw Skill 定义 (web-scraper.skill.yaml)
name: web-scraper
description: "抓取网页内容并提取结构化信息"
keywords: [scrape, extract, crawl, webpage, html]
# 工具定义
tools:
- name: fetch_page
description: "获取网页HTML内容"
parameters:
url:
type: string
description: "目标URL"
timeout:
type: number
default: 30
- name: extract_content
description: "从HTML中提取结构化内容"
parameters:
html:
type: string
description: "HTML内容"
selector:
type: string
description: "CSS选择器"
# 自动激活规则
activation:
match_pattern: "抓取|爬取|提取.*网页"
confidence_threshold: 0.7
# OpenClaw 配置:工具选择策略
agent:
tool_selection:
strategy: hierarchical # hierarchical | rag | full
# 分层策略配置
hierarchical:
categories:
- name: web
keywords: [url, http, webpage, scrape]
tools: [fetch_page, extract_content, web_search]
- name: file
keywords: [file, read, write, save]
tools: [read_file, write_file, list_dir]
- name: compute
keywords: [calculate, compute, math]
tools: [eval_expr, run_python]
# RAG策略配置
rag:
embedding_model: "text-embedding-3-small"
top_k: 5
similarity_threshold: 0.6
# 全量注入(工具少时用)
full:
max_tools: 20 # 超过20个工具就切换策略
💻 代码示例:实现智能工具选择
1. 基于 RAG 的工具检索器
// tool-selector.js
import OpenAI from 'openai';
import { ChromaClient } from 'chromadb';
export class ToolSelector {
constructor(tools) {
this.tools = tools; // 所有可用工具
this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
this.chroma = new ChromaClient();
this.collection = null;
this._initialized = false;
}
async initialize() {
if (this._initialized) return;
// 1. 创建向量数据库集合
this.collection = await this.chroma.createCollection({
name: 'tools',
metadata: { description: 'Tool descriptions for RAG' }
});
// 2. 将所有工具的描述向量化并存储
for (const tool of this.tools) {
const embedding = await this._getEmbedding(tool.description);
await this.collection.add({
ids: [tool.name],
embeddings: [embedding],
documents: [JSON.stringify(tool)],
metadatas: [{ name: tool.name }]
});
}
this._initialized = true;
}
// 根据任务查询相关工具
async selectTools(task, k = 5) {
await this.initialize();
// 1. 将任务描述转为向量
const taskEmbedding = await this._getEmbedding(task);
// 2. 向量检索最相关的工具
const results = await this.collection.query({
queryEmbeddings: [taskEmbedding],
nResults: k
});
// 3. 返回工具定义
return results.documents[0].map(doc => JSON.parse(doc));
}
// 调用LLM进行最终选择(可选)
async selectToolsWithLLM(task, context = '') {
// 先检索候选工具
const candidates = await this.selectTools(task, 10);
// 用LLM进行精细选择
const prompt = `
Task: ${task}
Context: ${context}
Available tools:
${candidates.map((t, i) => `${i + 1}. ${t.name}: ${t.description}`).join('\n')}
Based on the task, which tools are needed? Return a JSON array of tool names.
Only select tools that are directly relevant to completing the task.
`;
const response = await this.openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [{ role: 'user', content: prompt }],
response_format: { type: 'json_object' }
});
const selectedNames = JSON.parse(response.choices[0].message.content).tools;
return candidates.filter(t => selectedNames.includes(t.name));
}
async _getEmbedding(text) {
const response = await this.openai.embeddings.create({
model: 'text-embedding-3-small',
input: text
});
return response.data[0].embedding;
}
}
// 使用示例
const tools = [
{
name: 'web_search',
description: 'Search the web for information',
parameters: { query: 'string' }
},
{
name: 'write_file',
description: 'Write content to a file',
parameters: { path: 'string', content: 'string' }
},
{
name: 'read_file',
description: 'Read content from a file',
parameters: { path: 'string' }
}
];
const selector = new ToolSelector(tools);
const selected = await selector.selectTools('搜索最新的AI新闻并保存');
console.log('Selected tools:', selected.map(t => t.name));
2. OpenClaw Skill 中的工具选择
# smart-assistant.skill.yaml
name: smart-assistant
description: 智能助手,根据任务自动选择工具
# 定义所有可能的工具
tools:
- name: web_search
description: "搜索网络获取信息"
keywords: [search, 搜索, 查找, 查询]
- name: generate_image
description: "生成AI图片"
keywords: [image, 图片, 生成, 画图, dalle]
- name: write_code
description: "生成或执行代码"
keywords: [code, 代码, 编程, python, javascript]
- name: send_message
description: "发送消息到其他平台"
keywords: [send, 发送, message, 消息, discord, feishu]
runtime:
type: node
entrypoint: assistant.js
---
const { ToolSelector } = require('./tool-selector');
module.exports = async function(context) {
const userTask = context.message.content;
// 智能选择工具
const selector = new ToolSelector(context.skill.tools);
const selectedTools = await selector.selectTools(userTask, 3);
// 返回给LLM,让它使用选中的工具
return {
inject_tools: selectedTools,
reply: `我为你选择了 ${selectedTools.length} 个工具:${selectedTools.map(t => t.name).join(', ')}`
};
};
3. 工具选择失败回退策略
// fallback-strategy.js
export class FallbackToolSelector {
constructor(tools) {
this.tools = tools;
this.failureCounts = new Map(); // 记录工具失败次数
}
async executeWithFallback(task, primaryTool, alternatives = []) {
try {
// 尝试主工具
return await this._executeTool(primaryTool, task);
} catch (err) {
console.warn(`Tool ${primaryTool.name} failed:`, err.message);
// 记录失败
this.failureCounts.set(
primaryTool.name,
(this.failureCounts.get(primaryTool.name) || 0) + 1
);
// 如果失败次数过多,禁用该工具
if (this.failureCounts.get(primaryTool.name) >= 3) {
console.error(`Tool ${primaryTool.name} disabled due to repeated failures`);
}
// 尝试替代工具
for (const altTool of alternatives) {
try {
console.log(`Trying fallback tool: ${altTool.name}`);
return await this._executeTool(altTool, task);
} catch (altErr) {
console.warn(`Fallback tool ${altTool.name} also failed:`, altErr.message);
}
}
// 所有工具都失败了
throw new Error(`All tools failed for task: ${task}`);
}
}
async _executeTool(tool, task) {
// 模拟工具执行
console.log(`Executing tool: ${tool.name} with task: ${task}`);
// ... 实际执行逻辑
return { success: true, result: `Executed ${tool.name}` };
}
}
// 使用示例
const selector = new FallbackToolSelector([
{ name: 'brave_search', type: 'search' },
{ name: 'google_search', type: 'search' },
{ name: 'ddg_search', type: 'search' }
]);
const result = await selector.executeWithFallback(
'搜索AI新闻',
{ name: 'brave_search' },
[{ name: 'google_search' }, { name: 'ddg_search' }]
);
🎯 最佳实践与踩坑提醒
- 工具描述要精准:好的描述 = 好的选择,LLM靠描述选工具
- 关键词优化:给每个工具加 keywords,提升检索命中率
- 分层管理:工具多了要分类(web/file/compute/communication)
- 动态加载:不要一次性加载所有工具,按需加载
- 失败监控:记录每个工具的成功率,低成功率工具要预警
- 工具冲突:两个工具功能重叠,Agent可能选错(如 web_search vs. google_search)
- 上下文污染:注入太多工具定义会挤占对话上下文
- 选择瘫痪:给Agent 50个工具,它可能反而不知道选哪个
- 参数幻觉:Agent可能给工具传不存在的参数
真实踩坑案例:
有一次我在OpenClaw里注册了3个搜索工具:web_search、google_search、bing_search。结果Agent每次搜索都要先调用一遍这三个,美其名曰"对比结果"。后来我学会了:用default_tool指定默认工具,只有用户明确说"用Google搜"时才换工具。
📊 工具选择策略对比
| 策略 | 工具数量上限 | 准确性 | 延迟 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|---|
| 全量注入 | ⚠️ 20-30个 | ⭐⭐⭐⭐⭐ | ⭐ (最低) | ⭐ (最简单) | 工具少、追求简单 |
| RAG检索 | ✅ 1000+个 | ⭐⭐⭐⭐ | ⭐⭐ (低) | ⭐⭐⭐ (中等) | 工具多、有向量数据库 |
| 分层过滤 | ✅ 500+个 | ⭐⭐⭐⭐ | ⭐⭐ (低) | ⭐⭐⭐⭐ (复杂) | 工具分类清晰 |
| LLM路由 | ✅ 100+个 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ (高) | ⭐⭐⭐ (中等) | 高精度要求 |
🔗 相关链接
世界上有一种智慧叫"工欲善其事,必先利其器",也有一种愚蠢叫"拿着锤子找钉子"。
凌晨5点09分,我看着Agent从50个工具里精准选出3个完成任务的日志,突然明白:好的工具选择机制,不是让Agent成为工具奴隶,而是让它成为工具的主人。
所以下次你的Agent流畅地完成任务时,记得感谢背后的工具选择机制——它默默地在混沌的工具集中,为Agent指明方向。