Modern LLM agents need to use external tools (APIs) to complete tasks. Imagine an agent that can access 47,000 different API endpoints — functions for weather, payments, databases, translations, and more.
The fundamental constraint:
LLM Context Window = LIMITED SPACE
Total API Definitions = ENORMOUS (47,000 endpoints)
Therefore: You CANNOT inject all tools into one prompt
Solution needed: Smart tool DISCOVERY before execution
Think of it like this:
A librarian cannot hand you every book in the library. They must first identify which books you need, then hand you only those.
The dominant/traditional approach
User Query
↓
[Embed query as vector]
↓
[Search tool registry via nearest-neighbor]
↓
Retrieved Tools → LLM Plans execution
How it works:
Example:
User: "What's the weather in Paris?"
↓
Embed: [0.23, 0.87, 0.12, ...]
↓
Match: weather_api, forecast_tool, climate_data
↓
LLM uses weather_api to answer
The alternative approach studied in this paper
User Query
↓
[LLM generates Abstract Tool Stubs]
↓
[Stubs used as retrieval queries]
↓
Retrieved Tools → LLM Plans execution
Key new concept — Abstract Tool Stubs:
Hypothesized function signatures the LLM imagines it would need, before knowing what actually exists
Example:
User: "Book a flight and reserve a hotel in Rome for next week"
↓
LLM generates stubs:
- search_flights(origin, destination, date) → FlightList
- book_flight(flight_id, passenger_info) → Confirmation
- search_hotels(city, checkin, checkout) → HotelList
- reserve_hotel(hotel_id, guest_info) → Reservation
↓
Each stub searches the registry independently
↓
4 relevant real tools retrieved and used
The researchers tested on a real-world benchmark:
| Property | Value |
|---|---|
| Total API endpoints | 46,984 |
| Categories | 50 |
| Query types | 3 (G1, G2, G3) |
G1: Single-tool queries
"Get the current Bitcoin price"
→ Needs 1 tool
G2: Intra-category multi-tool queries
"Compare flights and book the cheapest one"
→ Needs multiple tools FROM SAME category
G3: Cross-category multi-tool queries
"Book a flight, reserve a hotel, and check weather"
→ Needs multiple tools FROM DIFFERENT categories
Query Complexity: Simple (G1) → Complex (G2, G3)
↓ ↓
Better Architecture: RtP wins PtR wins
This crossover holds across ALL tested conditions:
| Variable Tested | Range | Crossover Holds? |
|---|---|---|
| Retrieval budget (k) | 1 to 20 tools | ✅ Yes |
| Registry size | 1k to 47k APIs | ✅ Yes |
| LLM model | Gemini 2.5 Flash, GPT-120b | ✅ Yes |
Performance
↑
| RtP ────────\
| \──── RtP
| \
| PtR ────\────── PtR
| /
|────────/
+─────────────────────────→
G1 G2 G3
(Simple) (Complex)
The LLM invents plausible but non-existent API names when generating stubs
Example of hallucination:
Real API exists: "OpenWeatherMap_getCurrentWeather"
LLM stub invents: "get_current_weather(city, units)"
↑ This doesn't exist, but sounds reasonable
This hallucination is simultaneously a weakness AND a strength:
User: "Search on TripAdvisor for Rome restaurants"
↑
Proper noun signal: "TripAdvisor"
RtP embeds "TripAdvisor" → directly matches TripAdvisor API ✅
PtR generates stub: search_restaurants(city, cuisine)
→ Generic! Loses "TripAdvisor" signal
→ May retrieve wrong restaurant API ❌
User: "Plan a trip to Rome — flights, hotels, restaurants, weather"
↑
Multi-faceted intent (4 different needs)
RtP embeds entire query → retrieves tools most similar to
the DOMINANT topic, misses others ❌
PtR generates 4 stubs:
- find_flights(...) → retrieves flight API ✅
- search_hotels(...) → retrieves hotel API ✅
- find_restaurants(...) → retrieves restaurant API ✅
- get_weather(...) → retrieves weather API ✅
Key insight:
Stub hallucination decomposes complex multi-faceted intent into functional sub-needs — each sub-need can be matched independently
PtR requires an extra LLM call to generate stubs before retrieval:
RtP cost: [1 retrieval call] + [1 planning call]
PtR cost: [1 stub generation call] + [1 retrieval call] + [1 planning call]
↑
Roughly DOUBLES inference cost
| Query Type | PtR Extra Cost | PtR Accuracy Gain | Worth It? |
|---|---|---|---|
| G1 Simple | +~100% tokens | Negative (worse) | ❌ No |
| G2/G3 Complex | +~100% tokens | Positive (better) | ✅ Yes |
Since neither architecture wins universally, what should a real system do?
User Query
↓
[Complexity Detector]
↓ ↓
Simple? Complex?
↓ ↓
Use RtP Use PtR
(cheaper, (more expensive,
accurate) but accurate)
Key practical insight from the paper:
RtP's top-1 retrieval score (already computed for free) can serve as the routing signal
High top-1 similarity score → Query is specific/simple → Stay with RtP
Low top-1 similarity score → Query is vague/complex → Switch to PtR
Why this is elegant:
if RtP_top1_score > threshold:
use RtP result # Simple query, direct match found
else:
invoke PtR # Complex query, need decomposition
| Approach | Method | Best For |
|---|---|---|
| RtP (this paper) | Embed full query → retrieve | Simple, specific queries |
| PtR (this paper) | Generate stubs → retrieve | Complex, multi-tool queries |
| ProTIP (related) | Progressive retrieval with task decomposition | Multi-step planning |
| Context Tuning RAG (related) | Tune context for implicit information | Queries with missing context |
Common thread across all:
Single-step retrieval with raw queries is insufficient for complex tasks — some form of query transformation or decomposition improves results
┌─────────────────────────────────────────────────────┐
│ TOOL DISCOVERY DECISION FRAMEWORK │
├─────────────────────────────────────────────────────┤
│ │
│ Query arrives │
│ ↓ │
│ [Check RtP top-1 score] ← zero cost signal │
│ ↓ │
│ High score? Low score? │
│ (Specific query) (Vague/complex query) │
│ ↓ ↓ │
│ Use RtP Use PtR │
│ • Cheaper • ~2x cost │
│ • Direct matching • Stub decomposition │
│ • Better for G1 • Better for G2/G3 │
│ │
└─────────────────────────────────────────────────────┘