语义工具过滤器

基于嵌入相似度的超快速语义工具过滤服务,用于从1000+工具中筛选出最相关的10-20个工具,延迟低于10ms。

  • by Portkey-AI
  • TeX
  • 本地部署
  • 52 星标
  • 更新于
在 GitHub 查看 Portkey-AI/mcp-tool-filter

使用说明

@portkey ai/mcp工具过滤器

使用嵌入相似性对MCP(模型上下文协议)服务器进行超快速语义工具过滤。将您的工具上下文从1000多个工具减少到最相关的10-20个工具 10ms以下.

特性

  • ⚡ 迅速的:1000多种内置优化工具的过滤延迟<10ms
  • 🚀 性能优化:6-8倍更快的点积,智能top-K选择,真正的LRU缓存
  • 🎯 语义理解:使用嵌入进行智能工具匹配
  • 📦 对运行时零依赖:仅需要嵌入提供程序API
  • 🔄 灵活输入:接受聊天完成消息或原始字符串
  • 💾 智能缓存:缓存嵌入和上下文以获得最佳性能
  • 🎛️ 可配置的:调整得分阈值、top-k,并始终包含工具
  • 📊 性能指标:内置时间优化

安装

npm install @portkey-ai/mcp-tool-filter

快速开始

import { MCPToolFilter } from '@portkey-ai/mcp-tool-filter';

// 1. Initialize the filter (choose embedding provider)

// Option A: Local Embeddings (RECOMMENDED for low latency < 5ms)
const filter = new MCPToolFilter({
  embedding: {
    provider: 'local',
  }
});

// Option B: API Embeddings (for highest accuracy)
const filter = new MCPToolFilter({
  embedding: {
    provider: 'openai',
    apiKey: process.env.OPENAI_API_KEY,
  }
});

// 2. Load your MCP servers (one-time setup)
await filter.initialize(mcpServers);

// 3. Filter tools based on context
const result = await filter.filter(
  "Search my emails for the Q4 budget discussion"
);

// 4. Use the filtered tools in your LLM request
console.log(result.tools); // Top 20 most relevant tools
console.log(result.metrics.totalTime); // e.g., "2ms" for local, "500ms" for API

嵌入提供程序选项

局部嵌入(推荐)

赞成的意见:

  • ⚡ 超快:1-5ms延迟
  • 🔒 Private:没有数据发送到外部API
  • 💰 免费:无API费用
  • 🌐 离线:无需互联网即可工作

欺骗:

  • 精度略低于API模型
  • 首次初始化下载模型(~25MB)
const filter = new MCPToolFilter({
  embedding: {
    provider: 'local',
    model: 'Xenova/all-MiniLM-L6-v2', // Optional: default model
    quantized: true, // Optional: use quantized model for speed (default: true)
  }
});

可用型号:

  • Xenova/all-MiniLM-L6-v2 (默认)-384个维度,非常快
  • Xenova/all-MiniLM-L12-v2 -384个维度,更精确
  • Xenova/bge-small-en-v1.5 -384个维度,平衡良好
  • Xenova/bge-base-en-v1.5 -768维,更高质量

演出

  • 初始化:100ms-4s(一次性,下载模式)
  • 过滤请求:1-5ms
  • 缓存请求:<1ms

API嵌入

为了获得最高精度,请使用OpenAI或其他API提供商:

const filter = new MCPToolFilter({
  embedding: {
    provider: 'openai',
    apiKey: process.env.OPENAI_API_KEY,
    model: 'text-embedding-3-small', // Optional
    dimensions: 384, // Optional: match local model for fair comparison
  }
});

赞成的意见:

  • 🎯 最高精度:比当地好5-15%
  • 🔄 易于切换型号
  • 🌐 不需要当地资源

欺骗:

  • 🐌 慢速:每次请求400-800ms
  • 💰 成本:每100万代币约0.02美元
  • 🔒 发送到外部API的数据
  • 📶 需要互联网连接

演出

  • 初始化:200ms-60s(取决于刀具数量)
  • 滤波请求:400-800ms
  • 缓存请求:1-3ms

快速比较

方面 本地 API 获胜者
速度 1-5ms 400-800ms 🏆 本地(速度快200倍)
准确度 良好(85-90%) 最佳(100%) 🏆 API
成本 免费 约0.02美元/百万代币 🏆 本地
隐私 完全本地 发送到API的数据 🏆 本地
离线 ✅ 脱机工作 ❌ 需要互联网 🏆 本地
设置 零配置 需要API密钥 🏆 本地

📊 看 TRADEOFFS.md 进行详细分析

MCP服务器JSON格式

库需要一组具有以下结构的MCP服务器:

[
  {
    "id": "gmail",
    "name": "Gmail MCP Server",
    "description": "Email management tools",
    "categories": ["email", "communication"],
    "tools": [
      {
        "name": "search_gmail_messages",
        "description": "Search and find email messages in Gmail inbox. Use when user wants to find, search, look up emails...",
        "keywords": ["email", "search", "inbox", "messages"],
        "category": "email-search",
        "inputSchema": {
          "type": "object",
          "properties": {
            "q": { "type": "string" }
          }
        }
      }
    ]
  }
]

字段描述

必填字段:

  • id:服务器的唯一标识符
  • name:人类可读的服务器名称
  • tools:工具定义数组
    • name:唯一工具名称
    • description:对工具的功能和使用时间的详细描述

可选但推荐:

  • description:服务器级别描述
  • categories:用于分层过滤的类别标签数组
  • keywords:同义词/相关术语数组,以便更好地匹配
  • category:工具级别类别
  • inputSchema:参数的JSON模式(参数名称用于匹配)

最佳结果提示

  1. 丰富的描述:用用例写详细的描述

    "description": "Search emails in Gmail. Use when user wants to find, lookup, or retrieve messages, correspondence, or mail."
    
  2. 添加关键字:包括同义词和变体

    "keywords": ["email", "mail", "inbox", "messages", "correspondence"]
    
  3. 提及用例:明确说明何时使用该工具

    "description": "... Use when user wants to draft, compose, write, or prepare an email to send later."
    

API 参考

MCPToolFilter

工具过滤的主类。

构造函数

new MCPToolFilter(config: MCPToolFilterConfig)

配置选项:

{
  embedding: {
    // Local embeddings (recommended)
    provider: 'local',
    model?: string,               // Default: 'Xenova/all-MiniLM-L6-v2'
    quantized?: boolean,          // Default: true
    
    // OR API embeddings
    provider: 'openai' | 'voyage' | 'cohere',
    apiKey: string,
    model?: string,               // Default: 'text-embedding-3-small'
    dimensions?: number,          // Default: 1536 (or 384 for local)
    baseURL?: string,            // For custom endpoints
  },
  defaultOptions?: {
    topK?: number,              // Default: 20
    minScore?: number,          // Default: 0.3
    contextMessages?: number,   // Default: 3
    alwaysInclude?: string[],   // Always include these tools
    exclude?: string[],         // Never include these tools
    maxContextTokens?: number,  // Default: 500
  },
  includeServerDescription?: boolean,  // Default: false (see below)
  debug?: boolean               // Enable debug logging
}

关于 includeServerDescription:

启用后,此选项在工具嵌入中包含MCP服务器描述,提供有关工具域/类别的其他上下文。

// Enable server descriptions in embeddings
const filter = new MCPToolFilter({
  embedding: { provider: 'local' },
  includeServerDescription: true  // Default: false
});

权衡:

  • ✅ 帮助:通用查询,如“管理我的本地文件”(提高25%)
  • ❌ 伤害:特定的工具查询,如“执行此SQL查询”(降级50%)
  • ≈ 中性:总体影响为中性(0%变化)

建议: 保持此功能禁用(默认值: false)除非你的用例主要涉及高级意图查询。看 examples/benchmark-server-description.ts 详细的基准测试。

方法

initialize(servers: MCPServer[]): Promise<void>

使用MCP服务器初始化筛选器。这将预计算并缓存所有工具嵌入。

备注:在启动时调用一次。这是一个异步操作,可能需要几秒钟,具体取决于工具的数量。

await filter.initialize(servers);
filter(input: FilterInput, options?: FilterOptions): Promise<FilterResult>

基于输入上下文的过滤工具。

输入类型:

// String input
await filter.filter("Search my emails about the project");

// Chat messages
await filter.filter([
  { role: 'user', content: 'What meetings do I have today?' },
  { role: 'assistant', content: 'Let me check your calendar.' }
]);

选项 (全部可选,覆盖默认值):

{
  topK?: number,              // Max tools to return
  minScore?: number,          // Minimum similarity score (0-1)
  contextMessages?: number,   // How many recent messages to use
  alwaysInclude?: string[],   // Tool names to always include
  exclude?: string[],         // Tool names to exclude
  maxContextTokens?: number,  // Max context size
}

退货:

{
  tools: ScoredTool[],        // Filtered and ranked tools
  metrics: {
    totalTime: number,        // Total time in ms
    embeddingTime: number,    // Time to embed context
    similarityTime: number,   // Time to compute similarities
    toolsEvaluated: number,   // Total tools evaluated
  }
}
getStats()

获取有关筛选器状态的统计信息。

const stats = filter.getStats();
// {
//   initialized: true,
//   toolCount: 25,
//   cacheSize: 5,
//   embeddingDimensions: 1536
// }
clearCache()

清除上下文嵌入缓存。

filter.clearCache();

性能优化

内置优化

该库包括几个开箱即用的性能优化:

  1. 🚀 环形无卷点产品 -通过CPU流水线优化,向量相似度计算速度提高了6-8倍
  2. 📊 智能Top-K选择 -混合算法对典型工作负载使用快速内置排序,对500多种工具切换到基于堆的选择
  3. 💾 真正的LRU缓存 -基于访问模式的智能缓存驱逐,而不仅仅是插入顺序
  4. 🎯 就地操作 -通过就地向量归一化减少内存分配
  5. ⚡ 基于集合的查找 -O(1)排除检查,而不是O(n)阵列扫描

这些优化是自动和透明的,不需要配置!

延迟故障

1000个工具的典型性能:

Building context:        <1ms
Embedding API call:      3-5ms  (cached: 0ms)
Similarity computation:  1-2ms  (6-8x faster with optimizations)
Sorting/filtering:       <1ms   (hybrid algorithm)
─────────────────────────────
Total:                   5-9ms

用户配置提示

  1. 使用较小的嵌入:512或1024维,计算速度更快

    embedding: {
      provider: 'openai',
      model: 'text-embedding-3-small',
      dimensions: 512  // Faster than 1536
    }
    
  2. 减小上下文大小:消息更少=嵌入速度更快

    defaultOptions: {
      contextMessages: 2,  // Instead of 3-5
      maxContextTokens: 300
    }
    
  3. 利用缓存:相同的上下文重用缓存的嵌入(0ms)

  4. 调整topK:如果不需要20个工具,请减少工具数量

    await filter.filter(input, { topK: 10 });
    

性能基准

显示优化改进的微观基准:

Dot Product (1536 dims):        0.001ms vs 0.006ms (6x faster)
Vector Normalization:           0.003ms vs 0.006ms (2x faster)  
Top-K Selection (<500 tools):   Uses optimized built-in sort
Top-K Selection (500+ tools):   O(n log k) heap-based selection
LRU Cache Access:               True access-order tracking

请参阅现有的端到端性能测试基准示例:

npx ts-node examples/benchmark.ts

集成示例

使用Portkey AI网关

import Portkey from 'portkey-ai';
import { MCPToolFilter } from '@portkey-ai/mcp-tool-filter';

const portkey = new Portkey({ apiKey: '...' });
const filter = new MCPToolFilter({ /* ... */ });

await filter.initialize(mcpServers);

// Filter tools based on conversation
const { tools } = await filter.filter(messages);

// Convert to OpenAI tool format
const openaiTools = tools.map(t => ({
  type: 'function',
  function: {
    name: t.toolName,
    description: t.tool.description,
    parameters: t.tool.inputSchema,
  }
}));

// Make LLM request with filtered tools
const completion = await portkey.chat.completions.create({
  model: 'gpt-4',
  messages: messages,
  tools: openaiTools,
});

与LangChain合作

import { ChatOpenAI } from 'langchain/chat_models/openai';
import { MCPToolFilter } from '@portkey-ai/mcp-tool-filter';

const filter = new MCPToolFilter({ /* ... */ });
await filter.initialize(mcpServers);

// Create a custom tool selector
async function selectTools(messages) {
  const { tools } = await filter.filter(messages);
  return tools.map(t => convertToLangChainTool(t));
}

// Use in your agent
const model = new ChatOpenAI();
const tools = await selectTools(messages);
const response = await model.invoke(messages, { tools });

缓存策略

// Recommended: Initialize once at startup
let filterInstance: MCPToolFilter;

async function getFilter() {
  if (!filterInstance) {
    filterInstance = new MCPToolFilter({ /* ... */ });
    await filterInstance.initialize(mcpServers);
  }
  return filterInstance;
}

// Use in request handlers
app.post('/chat', async (req, res) => {
  const filter = await getFilter();
  const result = await filter.filter(req.body.messages);
  // ... use filtered tools
});

基准测试

各种刀具数量的性能(M1 Max):

局部嵌入(Xenova/全MiniLM-L6-v2):

工具 初始化 筛选器(冷) 筛选器(缓存)
10 ~100ms 2ms <1ms
100 ~500ms 3ms <1ms
500 ~2s 4ms 1ms
1000 ~4s 5ms 1ms
5000 ~20s 8ms 2ms

API嵌入(OpenAI text-embedding-3-商城):

工具 初始化 筛选器(冷) 筛选器(缓存)
10 ~200ms 500ms 1ms
100 ~1.5秒 550毫秒 2毫秒
500秒 ~6秒 600毫秒 2毫秒
1000 ~12秒 650毫秒 3毫秒
5000 ~60s 800ms 4ms

关键要点:

  • 🚀 本地嵌入是 速度提高200至300倍 用于筛选请求
  • ✅ 本地嵌入符合 <50ms目标 轻易地
  • 💰 本地嵌入没有API成本
  • 📊 API嵌入可能具有略高的精度
  • ⚡ 两者都从缓存中受益匪浅

备注:初始化是一次性成本。选择本地嵌入以获得低延迟,选择API嵌入以获得最大精度。

何时使用本地与API嵌入

在以下情况下使用本地嵌入:

  • ⚡ 您需要超低延迟(<10ms)
  • 🔒 隐私很重要(没有外部API调用)
  • 💰 您希望API成本为零
  • 🌐 您需要离线操作
  • 📊 “足够好”的精度是可以接受的

在以下情况下使用API嵌入:

  • 🎯 你需要最大的准确性
  • 🌍 您的互联网连接良好
  • 💵 API成本不值得关注
  • 📈 您正在处理复杂/微妙的查询

建议: 从本地嵌入开始。只有在精度不足时才切换到API。

测试本地与API

比较用例的性能:

npx ts-node examples/test-local-embeddings.ts

这将对这两个提供商进行基准测试,并向您展示:

  • 初始化时间
  • 平均过滤时间
  • 缓存筛选时间
  • 速度比较

调试和性能监控

启用调试日志记录

要查看每个请求的详细计时日志,请启用调试模式:

const filter = new MCPToolFilter({
  embedding: { /* ... */ },
  debug: true  // Enable detailed timing logs
});

这将输出每个筛选器请求的详细日志:

=== Starting filter request ===
[1/5] Options merged: 0.12ms
[2/5] Context built (156 chars): 0.34ms
[3/5] Cache MISS (lookup: 0.08ms)
     → Embedding generated: 1247.56ms
[4/5] Similarities computed: 1.23ms (25 tools, 0.049ms/tool)
[5/5] Tools selected & ranked: 0.15ms (5 tools returned)
=== Total filter time: 1249.48ms ===
Breakdown: merge=0.12ms, context=0.34ms, cache=0.08ms, embedding=1247.56ms, similarity=1.23ms, selection=0.15ms

时间分解

每个筛选器请求记录5个步骤:

  1. 选项合并 (merge):将提供的选项与默认值合并
  2. 上下文构建 (context):根据输入消息构建上下文字符串
  3. 缓存查找和嵌入 (cache + embedding):
    • 缓存HIT:0ms嵌入时间(重用缓存嵌入)
    • 缓存MISS:调用嵌入API(通常200-2000ms,取决于提供商)
  4. 相似度计算 (similarity):计算所有工具的余弦相似度
    • 还显示了每个工具的平均时间
  5. 工具选择 (selection):按分数筛选并选择前K名工具

示例:测试时间

看 examples/test-timings.ts 完整示例:

export OPENAI_API_KEY=your-key-here
npx ts-node examples/test-timings.ts

这将运行多个筛选器请求,显示:

  • 缓存未命中与缓存命中性能
  • 不同的查询类型
  • 聊天消息上下文处理

性能指标

每个过滤器请求都会返回详细的指标:

const result = await filter.filter(input);

console.log(result.metrics);
// {
//   totalTime: 1249.48,      // Total request time in ms
//   embeddingTime: 1247.56,  // Time spent on embedding API
//   similarityTime: 1.23,    // Time computing similarities
//   toolsEvaluated: 25       // Number of tools evaluated
// }

生产监控

const result = await filter.filter(messages);

// Log metrics for monitoring
logger.info('Tool filter performance', {
  totalTime: result.metrics.totalTime,
  embeddingTime: result.metrics.embeddingTime,
  cached: result.metrics.embeddingTime === 0,
  toolsReturned: result.tools.length,
});

// Alert if too slow
if (result.metrics.totalTime > 5000) {
  logger.warn('Slow filter request', result.metrics);
}

高级用法

两级过滤

对于非常大的工具集,使用分层过滤:

// Stage 1: Filter by server categories
const relevantServers = mcpServers.filter(server => 
  server.categories?.some(cat => userIntent.includes(cat))
);

// Stage 2: Filter tools within relevant servers
const result = await filter.filter(messages);

自定义评分

将嵌入相似性与关键字匹配相结合:

const { tools } = await filter.filter(input);

// Boost tools with exact keyword matches
const boostedTools = tools.map(tool => {
  const hasKeywordMatch = tool.tool.keywords?.some(kw => 
    input.toLowerCase().includes(kw.toLowerCase())
  );
  return {
    ...tool,
    score: hasKeywordMatch ? tool.score * 1.2 : tool.score
  };
}).sort((a, b) => b.score - a.score);

始终包括电动工具

始终包括某些基本工具:

const filter = new MCPToolFilter({
  // ...
  defaultOptions: {
    alwaysInclude: [
      'web_search',           // Always useful
      'conversation_search',  // Access to context
    ],
  }
});

故障排除

首次请求缓慢

问题:第一次筛选器调用缓慢。

解决方案:嵌入API调用需要3-5ms。具有类似上下文的后续调用会被缓存,而且速度更快。

// Warm up the cache
await filter.filter("hello"); // ~5ms
await filter.filter("hello"); // ~1ms (cached)

工具选择不当

问题:选择了错误的工具。

解决方案:

  1. 使用更多关键字和用例改进工具描述
  2. 降低 minScore 阈值
  3. 增加 topK 包含更多工具
  4. 添加重要工具 alwaysInclude

内存使用

问题:许多工具的内存使用率很高。

解决方案:使用较小的嵌入尺寸:

embedding: {
  dimensions: 512  // Instead of 1536
}

这将内存减少约66%,同时精度损失最小。

许可证

麻省理工学院

贡献

欢迎投稿!请打开问题或PR。

支持