1. Introduction
A few weeks ago, someone asked me a question:
"How can I use AI to improve my inventory if I have similar or duplicate products, but their names aren't written exactly the same way?"
To explore how to solve this problem, we'll use video games as an example.
⚠️ First spoiler: that's the type of data we'll be working with in this article.
- Call of Duty: Black Ops and Call of Duty: Black Ops 2 have almost identical titles, but they are different games. They are not duplicates.
- Black Ops 2 and Black Ops II are exactly the same game, even though a computer sees them as two different text strings.
- FIFA 14 for PlayStation 3 and FIFA 14 for Xbox 360 have the same title, but the platform is different, so they may represent distinct commercial products.
So, what does it really mean for two products to be duplicates?
- Two records may describe the same product even if their names are different.
- Nearly identical names may refer to different products.
- The same product may be sold in different presentations or formats, which we may need to keep separate depending on the business rules.
Each use case may require different business rules. Some differences may seem obvious to a person, but when we're dealing with thousands of records, we can't rely on someone detecting them manually.
⚠️ Second spoiler: AI isn't going to solve everything.
We could send all the information about each product to an AI model and ask it to determine whether it's a duplicate. But processing an entire catalog this way can be expensive, and we can't guarantee that the responses will always be consistent.
In other projects, I have worked with text normalization, regular expressions, and similarity techniques. These remain useful tools, and in many cases, they are sufficient. For this MVP, I wanted to explore a different approach: using rules to handle straightforward cases, while reserving AI for more ambiguous scenarios that cannot be resolved by a rule.
That's why, before we start building the solution, there are three questions I want to answer:
- How can we distinguish truly duplicate products from those that are simply similar?
- When does AI add value, and when is it better to use traditional rules?
- Can we build an MVP on AWS using a serverless architecture while keeping costs under control?
To answer these questions, we'll build a solution that combines AI with traditional rules. We'll walk through the process, from data analysis and normalization to architecture design, comparison logic, and evaluation of the results.
Because detecting duplicates isn't simply about finding similar names. First, we need to understand what we're comparing and what it actually means for two products to be the same.
2. The Business Problem
Poor product data quality is not an isolated problem. Various studies highlight the difficulties companies face in keeping information consistent, up to date, and accessible across their systems.
For example, in 2026, GS1 UK published a report on product information management in the construction sector, based on research involving more than 300 professionals and 15 prominent figures from the industry.
Some of its findings include:
- £3.8 billion estimated potential annual losses resulting from inefficient product data management in the UK construction industry.
- 60% of respondents say that inefficiencies in managing this information are hindering progress.
The problem is not limited to construction. The 2024 Product Information Management Report by RIWI, based on a survey of more than 300 professionals, found that 37% identified bringing products to market as one of their main pain points.
What consequences can this have for a product catalog?
When products are not identified consistently, several problems can arise:
- Stock: units of the same product may be spread across multiple records, making it difficult to determine actual inventory levels.
- Pricing: comparing offers for the same product across suppliers or sales channels can become more difficult.
- Search: users may encounter multiple listings for the same product or fail to find it when searching with a different variation of its name.
- Reviews: ratings and reviews may be distributed across different listings of the same product.
That is why the objective of this project is more specific: to explore how to detect potential duplicates in a catalog, distinguish them from products that should remain separate, and evaluate whether combining AI with traditional rules can address part of the problem without driving up costs.
3. How Do We Manage Product Information?
When a company sells products through multiple channels, keeping information up to date and consistent can become a challenge. Product Information Management (PIM) systems are designed to manage this process.
3.1. 🗂️ What Is a PIM?
A PIM allows companies to centralize, enrich, organize, and distribute product information from a single location. It can manage product names, descriptions, technical specifications, images, and other attributes needed to sell a product. Adobe explains here how these systems work.
Consider, for example, a company that sells video games through its own website and marketplaces such as Amazon or eBay. Product information may come from different suppliers and in different formats, so it needs to be organized before being published across each channel.
Although this article introduces the concept of PIM, depending on the organization, product information may be managed in an Excel spreadsheet, a database table, enterprise systems such as a CRM or SAP MM, or specialized solutions like a PIM.
The idea is to explore a solution that can be accessed through an API, for example, to help perform data quality checks and detect potential duplicates, regardless of where the product information is managed.
4. MVP Design Principles
Before starting to build the solution, I had to make several decisions: which data to use, which part of the catalog to process, and how to combine AI with traditional rules without driving up costs.
These are the principles that defined the scope and design of the MVP:
1. A public, realistic dataset.
I used the product metadata from Amazon Reviews 2023, in the Video Games category. I chose this category because it covers many different scenarios: the same game can be sold for different platforms (Nintendo, PlayStation, Xbox or PC), in different editions (standard, deluxe or collector’s), in physical or digital format, or as a refurbished product.
2. A limited scope based on an external criterion.
For the MVP, I selected a subset of products based on the 20 best-selling video game franchises according to Wikipedia. To identify the records, I used regular expressions that look for the names of these franchises and their variants in the dataset titles. This keeps the data volume manageable and makes the selection criterion reproducible.
3. Cost as a design variable.
I chose a serverless AWS architecture, with on-demand processing and low-cost AI models. Every technical decision is also evaluated for its impact on cost, from data preparation to model calls.
4. Rules first, AI where it adds value.
To avoid unnecessary costs, I first analyze the data and apply deterministic rules to the cases that can be solved simply. The language model is reserved for cases that require interpreting text, recognizing abbreviations or evaluating ambiguous matches.
5. Explicit business criteria.
Deciding whether two products are the same, a variant or different products is not just a technical question: it depends on the business. In this MVP, those criteria are defined in the prompts and in the pipeline rules, with examples that guide the model. They are a proposal for this case study: in a production environment, they must be reviewed and validated with the business teams of each organization.
6. A pipeline to evaluate, not a finished system.
This MVP is a practical approach to building a data pipeline for detecting duplicate products. The goal is not a perfect system, but to check whether the approach is viable and to measure its technical behavior: how long it takes, how much it costs, which share of the decisions is resolved with rules and which needs a model, and how extraction quality evolves across prompt versions. I also review specific cases to understand where it succeeds and where it fails.
5. Architecture
Before going into the details of each step, it is worth looking at the complete solution. The goal of the architecture is to transform a disorganized product catalog into one where each listing is associated with the actual product it represents, making that information available for real-time queries.
To achieve this, the solution combines batch processing, which processes the entire catalog at once, with a real-time component that handles new products or search queries as they arrive.
Source: Architecture diagram created by the author.
The image above shows the solution architecture, organized into three parts:
- Input: starts with the Amazon Reviews 2023 dataset. We apply the selection criteria defined for the MVP and the data quality rules identified during the EDA, discarding records that do not meet the requirements or fall outside the project's scope.
- Pipeline: processes the products from start to finish. It extracts their attributes using a language model, converts them into vectors, searches for similar products, and determines whether they are duplicates, variants, or different products. Finally, it assigns each product a product identifier and a family identifier, storing the results in DynamoDB, a NoSQL database.
-
Inference: allows us to query the catalog or evaluate new products. It receives a natural language search query, such as
FIFA 14 PS3 digital code, or the details of a new product, and determines whether it matches an existing product, is a variant, or is a new product. The response includes the corresponding identifiers.
One principle guided all our decisions: a fully serverless architecture that allows on-demand processing without managing dedicated servers.
For a catalog that is updated periodically, as in this case, this approach allows us to adjust resource consumption to actual needs, with costs primarily associated with data storage, operations performed, and resources used during each execution.
The following AWS services are grouped according to the diagram's legend:
🟩 Storage
- Amazon S3: stores the data from each stage in its own folder, allowing us to trace each product and reprocess data from any step.
- Amazon S3 Vectors: stores product vectors and performs similarity searches without requiring us to manage a vector database.
- Amazon DynamoDB: stores each product with its product and family identifiers, enabling millisecond key-based queries for the real-time component.
🟨 Compute / AI
- Amazon Bedrock, with three models, each selected for a specific task:
- Nova Micro: extracts the attributes of each product. It is the most cost-effective model and is used for all attribute extraction calls.
- Titan Text Embeddings V2: converts each product into a vector.
- Nova Lite: handles ambiguous cases. It has stronger reasoning capabilities than Nova Micro and is used only for a small proportion of product pairs.
🟪 Pipeline
- AWS Lambda: executes each processing step as an independent function.
- AWS Step Functions: orchestrates the pipeline steps, retries temporary failures, and displays the status of each execution.
🟥 Security
- AWS IAM: each Lambda function has its own role with the minimum permissions required (for example, reading only from the input folder and writing only to the output folder).
- AWS KMS: encrypts data at rest.
🟦 Logs
- Amazon CloudWatch: centralizes logs from all Lambda functions and Step Functions. It is the first place to look when a step fails, and it also allows us to examine metrics such as execution duration or memory usage for each run.
6. Use Case Development
6.1. 🔍 What Is EDA?
Exploratory Data Analysis (EDA) is a process that helps us better understand a dataset before applying models or making decisions. Through different exploration and visualization techniques, we can identify patterns, discover relationships between variables, detect anomalies, and assess data quality.
Although we often associate EDA with the early stages of Machine Learning projects, the concept is not new. Statistician John W. Tukey formalized it in his book Exploratory Data Analysis, published in 1977.
🎯 Why is it important?
Because it helps us understand the data before working with it, identify outliers, detect inconsistencies, and question our initial assumptions. Instead of assuming that the data has the expected structure or quality, we explore it to discover what it is actually telling us.
🤖 And what about generative AI projects?
It can be tempting to send raw data to a model with a prompt and hope for the best. But every unnecessary field adds tokens that we pay for with each call, and every inconsistent data point is a potential error that the model can propagate. EDA helps us decide which data to send, what to correct beforehand using simple rules, and which part of the catalog to process.
For this use case, I performed ten checks:

Source: Author's own infographic.
The findings that had the greatest impact on the design were:
-
main_categorydoes not describe the product: it refers to the store department. The platform is taken fromcategories. - There are hidden nulls: thousands of products have an empty categories list.
- Not everything in Games is actually a game: there are controllers, statues, and bundles.
- Accessory titles are full of keywords: so comparing titles directly does not work.
-
Some fields seem useful, but are not:
Pricingis a legal notice, andDate First Availableis not the release date.
These findings lead to an initial conclusion: before looking for duplicates, we need to understand what each field represents and distinguish the products that can actually be compared.
📓 Would you like to explore the full analysis? The
eda_video_games.ipynbnotebook, including the code and charts, is available in the project repository. There, you will also find the files needed to continue implementing the architecture.
6.2. ☁️ Infrastructure Deployment
With the data analyzed, the next step is to create the resources required by the pipeline.
Ideally, we would define the entire infrastructure as code using Terraform, AWS SAM, or CDK and deploy it with a single command. To keep this MVP simple, I created the resources through the AWS Console and included the scripts and AWS CLI commands in the repository to reproduce the deployment.
🧰 Prerequisites
Before getting started, you will need:
- An AWS account with access to Amazon Bedrock in us-east-1 and the Nova Micro, Nova Lite, and Titan Text Embeddings V2 models enabled.
- AWS CLI v2, configured with the permissions required to create the resources.
- The meta_Video_Games.jsonl file from the Amazon Reviews 2023 dataset.
- Clone the repository and navigate to the project directory:
git clone https://github.com/RominaElenaMendezEscobar/aws-games-products.git
cd aws-games-products
RominaElenaMendezEscobar
/
aws-games-products
A serverless AI solution on AWS to identify similar products and distinguish duplicates, variants, and different items using Amazon Bedrock, embeddings, business rules, and S3 Vectors.
🎮 Identical or Similar? AI-Powered Product Matching with Amazon Bedrock and S3 Vectors
Is it the same product? A serverless pipeline that detects duplicate and variant listings in an e-commerce catalog, combining a language model, vector search and simple rules, with cost as a design constraint.
The use case is a video game catalog: the same game can be sold for different platforms, in different editions and formats, as a budget reprint or refurbished, and its title can be written in many ways ("GTA: Vice City" vs "Grand Theft Auto: Vice City", "Black Ops II" vs "Black Ops 2"). Some of those listings are the same product; others look almost identical but are not.
📝 This repository accompanies the article Identical or Similar? AI-Powered Product Matching with Amazon Bedrock and S3 Vectors].
📊 Results at a glance
Processed with the final version of the pipeline (prompt v8) on the…
📁 Repository Structure
The resources are organized by service under aws/. Each Lambda function has its own directory containing the code, permissions policy (policy-role.json), and required configuration.
aws/
├── S3/ → data bucket
├── S3Vectors/ → vector bucket and index
├── DynamoDB/ → products table
├── lambda/ → 8 functions + deployment script
└── step-function/ → pipeline definition and its role
The deployment follows this order: storage → Lambda functions → Step Functions, as each stage depends on resources created earlier.
6.2.1. 🟩 Storage
6.2.1.1 Amazon S3 · game-catalog-dedup
This bucket stores the intermediate data generated during the different pipeline stages.
bash aws/S3/create_s3_bucket.sh
6.2.1.2 Amazon S3 Vectors · game-catalog-vectors / index games-v2
This is where the product embeddings are stored. First, we create the vector bucket and then the index, which uses 1,024-dimensional vectors compatible with the Titan Text Embeddings V2 configuration and cosine distance.
The title and base_title fields are configured as non-filterable metadata: they are preserved in the results but cannot be used as filtering criteria in the index.
aws s3vectors create-vector-bucket \
--vector-bucket-name game-catalog-vectors
aws s3vectors create-index \
--vector-bucket-name game-catalog-vectors \
--index-name games-v2 \
--data-type float32 \
--dimension 1024 \
--distance-metric cosine \
--metadata-configuration '{"nonFilterableMetadataKeys": ["title", "base_title"]}'
6.2.1.3 Amazon DynamoDB · game-catalog-products
The table stores products identified by parent_asin and uses on-demand capacity. Two global secondary indexes allow us to query the results by identifier:
-
canonical_id-index:all listings representing the same product. -
family_id-index:all variants of the same game.
bash aws/DynamoDB/create_dynamodb_table.sh
6.2.2. 🟥 Permissions and Configuration
Each function has its own IAM role with least-privilege permissions. The policies are defined in the policy-role.json file in each directory and specify which services each Lambda can access. Each role also includes the AWS managed policy AWSLambdaBasicExecutionRole, so the function can send logs to CloudWatch. The deployment script (6.2.3) creates the roles and attaches both policies.
Before deploying, review the ARNs in each policy-role.json and replace <account_number> with your own AWS account ID. Also check the resource names and the Region.
6.2.3. 🟪 Lambda Functions
All eight functions use Python 3.12. To simplify deployment, I prepared a script that creates each function's IAM role from its policy-role.json file, packages the code, and creates the Lambda function or updates it if it already exists.
The script receives the function’s folder, its name, the memory and the timeout, and optionally a Lambda Layer ARN and the ephemeral storage size:
./aws/lambda/deploy_lambda.sh <folder> <function-name> <memory-MB> <timeout-s> [layer-arn] [ephemeral-MB]
These are the commands to deploy the eight functions:
cd aws/lambda
./deploy_lambda.sh <folder> <function-name> <memory-MB> <timeout-s> [layer-arn] [ephemeral-MB]
./deploy_lambda.sh ETL01-game-catalog-etl game-catalog-etl 2048 900 <pandas-layer-arn> 2048
./deploy_lambda.sh SP01-game-catalog-prepare-batch game-catalog-prepare-batch 1024 180 "" 1024
./deploy_lambda.sh SP02-game-catalog-extract-ondemand game-catalog-extract-ondemand-access 512 900
./deploy_lambda.sh SP03-game-catalog-parse-results game-catalog-parse-results 1024 300 "" 1024
./deploy_lambda.sh SP04-game-catalog-embed game-catalog-embed 512 900
./deploy_lambda.sh SP05-game-catalog-decide-pairs game-catalog-decide-pairs 1024 900
./deploy_lambda.sh SP06-game-catalog-build-clusters game-catalog-build-clusters 1024 900
./deploy_lambda.sh RT01-game-catalog-resolve-product game-catalog-resolve-product 512 60
⚠️ The ETL Lambda function uses
pandas, which is not included in the default Lambda runtime. It requires the AWS-managed AWSSDKPandas-Python312 layer (in the Lambda console: Layers → Add a layer → AWS layers) and 2 GB of ephemeral storage, as it writes the cleaned catalog to/tmpbefore uploading it to S3.
The configuration depends on the task: the ETL Lambda requires more memory to process the dataset, while functions that invoke models may need longer execution times. The real-time query Lambda has a shorter timeout.
6.2.4. 🟪 Step Functions · game-catalog-dedup-pipeline
Finally, we create the Standard state machine game-catalog-dedup-pipeline, which coordinates the six processing stages. Its role has permission to invoke only the six Lambda functions used by the pipeline (LambdaInvokeScopedAccessPolicy.json).
aws iam create-role --role-name game-catalog-dedup-pipeline-role \
--assume-role-policy-document file://aws/step-function/trust-policy.json
aws iam put-role-policy --role-name game-catalog-dedup-pipeline-role \
--policy-name LambdaInvokeScopedAccessPolicy \
--policy-document file://aws/step-function/LambdaInvokeScopedAccessPolicy.json
aws stepfunctions create-state-machine \
--name game-catalog-dedup-pipeline \
--definition file://aws/step-function/game-catalog-dedup-pipeline.json \
--role-arn <step-functions-role-arn> \
--type STANDARD \
--region us-east-1
6.2.5. 📥 Upload the Raw Data
aws s3 cp data/meta_Video_Games.jsonl s3://game-catalog-dedup/raw/
6.3. 🛠️ Data Preparation
After exploring the dataset, the next step is to prepare it for the duplicate detection pipeline.
The EDA helped us understand its structure, identify data quality issues, and determine which attributes could be useful. Based on these findings, I divided the preparation process into two stages: first, cleaning and filtering the data; then, selecting the catalog we would use for the MVP.
6.3.1. 🧹 Data Cleaning and Filtering (ETL)
A Lambda function applies the rules defined during the EDA before any AI model is involved. The goal is to prevent incomplete records or unnecessary fields from affecting subsequent processing stages.
The main operations are:
- Discard products without titles or categories. Without this information, it becomes more difficult to identify the product and determine whether it falls within the experiment's scope.
- Remove columns without useful information. This reduces the amount of data we need to store and process, avoiding the need to carry empty fields into subsequent stages.
- Convert prices to numeric values. When available, this conversion makes prices easier to analyze and ensures a consistent data type.
-
Filter the
Gamescategory. We keep only records belonging to this dataset category, reducing the initial dataset before selecting the franchises.
These operations do not identify duplicates yet. They prepare the data so that subsequent stages can compare products using more consistent information.
This stage is important because it defines the information the model will receive: if a piece of data arrives incomplete or incorrect, the error carries over into the extraction, the search, and the decision-making process.
6.3.2. 🎯 Why the 20 Best-Selling Video Game Franchises?
As mentioned in the MVP design principles, I used the 20 best-selling video game franchises as a reference to define the scope of the dataset.
This choice was intentional: I wanted to work with franchises that would allow us to explore different duplicate and variant scenarios, such as annual releases, titles published for different consoles, re-releases, and versions with similar names.
For example, FIFA has successive releases that should not be confused with one another, while the Mario and The Legend of Zelda franchises include games released for different Nintendo consoles. In these cases, similar titles do not necessarily identify the same product.
Additionally, limiting the catalog helps control the volume of data and the cost of model invocations during experimentation.
The dataset is progressively reduced at each stage:
- 📦 Original dataset: 137,269 products
- 🧹 Products with titles and categories: 124,624
- 🎮 Products in the Games category: 46,530
- 🏆 Products from the 20 best-selling franchises: 3,740
6.3.3. 📦 Storing Each Stage's Results Separately
Each stage stores its results in a separate Amazon S3 folder (raw/,clean/,mvp/…). In addition, the pipeline stages store their outputs in an execution-specific subfolder, such as attributes/top20-v8/.
This separation offers two practical advantages:
First, it allows us to reuse the results of a stage without repeating the entire processing workflow.
Second, it makes it easier to compare different pipeline executions and identify what has changed between versions.
This proved especially useful when refining the prompt: I could rerun subsequent stages using the already prepared data, without repeating the catalog cleaning process every time I modified the model instructions.
6.4. 🧠 Prompt Design and Evaluation
Once the data is prepared, the next step is to define how we will use AI. The goal is not to ask a model to find duplicates directly across the entire catalog, but rather to break the problem down into more specific tasks.
To achieve this, I designed two prompts: one to normalize the attributes of each product and another to determine whether two candidate products are duplicates, variants, or different products.
However, before selecting the models and writing the instructions, there were several requirements the solution needed to meet.
6.4.1. 📋 What Do We Expect from AI?
The first requirement was consistency. If two listings describe the same video game, we need the model to generate normalized attributes that allow us to recognize them as the same product, even when their original titles are different.
We also needed structured output that followed a predefined schema and could be processed programmatically without requiring manual interpretation.

Source: Author's own infographic.
The main requirements were:
- Normalize products: extract the base title, platform, edition, format, region, publisher, and product type.
- Maintain structured output: return valid JSON with the fields and values expected by the pipeline.
- Apply business rules: distinguish an edition from a re-release, a physical product from a digital one, and a video game from an accessory.
- Avoid unnecessary assumptions: do not invent attributes when the available information is insufficient to determine them.
- Control costs and execution time: choose the appropriate model for each task and avoid unnecessary model calls.
6.4.2. ✂️ Two Prompts for Two Different Tasks
During testing, I decided to separate the process into two tasks: normalizing each product's attributes and evaluating candidate pairs that require further analysis. I also found that I could use different models depending on the task: Nova Micro for attribute extraction and Nova Lite for resolving ambiguous cases.
6.4.2.1. How Do We Invoke a Model from AWS Lambda?
One of the advantages of Amazon Bedrock is that we can invoke a model through its Converse API without having to manage the underlying model infrastructure. In this case, the prompt is sent as a system message, while the product listing is sent as a user message.
The basic implementation in Python is straightforward:
response = bedrock.converse(
modelId="us.amazon.nova-micro-v1:0",
system=[{"text": SYSTEM_PROMPT}],
messages=[{"role": "user",
"content": [{"text": json.dumps(build_payload(product))}]}],
inferenceConfig={"temperature": 0, "maxTokens": 300},
)
attributes = json.loads(response["output"]["message"]["content"][0]["text"])
The build_payload function sends only the fields I selected during the EDA: title, store, categories, four keys from details, and three product features. This way, we avoid sending unnecessary information to the model.
This snippet shows the basic model invocation. The complete implementation also needs to handle response validation and error management.
6.4.2.2. SYSTEM_PROMPT: Product Normalization (Nova Micro)
This prompt uses Amazon Nova Micro and runs on every product in the catalog. Its purpose is to transform an original product listing into a set of normalized attributes that we can use in subsequent stages.
For example, a product listing might contain:
{
"title": "GTA : Vice City - platinum [Playstation 2]",
"categories": [
"Video Games",
"Legacy Systems",
"PlayStation Systems",
"PlayStation 2",
"Games"
]
}
The expected output would have a structure similar to this:
{
"product_type": "game",
"base_title": "Grand Theft Auto: Vice City",
"platform": "PlayStation 2",
"edition": "Standard",
"format": "physical",
"region": "unknown",
"publisher": "Rockstar Games",
"confidence": "high"
}
In this example, the model normalizes the title and platform, while other attributes are determined according to the rules defined in the prompt. For instance, the Platinum label is treated as a budget re-release and normalized to Standard for this MVP. Other values may be inferred from the available information, so it is important to consider the limitations of these inferences.
The key is not simply to clean up the title, but to ensure that equivalent products are represented consistently without losing important distinctions, such as the platform.
📍 This prompt is used by the game-catalog-prepare-batch (batch) and game-catalog-resolve-product (on-demand) Lambda functions, both for the initial catalog processing and for resolving subsequent queries.
6.4.2.3. DECISION_PROMPT: Candidate Pair Comparison (Nova Lite)
This prompt uses Amazon Nova Lite and is reserved for ambiguous cases that require additional evaluation.
After normalization and candidate retrieval, not every match is straightforward. Two titles may be very similar while referring to different video games.
For example:
A: Mario & Sonic at the Olympic Games [Nintendo Wii]
B: Mario and Sonic at the Olympic Winter Games - Nintendo Wii
Although they share many words, these titles refer to different games. The presence of Winter is a meaningful distinction that we should not ignore.
The prompt must return a structured decision, for example:
{
"relation": "different",
"reason": "The base titles of the games are different."
}
📍 This prompt is used by the game-catalog-decide-pairs (batch) and game-catalog-resolve-product (on-demand) Lambda functions. By reserving Nova Lite for ambiguous pairs, we avoid using a more expensive model for every record in the catalog.
6.4.3. 🏗️ How I Built the Extraction Prompt
The SYSTEM_PROMPT was organized into five parts:
- Role: a product data specialist who responds only in JSON.
- Purpose: explains that the output will be used to detect duplicates and that consistency matters more than preserving the original title.
- Rules: each rule comes from an EDA finding or an observed error: labels in brackets, product condition (“Renewed”) is not an edition, budget re-releases are considered the same product, sequel numbers are part of the title.
- Schema: the fields and their allowed values, including a predefined list of platforms.
- Examples: two complete cases using franchises outside the evaluated dataset, to assess whether the model generalizes rather than simply copying the answers.
6.4.4. 🧪 Testing Plan in Amazon Bedrock
To develop and test the prompts, I used the Amazon Bedrock Playground, which allows you to change the prompt, model, and parameters and see the response immediately, without writing a single line of code. Its comparison mode displays two models side by side using the same input, along with token consumption and latency.
This was especially useful during experimentation: I could modify a rule, rerun a problematic case, and check whether the result improved or a new error appeared.

Source: Screenshot captured by the author from the Amazon Bedrock console (AWS).
To evaluate the models' behavior and improve the prompts, I organized the tests across several dimensions:
🤖 Model Comparison
I tested Amazon Nova Micro and Nova Lite with equivalent inputs to evaluate their performance on extraction and comparison tasks.
The final decision was to use Nova Micro to normalize all products and reserve Nova Lite for ambiguous pairs. This way, the model with stronger reasoning capabilities is used only when it can provide additional value.
🌡️ Generation Parameters
I used a temperature of 0 to reduce response variability during testing. This is useful when comparing prompt versions, although it does not guarantee that every execution will produce identical results.
📦 One Product per Call or Multiple Products Together
Grouping ten products into a single call could have reduced the number of extraction calls from approximately 3,740 to 374. However, during testing, I observed that the model could mix information between records, incorrectly modify titles, or return results without properly associating them with their identifiers.
For this reason, I kept one call per product and used eight concurrent calls. This decision increases the number of requests but makes it easier to maintain the correspondence between each input and its result.
🌍 Queries in Different Languages
Although the catalog is primarily in English, I also wanted to test how the system would respond to searches written in Spanish.
I tested queries such as pokemon esmeralda game boy advance, fifa 14 ps3 código digital, and el mario kart de la wii. The goal was to evaluate whether the model could interpret these expressions and normalize them to the vocabulary and attributes used in the catalog.
These tests are relevant for a system that not only processes existing records but can also receive search queries written by users.
🧪 Edge Cases and Regression Testing
Each new version was tested against previously failing cases and control cases that already worked, to ensure that fixing one issue did not introduce another. These included titles without numbers, titles that already use digits (“FIFA 23”, “NBA 2K17”), and letters that are part of a game's name (“Pokémon X”, “Call of Duty: WWII”, “Mega Man X”).
🧠 Influence of Conversation Context
During testing, I noticed another important detail: the Playground can preserve conversation history. When I tested many products consecutively in the same chat, previous responses could influence subsequent ones.
In one test, the model stopped correctly normalizing Roman numerals after several examples. Since each Lambda invocation is independent, this behavior did not reflect the actual conditions of the pipeline.
To avoid misleading comparisons, tests needed to use the same message sent by the application, without previous conversational context, starting a new conversation whenever necessary.
💰 Tokens, Costs, and Execution Time
The Amazon Bedrock Playground allowed me to observe token consumption and call latency to estimate costs before running the full pipeline.
I later compared these estimates with the actual results. I also measured the extraction time for all 3,740 products, which was 8 minutes and 26 seconds, mainly constrained by Bedrock's requests-per-minute quota.
These measurements allowed me to evaluate technical decisions beyond response quality, taking both cost and processing time into account.
📊 Results
The prompt went through eight versions. Here are some of the differences between the first run on the 20 franchises (version 6) and the final version (version 8):
- Games with no identified platform: from 5.3% to 1.1%.
- Games with no identified format: from 8.3% to 3.1%.
- Input tokens per product: from approximately 620 to 1,500.
- Estimated cost of extracting 3,740 products: from approximately $0.12 to $0.24. ##### 💡 Lessons Learned
- Examples work better than abstract rules. I tried four different ways of instructing the model to convert Roman numerals into digits (“Black Ops II” → “Black Ops 2”). A simple rule, one explaining the purpose, and a more detailed version all failed in some cases. With two examples, the model correctly converted “Black Ops II”, “Final Fantasy VII”, and “GTA V”, while also preserving “Mega Man X”, where the X is part of the name.
-
Examples can also teach the model what you don't want. A single example containing
"platform": "unknown"was enough for the model to leave the platform unidentified for 14% of the games. After correcting the examples to reflect the actual data format, that figure dropped to 1%. -
“Don't guess” is not always the best instruction. An
unknownvalue may seem like the safest choice, but in the comparison rules it acts as a wildcard and can cause different products to be grouped together. For the format attribute, an explicit business assumption worked better: in this marketplace, digital products are always labeled as such, so products without a digital label are treated as physical. - Test under the same conditions as production. The same product produced different results in the Playground and the pipeline because I sent only the title in the Playground, while the pipeline also included categories. Tests are only reliable when they use the actual application input.
- Measure every change. Every prompt improvement comes with a token cost, and a new rule can break something that previously worked. Comparing metrics across versions was what allowed me to detect regressions in time.
6.5. ⚙️ The Pipeline
The catalog is processed by an AWS Step Functions state machine that orchestrates six Lambda functions. Each function performs a single task, reads the previous stage's output from S3, and stores its results in a separate folder.

Source: Screenshot captured by the author from the AWS Step Functions console (AWS).
6.5.1.❓Why Did I Choose Step Functions?
I chose Step Functions to coordinate the processing for six reasons:
- Orchestration: executes the steps in the defined order and passes the required parameters to each function.
- Error handling: allows us to configure retries for temporary failures, such as certain Bedrock throttling errors.
- Traceability: provides a visual representation of each step's status, inputs, outputs, and any errors that occur.
-
Execution isolation: each execution has a
run_nameand stores its results in separate S3 paths, allowing us to compare versions without overwriting the results of other executions. - Serverless architecture: there is no need to provision or manage servers to coordinate the pipeline. The services manage the underlying infrastructure and scale according to processing needs.
- Cost control: processing runs on demand, without maintaining dedicated infrastructure while idle. This allows us to adjust resource consumption and pay for the resources used, including Lambda invocations, Step Functions state transitions, Bedrock calls, and storage.
6.5.2. 🔄 The Six Stages of the Pipeline
Before the Step Functions workflow begins, the game-catalog-etl Lambda function cleans the original dataset (raw/) and stores the products within the defined scope in mvp/.
-
PrepareRequests ·
game-catalog-prepare-batch
Filters the 20 franchises and generates one extraction request per product, including the extraction prompt and only the relevant fields.📥
mvp/→ 📤bedrock-input/ -
ExtractAttributes ·
game-catalog-extract-ondemand-access
Sends each request to Nova Micro, using eight concurrent calls, and stores the model's response.📥
bedrock-input/→ 📤bedrock-output/ -
ParseResults ·
game-catalog-parse-results
Validates the JSON returned by each request, separates video games from other products (bundles, DLC, accessories, etc.), and generates an extraction quality report.📥
bedrock-output/→ 📤attributes/, reports/ -
CreateEmbeddings ·
game-catalog-embed
Converts each game into a vector using Titan Text Embeddings V2 and stores it in the S3 Vectors index, along with its attributes.📥
attributes/→ 📤S3 Vectors -
DecidePairs ·
game-catalog-decide-pairs
Finds the nearest neighbors of each game and determines the relationship between each pair using deterministic rules or Nova Lite for cases in the gray area.📥 S3 Vectors → 📤
pairs/, reports/ -
BuildClusters ·
game-catalog-build-clusters
Groups related pairs and assigns each product acanonical_id(same product) and afamily_id(same game).📥
pairs/, attributes/→ 📤clusters/,reports/,DynamoDB
6.5.3. 🎛️ Execution Configuration
To run the pipeline, we start a Step Functions execution with the following input:
{
"bucket": "game-catalog-dedup",
"run_name": "top20-v8",
"title_contains": [
"\\bmario\\b", "\\btetris\\b", "\\bpok[eé]mon\\b", "\\bcall of duty\\b",
"\\bgrand theft auto\\b|\\bgta\\b", "\\bminecraft\\b", "\\bfifa\\b",
"\\bassassin'?s creed\\b", "\\bresident evil\\b", "\\bfinal fantasy\\b",
"\\bwii (sports|fit|play|party|music)\\b", "\\blego\\b", "\\bthe sims\\b",
"\\bnba 2k", "\\bzelda\\b", "\\bmadden\\b", "\\bneed for speed\\b",
"\\bsonic\\b", "\\bmonster hunter\\b", "\\bred dead\\b"
],
"model_id": "us.amazon.nova-micro-v1:0",
"prompt_version": "v8",
"index_name": "games-v2",
"decision_model_id": "us.amazon.nova-lite-v1:0",
"pairs_run": "pairs-top20-v8",
"clusters_run": "clusters-top20-v8",
"attribute_keys": ["attributes/top20-v8/attributes_games.jsonl"]
}
What each parameter represents:
- bucket: the S3 bucket where data from all stages is read and stored.
- run_name: the execution name. Everything generated is stored under this name (for example, attributes/top20-v8/), keeping each version separate so it can be compared with previous ones.
- title_contains: the patterns used to select products within the scope, in this case, the 20 franchises. These are regular expressions; \b represents a word boundary, preventing false matches such as “gta” appearing within another word.
- model_id: the model used to extract attributes (Nova Micro).
- prompt_version: the extraction prompt version. It is stored alongside each result to track which version generated it.
- index_name: the S3 Vectors index where vectors are stored and queried.
- decision_model_id: the model used to resolve ambiguous cases in the gray area (Nova Lite).
- pairs_run and clusters_run: the folder names where the pair decisions and final clusters are stored.
- attribute_keys: the attributes file used by the final stage to build the clusters.
6.5.4. ⏱️ Execution Time and Scalability
Processing all 3,740 products in the MVP took 14 minutes and 4 seconds. Attribute extraction was the slowest stage, taking 8 minutes and 26 seconds, mainly due to Bedrock's available requests-per-minute quota. The remaining stages took anywhere from a few seconds to a few minutes.
There is an important limitation to consider when scaling the solution: a single Lambda invocation cannot run for more than 15 minutes. To process a larger catalog, such as the more than 46,000 products in the Games category, it would be advisable to distribute requests across batches and control concurrency. Step Functions Distributed Map is one option for orchestrating this processing, adjusting concurrency according to model quotas and service limits.
📄 The Lambda source code, permissions, memory configuration, and maximum execution times are available in the repository.
6.6. ⚖️ How Do We Determine Whether Two Products Are the Same?
The central idea behind the design is to separate two questions:
- 🔎 Which products are similar? Vector search answers this question.
- ⚖️ Are they the same product? The attributes determine the answer.
If we relied on similarity alone, FIFA 14 for PS3 and FIFA 14 for Xbox 360 would be classified as duplicates because their vectors are very close. However, they are different products, each with its own stock and price. Vector distance helps us find candidates, while attributes help us make the final decision.
This process takes place in the last three stages of the pipeline:
- CreateEmbeddings: converts normalized attributes into vectors.
- DecidePairs: finds candidates and determines the relationship between each pair.
- BuildClusters: groups related products and assigns identifiers.
6.6.1. 🔎 Finding Candidates
To find similar products, each game is converted into an identity text using its normalized attributes:
base title | platform | edition | format
For example:
Grand Theft Auto: Vice City | PlayStation 2 | Standard | physical
Normalized attributes are used instead of the original title so that two listings written differently can generate the same representation when they describe the same product. This helps reduce naming inconsistencies and improves candidate retrieval through vector similarity search.
Next, Amazon Titan Text Embeddings V2 converts this representation into a vector. Amazon S3 Vectors returns the 20 nearest neighbors for each product in the index, and we keep those with a cosine distance below 0.25.
A distance of zero indicates identical vectors; the greater the distance, the lower the similarity between them.
This threshold helps reduce the number of comparisons, but it does not determine on its own whether two products are duplicates. That decision is made in the next stage by evaluating their attributes and applying the defined business rules.
6.6.1.1. How Do We Generate the Identity Text and Embeddings?
The implementation is divided into two functions: one builds the text from the normalized attributes, and the other invokes the Bedrock embedding model.
def identity_text(attrs):
parts = [
attrs["base_title"],
attrs["platform"],
attrs["edition"],
attrs["format"],
]
return " | ".join(
p for p in parts if p and p != "unknown"
)
def embed(text):
response = bedrock.invoke_model(
modelId="amazon.titan-embed-text-v2:0",
body=json.dumps({
"inputText": text,
"dimensions": 1024,
"normalize": True,
}),
)
return json.loads(
response["body"].read()
)["embedding"]
The identity_text function omits unknown attributes: if one listing does not specify the format and another does, including "unknown" would make their identity texts different and push apart two products that might actually be the same. The embed function sends the text to Titan Text Embeddings V2 and returns the generated vector.
For this MVP, I configured 1,024 dimensions to match the S3 Vectors index.
6.6.2. 🌳 Determining the Relationship
Once we have identified the candidate products, the next step is to determine the relationship between them. Similar titles are not enough: we need to check whether they represent the same product, a variant, or different games.
To do this, we combine business rules with an AI model that intervenes only when the rules are not sufficient.
The following decision tree summarizes the logic implemented in the MVP.
Source: Decision diagram created by the author.
6.6.2.1. Do the Base Titles Match?
First, we normalize the titles to prevent differences in capitalization, apostrophes, or punctuation from affecting matching. For example, “Link’s Awakening” and “Links Awakening” are considered equivalent after normalization.
6.6.2.2. Are the Attributes Compatible?
If the base titles match, we compare the platform, edition, format, and region.
- If the attributes are compatible, we classify the pair as a duplicate.
- If any known attribute differs, we classify the pair as a variant.
In this implementation, two attributes are considered compatible when they match or when one of them is unknown. This rule makes it easier to work with incomplete product listings, although it also introduces a risk: an unknown value may hide a real difference. Therefore, the classification needs to be evaluated alongside the results and any errors identified.
6.6.2.3. What Happens When the Titles Don't Match?
If the base titles differ but the vectors are very close, we enter a gray area. For this MVP, we use a distance below 0.08 as the threshold for sending these pairs to Amazon Nova Lite.
The model evaluates whether the titles describe the same product or refer to different games. When the conditions for this evaluation are not met, the pair is classified as different.
6.6.2.4. The Complete Rule in Code
The three steps described above are implemented in a single function, following the same sequence as the decision tree:
def rule_decision(a, b, distance):
"""Returns (relation, reason), or None when the pair goes to Nova Lite."""
if normalize_title(a["base_title"]) == normalize_title(b["base_title"]):
diffs = [
f for f in ["platform", "edition", "format", "region"]
if not compatible(a.get(f), b.get(f))
]
if not diffs:
return "duplicate", "same base_title and compatible attributes"
return "variant", "differs in " + ", ".join(diffs)
if distance < 0.08:
return None # gray zone: Nova Lite decides
return "different", "different base_title"
The normalize_title and compatible functions apply the rules from steps 1 and 2: the first normalizes the titles, and the second checks whether two attributes match or whether one of them is unknown. When rule_decision returns None, the pair is sent to Nova Lite together with the DECISION_PROMPT.
📄 The full code is in
aws/lambda/SP05-game-catalog-decide-pairs/lambda_function.py.
In the final run, 10,209 of the 10,449 candidate pairs (97.7%) were classified using deterministic rules applied to the attributes extracted by the AI, and only 240 pairs (2.3%) required an additional evaluation with Nova Lite.
This result shows that combining AI-based attribute extraction with deterministic rules can significantly reduce the number of additional model calls. However, these figures describe how the decisions were made, not how accurately the system identified duplicates.
In addition, the distance thresholds used in this MVP (0.25 to select candidates and 0.08 for the gray zone) were set empirically, based on cases observed during development. For a production implementation, a labeled reference set would be needed to check whether these thresholds are appropriate for each use case.
6.6.3. 🧩 Grouping Related Products
Once the pairs have been classified, we need to turn these individual relationships into groups that we can query and use later.
In this MVP, we create two levels of grouping:
- 🏷️ canonical_id: identifies listings that represent the same commercial product.
- 👨👩👧 family_id: groups duplicates and their variants, such as the same game available on different platforms, in different editions, or in different formats.
This distinction helps us answer two questions that are often confused: how many listings represent the same product, and how many products belong to the same game family.
6.7. ⚡ Inference and Real-Time Queries
So far, the pipeline has processed the entire catalog in a single run. But the result is more than just a duplicate detection report: once processing is complete, we have a product database in DynamoDB, where each listing is associated with its corresponding product and family, and a vector index in S3 Vectors containing each game's vector representation.
With these two components, we can query the catalog in real time. This also opens the door to integrating the solution with systems used to manage product information, such as a PIM or ERP.
Some possible scenarios include:
- 🛒 A purchasing team searches for “fifa 14 ps3 digital” and finds the correct listing, even if it is written differently in the catalog.
- 📦 A supplier submits a new product, and before it is added to the catalog, the system indicates whether it already exists or is a variant of another product.
- 🌍 Someone searches in Spanish within a catalog written in English.
To support these scenarios, the Lambda function has two modes: search, which receives a free-text query and identifies the corresponding product, and product registration, which receives the complete details of a new product, determines whether it already exists, and stores it with its identifiers.
6.7.1. 🔍 How Search Works
The important point is that the search does not directly compare the user's query with the catalog's product titles. Instead, it first converts the query into the same format used to represent products in the catalog and only then performs the comparison. Let's look at an example.
1. The User's Query
The following query is in Spanish, uses an abbreviation (“ps3”), and does not look like the product titles in the catalog, which are written in English and follow many different naming conventions.
fifa 14 ps3 codigo digital
2. Extract Attributes as JSON
The query is sent to Nova Micro using the same extraction prompt as the pipeline. The model returns the normalized attributes:
{
"product_type": "game",
"base_title": "FIFA 14",
"platform": "PlayStation 3",
"edition": "Standard",
"format": "digital",
"region": "unknown"
}
The key transformation has already happened: “ps3” has become “PlayStation 3”, and “codigo digital” has been interpreted as format: digital. The user's query now uses the same vocabulary as the catalog.
3. Build the Identity Text and Vector
Using these attributes, we build the same identity text format used for every game in the catalog, and Titan converts it into a vector:
FIFA 14 | PlayStation 3 | Standard | digital
4. Find Candidate Products
S3 Vectors returns the candidates with the highest vector similarity. Among them is the listing “FIFA 14 - PS3 [Digital Code]”, which has the same normalized identity text as the query and therefore a distance of zero, along with other physical versions of FIFA 14.
5. Retrieve the Identifiers
For each candidate, we query DynamoDB to retrieve its canonical_id (which commercial product it represents) and family_id (which game it belongs to).
6. Determine the Relationship
Using the same rules as the pipeline, we compare the query against each candidate: the digital listing is a duplicate because its title, platform, and format match, while the physical copies are variants because the format is different.
7. Output
{
"decision": "duplicate",
"canonical_id": "CAN-B00GGUW312",
"family_id": "FAM-B00CD90R7M",
"identity_text": "FIFA 14 | PlayStation 3 | Standard | digital",
"matched_with": {
"title": "FIFA 14 - PS3 [Digital Code]",
"distance": 0,
"method": "rule"
}
}
The response does more than identify the product: the family_id also allows us to retrieve all its variants (other platforms, physical copies, and different editions).
🧪 More Examples
The game-catalog-resolve-product Lambda function allows us to test different queries and see how the system identifies a product and retrieves its variants. The following images show some examples of the responses.
Two fields are especially important:
- canonical_id: identifies a specific commercial product. Listings that share this identifier are considered duplicates, even when their original titles are written differently.
- family_id: groups products belonging to the same game family, including variants across different platforms, editions, or formats.
This distinction allows us to query not only duplicate listings but also the different versions of the same game.
For example, when querying the Assassin’s Creed III family in DynamoDB, we find 8 listings grouped into 4 commercial products: Xbox 360, PlayStation 3, PC in physical format, and PC via digital code.

Source: Screenshot captured by the author from the AWS Lambda console (AWS).

Source: Screenshot captured by the author from the AWS Lambda console (AWS).
7. Results
The pipeline processed 3,740 product listings in 14 minutes and 4 seconds, from preparing the requests to creating the product clusters.
7.1. ⏱️ Execution Time
According to the AWS Step Functions execution history, the most time-consuming stages were:
- ExtractAttributes: 8 min 26 s, to extract and normalize attributes using Nova Micro.
- CreateEmbeddings: 4 min 21 s, to generate embeddings and store them in S3 Vectors.
- DecidePairs: 55 s, to evaluate 10,449 candidate pairs.
Each of the remaining stages took less than 15 seconds. Attribute extraction was mainly constrained by Bedrock's requests-per-minute quota, while embedding generation was the second most time-consuming stage.
7.2. ⚖️ Rules Before AI in the Decision Step
In the final run, 10,209 of the 10,449 candidate pairs (97.7%) were classified using deterministic rules applied to the attributes previously extracted by the AI model, and only 240 pairs (2.3%) required an additional evaluation with Nova Lite.
This approach reserves the model for ambiguous cases and avoids unnecessary model calls. These decisions were reviewed through specific cases, but not validated one by one; measuring their accuracy would require a labeled reference dataset, as discussed in the conclusions.
7.3. 💰 Attribute Extraction Cost
The game-catalog-extract-ondemand-access Lambda function collects the input and output token counts returned by Bedrock. The execution summary, available in the ExtractAttributes step output in Step Functions, recorded:
- 3,740 products successfully processed, with no errors.
- 5,661,802 input tokens.
- 264,153 output tokens.
Based on these figures and Amazon Nova Micro pricing in us-east-1, the estimated extraction cost is:
- Input: 5.66 million tokens × $0.035 per million = $0.20.
- Output: 0.264 million tokens × $0.14 per million = $0.04.
- Total extraction cost: approximately $0.24.
This calculation covers attribute extraction only. The total pipeline cost also includes embeddings, Nova Lite decisions, Lambda and Step Functions executions, and storage.
7.4. 📈 Prompt Evolution
Prompt engineering was an important part of this project. I used the Amazon Bedrock Playground to test instructions and examples, compare models, review errors, and refine the prompt without having to modify or deploy the code after each iteration. I then validated the changes by running the complete pipeline.
Between versions 6 and 8, the extraction metrics improved (Section 6.4.4), with fewer games missing an identified platform or format.
These metrics indicate an improvement in attribute extraction, although they do not, on their own, demonstrate greater accuracy in duplicate detection.
8. Conclusions
This MVP allowed me to demonstrate that deterministic rules, embeddings, and language models can be combined to detect potential duplicates without sending every comparison to an AI model.
It also showed that cost does not depend solely on the model selected. Data preparation, attribute selection, prompt design, and the rules used to handle straightforward cases all have a direct impact on resource consumption and execution time.
During the development of this MVP, I tested various products, edge cases, and prompt versions using a subset of the catalog. These tests made it possible to refine the extraction rules and evaluate the technical behavior of the pipeline, but they do not replace a systematic assessment of accuracy using labeled data.
However, the results should be interpreted within the scope of the experiment. Before using a similar solution with a real-world catalog, I would focus on several areas:
- 📊 Measure accuracy: create a reference dataset with pairs labeled as duplicates, variants, or different products.
- 🧪 Expand testing: cover more edge cases, such as titles with subtle differences or queries containing limited information.
- 🧩 Use more data: experiment with descriptions, images, and additional attributes, evaluating their impact on quality and cost.
- 🤖 Compare models: evaluate alternatives based on accuracy, latency, and cost.
- 📈 Scale processing: review Bedrock quotas, concurrency, and options such as batch inference.
- 🏗️ Automate infrastructure: define the deployment using Terraform, AWS SAM, or CDK.
- 🗂️ Version prompts: use Amazon Bedrock Prompt Management to maintain traceability across prompt versions.
- 👥 Incorporate feedback and human review: allow users to validate results and manually review the most ambiguous cases.
This exercise shows that identifying duplicate or similar products is not just about finding matches between their names, but about understanding which attributes define their identity and how to combine them with business rules. AI can help us solve the most complex cases, but its real value appears when we use it as part of a solution that also takes data quality, efficiency and costs into account.
⚠️ Note: this project is an exploratory MVP and is not intended for production use as is. Keep in mind that:
- The figures come from a single run on a subset of 3,740 products, so the results may vary with other catalogs.
- The rules work on the attributes extracted by the model: if the extraction fails, the error propagates to the search and the decision.
- The thresholds, rules and business criteria were defined empirically.
Before applying a similar approach to a real catalog, it should be adapted to each organization's business rules, the models should be evaluated, and the accuracy of the results should be measured with your own data.
9. 📚References
- Adobe. (s. f.). Product information management (PIM). https://business.adobe.com/blog/perspectives/product-information-management
- Amazon Reviews 2023. (s. f.). Amazon Reviews 2023. https://amazon-reviews-2023.github.io/
- Amazon Web Services. (s. f.). Amazon Bedrock pricing. Recuperado el 20 de octubre de 2026, de https://aws.amazon.com/bedrock/pricing/
- Amazon Web Services. (s. f.). AWS Step Functions pricing. Recuperado el 20 de octubre de 2026, de https://aws.amazon.com/step-functions/pricing/
- GS1 UK. (s. f.). Construction’s product information problem. https://www.gs1uk.org/insights/thought-leadership/constructions-product-information-problem
- IBM. (s. f.). Exploratory data analysis (EDA). https://www.ibm.com/think/topics/exploratory-data-analysis
- RIWI. (2024). 2024 product information management report. https://riwi.com/2024-product-information-management-report-2/
- Tukey, J. W. (1977). Exploratory data analysis. Addison-Wesley.
- Wikipedia contributors. (s. f.). List of best-selling video game franchises. Wikipedia. Recuperado el 20 de octubre de 2026, de https://en.wikipedia.org/wiki/List_of_best-selling_video_game_franchises






Top comments (0)