feat: 实现 Tool Search Tool 动态工具发现机制

- 新增 ToolRegistry 工具注册表,支持核心工具和延迟加载工具分离
- 新增 tool_search 元工具,支持关键词搜索发现可用工具
- 新增基于关键词的搜索算法,按相关度评分排序
- 为所有工具添加 metadata(分类、关键词、延迟加载标识)
- 修改 Agent 支持动态工具注入,tool_search 结果自动添加到可用工具
- 核心工具(tool_search, bash)始终加载,其他工具按需发现
This commit is contained in:
2025-12-10 19:51:25 +08:00
parent e435b2f8f8
commit bc1ece3dad
19 changed files with 569 additions and 66 deletions
+121 -22
View File
@@ -1,8 +1,16 @@
import { createAnthropic } from '@ai-sdk/anthropic';
import { createDeepSeek } from '@ai-sdk/deepseek';
import { generateText, streamText, stepCountIs, type ModelMessage, type Tool as AITool, type LanguageModel } from 'ai';
import {
generateText,
streamText,
stepCountIs,
type ModelMessage,
type Tool as AITool,
type LanguageModel,
} from 'ai';
import type { Tool, ToolResult, Message, AgentConfig, ProviderType } from '../types/index.js';
import { buildZodSchema } from '../types/index.js';
import { ToolRegistry } from '../tools/registry.js';
// Provider 工厂函数类型
type ProviderFactory = (apiKey: string) => (model: string) => LanguageModel;
@@ -22,9 +30,17 @@ const providers: Record<ProviderType, ProviderFactory> = {
export class Agent {
private getModel: (model: string) => LanguageModel;
private config: AgentConfig;
private tools: Map<string, Tool> = new Map();
private conversationHistory: ModelMessage[] = [];
// 工具注册表
private registry: ToolRegistry | null = null;
// 已发现的工具(通过 tool_search 发现的)
private discoveredTools: Set<string> = new Set();
// 兼容旧模式:直接注册的工具
private legacyTools: Map<string, Tool> = new Map();
constructor(config: AgentConfig) {
this.config = config;
@@ -35,30 +51,67 @@ export class Agent {
this.getModel = providerFactory(config.apiKey);
}
// 注册单个工具
registerTool(customTool: Tool): void {
this.tools.set(customTool.name, customTool);
/**
* 设置工具注册表(新模式:支持动态工具发现)
*/
setRegistry(registry: ToolRegistry): void {
this.registry = registry;
}
// 批量注册工具
/**
* 注册单个工具(兼容旧代码)
*/
registerTool(customTool: Tool): void {
this.legacyTools.set(customTool.name, customTool);
}
/**
* 批量注册工具(兼容旧代码)
*/
registerTools(tools: Tool[]): void {
for (const tool of tools) {
this.tools.set(tool.name, tool);
this.legacyTools.set(tool.name, tool);
}
}
// 将自定义工具转换为 Vercel AI SDK 的工具格式
/**
* 获取当前可用的工具
* - 如果使用 registry 模式:返回核心工具 + 已发现的工具
* - 如果使用旧模式:返回所有注册的工具
*/
private getAvailableTools(): Tool[] {
if (this.registry) {
// 新模式:核心工具 + 已发现的工具
const coreTools = this.registry.getCoreTools();
const discoveredTools = this.registry.getTools([...this.discoveredTools]);
return [...coreTools, ...discoveredTools];
} else {
// 旧模式:返回所有注册的工具
return [...this.legacyTools.values()];
}
}
/**
* 将工具转换为 Vercel AI SDK 的工具格式
*/
private getVercelTools(): Record<string, AITool> {
const vercelTools: Record<string, AITool> = {};
const availableTools = this.getAvailableTools();
for (const [name, customTool] of this.tools) {
const schema = buildZodSchema(customTool.parameters);
for (const tool of availableTools) {
const schema = buildZodSchema(tool.parameters);
vercelTools[name] = {
description: customTool.description,
vercelTools[tool.name] = {
description: tool.description,
inputSchema: schema,
execute: async (params) => {
const result = await customTool.execute(params as Record<string, unknown>);
const result = await tool.execute(params as Record<string, unknown>);
// 如果是 tool_search 调用,解析结果并注入发现的工具
if (tool.name === 'tool_search' && result.success) {
this.handleToolSearchResult(result.output);
}
return result;
},
} as AITool;
@@ -67,11 +120,25 @@ export class Agent {
return vercelTools;
}
// 发送消息并处理响应(流式)
async chat(
userMessage: string,
onStream?: (text: string) => void
): Promise<string> {
/**
* 处理 tool_search 的结果,将发现的工具添加到可用列表
*/
private handleToolSearchResult(output: string): void {
// 解析输出,提取工具名称
// 格式: "- tool_name: description [category]"
const matches = output.matchAll(/^- (\w+):/gm);
for (const match of matches) {
const toolName = match[1];
if (this.registry?.has(toolName)) {
this.discoveredTools.add(toolName);
}
}
}
/**
* 发送消息并处理响应(流式)
*/
async chat(userMessage: string, onStream?: (text: string) => void): Promise<string> {
// 添加用户消息到历史
this.conversationHistory.push({
role: 'user',
@@ -97,7 +164,12 @@ export class Agent {
const output = (chunk as { output?: ToolResult }).output;
if (output && typeof output === 'object') {
if (output.success) {
onStream(`[结果: ${output.output}]\n`);
// 截断过长的输出
const displayOutput =
output.output.length > 500
? output.output.substring(0, 500) + '...(截断)'
: output.output;
onStream(`[结果: ${displayOutput}]\n`);
} else {
onStream(`[错误: ${output.error}]\n`);
}
@@ -137,15 +209,21 @@ export class Agent {
return fullResponse;
}
// 清空对话历史
/**
* 清空对话历史和发现的工具
*/
clearHistory(): void {
this.conversationHistory = [];
this.discoveredTools.clear();
}
// 获取对话历史
/**
* 获取对话历史
*/
getHistory(): Message[] {
return this.conversationHistory
.filter((msg): msg is ModelMessage & { role: 'user' | 'assistant' } =>
.filter(
(msg): msg is ModelMessage & { role: 'user' | 'assistant' } =>
msg.role === 'user' || msg.role === 'assistant'
)
.map((msg) => ({
@@ -153,4 +231,25 @@ export class Agent {
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
}));
}
/**
* 获取当前可用工具的数量
*/
getToolCount(): { core: number; discovered: number; total: number } {
if (this.registry) {
const coreCount = this.registry.getCoreTools().length;
const discoveredCount = this.discoveredTools.size;
return {
core: coreCount,
discovered: discoveredCount,
total: coreCount + discoveredCount,
};
} else {
return {
core: this.legacyTools.size,
discovered: 0,
total: this.legacyTools.size,
};
}
}
}
+5 -5
View File
@@ -4,7 +4,7 @@ import { Command } from 'commander';
import { Agent } from './core/agent.js';
import { TerminalUI } from './ui/terminal.js';
import { loadConfig, initConfig } from './utils/config.js';
import { allTools } from './tools/index.js';
import { toolRegistry } from './tools/index.js';
import { getPermissionManager, promptPermission } from './permission/index.js';
const program = new Command();
@@ -37,8 +37,8 @@ program
const config = loadConfig();
const agent = new Agent(config);
// 注册所有工具
agent.registerTools(allTools);
// 设置工具注册表(支持动态工具发现)
agent.setRegistry(toolRegistry);
try {
await agent.chat(question, (text) => {
@@ -60,8 +60,8 @@ program.action(async () => {
const config = loadConfig();
const agent = new Agent(config);
// 注册所有工具
agent.registerTools(allTools);
// 设置工具注册表(支持动态工具发现)
agent.setRegistry(toolRegistry);
// 启动终端 UI
const ui = new TerminalUI(agent);
+10 -2
View File
@@ -1,6 +1,7 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
@@ -18,9 +19,16 @@ async function copyRecursive(source: string, dest: string): Promise<void> {
}
}
export const copyFileTool: Tool = {
export const copyFileTool: ToolWithMetadata = {
name: 'copy_file',
description: loadDescription('copy_file'),
metadata: {
name: 'copy_file',
category: 'filesystem',
description: '复制文件或目录',
keywords: ['copy', 'file', 'cp', 'duplicate', '复制', '文件', '拷贝'],
deferLoading: true,
},
parameters: {
source: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const createDirectoryTool: Tool = {
export const createDirectoryTool: ToolWithMetadata = {
name: 'create_directory',
description: loadDescription('create_directory'),
metadata: {
name: 'create_directory',
category: 'filesystem',
description: '创建目录',
keywords: ['create', 'directory', 'mkdir', 'folder', 'new', '创建', '目录', '文件夹', '新建'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const deleteFileTool: Tool = {
export const deleteFileTool: ToolWithMetadata = {
name: 'delete_file',
description: loadDescription('delete_file'),
metadata: {
name: 'delete_file',
category: 'filesystem',
description: '删除文件或目录',
keywords: ['delete', 'remove', 'file', 'rm', '删除', '移除', '文件'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const editFileTool: Tool = {
export const editFileTool: ToolWithMetadata = {
name: 'edit_file',
description: loadDescription('edit_file'),
metadata: {
name: 'edit_file',
category: 'filesystem',
description: '编辑文件内容(查找替换)',
keywords: ['edit', 'file', 'replace', 'modify', 'change', 'update', '编辑', '文件', '替换', '修改', '更新'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,6 +1,7 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
@@ -45,9 +46,16 @@ function formatPermissions(mode: number): string {
return `${fileType?.[1] || 'unknown'} (${perms})`;
}
export const getFileInfoTool: Tool = {
export const getFileInfoTool: ToolWithMetadata = {
name: 'get_file_info',
description: loadDescription('get_file_info'),
metadata: {
name: 'get_file_info',
category: 'filesystem',
description: '获取文件元信息',
keywords: ['file', 'info', 'stat', 'size', 'permission', 'metadata', '文件', '信息', '大小', '权限', '属性'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,6 +1,7 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
@@ -10,9 +11,16 @@ interface GrepMatch {
content: string;
}
export const grepContentTool: Tool = {
export const grepContentTool: ToolWithMetadata = {
name: 'grep_content',
description: loadDescription('grep_content'),
metadata: {
name: 'grep_content',
category: 'filesystem',
description: '在文件内容中搜索文本',
keywords: ['grep', 'search', 'content', 'text', 'find', 'regex', '搜索', '内容', '文本', '查找', '正则'],
deferLoading: true,
},
parameters: {
directory: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const listDirTool: Tool = {
export const listDirTool: ToolWithMetadata = {
name: 'list_directory',
description: loadDescription('list_directory'),
metadata: {
name: 'list_directory',
category: 'filesystem',
description: '列出目录内容',
keywords: ['list', 'directory', 'ls', 'dir', 'folder', '列出', '目录', '文件夹', '查看'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const moveFileTool: Tool = {
export const moveFileTool: ToolWithMetadata = {
name: 'move_file',
description: loadDescription('move_file'),
metadata: {
name: 'move_file',
category: 'filesystem',
description: '移动或重命名文件/目录',
keywords: ['move', 'rename', 'file', 'mv', '移动', '重命名', '文件'],
deferLoading: true,
},
parameters: {
source: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const readFileTool: Tool = {
export const readFileTool: ToolWithMetadata = {
name: 'read_file',
description: loadDescription('read_file'),
metadata: {
name: 'read_file',
category: 'filesystem',
description: '读取文件内容',
keywords: ['read', 'file', 'content', 'cat', 'view', 'open', '读取', '文件', '内容', '查看', '打开'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const searchFilesTool: Tool = {
export const searchFilesTool: ToolWithMetadata = {
name: 'search_files',
description: loadDescription('search_files'),
metadata: {
name: 'search_files',
category: 'filesystem',
description: '按文件名搜索文件',
keywords: ['search', 'file', 'find', 'glob', 'pattern', '搜索', '文件', '查找', '匹配'],
deferLoading: true,
},
parameters: {
directory: {
type: 'string',
+10 -2
View File
@@ -1,12 +1,20 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
export const writeFileTool: Tool = {
export const writeFileTool: ToolWithMetadata = {
name: 'write_file',
description: loadDescription('write_file'),
metadata: {
name: 'write_file',
category: 'filesystem',
description: '写入文件内容',
keywords: ['write', 'file', 'save', 'create', '写入', '文件', '保存', '创建', '新建'],
deferLoading: true,
},
parameters: {
path: {
type: 'string',
+21 -14
View File
@@ -1,8 +1,12 @@
import type { Tool } from '../types/index.js';
import type { ToolWithMetadata } from './types.js';
import { toolRegistry } from './registry.js';
// Shell 工具
import { bashTool } from './shell/index.js';
// 核心工具
import { toolSearchTool } from './tool-search.js';
// 文件系统工具
import {
readFileTool,
@@ -18,30 +22,33 @@ import {
deleteFileTool,
} from './filesystem/index.js';
// 所有可用工具的注册中心
// 添加新工具只需在此数组中添加一行
export const allTools: Tool[] = [
// Shell
// 所有工具列表(用于注册)
const allToolsWithMetadata: ToolWithMetadata[] = [
// 核心工具 (deferLoading: false)
toolSearchTool,
bashTool,
// 文件读写
// 文件系统工具 (deferLoading: true)
readFileTool,
writeFileTool,
editFileTool,
// 目录操作
listDirTool,
createDirectoryTool,
// 搜索
searchFilesTool,
grepContentTool,
// 文件信息
getFileInfoTool,
// 文件管理
moveFileTool,
copyFileTool,
deleteFileTool,
];
// 注册所有工具到 registry
toolRegistry.registerAll(allToolsWithMetadata);
// 导出
export { toolRegistry } from './registry.js';
export { toolSearchTool } from './tool-search.js';
export type { ToolWithMetadata, ToolMetadata, ToolCategory, ToolSearchResult } from './types.js';
// 兼容旧代码:导出所有工具数组(基础 Tool 类型)
export const allTools = toolRegistry.getAllTools();
+120
View File
@@ -0,0 +1,120 @@
import type { Tool } from '../types/index.js';
import type { ToolMetadata, ToolWithMetadata, ToolSearchResult } from './types.js';
import { searchTools } from './search.js';
/**
* 工具注册中心
* 管理所有工具的注册、查询和搜索
*/
class ToolRegistry {
private tools: Map<string, ToolWithMetadata> = new Map();
/**
* 注册单个工具
*/
register(tool: ToolWithMetadata): void {
this.tools.set(tool.name, tool);
}
/**
* 批量注册工具
*/
registerAll(tools: ToolWithMetadata[]): void {
for (const tool of tools) {
this.register(tool);
}
}
/**
* 获取核心工具 (deferLoading: false)
* 这些工具在会话开始时就可用
*/
getCoreTools(): Tool[] {
const coreTools: Tool[] = [];
for (const tool of this.tools.values()) {
if (!tool.metadata.deferLoading) {
coreTools.push(this.toBasicTool(tool));
}
}
return coreTools;
}
/**
* 获取指定工具
*/
getTool(name: string): Tool | undefined {
const tool = this.tools.get(name);
return tool ? this.toBasicTool(tool) : undefined;
}
/**
* 获取多个工具
*/
getTools(names: string[]): Tool[] {
const result: Tool[] = [];
for (const name of names) {
const tool = this.getTool(name);
if (tool) {
result.push(tool);
}
}
return result;
}
/**
* 搜索工具
* @param query 搜索查询
* @param limit 返回结果数量限制
* @returns 匹配的工具元数据列表
*/
search(query: string, limit: number = 5): ToolSearchResult[] {
const allMetadata = this.getAllMetadata();
return searchTools(query, allMetadata, limit);
}
/**
* 获取所有工具的元数据
*/
getAllMetadata(): ToolMetadata[] {
return [...this.tools.values()].map((tool) => tool.metadata);
}
/**
* 获取所有工具 (用于兼容旧代码)
*/
getAllTools(): Tool[] {
return [...this.tools.values()].map((tool) => this.toBasicTool(tool));
}
/**
* 检查工具是否存在
*/
has(name: string): boolean {
return this.tools.has(name);
}
/**
* 获取工具数量
*/
get size(): number {
return this.tools.size;
}
/**
* 将 ToolWithMetadata 转换为基础 Tool 类型
*/
private toBasicTool(tool: ToolWithMetadata): Tool {
return {
name: tool.name,
description: tool.description,
parameters: tool.parameters,
execute: tool.execute,
};
}
}
// 导出单例
export const toolRegistry = new ToolRegistry();
// 也导出类,方便测试
export { ToolRegistry };
+83
View File
@@ -0,0 +1,83 @@
import type { ToolMetadata, ToolSearchResult } from './types.js';
/**
* 分词函数,支持中英文
*/
function tokenize(text: string): string[] {
return text
.toLowerCase()
.split(/[\s,,、_\-]+/)
.filter((t) => t.length > 0);
}
/**
* 计算工具与查询的匹配分数
*/
function calculateScore(queryTerms: string[], tool: ToolMetadata): number {
let score = 0;
const nameLower = tool.name.toLowerCase();
const descLower = tool.description.toLowerCase();
const keywordsLower = tool.keywords.map((k) => k.toLowerCase());
for (const term of queryTerms) {
// 名称精确匹配 (最高分)
if (nameLower === term) {
score += 10;
}
// 名称包含匹配
else if (nameLower.includes(term)) {
score += 5;
}
// 关键词精确匹配
if (keywordsLower.includes(term)) {
score += 8;
}
// 关键词包含匹配
else if (keywordsLower.some((k) => k.includes(term) || term.includes(k))) {
score += 3;
}
// 描述包含匹配
if (descLower.includes(term)) {
score += 2;
}
}
return score;
}
/**
* 搜索工具
* @param query 搜索查询
* @param allTools 所有工具的元数据
* @param limit 返回结果数量限制
* @returns 匹配的工具列表(按分数排序)
*/
export function searchTools(
query: string,
allTools: ToolMetadata[],
limit: number = 5
): ToolSearchResult[] {
const queryTerms = tokenize(query);
if (queryTerms.length === 0) {
return [];
}
const results = allTools
// 只搜索延迟加载的工具
.filter((tool) => tool.deferLoading)
.map((tool) => ({
name: tool.name,
description: tool.description,
category: tool.category,
score: calculateScore(queryTerms, tool),
}))
.filter((r) => r.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, limit);
return results;
}
+10 -2
View File
@@ -1,14 +1,22 @@
import { exec } from 'child_process';
import { promisify } from 'util';
import type { Tool, ToolResult } from '../../types/index.js';
import type { ToolResult } from '../../types/index.js';
import type { ToolWithMetadata } from '../types.js';
import { loadDescription } from '../load_description.js';
import { getPermissionManager } from '../../permission/index.js';
const execAsync = promisify(exec);
export const bashTool: Tool = {
export const bashTool: ToolWithMetadata = {
name: 'bash',
description: loadDescription('bash'),
metadata: {
name: 'bash',
category: 'shell',
description: '执行 shell 命令',
keywords: ['bash', 'shell', 'command', 'execute', 'run', 'terminal', '命令', '执行', '终端', 'sh', 'cmd'],
deferLoading: false, // 核心工具,始终加载
},
parameters: {
command: {
type: 'string',
+68
View File
@@ -0,0 +1,68 @@
import type { ToolResult } from '../types/index.js';
import type { ToolWithMetadata } from './types.js';
import { toolRegistry } from './registry.js';
/**
* tool_search 工具
* 用于搜索可用的工具,实现动态工具发现
*/
export const toolSearchTool: ToolWithMetadata = {
name: 'tool_search',
description: `搜索可用的工具。当你需要执行某项任务但当前没有合适的工具时,使用此工具搜索。
可搜索的能力类别:
- 文件操作: 读取、写入、编辑、复制、移动、删除文件
- 目录操作: 列出目录、创建目录、搜索文件
- 内容搜索: 在文件中搜索文本、grep
- Shell: 执行命令行命令
- Git: 版本控制操作 (即将支持)
- 网络: HTTP请求、网页抓取 (即将支持)
搜索后返回的工具将可以直接使用。`,
parameters: {
query: {
type: 'string',
description: '描述你需要的功能,如 "读取文件内容"、"搜索代码"、"移动文件"、"执行命令"',
required: true,
},
},
metadata: {
name: 'tool_search',
category: 'core',
description: '搜索可用工具',
keywords: ['search', 'find', 'tool', 'discover', '搜索', '查找', '工具', '发现'],
deferLoading: false, // 核心工具,始终加载
},
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
const query = params.query as string;
if (!query || query.trim().length === 0) {
return {
success: false,
output: '',
error: '请提供搜索关键词',
};
}
const results = toolRegistry.search(query, 5);
if (results.length === 0) {
return {
success: true,
output: `没有找到与 "${query}" 匹配的工具。请尝试其他关键词,或使用更通用的描述。`,
};
}
const toolList = results
.map((t) => `- ${t.name}: ${t.description} [${t.category}]`)
.join('\n');
return {
success: true,
output: `找到 ${results.length} 个相关工具:\n\n${toolList}\n\n这些工具现在可以使用了。请选择合适的工具来完成任务。`,
};
},
};
+30
View File
@@ -0,0 +1,30 @@
import type { ToolParameter, ToolResult } from '../types/index.js';
// 工具类别
export type ToolCategory = 'core' | 'filesystem' | 'shell' | 'git' | 'web' | 'database';
// 工具元数据
export interface ToolMetadata {
name: string;
category: ToolCategory;
description: string; // 简短描述,用于搜索结果展示
keywords: string[]; // 搜索关键词
deferLoading: boolean; // true = 延迟加载,false = 始终加载
}
// 扩展后的工具定义(包含元数据)
export interface ToolWithMetadata {
name: string;
description: string;
parameters: Record<string, ToolParameter>;
execute: (params: Record<string, unknown>) => Promise<ToolResult>;
metadata: ToolMetadata;
}
// 搜索结果
export interface ToolSearchResult {
name: string;
description: string;
category: ToolCategory;
score: number;
}