`bypass_multi_tools_limit` on `VertexAiSearchTool` is a leaky abstraction: unexpected dependencies, silent shift from inbuilt RAG to tool calling, and prompt fragility
- 主要言語
- Python
- スター
- 21.5k
- フォーク
- 4k
- 平均マージ
- 1日 14時間
- マージ済み PR(30日)
- 37
説明
The `bypass_multi_tools_limit=True` flag on `VertexAiSearchTool` is presented as a convenient boolean toggle to allow using Vertex AI Search alongside other tools. In practice, this flag is implemented as a hidden class replacement (`VertexAiSearchTool` → `DiscoveryEngineSearchTool`) that breaks the developer experience in three major ways:
1. **Unexpected runtime dependency:** Silently requires `google-cloud-discoveryengine` (`google-adk[gcp]`), failing at runtime with a `ModuleNotFoundError`.
2. **Silent shift from inbuilt RAG to client tool calling:** Transparent, citation-backed model retrieval is secretly replaced with an explicit client-side function call.
3. **Hardcoded tool naming and prompt fragility:** The substituted tool is hardcoded as `discovery_engine_search`. Because developers write instructions for their domain (e.g., "use the knowledge base"), the model naturally hallucinates calling `search(...)` instead, causing runtime crashes (`ValueError: Tool 'search' not found`).
This breaks the illusion that setting `bypass_multi_tools_limit=True` is a clean, seamless configuration switch.
---
## Minimal Reproduction
Consider a minimal agent combining a knowledge base search with a single custom helper function:
```python
from google.adk.agents import Agent
from google.adk.tools import VertexAiSearchTool
def get_user_tier() -> str:
"""Returns the loyalty tier of the current user."""
return "Platinum"
search_tool = VertexAiSearchTool(
data_store_id="projects/my-project/locations/global/collections/default_collection/dataStores/my-store",
max_results=10,
bypass_multi_tools_limit=True,
)
agent = Agent(
name="support_agent",
model="gemini-flash-lite-latest",
static_instruction="You are a helpful support assistant. Answer user questions using the knowledge base.",
tools=[search_tool, get_user_tier],
)
```
---
## The Issues
### 1. Undeclared Runtime Dependency (`google-adk[gcp]`)
When `bypass_multi_tools_limit=False` (or when the agent has only one tool), `VertexAiSearchTool` works with the base `google-adk` package using the Gemini API's built-in grounding (`types.Tool(retrieval=...)`).
However, the moment a second tool is added and `bypass_multi_tools_limit=True` is set, ADK's internal resolver ([`llm_agent.py`](file:///Users/kvadakattu/Documents/code/adkbook/.venv/lib/python3.12/site-packages/google/adk/agents/llm_agent.py)) silently swaps `VertexAiSearchTool` for `DiscoveryEngineSearchTool`. This class imports `google.cloud.discoveryengine`, immediately crashing with:
```text
ModuleNotFoundError: No module named 'google.cloud.discoveryengine'
```
There is no warning at agent instantiation time indicating that setting this flag requires the `[gcp]` extra.
### 2. Silent Paradigm Shift: Inbuilt RAG Grounding → Tool Calling
Developers choose `VertexAiSearchTool` because it integrates natively with Gemini's retrieval capability:
- Grounding happens transparently inside the model generation turn.
- The model returns grounded responses with citations and `groundingMetadata`.
- The model does not need to decide *whether* to execute a function call, construct JSON arguments, or wait for a second inference round-trip.
Flipping `bypass_multi_tools_limit=True` quietly transforms this into a client-side function calling tool:
- The model must now generate a structured tool call.
- A local API client executes an RPC to Discovery Engine.
- The raw JSON results are piped back into the conversation context for a second LLM turn.
This fundamental architectural shift is completely hidden behind what appears to be a minor transport flag.
### 3. Leaky Tool Naming & Prompt Fragility (`discovery_engine_search`)
In [`discovery_engine_search_tool.py`](file:///Users/kvadakattu/Documents/code/adkbook/.venv/lib/python3.12/site-packages/google/adk/tools/discovery_engine_search_tool.py), the substituted tool inherits from `FunctionTool` and registers `self.discovery_engine_search`:
```python
class DiscoveryEngineSearchTool(FunctionTool):
def __init__(self, ...):
super().__init__(self.discovery_engine_search)
```
This creates two critical problems:
1. **The name cannot be customized:** The function name is hardcoded to `"discovery_engine_search"` with the docstring `"Search through Vertex AI Search's discovery engine search API."` Neither `VertexAiSearchTool` nor `DiscoveryEngineSearchTool` accepts a `name` or `description` parameter.
2. **The model fails to call it:** Prompts written naturally (e.g. *"Answer user questions using the knowledge base"*) give the model no reason to suspect the tool is called `discovery_engine_search`. LLMs (especially lightweight models like `gemini-flash-lite`) guess generic names like `search(query=...)`, leading directly to:
```text
ValueError: Tool 'search' not found.
Available tools: discovery_engine_search, get_user_tier
```
To make the agent work, developers are forced to leak internal Google Cloud plumbing into user-facing prompts:
```text
"Answer user questions using the knowledge base by calling discovery_engine_search."
```
---
## Suggested Improvements
1. **Allow Custom Tool Naming and Description:**
If `VertexAiSearchTool` is going to be transformed into a client `FunctionTool`, it must accept `name` and `description` parameters (e.g., `name="knowledge_base_search"`), forwarding them to `DiscoveryEngineSearchTool` so the tool declaration matches the domain instructions.
2. **Handle Common Name Aliases / Fuzzy Resolution:**
When `DiscoveryEngineSearchTool` is the only search tool present, ADK should either register `search` as an alias or allow flexible resolution rather than failing with a hard `ValueError`.
3. **Explicit Tooling over Magic Flags:**
Rather than hiding a completely different execution model and dependency set behind `bypass_multi_tools_limit=True`, consider deprecating the flag in favor of:
- Providing `DiscoveryEngineSearchTool` directly as a first-class, documented tool when client-side search is desired.
- Documenting the sub-agent pattern (`AgentTool` / `sub_agents`) as the recommended architectural pattern when combining native search retrieval grounding with function tools.
4. **Fail Fast with Clear Dependency Errors:**
If `bypass_multi_tools_limit=True` is used without `google-cloud-discoveryengine` installed, raise a clear error during `Agent.__init__` instructing the user to install `google-adk[gcp]`.
コントリビューションガイド
評価
この issue はまだ評価されていません。