Build Agentic Movie Retrieval

Use OpenSearch and Bedrock to plan and evaluate movie searches.

Introduction

30 Second Summary

A movie request can sound simple while hiding rules about genre or release year. Basic search can recognize the words while missing what those rules mean.

In this project, you will build a movie retrieval lab with Amazon OpenSearch Serverless and Amazon Bedrock. You will compare lexical retrieval with agentic query planning by checking planner output against known-correct movie results.

What You'll Build

Your finished lab turns a detailed movie request into visible search logic while returning the titles that satisfy every constraint.

By the end of this project, you'll have:

  • A direct title lookup that returns Echo Protocol first. This gives you a fast baseline to demo.
  • A visible lexical failure that returns at least one movie breaking the requested rules. You can use the bad result to explain why word matching misses typed constraints.
  • An agentic retrieval path that displays its generated OpenSearch DSL beside matching movie hits. You can compare those hits against deterministic ground truth. You can record the observed response time.
  • Secret Mission: Prove a two-path retrieval policy that sends exact-title requests to direct search while reserving agentic search for multi-constraint requests.

Are there any prerequisites?

You need an AWS account that can access Amazon Bedrock models. Your AWS identity needs permission to create AWS Identity and Access Management (IAM) roles plus Amazon OpenSearch Serverless resources.

The lab assumes prior OpenSearch and Bedrock project experience on a Mac.

Before We Start

This opening checkpoint locks in the agentic movie retrieval lab you are about to build. You will compare direct lexical retrieval with agentic retrieval to reveal when typed query planning improves correctness and when model-call latency is unnecessary.

Set Up the Serverless Retrieval Lab

Native Agentic Search needs an Amazon OpenSearch Serverless collection to hold its indexes. It also needs a trusted IAM role that lets OpenSearch invoke Amazon Bedrock.

This step creates both cloud resources. You will prove that your current principal can reach the collection before loading any movies.

In this step, get ready to:
  • Create a trusted Bedrock invocation role for OpenSearch.
  • Create an active Search collection in OpenSearch Serverless.
  • Verify access through OpenSearch Dashboards Dev Tools.
Create the Bedrock invocation role

A role has two separate permission layers. Its trust policy controls which service can assume it.

Its permissions policy controls what the service can do after assuming it. OpenSearch needs both layers before it can invoke a Bedrock model.

  • Open the AWS Management Console in your browser.
  • Sign in with the AWS account you are using for this lab.
  • Choose US East (N. Virginia) from the Region menu so the console uses us-east-1.
  • Enter IAM in the console search bar.
  • Choose IAM from the search results.
  • Choose Roles in the IAM navigation pane.
  • Choose Create role.
  • Choose Custom trust policy as the trusted entity type.
  • Replace the policy editor contents with the OpenSearch Serverless trust policy by pasting the code below:
{
 "Version":"2012-10-17",
 "Statement": [
 {
 "Action": [
 "sts:AssumeRole"
 ],
 "Effect": "Allow",
 "Principal": {
 "Service": [
 "ml.opensearchservice.amazonaws.com"
 ]
 }
 }
 ]
}

What does this trust policy do?

  • The Principal entry trusts the OpenSearch Serverless machine learning service.
  • The sts:AssumeRole action lets that service use this role.
  • This trust relationship becomes the identity bridge between OpenSearch and Bedrock.
  • Choose Next below the trust policy editor.
  • Choose Next on the permissions page without selecting a managed policy.
  • Enter OpenSearchBedrockInvokeRole in the Role name field.
  • Choose Create role.
  • Select OpenSearchBedrockInvokeRole from the role list.
  • Select the Trust relationships tab.

You should see ml.opensearchservice.amazonaws.com as the trusted service principal. This confirms that the role can be assumed by OpenSearch Serverless.

Role creation blocked?

  • Check that every brace in the trust policy has a matching closing brace.
  • Confirm that the service principal is spelled exactly as shown in the policy.
  • Ask your AWS administrator to confirm that your current principal can create IAM roles.

Get help with an IAM role creation problem.

The role can now be assumed by OpenSearch. It still needs permission to invoke Bedrock after that handoff.

  • Select the Permissions tab on the role details page.
  • Choose Add permissions.
  • Choose Create inline policy.
  • Choose the JSON option in the policy editor.
  • Replace the editor contents with the official Bedrock runtime inference policy by pasting the code below:
{
 "Version": "2012-10-17", 
 "Statement": [
 {
 "Sid": "ModelInvocationPermissions",
 "Effect": "Allow",
 "Action": [
 "bedrock:InvokeModel",
 "bedrock:InvokeModelWithResponseStream",
 "bedrock:GetInvoke",
 "bedrock:CancelInvoke",
 "bedrock:DeleteInvoke",
 "bedrock:GetInferenceProfile",
 "bedrock:ListInferenceProfiles",
 "bedrock:RenderPrompt",
 "bedrock:GetCustomModel",
 "bedrock:ListCustomModels",
 "bedrock:GetImportedModel",
 "bedrock:ListImportedModels",
 "bedrock:GetProvisionedModelThroughput",
 "bedrock:ListProvisionedModelThroughputs",
 "bedrock:GetGuardrail",
 "bedrock:ListGuardrails",
 "bedrock:ApplyGuardrail"
 ],
 "Resource": "*"
 }
 ]
}

What does this permissions policy do?

  • The Allow effect grants the listed Bedrock runtime operations.
  • The bedrock:InvokeModel action authorizes model invocation through the Bedrock runtime.
  • The Resource value of * makes the listed permissions available to supported Bedrock resources.
  • Choose Next below the policy editor.
  • Enter ModelInvocationPermissions in the Policy name field.
  • Choose Create policy.
  • Select the Summary tab for OpenSearchBedrockInvokeRole.
  • Copy the value beside ARN.
  • Store the copied value here: your OpenSearchBedrockInvokeRole ARN.
  • Select the account menu in the console header to identify the IAM user or role running this lab.
  • Grant iam:PassRole on your OpenSearchBedrockInvokeRole ARN to that principal through its IAM permissions policy.

That is the identity chain ready. OpenSearch can assume the role when the agentic connector is registered later.

Unable to update the lab principal?

Permission management varies between personal accounts and organization-managed accounts. Ask your AWS administrator to grant the current principal iam:PassRole for the role ARN you copied.

Get help with checking the role and pass-role permissions.

Create the Serverless collection

A collection groups the OpenSearch indexes for one workload. The Search collection type supports the lexical movie queries you will build next.

This collection uses paid, usage-based compute and storage. The cleanup section shows you how to remove it after the lab.

  • Return to the AWS Management Console from earlier.
  • Confirm that the Region menu shows US East (N. Virginia).
  • Enter Amazon OpenSearch Service in the console search bar.
  • Choose Amazon OpenSearch Service from the search results.
  • Choose Collections in the left navigation pane.
  • Choose Create collection.
  • Enter movie-agentic-lab in the Name field.
  • Choose Search for the collection type.

Why Express Create?

Express Create sets up the collection group and OpenSearch UI application for this short lab. It also creates the initial encryption, network, and data-access configuration.

A managed domain requires instance sizing and lifecycle management. Serverless keeps this project focused on query planning.

  • Choose Express Create for the collection creation method.
  • Review the generated collection group setting.
  • Review the generated OpenSearch UI application setting.
  • Review the encryption setting that uses an AWS owned key.
  • Review the public network access setting for this synthetic catalog.
  • Review the auto-generated data access policy.

Provisioning can take several minutes, so a quiet status screen is expected. AWS is creating the collection and its supporting configuration during this wait.

  • Choose Create collection.
  • Wait for the collection status to change to Active.

Your movie-agentic-lab collection is now active in us-east-1. The Serverless retrieval environment is ready for an access check.

Collection creation blocked?

  • Confirm that the console Region remains US East (N. Virginia).
  • Ask your AWS administrator to confirm that your principal can create OpenSearch Serverless collections and their generated policies.
  • Refresh the collection list if the status remains unchanged after several minutes.

Get help with an OpenSearch Serverless collection problem.

Verify Dev Tools access

OpenSearch Serverless checks identity permissions and collection data-access rules separately. Both layers must authorize your current principal before Dev Tools can query the collection.

  • Choose movie-agentic-lab from the active collection list.
  • Choose the collection's OpenSearch Dashboards URL on its details page.
  • Expand the left navigation pane in OpenSearch Dashboards.
  • Choose Dev Tools.

Before you run the access check, what response do you expect from a new collection that has no indexes?

  • Run the collection access check by entering the request below in the Dev Tools editor:
GET _cat/indices

What does this request prove?

The _cat/indices endpoint lists the indexes that your principal can access. A successful request proves that the IAM and data-access layers both accepted the call.

You should see a successful empty response because the collection has no indexes yet. An authorization failure shows a 403 error instead.

✔️ I see an empty response

You cleared the cloud setup hurdle. Your empty collection is accessible and ready for the movie index.

ⓧ I see a 403 error

A 403 response means one of the two access layers still needs permission for your current principal.

  • Return to IAM in the AWS Management Console.
  • Open the Permissions tab for the current lab principal.
  • Allow aoss:APIAccessAll for the collection resource.
  • Allow aoss:DashboardsAccessAll for the collection resource.

Why are both access layers required?

IAM permissions authorize access to the Serverless data plane and OpenSearch Dashboards. The collection's data access policy controls the OpenSearch operations available inside that data plane.

  • Return to the Amazon OpenSearch Service console.
  • Expand Serverless in the left navigation pane.
  • Choose Data access policies under Security.
  • Select the generated policy that applies to movie-agentic-lab.
  • Choose Edit.
  • Add the current lab principal to the policy.
  • Grant the index rule aoss:CreateIndex, aoss:DescribeIndex, aoss:WriteDocument, aoss:ReadDocument, aoss:DeleteIndex, and aoss:UpdateIndex.
  • Grant the model rule aoss:CreateMLResource, aoss:DescribeMLResource, aoss:UpdateMLResource, aoss:DeleteMLResource, and aoss:ExecuteMLResource.
  • Grant the agent rule aoss:CreateAgent, aoss:DescribeAgent, aoss:UpdateAgent, aoss:DeleteAgent, aoss:InvokeAgent, and aoss:SearchAgents.
  • Grant the collection rule aoss:CreateCollectionItems, aoss:DescribeCollectionItems, aoss:UpdateCollectionItems, and aoss:DeleteCollectionItems.
  • Choose Save.
  • Wait about one minute for the updated permissions to take effect.
  • Switch back to Dev Tools from earlier.
  • Run the access-check request again.

The repeated request should now return an empty response. This confirms that your principal can use the collection's data plane.

Still seeing a 403 error?

Confirm that the principal in the data access policy matches the user or assumed role shown in your console session. Permission updates can also take a short time to propagate.

Get help with finding the missing Serverless permission.

✔️ Awesome, my lab is ready

Your invocation role, active collection, and Dev Tools access are all in place.

ⓧ I'd like to double check the full setup

  • Confirm that OpenSearchBedrockInvokeRole trusts ml.opensearchservice.amazonaws.com through sts:AssumeRole.
  • Confirm that its inline policy allows bedrock:InvokeModel on *.
  • Confirm that the lab principal can perform iam:PassRole on the copied role ARN.
  • Confirm that movie-agentic-lab is an active Search collection in us-east-1.
  • Confirm that the collection uses Express Create with the reviewed public-network and generated access settings.
  • Confirm that GET _cat/indices succeeds in Dev Tools with an empty response.

Your Serverless retrieval lab is ready. Next up, you will create the movie index and prove that direct title retrieval already works.

Build a Searchable Movie Baseline

Your Amazon OpenSearch Serverless collection is active. Its empty index list proves the retrieval lab is ready for data.

A direct-search baseline shows what OpenSearch retrieves well before an LLM is involved. A title lookup against a typed catalog gives you that evidence.

In this step, get ready to:
  • Define explicit field types for the movie catalog.
  • Load eight synthetic movie records.
  • Prove that a direct title search returns the expected movie first.
Create the explicit movie mapping

An index mapping defines how OpenSearch stores each field. Explicit types prepare the catalog for text ranking plus typed constraints later in the lab.

  • Replace the contents of the Dev Tools editor with this request:
PUT movies-index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "plot": { "type": "text" },
      "genres": { "type": "keyword" },
      "themes": { "type": "keyword" },
      "year": { "type": "integer" },
      "rating": { "type": "float" },
      "is_superhero": { "type": "boolean" }
    }
  }
}

What does this mapping do?

  • The two text fields support analyzed matching across movie titles or plots.
  • The title.keyword subfield preserves each full title for exact-value operations.
  • The keyword fields preserve exact genre or theme values.
  • The integer type stores each release year as a whole number.
  • The float type preserves decimal ratings.
  • The boolean type stores the superhero flag as a true-or-false value.
  • Run the mapping request from Dev Tools.

The catalog structure is locked in: movies-index now stores each field with its intended type.

Index request rejected?

  • Confirm the first line is PUT movies-index.
  • Confirm every JSON key uses double quotes.
  • Check that every opening brace has a matching closing brace.

Help me fix a rejected movie index mapping request.

Load the synthetic movie catalog

The _bulk endpoint writes several documents in one request. OpenSearch expects NDJSON with one action line before each source document.

  • Replace the contents of the Dev Tools editor with this bulk request:
POST movies-index/_bulk?refresh=wait_for
{ "index": { "_id": "1" } }
{ "title": "Echo Protocol", "plot": "An engineer and a learning machine uncover sabotage on a lunar colony.", "genres": ["science-fiction", "drama"], "themes": ["artificial-intelligence", "identity"], "year": 2018, "rating": 8.6, "is_superhero": false }
{ "index": { "_id": "2" } }
{ "title": "Synapse Harbor", "plot": "A coastal city asks a civic AI to predict floods, forcing its designer to challenge the system's choices.", "genres": ["science-fiction", "drama"], "themes": ["artificial-intelligence", "climate"], "year": 2023, "rating": 8.5, "is_superhero": false }
{ "index": { "_id": "3" } }
{ "title": "Neon Vanguard", "plot": "A masked superhero uses an artificial-intelligence suit to defend a megacity.", "genres": ["science-fiction", "action"], "themes": ["artificial-intelligence", "superhero"], "year": 2021, "rating": 8.7, "is_superhero": true }
{ "index": { "_id": "4" } }
{ "title": "Machine Dawn", "plot": "A dormant factory intelligence awakens and negotiates its freedom.", "genres": ["science-fiction", "thriller"], "themes": ["artificial-intelligence", "freedom"], "year": 2014, "rating": 8.8, "is_superhero": false }
{ "index": { "_id": "5" } }
{ "title": "Cape Circuit", "plot": "A superhero team hunts a rogue artificial-intelligence system across the city grid.", "genres": ["science-fiction", "action"], "themes": ["artificial-intelligence", "superhero"], "year": 2019, "rating": 8.4, "is_superhero": true }
{ "index": { "_id": "6" } }
{ "title": "Silent Orbit", "plot": "A stranded crew repairs a damaged research station before its orbit decays.", "genres": ["science-fiction", "drama"], "themes": ["space-survival", "teamwork"], "year": 2020, "rating": 8.4, "is_superhero": false }
{ "index": { "_id": "7" } }
{ "title": "Algorithm of Rain", "plot": "A detective questions a prediction engine while investigating a disappearance.", "genres": ["thriller", "drama"], "themes": ["artificial-intelligence", "surveillance"], "year": 2022, "rating": 8.5, "is_superhero": false }
{ "index": { "_id": "8" } }
{ "title": "Analog Summer", "plot": "Three friends rebuild a radio station during their final summer together.", "genres": ["drama", "coming-of-age"], "themes": ["friendship", "music"], "year": 2017, "rating": 8.3, "is_superhero": false }

What does this bulk request do?

  • Each index action assigns a stable document ID to the next movie record.
  • The refresh=wait_for parameter waits until the indexed documents become searchable.
  • The top-level errors value summarizes whether any item in the batch failed.

If the collection has been idle, a 10 to 30 second wait is normal while capacity returns.

  • Run the bulk request from Dev Tools.

You should see "errors": false in the response.

Your dataset is ready: all eight movie records are now searchable in movies-index.

Bulk load incomplete?

  • Check that every action line is followed by one movie document line.
  • Place the cursor after the final closing brace.
  • Press Enter once to add the required final newline.
  • Inspect the item responses if the top-level errors value is not false.

Help me diagnose an incomplete OpenSearch bulk load.

Test a direct title search

OpenSearch uses BM25 to rank keyword-search results by default. A multi_match query can give title matches more influence than plot matches.

  • Replace the contents of the Dev Tools editor with this search request:
POST movies-index/_search
{
  "query": {
    "multi_match": {
      "query": "Echo Protocol",
      "fields": ["title^4", "plot"]
    }
  }
}

How does this search rank results?

  • The multi_match query searches both configured text fields.
  • The title^4 boost gives title matches four times the influence of the unboosted plot field.
  • The _score value records the relevance score used to order each hit.

Before you run this, which movie do you expect OpenSearch to rank first?

  • Run the search request from Dev Tools.

You should see Echo Protocol as the first hit. Its _score is available in the hit metadata.

You have your first retrieval win: a direct title request now puts Echo Protocol first.

Expected movie missing?

  • Confirm the bulk response showed "errors": false.
  • Check that the query text is exactly Echo Protocol.
  • Confirm the fields list contains title^4 plus plot.

Help me find why Echo Protocol is missing from the first search hit.

Your baseline now proves that direct title retrieval works. Next, you will test where lexical matching falls short on typed constraints.

Watch a Lexical Query Misread Intent

Your direct title lookup in OpenSearch placed Echo Protocol at the top of the results. That proved keyword ranking handles a precise title well.

A natural-language request can hide several typed conditions inside one sentence. In this step, you will test whether a single lexical query preserves those conditions.

In this step, get ready to:
  • Send a complex movie request through one lexical query.
  • Inspect the returned movies for violated constraints.
  • Record the typed acceptance test for a correct result.
Run the naive full-sentence search

A multi_match query scores one piece of text across several fields. This test keeps the complete movie request inside one query.

Before you run it, which parts of the sentence do you think the query will treat as searchable words?

  • Test the full sentence in Dev Tools by running this request:
GET movies-index/_search
{
  "query": {
    "multi_match": {
      "query": "Find science-fiction movies about artificial-intelligence released after 2015 with rating at least 8.5, excluding superhero movies",
      "fields": ["title", "plot", "genres", "themes"]
    }
  }
}

What does this query test?

  • The query value sends the complete request through one lexical search.
  • The fields list limits scoring to four mapped movie fields.
  • The response keeps the movie documents available for a field-by-field audit.
  • Review the response in Dev Tools.

You should see a ranked list of movie hits with their mapped fields. The request executes successfully, which gives you evidence to audit.

Not seeing movie hits?

  • Confirm the request targets movies-index.
  • Confirm the multi_match object sits inside the top-level query object.
  • Confirm every field name matches the existing index mapping.

Ask for help diagnosing the request if the response has no movie hits: help me troubleshoot why my OpenSearch multi_match request against movies-index returns no movie hits.

Audit the typed constraints

A search score measures textual relevance. Correctness requires each returned movie to satisfy the request's field-level conditions.

  • Use each returned title to identify the movie.
  • Inspect each returned year value.
  • Inspect each returned rating value.
  • Inspect each returned is_superhero value.

You should find at least one hit with is_superhero = true, year <= 2015, or rating < 8.5.

You found the intended shortfall. The query returned related movies without enforcing every condition.

Why does superhero help the score?

OpenSearch uses BM25 to score keyword matches by default. The word superhero appears in the request.

A movie containing that word can gain positive match evidence. The request contains no typed conditions for year, rating, or is_superhero.

Record the acceptance test

An acceptance test converts the sentence into exact conditions. It gives you a deterministic target for evaluating later query plans.

  • Record genres contains science-fiction.
  • Record themes contains artificial-intelligence.
  • Record year > 2015.
  • Record rating >= 8.5.
  • Record is_superhero = false.

Before you compare this test with the response, do you think every returned hit will pass all five conditions?

  • Compare every returned hit with all five recorded conditions.

You should confirm that at least one returned hit is a superhero movie, was released in 2015 or earlier, or has a rating below 8.5.

Your evidence now separates ranked relevance from typed correctness. The acceptance test provides the exact standard for evaluating the next query path.

✔️ Awesome, I've got everything!

Your evidence includes the naive request, a violating hit, and the five-condition acceptance test.

ⓧ I'd like to double check the full code

Compare your Dev Tools request with this reference.

GET movies-index/_search
{
  "query": {
    "multi_match": {
      "query": "Find science-fiction movies about artificial-intelligence released after 2015 with rating at least 8.5, excluding superhero movies",
      "fields": ["title", "plot", "genres", "themes"]
    }
  }
}

Required conditions are genres contains science-fiction, themes contains artificial-intelligence, year > 2015, rating >= 8.5, and is_superhero = false.

Your lexical test now has a documented failure and a precise correctness target. Next up, you will add an agentic query planner that turns natural-language constraints into typed OpenSearch DSL.

Add the Agentic Query Planner

Your previous search proved that OpenSearch can match words across the movie catalog. It also exposed how lexical matching treats phrases such as “after,” “at least,” and “excluding” as text instead of typed constraints.

Now you’ll connect a QueryPlanningTool that uses the index mapping to generate DSL before OpenSearch runs the search. Can the planner turn the same request into filters for genre, theme, year, rating, and superhero status?

In this step, get ready to:
  • Register an inline connector for Amazon Bedrock.
  • Create a flow agent that generates query DSL.
  • Create a search pipeline that executes the planned query.
Connect OpenSearch to Claude Haiku 4.5

An inline connector gives OpenSearch a signed route to Claude Haiku 4.5 through Amazon Bedrock. The existing IAM role supplies permission to invoke the model without placing AWS credentials in the request.

This connection enables usage-based model calls through AWS Marketplace. You will send one test request in this step.

  • Return to the Dev Tools panel from earlier.
  • Store the copied role ARN here: your OpenSearchBedrockInvokeRole ARN.
  • Send the inline connector definition by running this request:
POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "Bedrock Claude Haiku",
  "function_name": "remote",
  "connector": {
    "name": "Amazon Bedrock Connector",
    "description": "Connector for Amazon Bedrock Claude Haiku",
    "version": 1,
    "protocol": "aws_sigv4",
    "parameters": {
      "region": "us-east-1",
      "service_name": "bedrock",
      "model": "us.anthropic.claude-haiku-4-5-20251001-v1:0"
    },
    "credential": {
      "roleArn": "[[BEDROCK_ROLE_ARN="your OpenSearchBedrockInvokeRole ARN"]]"
    },
    "actions": [
      {
        "action_type": "predict",
        "method": "POST",
        "url": "https://bedrock-runtime.${parameters.region}.amazonaws.com/model/${parameters.model}/converse",
        "headers": {
          "content-type": "application/json"
        },
        "request_body": "{ \"system\": [{\"text\": \"${parameters.system_prompt}\"}], \"messages\": [${parameters._chat_history:-}{\"role\":\"user\",\"content\":[{\"text\":\"${parameters.user_prompt}\"}]}${parameters._interactions:-}]${parameters.tool_configs:-} }"
      }
    ]
  }
}

What does this connector do?

  • The aws_sigv4 protocol lets OpenSearch sign model requests with the IAM role.
  • The region and model parameters route each request to the Claude Haiku 4.5 inference profile.
  • The request body passes the planner’s prompts and tool configuration to the Bedrock Converse API.
  • Locate model_id in the response.
  • Store the returned value here: your returned model_id.

The model bridge is ready. OpenSearch now has a deployed model reference that the query planner can call.

Model registration failed?

  • Confirm that the role ARN belongs to OpenSearchBedrockInvokeRole.
  • Confirm that the role trust policy permits sts:AssumeRole for ml.opensearchservice.amazonaws.com.
  • Confirm that your lab principal can perform iam:PassRole on the role.

Help me diagnose an OpenSearch Bedrock model registration failure.

Create the movie retrieval flow agent

A flow agent runs one QueryPlanningTool for a one-shot planning task. It reads the natural-language request alongside the index mapping before returning generated DSL.

Why a flow agent?

A flow agent focuses on query planning through one model call. It requires the target index in each search request.

Conversational agents add memory and multi-tool orchestration. This lab uses a known index with a single planning task, so the flow stays direct.

  • Bind the deployed model to the QueryPlanningTool by running this request:
POST /_plugins/_ml/agents/_register
{
  "name": "Movie Retrieval Flow Agent",
  "type": "flow",
  "description": "Agent for agentic search",
  "tools": [
    {
      "type": "QueryPlanningTool",
      "parameters": {
        "model_id": "[[MODEL_ID="your returned model_id"]]",
        "response_filter": "$.output.message.content[0].text"
      }
    }
  ]
}

What does this agent do?

  • The flow type runs the configured tool as a focused sequence.
  • The model_id connects the QueryPlanningTool to the deployed Claude Haiku 4.5 model.
  • The response filter extracts the generated DSL text from the Bedrock response.
  • Locate agent_id in the response.
  • Store the returned value here: your returned agent_id.

Your planner now has an orchestration layer. Movie Retrieval Flow Agent can send a request to the model and extract its DSL output.

Agent registration failed?

  • Confirm that the pasted model_id matches the value returned by model registration.
  • Confirm that the collection data-access policy grants the current principal permission to create agents.
  • Use a unique agent name if an earlier attempt already created Movie Retrieval Flow Agent.

Help me troubleshoot a failed OpenSearch flow-agent registration.

Build the pipeline and run the planned search

The search pipeline joins planning to retrieval. Its request processor translates natural language into DSL before OpenSearch executes the resulting query.

  • Bind the flow agent to movie-agentic-pipeline by running this request:
PUT /_search/pipeline/movie-agentic-pipeline
{
  "request_processors": [
    {
      "agentic_query_translator": {
        "agent_id": "[[AGENT_ID="your returned agent_id"]]"
      }
    }
  ],
  "response_processors": [
    {
      "agentic_context": {
        "dsl_query": true
      }
    }
  ]
}

What does this pipeline do?

  • The agentic_query_translator processor sends the natural-language request to the flow agent.
  • The agentic_context processor adds the executed query to the response.
  • Setting dsl_query to true makes the generated query visible under ext.dsl_query.
  • Confirm that the response reports successful pipeline creation.

Pipeline creation failed?

  • Confirm that the pasted agent_id matches the value returned by agent registration.
  • Confirm that the collection data-access policy permits collection-item operations for search pipelines.

Help me diagnose an OpenSearch agentic pipeline creation failure.

The first request after an idle NextGen collection can take 10 to 30 seconds while capacity returns. That pause is expected during scale-up.

  • Predict whether the planner can convert every phrase in the movie request into a typed constraint.
  • Execute the complex request through movie-agentic-pipeline by running this search:
GET movies-index/_search?search_pipeline=movie-agentic-pipeline
{
  "query": {
    "agentic": {
      "query_text": "Find science-fiction movies about artificial-intelligence released after 2015 with rating at least 8.5, excluding superhero movies",
      "query_fields": [
        "genres", "themes", "year", "rating", "is_superhero", "title", "plot"
      ]
    }
  }
}

What happens during this search?

  • The agentic query sends the full request and selected mapping fields to the planner.
  • The QueryPlanningTool can choose exact clauses for keyword and Boolean values.
  • The QueryPlanningTool can choose range clauses for year and rating comparisons.
  • OpenSearch executes the returned DSL against movies-index.

You’ll see movie documents under hits. You’ll also see a generated query string under ext.dsl_query.

Your agentic path is live. The same natural-language request now produces visible DSL before retrieval.

Missing hits or generated DSL?

  • Confirm that the search URL names movies-index and movie-agentic-pipeline exactly.
  • Confirm that every query_fields entry exists in the current index mapping.
  • Confirm that the data-access policy permits agent invocation and ML-resource execution.

Help me troubleshoot an agentic search that lacks hits or ext.dsl_query.

The planner can now expose the query it generated. Next, you’ll compare that plan with deterministic ground truth and separate correctness from observed latency.

Evaluate the Plan, Results, and Latency

Your agentic search pipeline can now return movies from a natural-language request. A plausible result list still leaves the planner's constraint handling unproven.

This step tests the planner against deterministic ground truth. You will also separate correctness evidence from observed response time.

In this step, get ready to:
  • Run a deterministic ground-truth query for the five acceptance conditions.
  • Grade the agentic hits plus generated query against that reference.
  • Benchmark the direct path plus agentic path with three runs each.
Build the deterministic ground truth

A deterministic ground truth encodes each requirement directly in OpenSearch Query DSL. It gives you a fixed reference for grading the planner.

Before you run this, which two catalog titles do you expect to satisfy every acceptance condition?

  • Run the acceptance test in the Dev Tools panel from earlier by sending this request:
GET movies-index/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "genres": "science-fiction" } },
        { "term": { "themes": "artificial-intelligence" } },
        { "range": { "year": { "gt": 2015 } } },
        { "range": { "rating": { "gte": 8.5 } } },
        { "term": { "is_superhero": false } }
      ]
    }
  }
}

What does this query prove?

  • The genres term requires the exact value science-fiction.
  • The themes term requires the exact value artificial-intelligence.
  • The year range requires a value greater than 2015.
  • The rating range requires a value of at least 8.5.
  • The is_superhero term requires the Boolean value false.
  • The bool.filter structure applies every condition without adding relevance scores.
  • Inspect the _id plus title fields in hits.hits.

You should see Echo Protocol with ID 1. You should also see Synapse Harbor with ID 2.

That locks down your reference set. Every planner result now has a concrete standard to meet.

Seeing extra or missing titles?

Compare every term value with the acceptance test from earlier. Check that the two range operators are gt plus gte.

Confirm that all five clauses sit inside the same bool.filter array.

Help me diagnose my deterministic movie query.

Grade the agentic plan

The generated query is the planner's auditable artifact. Its clauses reveal whether the natural-language constraints became typed filters.

  • Re-run the agentic request in the Dev Tools panel from earlier by sending this request:
GET movies-index/_search?search_pipeline=movie-agentic-pipeline
{
  "query": {
    "agentic": {
      "query_text": "Find science-fiction movies about artificial-intelligence released after 2015 with rating at least 8.5, excluding superhero movies",
      "query_fields": [
        "genres",
        "themes",
        "year",
        "rating",
        "is_superhero",
        "title",
        "plot"
      ]
    }
  }
}

What does this request expose?

  • The agentic query sends the complete request to the planner.
  • The query_fields array names the mapped fields available for planning.
  • The movie-agentic-pipeline pipeline executes the generated query.
  • The response includes the generated query under ext.dsl_query.
  • Record the returned titles from hits.hits.
  • Record the returned hit IDs from hits.hits.
  • Inspect ext.dsl_query for exact-value clauses.
  • Inspect ext.dsl_query for numeric range clauses.
  • Compare the agentic hit IDs with ground-truth IDs 1 plus 2.

How should you label correctness?

  • Exact means the hit IDs match the ground truth. The generated query must also represent all five acceptance conditions.
  • Partial means some evidence aligns with the ground truth. At least one hit or constraint still differs.
  • Mismatch means the returned set conflicts with the ground truth.

Agentic evaluation table columns

  • Path identifies the deterministic or agentic row.
  • Returned titles captures the visible movie results.
  • Hit IDs provides the exact set for comparison.
  • Generated ext.dsl_query clauses records the planner's typed structure.
  • Observed took captures the response value.
  • Correctness records an exact, partial, or mismatch label.
  • Create an agentic evaluation table in your notes using the columns above.
  • Add a ground-truth row containing Echo Protocol plus Synapse Harbor.
  • Add an agentic row containing the response you observed.
  • Assign the agentic row an exact, partial, or mismatch label.

Your table should now connect the returned movies to the planner's generated clauses. This makes the correctness decision reproducible.

Missing the generated query?

Confirm that the request uses movie-agentic-pipeline as its search pipeline. The pipeline's response processor must have dsl_query set to true.

Help me find the missing generated query.

Benchmark both paths

One timing can reflect temporary service conditions. Three runs let you use the middle value as a small latency summary.

After scale-to-zero, a 10 to 30 second first request is expected while capacity returns.

Before you run the benchmark, which path do you expect to have the lower median took?

  • Load the direct title request in the Dev Tools panel from earlier by sending this request:
GET movies-index/_search
{
  "query": {
    "multi_match": {
      "query": "Echo Protocol",
      "fields": ["title^4", "plot"]
    }
  }
}

What does the direct request measure?

  • The multi_match query uses BM25 lexical ranking across two text fields.
  • The title^4 boost makes a title match more influential than a plot match.
  • The response's took value supplies one observed timing for this path.
  • Run the direct title request three times.
  • Record the took value after each run.

Each response should return Echo Protocol as the first hit.

Direct results changed?

Confirm that the query text is Echo Protocol. Check that the fields remain title^4 plus plot.

Help me troubleshoot the direct title benchmark.

  • Load the complex agentic request in the Dev Tools panel by sending this request:
GET movies-index/_search?search_pipeline=movie-agentic-pipeline
{
  "query": {
    "agentic": {
      "query_text": "Find science-fiction movies about artificial-intelligence released after 2015 with rating at least 8.5, excluding superhero movies",
      "query_fields": [
        "genres",
        "themes",
        "year",
        "rating",
        "is_superhero",
        "title",
        "plot"
      ]
    }
  }
}

What does the agentic request measure?

  • The request sends the multi-constraint sentence through the configured planner.
  • The pipeline executes the generated query against movies-index.
  • The response's took value supplies one observed timing for the agentic path.
  • Run the complex agentic request three times.
  • Record the took value after each run.

Each response should contain movie hits. You should also see the generated query under ext.dsl_query.

Agentic request stopped returning results?

Confirm that movie-agentic-pipeline still exists. Confirm that the deployed model plus Movie Retrieval Flow Agent remain available.

Help me troubleshoot the agentic benchmark.

  • Order the three direct took values from smallest to largest.
  • Record the middle direct value as its median.
  • Order the three agentic took values from smallest to largest.
  • Record the middle agentic value as its median.
  • Add all six observed values to your latency benchmark.
  • Add both medians to your latency benchmark.

You should now have three direct took values plus their median. You should also have three agentic took values plus their median.

How should you discuss the evidence?

Correctness measures whether the planner honored the five acceptance conditions. The exact, partial, or mismatch label captures that result.

Latency measures the observed response times for each path. Keep this evidence separate so speed never hides a retrieval error.

These three-run medians support a lab discussion. They are not enterprise performance benchmarks.

You have turned the planner demonstration into evidence. Your tables now show what the planner generated, what it returned, how correct it was, plus how long each path took.

Secret mission

Prove a Two-Path Retrieval Policy

A title lookup should not pay the planning cost of a multi-constraint request. Test four requests to prove when direct search is enough and when the agentic planner earns its place.

Clean Up Your Resources

Clean Up Your Resources

This cloud lab can continue generating AWS charges while its resources remain configured. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Cost warning

Amazon OpenSearch Serverless charges separately for compute usage and stored data.

Model calls through Amazon Bedrock incur usage charges through AWS Marketplace.

Eligible NextGen compute can scale to zero after 10 minutes without requests. Stored data remains billable until you delete it.

Resources you used:

  • The movie-agentic-lab collection containing movies-index.
  • The deployed model_id registration inside the collection.
  • The Movie Retrieval Flow Agent flow agent inside the collection.
  • The movie-agentic-pipeline search pipeline inside the collection.
  • The NextGen collection group created by Express Create.
  • The OpenSearch UI application created for the collection.
  • The collection-specific workspace.
  • The collection security configuration and data-access configuration.
  • The OpenSearchBedrockInvokeRole IAM role with its trust policy and invocation permissions.

Keep everything running

No action is needed. Choose this if you plan to continue testing retrieval strategies with the existing movie catalog.

  • Continue using movie-agentic-lab for direct lexical searches.
  • Continue using movie-agentic-pipeline for planner-worthy requests.
  • Keep OpenSearchBedrockInvokeRole available for model invocation.
  • Leave the collection group's minimum indexing capacity at 0.
  • Leave the collection group's minimum search capacity at 0.

Idle compute can scale to zero when the collection group receives no requests. Your stored movie data remains available for later experiments.

Pause - I'll come back to this later

Pausing keeps the lab configuration available while eligible NextGen compute scales down. Storage charges continue while the collection exists.

  • Stop sending indexing requests to movie-agentic-lab.
  • Stop sending search requests to movie-agentic-lab.
  • Leave the collection idle for at least 10 minutes.
  • Keep OpenSearchBedrockInvokeRole configured for your return.

After 10 minutes without requests, eligible NextGen indexing compute can scale to 0 OCU. Eligible search compute can also scale to 0 OCU.

The first request after zero capacity can take 10 to 30 seconds while capacity returns. That delay is expected after an idle period.

  • Resume the lab later by sending an indexing request or search request.

Delete - I don't want to use this again

Deletion permanently removes the lab resources that can generate further storage or inference usage. Your copied benchmark results remain useful evidence of the architecture you tested.

  • Copy any agentic evaluation or routing benchmark evidence that you want to keep.

The collection is the first dependency in the teardown order.

  • Return to Amazon OpenSearch Service in the AWS Management Console.
  • Choose Collections.
  • Select movie-agentic-lab.
  • Choose Delete.
  • Confirm the collection deletion.

You should no longer see movie-agentic-lab listed as Active under Collections.

The dedicated model invocation role comes next.

  • Return to IAM in the AWS Management Console.
  • Choose Roles.
  • Select OpenSearchBedrockInvokeRole.
  • Choose Delete on the role page.
  • Confirm the role deletion.

You should no longer see OpenSearchBedrockInvokeRole under Roles.

Express Create also generated resources around the collection. Remove each collection-specific entry if you do not plan to reuse it.

  • Return to Amazon OpenSearch Service.
  • Delete the collection group created for movie-agentic-lab if it remains without reusable collections.
  • Delete the collection-specific OpenSearch UI application.
  • Delete the collection-specific workspace.
  • Delete the security configuration dedicated to movie-agentic-lab.
  • Delete the data-access policies dedicated to movie-agentic-lab.

Confirm that no collection group, application, workspace, or policy dedicated to movie-agentic-lab remains. That completes the teardown for the paid cloud lab.

Nice Work!

Nice Work!

You did it! Your Amazon OpenSearch Serverless movie lab now demonstrates direct retrieval, agentic query planning, deterministic evaluation, and latency-aware routing.

You've learned how to:

  • Build a typed movie index with explicit mappings. You also loaded a controlled catalog that made retrieval results easy to verify.
  • Demonstrate a lexical search win with an exact title. You also exposed how a naive full-sentence query misreads numeric constraints and exclusions.
  • Connect Amazon Bedrock to a flow agent that generates typed query clauses. You evaluated its generated DSL against deterministic ground truth. You also separated retrieval correctness from observed response time.
  • Secret Mission: Proved a two-path routing policy that sends exact-title lookups through direct lexical search. Multi-constraint requests use the agentic planner.

Ready to quiz yourself?