ツール検索ツール

AI エージェントが Slack、GitHub、Jira、MCP サーバーなど、より多くのサービスに接続するにつれて、ツールライブラリは急速に拡大します。一般的なマルチサーバー構成では、会話が始まる前に 55,000+ トークンを使用するツールが 50 個以上集積される可能性があります。また、モデルが 30 個以上の類似した名前のツールに直面すると、ツールの選択精度が低下します。

ToolSearchToolCallingAdvisor は、デフォルトの ToolCallingAdvisor をプログレッシブツール開示パターンの実装に置き換えることでこの問題を解決します。ツール定義は、事前に送信されるのではなく、必要に応じて段階的にモデルに公開されます。OpenAI、Anthropic、Gemini のベンチマークでは、大規模なツールカタログへのアクセスを維持しながら 34 – 64% トークン削減が実現されています。測定方法と手法については、Smart Tool Selection blog post (英語) を参照してください。

このページは、アドバイザー、その ToolIndex 戦略、構成、Spring Boot 自動構成に関するリファレンスです。このアドバイザーがより広範なツール呼び出しアーキテクチャの中でどのような位置づけにあるかについては、Scaling to Hundreds of Tools を参照してください。

使い方

Tool Search Tool Calling Flow

ToolSearchToolCallingAdvisor extends ToolCallingAdvisor and overrides the loop’s initialization and per-iteration hooks. The runtime flow:

  1. インデックス作成 — at session start, all registered tools are indexed in the configured ToolIndex. No tool definitions are sent to the model

  2. Initial request — the first request to the LLM contains only the built-in toolSearchTool definition.

  3. Discovery call — when the model needs a capability, it calls toolSearchTool with a natural-language query.

  4. Search & expand — the ToolIndex finds matching tools; their definitions are appended to the conversation for the next iteration.

  5. Tool invocation — the model, now equipped with the relevant definition, issues a normal tool call.

  6. Tool execution — ToolCallingManager executes the discovered tool and returns its result.

  7. レスポンス — the model produces the final answer using the tool result.

The indexed tool set is scoped per session (see Session Scoping ); concurrent conversations have isolated indexes.

いつ使うか

ぴったり合う:

  • 10+ tools registered with the ChatClient.

  • Tool definitions consuming more than 10K tokens per request.

  • Multi-server MCP setups where the aggregated tool catalog is large.

  • Symptoms of tool-selection accuracy issues with large tool sets.

Stick with the default ToolCallingAdvisor when:

  • Your tool library is small (under 10 tools).

  • すべてのツールは、各セッションで頻繁に使用されます。

  • Tool definitions are very compact (the search round-trips would outweigh the token savings).

インストール

最も簡単なセットアップには、Spring Boot スターターを使用してください(Lucene と自動構成が含まれています)。

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-tool-search-advisor</artifactId>
</dependency>
dependencies {
    implementation 'org.springframework.ai:spring-ai-starter-tool-search-advisor'
}

または、ライブラリを直接使用して手動で設定することもできます。

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-tool-search-advisor</artifactId>
</dependency>
dependencies {
    implementation 'org.springframework.ai:spring-ai-tool-search-advisor'
}

クイックスタート

The fastest path is the auto-configuration — see Spring Boot 自動構成 below. For manual wiring:

// 1. Configure a ToolIndex (semantic, keyword, or regex)
@Bean
ToolIndex toolIndex(VectorStore vectorStore) {
    return new VectorToolIndex(vectorStore);
}

// 2. Build the advisor
var toolSearchAdvisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .maxResults(5)
    .build();

// 3. Register with ChatClient — tools are indexed but NOT sent to the LLM up front
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools(new MyTools())
    .defaultAdvisors(toolSearchAdvisor)
    .build();

// 4. Make a request — supply a session ID via the advisor context
String answer = chatClient.prompt("Help me plan what to wear today in Amsterdam")
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, "user-42-session"))
    .call()
    .content();

Session Scoping

ToolSearchToolCallingAdvisor indexes tools per session. The session ID determines which tool index a request sees — this enables multi-tenant and multi-conversation isolation.

The caller must supply a session ID with every request. By default the advisor reads the session ID from the advisor context under the ChatMemory.CONVERSATION_ID key:

chatClient.prompt()
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, "user-42-session"))
    .user("...")
    .call()
    .content();

If your application already passes a session identifier under a different key — for example tenantId or userId — change the lookup key via sessionIdKeyName(…​) (or the corresponding property):

var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .sessionIdKeyName("tenantId")
    .build();
If memory advisors are configured with conversation IDs (the standard pattern with MessageChatMemoryAdvisor), the same key is already in the context — you get session scoping "for free" by virtue of the memory setup.

検索戦略

ToolIndex インターフェースは検索の実装を抽象化します。標準で 3 つの検索戦略が提供されています。

戦略 実装 最適な用途

セマンティック

VectorToolIndex

Natural-language queries, fuzzy matching, novel phrasings — when callers describe what they need rather than naming the tool

キーワード

LuceneToolIndex

Exact-term matching, fast retrieval, known vocabulary

正規表現

RegexToolIndex

Tool name patterns (e.g. get_*_data); lightweight default with no dependencies

VectorToolIndex (セマンティック)

Uses embedding-based similarity search. Best when callers describe what they need in natural language.

@Bean
ToolIndex vectorToolIndex(VectorStore vectorStore) {
    return new VectorToolIndex(vectorStore);
}

Requires a VectorStore bean (e.g. via spring-ai-starter-vector-store-pgvector). Tool name and description are embedded on indexing; queries from toolSearchTool are embedded and the top-K matches are returned.

LuceneToolIndex (キーワード)

Uses Apache Lucene for keyword-based search. Fast, no embedding model required.

@Bean
ToolIndex luceneToolIndex() {
    return new LuceneToolIndex();          // default minimum score 0.25
    // return new LuceneToolIndex(0.4f);   // custom minimum score threshold
}

Hits below the minimum score threshold are silently dropped. Raise the threshold to be more selective; lower it to be more permissive.

RegexToolIndex (パターン)

Uses regex pattern matching against tool names. Useful when tool names follow a strict naming convention (e.g. get_*database). Zero additional dependencies.

@Bean
ToolIndex regexToolIndex() {
    return new RegexToolIndex();
}

The default index when no explicit tool-index-type is configured.

構成

ToolSearchToolCallingAdvisor.Builder extends ToolCallingAdvisor.Builder and adds search-specific options. See ToolCallingAdvisor Builder Options for inherited settings.

オプション 説明 デフォルト

toolIndex(ToolIndex)

使用する検索実装。

必須

maxResults(Integer)

Maximum tool references returned per toolSearchTool call. When null, the LLM decides (the built-in tool description hints at 5).

null

systemMessageSuffix(String)

Custom prompt suffix appended to the system message to instruct the model on how to use toolSearchTool.

組み込みテンプレート (DEFAULT_SYSTEM_PROMPT_SUFFIX.md を参照)

referenceToolNameAccumulation(boolean)

When true, tool names discovered across all prior toolSearchTool calls are accumulated and injected. When false, only the results from the most recent turn are used (including all parallel toolSearchTool calls within that turn).

true

sessionIdKeyName(String)

Advisor context key used to look up the conversation/session ID.

ChatMemory.CONVERSATION_ID

evictionStrategy(ToolIndexEvictionStrategy)

Determines when session tool indexes are freed. See インデックス退去

LruEvictionStrategy(1000)

ToolIndex API

The ToolIndex interface and its companion types (ToolSearchRequestToolSearchResponseToolReference) live in the spring-ai-tool-search-tool module under org.springframework.ai.tool.toolsearch. The built-in implementations (LuceneToolIndexVectorToolIndexRegexToolIndex) are also in this module.

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-tool-search-tool</artifactId>
</dependency>
public interface ToolIndex {

    void indexTool(String sessionId, ToolReference toolReference);

    /** Default implementation loops over indexTool. */
    void indexTools(String sessionId, List<ToolReference> toolReferences);

    ToolSearchResponse search(ToolSearchRequest request);

    void clearIndex(String sessionId);
}

Every operation is scoped by sessionId. Implement ToolIndex directly when you need a custom search strategy — for example, a database-backed catalog with role-based filtering, or a cached remote tool registry.

インデックス退去

Per-session tool indexes consume memory. The ToolIndexEvictionStrategy decides when to free them.

By default (LruEvictionStrategy(1000)), up to 1,000 active sessions are retained and the least-recently-used session is evicted once the cap is exceeded. Call advisor.evictSession(sessionId) to release a session eagerly (e.g. on logout).

退去処理は各リクエストごとに遅延評価されます。バックグラウンドスレッドは必要ありません。

Five built-in strategies are provided:

戦略 振る舞い

LruEvictionStrategy(maxSessions) (default)

Evicts the least-recently-used session once the number of active sessions exceeds maxSessions.

NeverEvictStrategy.INSTANCE

自動的に削除されることはありません。インデックスは、evictSession() が明示的に呼び出されるまで保持されます。

AlwaysEvictStrategy.INSTANCE

リクエストごとにセッションのインデックスをクリアし、毎回完全な再インデックスを強制します。テスト時や、ツールセットがリクエストごとに変更される場合に便利です。

TtlEvictionStrategy(duration)

Evicts sessions whose last-access time exceeds the given TTL.

CompositeEvictionStrategy(strategies…​)

Delegates to multiple strategies; evicts a session if any delegate requests it.

// Default: LRU cap of 1000 sessions — no configuration needed
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .build();

// Never evict — manage session lifetime yourself
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .evictionStrategy(NeverEvictStrategy.INSTANCE)
    .build();

// Always evict — re-index every request (useful for testing)
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .evictionStrategy(AlwaysEvictStrategy.INSTANCE)
    .build();

// LRU with custom cap
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .evictionStrategy(new LruEvictionStrategy(200))
    .build();

// Evict sessions idle for more than 30 minutes
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .evictionStrategy(new TtlEvictionStrategy(Duration.ofMinutes(30)))
    .build();

// Combine: TTL + LRU cap
var advisor = ToolSearchToolCallingAdvisor.builder()
    .toolIndex(toolIndex)
    .evictionStrategy(new CompositeEvictionStrategy(
        new TtlEvictionStrategy(Duration.ofMinutes(30)),
        new LruEvictionStrategy(200)))
    .build();

Spring Boot 自動構成

spring-ai-starter-tool-search-advisor スターターは、定型的な設定を一切必要としません。以下のプロパティを 1 つ設定するだけで有効になります。

spring.ai.chat.client.tool-search-advisor.enabled=true

When enabled, the auto-configuration:

  • Registers a ToolSearchToolCallingAdvisor.Builder bean typed as ToolCallingAdvisor.Builder<?>. This transparently replaces the default ToolCallingAdvisor thanks to the @ConditionalOnMissingBean guard on the default builder — no code changes to your ChatClient are needed. See カスタム ToolAdvisor: Auto-Configuration Integration for the underlying mechanism.

  • Auto-registers a ToolIndex bean unless your application declares one explicitly.

ToolIndex Auto-Selection

spring.ai.chat.client.tool-search-advisor.tool-index-type を設定して実装を選択します。

実装 要件

regex (default)

RegexToolIndex

追加の依存関係はありません

lucene

LuceneToolIndex

org.apache.lucene:lucene-core on the classpath (bundled in the starter)

vector

VectorToolIndex

アプリケーションコンテキストにおける VectorStore Bean

A custom ToolIndex bean declared by the application always takes precedence — @ConditionalOnMissingBean skips the auto-configured one.

構成プロパティリファレンス

プロパティ 説明 デフォルト

spring.ai.chat.client.tool-search-advisor.enabled

アドバイザーを有効にします。true の場合、デフォルトの ToolCallingAdvisor を置き換えます。

false

spring.ai.chat.client.tool-search-advisor.tool-index-type

ToolIndex implementation: regex, lucene, or vector.

regex

spring.ai.chat.client.tool-search-advisor.max-results

検索呼び出しごとに返されるツール参照の最大数。null は組み込みのデフォルト値を使用します。

null

spring.ai.chat.client.tool-search-advisor.system-message-suffix

システムメッセージにカスタムプロンプトサフィックスが追加されます。null は組み込みテンプレートを使用します。

null

spring.ai.chat.client.tool-search-advisor.reference-tool-name-accumulation

When true, accumulate tool names across all search turns; when false, keep only the most recent turn (all parallel calls within it included).

true

spring.ai.chat.client.tool-search-advisor.session-id-key-name

会話 / セッション ID を保持するアドバイザーコンテキストキー。

chat_memory_conversation_id

spring.ai.chat.client.tool-search-advisor.advisor-order

このアドバイザーの位置は、アドバイザーチェーン内にあります。

HIGHEST_PRECEDENCE + 300

spring.ai.chat.client.tool-search-advisor.eviction.lru-max-sessions

LRU(最下位更新)によるセッション削減戦略によって保持されるアクティブセッションの最大数。

1000

spring.ai.chat.client.tool-search-advisor.eviction.ttl

TTL for idle sessions. When set, a composite LRU+TTL strategy is used. Accepts a java.time.Duration string (e.g. 30m1h).

null

spring.ai.chat.client.tool-search-advisor.lucene.min-score-threshold

ヒットがカウントされるための最低 Lucene スコア。tool-index-type=lucene の場合に適用されます。

0.25

構成例

Lucene with custom threshold and TTL eviction:

spring.ai.chat.client.tool-search-advisor.enabled=true
spring.ai.chat.client.tool-search-advisor.tool-index-type=lucene
spring.ai.chat.client.tool-search-advisor.lucene.min-score-threshold=0.4
spring.ai.chat.client.tool-search-advisor.eviction.ttl=30m

Vector search (requires a VectorStore bean):

spring.ai.chat.client.tool-search-advisor.enabled=true
spring.ai.chat.client.tool-search-advisor.tool-index-type=vector

Custom session-ID key for a multi-tenant deployment:

spring.ai.chat.client.tool-search-advisor.enabled=true
spring.ai.chat.client.tool-search-advisor.tool-index-type=vector
spring.ai.chat.client.tool-search-advisor.session-id-key-name=tenantId

関連事項