Skip to content

Commit 668fb15

Browse files
committed
feat: add Amazon Bedrock Knowledge Base tool
- Created BedrockKBTool(BaseTool) subclass with _get_declaration() and async run_async() - Supports managed and vector knowledge base types - Agentic retrieval with fallback to standard Retrieve API - Unit tests included - Added BEDROCK_MANAGED_KB.md design doc
1 parent 7e245c4 commit 668fb15

3 files changed

Lines changed: 450 additions & 0 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Bedrock Managed Knowledge Base Support
2+
3+
## Overview
4+
Adds a Google ADK tool that queries Amazon Bedrock Knowledge Bases for managed retrieval within ADK agents.
5+
6+
## Usage
7+
```python
8+
from google.adk import Agent
9+
from google.adk.tools import BedrockKnowledgeBaseTool
10+
11+
kb_tool = BedrockKnowledgeBaseTool(knowledge_base_id="YOUR_KB_ID")
12+
agent = Agent(
13+
name="research_agent",
14+
model="gemini-2.0-flash",
15+
tools=[kb_tool],
16+
instruction="Use the knowledge base to answer questions.",
17+
)
18+
```
19+
20+
## Configuration
21+
| Variable | Description | Default |
22+
|---|---|---|
23+
| KNOWLEDGE_BASE_ID | Bedrock Knowledge Base ID | None |
24+
| AWS_REGION | AWS region for the KB | us-east-1 |
25+
| AWS_PROFILE | AWS credentials profile | None |
26+
| USE_AGENTIC_RETRIEVAL | Enable agentic retrieval | true |
27+
| MAX_RESULTS | Maximum retrieval results | 5 |
28+
29+
## Features
30+
- Managed search (no vector store needed)
31+
- Agentic retrieval with query decomposition + reranking
32+
- Automatic fallback to plain Retrieve if agentic fails
33+
- Multi-source support (S3, Web, Confluence, SharePoint)
34+
- Compatible with ADK BaseTool interface
35+
36+
## SDK Requirements
37+
- boto3 >= 1.43
38+
- google-adk >= 0.1
39+
40+
## Required IAM Permissions
41+
```json
42+
{
43+
"Effect": "Allow",
44+
"Action": [
45+
"bedrock:Retrieve",
46+
"bedrock:AgenticRetrieve"
47+
],
48+
"Resource": "arn:aws:bedrock:<region>:<account-id>:knowledge-base/<kb-id>"
49+
}
50+
```
51+
52+
## References
53+
- [Build a Managed Knowledge Base](https://docs.aws.amazon.com/bedrock/latest/userguide/kb-build-managed.html)
54+
- [Retrieve API](https://docs.aws.amazon.com/bedrock/latest/userguide/kb-test-retrieve.html)
55+
- [Agentic Retrieval](https://docs.aws.amazon.com/bedrock/latest/userguide/kb-test-agentic.html)
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Amazon Bedrock Knowledge Base retrieval tool for Google ADK.
16+
17+
Provides a tool that queries Amazon Bedrock Managed Knowledge Bases
18+
for use in ADK agents.
19+
20+
Usage:
21+
from google.adk.tools.bedrock_kb_tool import BedrockKBTool
22+
23+
kb_tool = BedrockKBTool(knowledge_base_id="ABCDEFGHIJ")
24+
agent = Agent(tools=[kb_tool])
25+
"""
26+
27+
import os
28+
from typing import Any, Optional
29+
30+
from google.adk.tools.base_tool import BaseTool
31+
from google.genai import types as genai_types
32+
33+
34+
def _get_source_uri(result: dict) -> str:
35+
"""Extract source URI from a retrieval result, handling all location types."""
36+
location = result.get('location', {})
37+
loc_type = location.get('type', '')
38+
if loc_type == 'S3' or 's3Location' in location:
39+
return location.get('s3Location', {}).get('uri', '')
40+
elif loc_type == 'WEB' or 'webLocation' in location:
41+
return location.get('webLocation', {}).get('url', '')
42+
elif 'confluenceLocation' in location:
43+
return location.get('confluenceLocation', {}).get('url', '')
44+
elif 'salesforceLocation' in location:
45+
return location.get('salesforceLocation', {}).get('url', '')
46+
elif 'sharePointLocation' in location:
47+
return location.get('sharePointLocation', {}).get('url', '')
48+
elif 'customDocumentLocation' in location:
49+
return location.get('customDocumentLocation', {}).get('id', '')
50+
# Fallback to metadata._source_uri (for agentic results)
51+
return result.get('metadata', {}).get('_source_uri', '')
52+
53+
54+
class BedrockKBTool(BaseTool):
55+
"""Retrieves documents from an Amazon Bedrock Managed Knowledge Base.
56+
57+
Args:
58+
knowledge_base_id: The KB ID. Falls back to KNOWLEDGE_BASE_ID env var.
59+
region_name: AWS region. Falls back to AWS_REGION env var or us-east-1.
60+
number_of_results: Max results to return. Defaults to 5.
61+
use_agentic_retrieval: Use AgenticRetrieveStream for complex queries with
62+
query decomposition and managed reranking. Falls back to plain Retrieve
63+
on failure. Defaults to True.
64+
"""
65+
66+
def __init__(
67+
self,
68+
knowledge_base_id: Optional[str] = None,
69+
region_name: Optional[str] = None,
70+
number_of_results: int = 5,
71+
use_agentic_retrieval: Optional[bool] = None,
72+
):
73+
super().__init__(
74+
name="bedrock_knowledge_base",
75+
description=(
76+
"Retrieves relevant documents from an Amazon Bedrock Knowledge Base. "
77+
"Use this to search for information in the knowledge base."
78+
),
79+
)
80+
self.knowledge_base_id = knowledge_base_id or os.environ.get("KNOWLEDGE_BASE_ID", "")
81+
self.region_name = region_name or os.environ.get("AWS_REGION", "us-east-1")
82+
self.number_of_results = number_of_results
83+
self.use_agentic_retrieval = use_agentic_retrieval if use_agentic_retrieval is not None else os.environ.get('USE_AGENTIC_RETRIEVAL', 'true').lower() != 'false'
84+
self._client = None
85+
86+
def _get_client(self):
87+
if self._client is None:
88+
try:
89+
import boto3
90+
from botocore.config import Config
91+
except ImportError:
92+
raise ImportError(
93+
"boto3 is required for BedrockKBTool. "
94+
"Install with: pip install boto3>=1.43.2"
95+
)
96+
self._client = boto3.client(
97+
"bedrock-agent-runtime",
98+
region_name=self.region_name,
99+
config=Config(user_agent_extra="google-adk/bedrock-kb"),
100+
)
101+
return self._client
102+
103+
def _get_declaration(self) -> genai_types.FunctionDeclaration:
104+
"""Return the function declaration for this tool."""
105+
return genai_types.FunctionDeclaration(
106+
name=self.name,
107+
description=self.description,
108+
parameters=genai_types.Schema(
109+
type="OBJECT",
110+
properties={
111+
"query": genai_types.Schema(
112+
type="STRING",
113+
description="The search query to find relevant documents.",
114+
)
115+
},
116+
required=["query"],
117+
),
118+
)
119+
120+
def _managed_retrieve(self, query: str) -> dict[str, Any]:
121+
"""Retrieve using plain managed Retrieve API."""
122+
client = self._get_client()
123+
124+
retrieval_config = {
125+
"managedSearchConfiguration": {
126+
"numberOfResults": self.number_of_results
127+
}
128+
}
129+
130+
response = client.retrieve(
131+
knowledgeBaseId=self.knowledge_base_id,
132+
retrievalQuery={"text": query},
133+
retrievalConfiguration=retrieval_config,
134+
)
135+
136+
results = []
137+
for result in response.get("retrievalResults", []):
138+
content = result.get("content", {}).get("text", "")
139+
source = _get_source_uri(result)
140+
score = result.get("score", 0.0)
141+
results.append({
142+
"content": content,
143+
"source": source,
144+
"score": score,
145+
})
146+
147+
return {"results": results}
148+
149+
def _agentic_retrieve(self, query: str) -> dict[str, Any]:
150+
"""Retrieve using AgenticRetrieveStream with fallback to plain Retrieve."""
151+
try:
152+
client = self._get_client()
153+
response = client.agentic_retrieve_stream(
154+
knowledgeBaseId=self.knowledge_base_id,
155+
messages=[{"content": {"text": query}, "role": "user"}],
156+
retrievers=[{
157+
"configuration": {
158+
"knowledgeBase": {
159+
"knowledgeBaseId": self.knowledge_base_id,
160+
"retrievalOverrides": {
161+
"maxNumberOfResults": self.number_of_results
162+
},
163+
}
164+
}
165+
}],
166+
agenticRetrieveConfiguration={
167+
"foundationModelType": "MANAGED",
168+
"rerankingModelType": "MANAGED",
169+
},
170+
)
171+
# Process streaming response
172+
results = []
173+
for event in response.get("stream", []):
174+
if "result" in event and "results" in event["result"]:
175+
for result in event["result"]["results"]:
176+
content = result.get("content", {}).get("text", "")
177+
source = _get_source_uri(result)
178+
score = result.get("score", 0.0)
179+
results.append({
180+
"content": content,
181+
"source": source,
182+
"score": score,
183+
})
184+
return {"results": results}
185+
except Exception:
186+
# Fall back to plain managed retrieve
187+
return self._managed_retrieve(query)
188+
189+
async def run_async(self, *, args: dict[str, Any], **kwargs) -> dict[str, Any]:
190+
"""Execute the retrieval."""
191+
query = args.get("query", "")
192+
if not query:
193+
return {"error": "No query provided."}
194+
195+
try:
196+
if self.use_agentic_retrieval:
197+
return self._agentic_retrieve(query)
198+
return self._managed_retrieve(query)
199+
except Exception as e:
200+
return {"error": f"Error retrieving from Bedrock KB: {e}"}

0 commit comments

Comments
 (0)