Skip to main content

Semantic Tool Selection

The problem

When an agent has many tools — common with multiple MCP servers connected, or skill bundles — passing every tool definition to the model on every turn causes three problems:
  1. Prompt bloat. 100 tools × ~80 tokens each = 8K tokens of pure tool definitions per turn, before the user’s message even gets to the model.
  2. Tool confusion. Models pick worse tools when more options are available (a phenomenon Cohere and Anthropic have both documented).
  3. Slower generation. More input tokens means more time-to-first-token.
SemanticToolSelector solves this by embedding each tool’s name + description once on init, then picking the top-K most relevant tools per user turn.

How it works

The cost per turn is one embed call (~10ms) instead of thousands of extra prompt tokens.

Quick start

Configuration

embedder

Any EmbeddingProvider works. For tool selection you want:
  • Cheap and fast — picks happen every turn.
  • Decent on short text — tool names + 1-2 sentence descriptions.
text-embedding-3-small is the sweet spot. text-embedding-3-large is overkill; gemini-embedding-2 works too but is slower.

reranker

Optional second pass. Useful when top-K from the bi-encoder still has too many irrelevant tools.
When reranker is set:
  1. Bi-encoder scores all indexed tools by cosine similarity to the query.
  2. Top topK * rerankMultiplier tools are passed to the reranker.
  3. Reranker scores each (query, tool description) pair and returns the top topK.

topK and rerankMultiplier

API

indexTools(tools: ToolDef[])

Embeds each tool’s name: description string in parallel. Async; await before the first select() call.
Call this:
  • At agent boot
  • Whenever the tool set changes (e.g. an MCP server connects)
  • Not on every turn

select(query: string, options?: { topK?: number }): Promise<ToolDef[]>

Returns a shortened ToolDef[] ready to drop into a new Agent.
options.topK overrides the constructor default for this call. Behavior:
  • Returns [] if indexTools hasn’t been called.
  • Empty query — still returns the closest topK (the embedding of "" is rarely useful but doesn’t error).
  • Same tool indexed twice — both copies returned independently; dedupe at construction time.

size

Wire into a per-request agent

Pairs naturally with AgentFactory:

Tips

  • Always include critical tools unconditionally. handoff, approval, pollResult, getArtifact should always be in the agent’s toolset regardless of semantic match.
  • Tool descriptions matter more than names. “fetch the temperature for a city” beats “weather_api_v3”.
  • Reindex after dynamic tool registration. MCP connections happen async; call indexTools again after a successful connect.
  • Cache the index. If your tool set is stable across processes, persist the embeddings to disk and re-hydrate on boot.

Performance characteristics

See also