Kendra & Amazon Q
Kendra is managed enterprise search with 40+ connectors; Amazon Q is a ready-made assistant family. Both sit at the 'buy' end of the build-vs-buy spectrum.
- AWS credentials (
aws configure) +pip install boto3
Learning objectives
- Explain Kendra vs. a Bedrock Knowledge Base — managed enterprise search vs. RAG store.
- Query Kendra and read ranked results with connectors to enterprise sources.
- Explain what Amazon Q Developer and Q Business give you out of the box.
- Choose the right tool: Kendra, a KB, or Q.
code/aws12-kendra-q/ in the course, with a README on how to run it. Read here, run there — no need to copy-paste.Kendra vs. Knowledge Base advanced
Both search your documents, but for different jobs. A Knowledge Base (W4) is a vector store you build RAG on. Kendra is a managed enterprise search service with 40+ connectors (SharePoint, S3, Confluence, ServiceNow…), natural-language ranking, and access control baked in. Use Kendra when the value is finding across many enterprise systems; use a KB when you are building a RAG app.
| Need | Use |
|---|---|
| RAG store for an app you build | Bedrock Knowledge Base (W4) |
| Search across many enterprise systems | Kendra |
| A ready-made coding assistant | Amazon Q Developer |
| A ready-made assistant over company data | Amazon Q Business |
Query Kendra advanced
kendra_query.pyimport boto3
kendra = boto3.client("kendra", region_name="us-east-1")
resp = kendra.query(
IndexId="index-abc123",
QueryText="how do I request production access?",
)
for item in resp["ResultItems"][:3]:
title = item.get("DocumentTitle", {}).get("Text", "")
excerpt = item.get("DocumentExcerpt", {}).get("Text", "")
print(f"- {title}: {excerpt[:80]}")
Kendra is Amazon's managed enterprise search — think of it as a smart search box wired into your company's documents (SharePoint, S3, Confluence, and so on). This little program asks Kendra a plain-English question and prints the three best matching documents. There is no AI text generation here — Kendra just finds and ranks real documents, the way a very good internal Google would.
import boto3loads the AWS SDK for Python — the library that lets your code talk to AWS services.boto3.client("kendra", region_name="us-east-1")opens a connection to the Kendra service in theus-east-1region and stores it in the variablekendra. (This needs AWS credentials to actually run — see the orange box at the top of the page.)kendra.query(...)is the actual search call.IndexId="index-abc123"names which Kendra index (the pre-built collection of your indexed documents) to search — that placeholder ID would be swapped for your real one.QueryText="how do I request production access?"is the natural-language question you're asking.- The reply comes back as a dictionary stored in
resp.resp["ResultItems"]is the list of matches, already ranked best-first by Kendra.[:3]is a slice that keeps only the first three, and thefor item in ...loop then handles each one in turn. - For each result,
item.get("DocumentTitle", {}).get("Text", "")safely digs out the document's title..get(key, default)returns the default instead of crashing when a field is missing — so a result with no title becomes an empty string""rather than an error. The same pattern pulls out a shortexcerpt(a snippet of the matching text). print(f"- {title}: {excerpt[:80]}")prints one line per result. Thef"..."is an f-string that drops the variables' values into the text, andexcerpt[:80]trims the snippet to its first 80 characters so each line stays short.
What the output means: Up to three lines, each starting with -, showing a document's title followed by the first 80 characters of the matching passage — for example - Prod Access Runbook: To request production access, open a ticket in.... These are real ranked search hits, not a generated answer.
Try this: Change QueryText to a different question your docs would cover, or change [:3] to [:5] to see the top five results instead of three. To turn this into a full answer, you'd feed those excerpts to Claude (see Exercise W12.1) — Kendra finds the sources, Claude writes the reply.
Amazon Q expert
Amazon Q is AWS's ready-made assistant family — you configure, not build. Q Developer is a coding assistant (IDE + CLI, AWS-aware) comparable to the tools in AI-Assisted Development. Q Business is a managed assistant over your connected company data, with the retrieval, security, and UI handled.
Exercise W12.1 — Kendra-backed Claude
Context: You have already built a Bedrock Knowledge Base RAG answer in an earlier week. Doing the same question through Kendra + Claude lets you feel the trade-offs between managed enterprise search and a KB you own — on identical inputs.
Your task: Query Kendra for a question, take the top 3 excerpts, feed them to Claude via converse to synthesize a cited answer, and compare the result to your earlier Knowledge Base answer on the same question.
Requirements:
- Query Kendra and take the top 3
ResultItems' excerpts as context - Prompt Claude via
converseto answer from those excerpts and cite which ones it used - Ensure every claim in the answer traces to a supplied excerpt (grounded, cited)
- Run the identical question through your earlier Bedrock KB pipeline
- Compare the two answers — grounding quality, citations, and effort to build
- Note when you'd pick Kendra (many connectors, turnkey search) vs. a KB (own the RAG)
💡 Hint: Hold the question fixed and only swap the retriever; the interesting differences are in citation quality and how much of the pipeline you had to build yourself.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: Kendra is AWS's managed enterprise search: point it at your documents and ask natural-language questions, and it returns ranked passages with titles and excerpts — no embeddings or vector store to run yourself.
Your task: Use Kendra query with an index id and a natural-language question, and print each result's title and excerpt.
Requirements:
- Call
queryon aboto3kendraclient, passing anIndexIdand theQueryText - Iterate the ranked
ResultItemsthe response returns - Read the title from
DocumentTitle.Textand the snippet fromDocumentExcerpt.Text, guarding for missing keys - Print title and a trimmed excerpt for each hit
💡 Hint: Kendra already ranks the results; those nested DocumentTitle/DocumentExcerpt objects can be absent, so use .get defensively.
Show solution
Kendra returns ranked ResultItems; each carries a DocumentTitle and a DocumentExcerpt.
import boto3
kendra = boto3.client("kendra", region_name="us-east-1")
resp = kendra.query(
IndexId="index-abc123",
QueryText="how do I request production access?",
)
for item in resp["ResultItems"]:
title = item.get("DocumentTitle", {}).get("Text", "")
excerpt = item.get("DocumentExcerpt", {}).get("Text", "")
print(title, "::", excerpt[:60])
Context: Kendra and a Bedrock Knowledge Base look similar but solve different problems: Kendra is managed search across many enterprise connectors, while a KB is a vector store you build your own RAG generation on top of.
Your task: Write a short decision function that states when to reach for Kendra versus a Bedrock Knowledge Base. Runs offline.
Requirements:
- Take the deciding factors as inputs (e.g. whether you need many connectors, whether you are building your own RAG app)
- Return
"bedrock-knowledge-base"when you own the RAG generation and don't need broad connectors - Return
"kendra"when you need managed search across sources like SharePoint/S3 - Capture the core distinction: Kendra = search-as-a-service; KB = a vector store you generate answers from
💡 Hint: Frame it as buy-the-search vs. build-the-RAG: connectors and turnkey search point at Kendra; owning generation points at a Knowledge Base.
Show solution
Kendra is managed enterprise search across many connectors; a KB is a vector store you build a RAG app on.
def search_choice(need_many_connectors, building_rag_app):
if building_rag_app and not need_many_connectors:
return "bedrock-knowledge-base" # you own the RAG generation
if need_many_connectors:
return "kendra" # search across SharePoint/S3/etc.
return "kendra"
print(search_choice(True, False)) # kendra
print(search_choice(False, True)) # bedrock-knowledge-base
Context: Kendra finds the right passages; Claude phrases the answer. Wiring them together by hand — search hits as grounding context in a converse prompt — is RAG assembled from parts, giving you full control over the prompt.
Your task: Take Kendra excerpts and build a grounded prompt for Bedrock converse so Claude answers strictly from the search hits (Kendra + Claude = RAG by hand).
Requirements:
- Query Kendra for the question and take the top few
ResultItems' excerpts - Concatenate those excerpts into a single context block
- Build a prompt that supplies the context and the question and instructs the model to answer from the context only
- Send it via
converseon abedrock-runtimeclient and print the model's text reply - Keep search (Kendra) and generation (Claude) as two distinct steps
💡 Hint: Kendra supplies the passages, Claude supplies the wording; the "answer from context only" instruction is what keeps it grounded in the retrieved hits.
Show solution
Kendra finds passages; Claude phrases the answer. Concatenate the top excerpts as context.
import boto3
kendra = boto3.client("kendra", region_name="us-east-1")
brt = boto3.client("bedrock-runtime", region_name="us-east-1")
q = "What is the on-call escalation path?"
hits = kendra.query(IndexId="index-abc123", QueryText=q)["ResultItems"]
context = "\n---\n".join(h.get("DocumentExcerpt", {}).get("Text", "")
for h in hits[:3])
prompt = f"Context:\n{context}\n\nQuestion: {q}\nAnswer from context only."
r = brt.converse(modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
messages=[{"role":"user","content":[{"text": prompt}]}])
print(r["output"]["message"]["content"][0]["text"])
Context: The AWS assistant landscape is a build-vs-buy spectrum. Amazon Q is the buy end (fastest, least control), a Bedrock Agent is the build end (full control, you wire the tools and loop), and Kendra sits in between as managed search.
Your task: Map Amazon Q (Developer / Business), Kendra, and a Bedrock Agent onto a build-vs-buy spectrum, then recommend one for ‘a managed assistant over our Confluence + ServiceNow with zero code’. Runs offline.
Requirements:
- Place each option on the spectrum with a one-line role: Q Business/Developer at the buy end, Kendra mid, Bedrock Agent at the build end
- Encode the recommendation as a function of the requirements (zero-code, over connected data)
- Recommend
q-businessfor the zero-code managed-assistant-over-connectors case - Fall back to
bedrock-agentwhen full control is required - Justify why Q Business fits: managed, connector-driven, no code to own
💡 Hint: Zero code plus "over our existing systems" is the tell for the buy end; reach for a Bedrock Agent only when you must own the tools and control loop.
Show solution
Q is the buy end (fastest, least control); a Bedrock Agent is the build end. Q Business fits a zero-code managed assistant over connectors.
SPECTRUM = {
"q-business": ("buy", "managed assistant over connected company data"),
"q-developer": ("buy", "AWS-aware coding assistant in the IDE/CLI"),
"kendra": ("mid", "managed enterprise search you query"),
"bedrock-agent": ("build","full control, you wire tools + loop"),
}
def recommend(zero_code, over_connectors):
if zero_code and over_connectors:
return "q-business"
return "bedrock-agent"
print(recommend(True, True)) # q-business
print(SPECTRUM["q-business"])
Context: Grounding an LLM on weak search hits is how you get confident nonsense. Kendra tags each result with a ScoreConfidence band (VERY_HIGH…LOW), so you can drop low-confidence passages before they ever reach the model.
Your task: Build a relevance gate over Kendra scores that keeps only HIGH-or-better results before handing context to Claude. Runs offline.
Requirements:
- Map the confidence bands (
VERY_HIGH,HIGH,MEDIUM,LOW) to an order so they can be compared - Read each item's band from
ScoreAttributes.ScoreConfidence, defaulting safely when it is missing - Keep only items at or above a minimum band (default
HIGH) - Return the surviving items so only strong hits become grounding context
- Demonstrate offline that a
LOWhit is dropped and aVERY_HIGHhit is kept
💡 Hint: Turn the ordinal bands into numbers so "HIGH+" is a simple >= comparison; filter the list before it reaches the prompt.
Show solution
Filtering low-confidence hits keeps the LLM from grounding on noise.
ORDER = {"VERY_HIGH": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1}
def strong_hits(items, min_conf="HIGH"):
floor = ORDER[min_conf]
out = []
for it in items:
conf = it.get("ScoreAttributes", {}).get("ScoreConfidence", "LOW")
if ORDER.get(conf, 0) >= floor:
out.append(it)
return out
items = [{"ScoreAttributes": {"ScoreConfidence": "VERY_HIGH"}},
{"ScoreAttributes": {"ScoreConfidence": "LOW"}}]
print(len(strong_hits(items))) # 1
Context: Leadership wants an internal help desk over SharePoint + ServiceNow live in two weeks, with room to add custom actions later. The pragmatic answer is phased: buy for speed now, keep a build path open for what buying can't do.
Your task: Encode the decision for a phased help-desk rollout — ship fast on the buy end, then migrate to a build option when custom actions are needed — and justify it. Runs offline.
Requirements:
- Take the deadline and whether custom actions are needed now as inputs
- Choose
q-businessfor phase 1 when the deadline is tight (connectors + zero code go live fast) - Reserve
bedrock-agentfor a later phase when custom actions are required, else ‘revisit later’ - Return a phased plan (phase 1 / phase 2) rather than a single tool
- Explain the rationale: buy first for speed, build later for control over hot paths
💡 Hint: Two weeks rules out building from scratch; let the deadline pick Q Business for phase 1 and gate the Bedrock Agent on a real need for custom actions.
Show solution
Start on the buy end for speed, keep a build path open for the custom actions Q can't do.
def plan(deadline_weeks, need_custom_actions_now):
phase1 = "q-business" # connectors + zero code -> live fast
phase2 = "bedrock-agent" if need_custom_actions_now else "revisit later"
fast = deadline_weeks <= 3
return {"phase1": phase1 if fast else "bedrock-agent",
"phase2": phase2}
print(plan(2, need_custom_actions_now=False))
# phase1 q-business (2 wks, connectors, no code);
# phase2 revisit later -> migrate hot paths to a Bedrock Agent when custom
# actions are needed. Buy first for speed, build later for control.
✓ Checkpoint — you can move on when you can…
- Explain when to use Kendra vs. a Bedrock Knowledge Base.
- Query Kendra and read ranked results.
- Describe what Q Developer and Q Business provide.
- Place Kendra, KB, and Q on the build-vs-buy spectrum.
Knowledge check check yourself
When should you reach for Kendra versus a Bedrock Knowledge Base?
Show answer
Place Amazon Q and a Bedrock Agent on the build-vs-buy spectrum, and note what Q Developer vs Q Business offer.