View a markdown version of this page

教程:首次向量搜索 - Amazon DynamoDB

教程:首次向量搜索

设想有一个商品目录,购物者在其中可以用自己的语言来描述需求,而无需精确匹配关键字。您可以将每个商品描述的向量嵌入存储在 DynamoDB 中,然后通过查询向量索引来查找最匹配的结果。

本教程创建一个带有向量索引的表,使用 Amazon Bedrock Titan Text Embeddings V2 生成 1024 个维度的嵌入,加载了 50 种商品,并运行相似性搜索来返回 5 个最匹配的结果。每个命令都可以直接粘贴到终端中。有关如何将嵌入与 DynamoDB 结合使用的背景信息,请参阅生成向量嵌入

关于规模和查全率

一个生产向量索引可以包含数百万到数十亿个向量。向量索引使用近似最近邻搜索,这种方法的查全率特性只有在更大规模下才能观察到。请将此处包含 50 个项目的目录视为对机制的演示。有关大小调整和调优的指导,请参阅向量索引的最佳实践

先决条件

在开始之前,请确保您具有以下各项:

  • AWS CLI 2.36.16 版或更高版本。在 2026 年 8 月 4 日发布的服务模型更新中,向 AWS CLI 和 AWS SDK 添加了向量索引支持。早期版本无法识别 --vector-indexes 参数或 search-vectors 命令。请使用 aws --version 检查您的版本,如有需要请升级。如果您使用 AWS SDK 而不是 AWS CLI,则需要 botocore 1.43.64 或更高版本,或所用语言 SDK 的同等版本。

  • 具有执行 DynamoDB 操作 CreateTableDescribeTablePutItemBatchWriteItemScanSearchVectorsUpdateTableDeleteTable 以及 Amazon Bedrock 操作的 InvokeModel 权限的凭证。dynamodb:SearchVectors 是一项新操作,因此授予 DynamoDB 读取权限的现有策略不包括该操作。

  • 在您的账户和区域中,对 Amazon Bedrock 中启用的 Titan Text Embeddings V2 模型的访问权限。Amazon Bedrock 模型访问权限按账户和区域授予,因此您必须先启用该模型,然后才能进行调用。

  • 已安装 jq,用于将模型输出改为 DynamoDB 格式。

  • DynamoDB 向量索引和 Amazon Bedrock Titan Text Embeddings V2 均可用的区域。Amazon Bedrock 模型的可用性因区域而异,通常比提供 DynamoDB 的区域范围更小,因此在选择区域之前,请先确认是否提供了这两项服务。有关 Amazon Bedrock 模型可用性的信息,请参阅《Amazon Bedrock 用户指南》中的 AWS 区域支持的模型

收费

本教程会产生 Amazon Bedrock 模型调用和 DynamoDB 存储费用。本教程将针对简短的单句输入发出 51 个嵌入调用,每个调用均按一个 Amazon Bedrock 推理请求计费。只要表和向量索引存在,就会产生 DynamoDB 存储费用。

确认您要使用的区域

本教程中的每条命令都必须在同一个区域中运行。在开始之前,请确认 AWS CLI 将实际使用的区域,因为 AWS_REGION 优先于 AWS_DEFAULT_REGION,并且两者都优先于 AWS CLI 配置文件中的 region 设置。您的 Shell 中不正确的 AWS_REGION 值会在目标区域之外的区域创建表,并且可能无法在其中启用 Amazon Bedrock 模型。要避免产生任何疑问,请在每条命令中明确传递 --region region

SearchVectors 使用单独的端点

SearchVectors 解析为专用的搜索端点,而不是标准的 DynamoDB 端点。在商业区域中,请求会发送到 search-dynamodb.region.amazonaws.com,而本教程中的所有其他操作都发送到 dynamodb.region.amazonaws.com。FIPS 和双堆栈变体遵循相同的模式。这会带来两个后果:

  • 如果您的网络通过 VPC 端点、代理或出口允许列表限制出站流量,则您还必须允许搜索主机名。否则 CreateTable 和写入操作会成功,只有 SearchVectors 会失败,通常会出现连接错误并且不能指明原因。

  • 请勿使用 --endpoint-url 覆盖这些命令的 DynamoDB 端点。单个覆盖不能提供两个主机名,并且会中断搜索路由。

  1. 创建带有向量索引的表。这将创建一个 Products 表,并在 DescriptionVector 属性上构建名为 DescriptionIndex 的向量索引。索引使用 1024 个维度的 COSINE 距离函数来匹配 Titan Text Embeddings V2 的输出。由于未定义向量索引分区键,因此无需使用 SearchConditionExpression 进行搜索。

    aws dynamodb create-table \ --table-name Products \ --attribute-definitions AttributeName=ProductId,AttributeType=S \ --key-schema AttributeName=ProductId,KeyType=HASH \ --billing-mode PAY_PER_REQUEST \ --vector-indexes \ "[ { \"IndexName\": \"DescriptionIndex\", \"VectorAttribute\": {\"AttributeName\": \"DescriptionVector\"}, \"Projection\": {\"ProjectionType\": \"ALL\"}, \"Dimensions\": 1024, \"DistanceFunction\": \"COSINE\" } ]"

    此示例为 ProjectionType 使用 ALL,因此搜索结果中每个属性都可用。如果您改用 INCLUDE,请注意投影的非键属性预算是共享的:向量属性计为一个属性,每个 INLINE_FILTER 搜索架构元素计为一个属性。HASH 搜索架构元素不计入限制。

  2. 等待索引变为活动状态。运行此过程直至 IndexStatus 变成 ACTIVE

    aws dynamodb describe-table \ --table-name Products \ --query 'Table.VectorIndexes[0].[IndexName,IndexStatus,Backfilling]'

    对于作为 CreateTable 的一部分创建的索引(如本教程所示),系统不会报告 Backfilling,命令将为其返回 null。在这种情况下,单独使用 IndexStatus 作为信号。

    对于您使用 UpdateTable 添加到现有表中的向量索引,系统才会报告 Backfilling。在本例中,请等待直至 IndexStatusACTIVEBackfillingfalse 后,您才可以放心地使用搜索结果。

    等待索引,而不仅仅是表

    请勿使用 aws dynamodb wait table-exists 来限制搜索。该等待线程匹配 Table.TableStatus,其值为 ACTIVE 时向量索引可能仍为 CREATING。向量索引就绪状态没有对应的等待线程,因此您必须如教程所示轮询 DescribeTable。搜索尚未进入 ACTIVE 状态的索引会失败,在回填期间进行搜索可能会返回不完整的结果。

    由于同样的原因,在所有向量索引创建完毕之前,您无法删除表。DeleteTable 返回 ResourceInUseException 并显示消息“在创建、更新或删除索引时无法删除表”。

  3. 创建商品目录。将以下 50 种商品保存到名为 products.tsv 的制表符分隔文件中。每行包含一个商品 ID、一个名称和一句话的描述。该目录分为十组,每组有五种相关商品,这是为了使最后一步的搜索结果易于解释。

    p01 Insulated Travel Mug A vacuum insulated stainless steel mug that keeps hot drinks warm for up to twelve hours. p02 Stovetop Espresso Maker A compact aluminum pot that brews strong espresso style coffee directly on a gas or electric burner. p03 Manual Burr Coffee Grinder A hand cranked grinder with adjustable ceramic burrs for consistent coffee grounds. p04 Pour Over Coffee Dripper A ceramic cone that sits on a mug and brews a single cup of filter coffee. p05 Electric Milk Frother A handheld battery powered whisk that creates dense foam for lattes and cappuccinos. p06 Lightweight Running Shoe A breathable mesh road shoe with cushioned foam midsole for daily training runs. p07 Trail Running Shoe An aggressive lugged outsole shoe built for grip on loose gravel and muddy trails. p08 Moisture Wicking Running Socks Ankle height socks knitted from synthetic yarn that pulls sweat away from the skin. p09 Reflective Running Vest A lightweight vest with high visibility strips for running safely after dark. p10 Hydration Waist Belt An elastic belt that holds two small water flasks and a phone during long runs. p11 Ergonomic Mesh Office Chair An adjustable desk chair with breathable mesh back and lumbar support for long work sessions. p12 Sit Stand Desk Converter A height adjustable platform that raises a monitor and keyboard for standing work. p13 Monitor Arm Mount A clamp mounted articulating arm that lifts a display off the desk surface. p14 Under Desk Footrest An angled cushioned platform that supports the feet and improves seated posture. p15 Wireless Split Keyboard A two piece keyboard that separates for a natural shoulder width typing position. p16 Noise Cancelling Headphones Over ear wireless headphones that actively silence engine noise on long flights. p17 Wireless Earbuds Compact in ear buds with a charging case and multi hour battery for commuting. p18 Portable Bluetooth Speaker A water resistant rechargeable speaker sized to fit in a backpack side pocket. p19 Studio Monitor Headphones Wired closed back headphones with flat frequency response for audio mixing. p20 Wired Lapel Microphone A small clip on microphone for recording clear speech during interviews. p21 Four Season Backpacking Tent A double wall tent with an aluminum pole set rated for wind and heavy rain. p22 Down Sleeping Bag A mummy shaped bag filled with compressible down insulation for cold weather camping. p23 Inflatable Sleeping Pad A lightweight pad that inflates in a few breaths and packs down to bottle size. p24 Canister Camping Stove A screw on burner that boils water quickly using a compact fuel canister. p25 Rechargeable Camp Lantern A collapsible lantern with adjustable brightness and a built in battery. p26 Cast Iron Skillet A preseasoned heavy pan that holds heat evenly for searing and oven baking. p27 Nonstick Frying Pan A coated aluminum pan that releases eggs and fish without added oil. p28 Stainless Steel Stock Pot A tall wide pot for boiling pasta and simmering large batches of soup. p29 Enameled Dutch Oven A heavy lidded pot that moves from stovetop to oven for slow braising. p30 Bamboo Cutting Board A large reversible board with a juice groove around the edge. p31 Padded Laptop Backpack A water resistant pack with a suspended sleeve that protects a fifteen inch laptop. p32 Slim Laptop Sleeve A close fitting neoprene case that shields a notebook inside a larger bag. p33 Rolling Carry On Suitcase A hard shell four wheel case sized to fit most overhead cabin bins. p34 Packing Cube Set Zippered fabric cubes that compress clothing and organize a suitcase. p35 Leather Messenger Bag A single strap shoulder bag with a padded compartment and interior pockets. p36 Daily Facial Moisturizer A light lotion with humectants that hydrates skin without leaving residue. p37 Mineral Sunscreen Lotion A broad spectrum zinc based sunscreen formulated for sensitive facial skin. p38 Gentle Foaming Cleanser A low pH face wash that removes oil and sunscreen without stripping the skin. p39 Vitamin C Serum A brightening serum applied before moisturizer to even skin tone over time. p40 Overnight Repair Cream A rich night cream with ceramides that restores the skin barrier while sleeping. p41 Stainless Steel Dog Bowl A weighted nonslip bowl that resists tipping during enthusiastic feeding. p42 Padded Dog Harness An adjustable chest harness that distributes pull away from the neck on walks. p43 Retractable Dog Leash A spring loaded leash that extends and locks at several walking lengths. p44 Interactive Cat Puzzle Feeder A slow feed tray that makes a cat work for dry food and eat more slowly. p45 Self Cleaning Litter Box An enclosed box with a raking mechanism that sifts waste after each use. p46 Bypass Pruning Shears Sharp hardened blades that make clean cuts on green stems and small branches. p47 Long Handled Garden Spade A steel bladed spade with a wooden shaft for turning soil and digging beds. p48 Adjustable Hose Spray Nozzle A metal nozzle that shifts from a fine mist to a strong jet stream. p49 Raised Garden Bed Kit Interlocking cedar panels that assemble into an elevated planting box. p50 Drip Irrigation Starter Kit Tubing and emitters that deliver water slowly to the base of each plant.

    三个字段之间的分隔符必须是文字制表符。如果您从浏览器复制目录,请先确认保留了制表符,然后再继续。

  4. 为一个商品生成嵌入。先运行一次嵌入调用,确认您的 Amazon Bedrock 模型访问权限是否有效。inputText 字段包含要嵌入的文本,dimensions 设置输出大小(有效值为 256、512 或 1024),normalize 生成单位长度向量,建议将其用于余弦相似度搜索。

    mkdir -p emb aws bedrock-runtime invoke-model \ --model-id amazon.titan-embed-text-v2:0 \ --body '{"inputText":"A vacuum insulated stainless steel mug that keeps hot drinks warm for up to twelve hours.","dimensions":1024,"normalize":true}' \ --cli-binary-format raw-in-base64-out \ --content-type application/json \ --accept application/json \ emb/p01.json

    --cli-binary-format raw-in-base64-out 标志为必填项。AWS CLI v2 对二进制参数默认使用 base64 编码,因此如果没有此标志,原始 JSON 正文将无法正确发送。响应写入 emb/p01.json 中,包含一个由 1024 个浮点数组成的 embedding 数组。确认维度计数。

    jq '.embedding | length' emb/p01.json

    输出为 1024

  5. 写入第一个商品。将嵌入转换为 DynamoDB 项目格式并写入。存储的向量属性使用 DynamoDB L(列表)类型,将每个数字封装成 N 类型。

    jq '{"ProductId":{"S":"p01"},"Title":{"S":"Insulated Travel Mug"},"DescriptionVector":{"L":[.embedding[]|{"N":(.|tostring)}]}}' emb/p01.json > item-p01.json aws dynamodb put-item --table-name Products --item file://item-p01.json
    向量大小和项目限制

    1024 维的向量会在请求有效载荷中增加约 32 KB 的数据量,对存储的项目增加约 5 KB 的数据量,完全在 400 KB 的 DynamoDB 项目大小限制之内。存储嵌入时,维度数量是影响项目大小的主要因素。

  6. 嵌入其余商品。此循环会为剩余的每个商品生成嵌入。它会跳过任何已存在的文件,因此在调用失败时,您可以安全地重新运行循环。

    while IFS=$'\t' read -r id title description; do [ -s "emb/$id.json" ] && continue body=$(jq -n --arg t "$description" '{inputText:$t,dimensions:1024,normalize:true}') aws bedrock-runtime invoke-model \ --model-id amazon.titan-embed-text-v2:0 \ --body "$body" \ --cli-binary-format raw-in-base64-out \ --content-type application/json \ --accept application/json \ "emb/$id.json" >/dev/null || echo "FAILED $id" done < products.tsv ls emb/*.json | wc -l

    您必须确认数量为 50,然后再继续。如果有任何调用输出 FAILED,请再次运行循环,这会只重试缺失的文件。

    InvokeModel 速率配额

    Amazon Bedrock 将请求速率配额应用于 InvokeModel。如果您修改此循环来并行发出调用,则预计一些调用会出现节流异常,请始终验证最终文件数,而不是假设每个调用都成功。部分嵌入的目录在加载时不会出现错误,但生成的搜索结果会隐蔽地忽略缺失的商品。

  7. 加载其余商品。构建请求有效载荷,每个有效载荷中包含 25 个项目,这是 BatchWriteItem 接受的最大值。

    batch=0 count=0 echo -n '{"Products":[' > batch-0.json while IFS=$'\t' read -r id title description; do if [ "$count" -eq 25 ]; then echo ']}' >> "batch-$batch.json" batch=$((batch+1)); count=0 echo -n '{"Products":[' > "batch-$batch.json" fi [ "$count" -gt 0 ] && echo -n ',' >> "batch-$batch.json" jq -c --arg id "$id" --arg title "$title" \ '{PutRequest:{Item:{ProductId:{S:$id},Title:{S:$title}, DescriptionVector:{L:[.embedding[]|{"N":(.|tostring)}]}}}}' \ "emb/$id.json" >> "batch-$batch.json" count=$((count+1)) done < <(tail -n +2 products.tsv) echo ']}' >> "batch-$batch.json"

    循环从重定向而不是管道读取,因为在某些 Shell 中,管道 while 循环在子 Shell 中运行,这会丢弃 batchcount 值,生成格式错误的批次文件。

    提交每个批次。BatchWriteItem 可以部分成功,因此请重新提交它在 UnprocessedItems 中返回的任何内容。

    for f in batch-*.json; do cp "$f" pending.json for attempt in 1 2 3 4 5; do aws dynamodb batch-write-item \ --request-items file://pending.json \ --output json > resp.json left=$(jq '(.UnprocessedItems.Products // []) | length' resp.json) echo "$f attempt $attempt: unprocessed=$left" [ "$left" -eq 0 ] && break jq '.UnprocessedItems' resp.json > pending.json sleep 2 done done

    确认所有 50 个项目均存在。

    aws dynamodb scan --table-name Products --select COUNT --query 'Count'
    ItemCount 更新延迟

    DescribeTable 为向量索引报告 ItemCountIndexSizeBytes 值,大约每六小时更新一次,因此,即使每个项目都已写入,如果在加载后马上查看,它们仍可能显示为 0。如图所示,将 Scan--select COUNT 结合使用来验证加载。不要将 ItemCount 为零视为加载失败。

  8. 生成查询嵌入并搜索。使用与存储项目相同的模型和维度数量,嵌入搜索短语。

    aws bedrock-runtime invoke-model \ --model-id amazon.titan-embed-text-v2:0 \ --body '{"inputText":"How can I make my desk more comfortable to work at","dimensions":1024,"normalize":true}' \ --cli-binary-format raw-in-base64-out \ --content-type application/json \ --accept application/json \ embedding-query.json
    SearchVector 格式与存储的向量格式不同

    当您将向量存储在项目属性中时,会将其封装为 L(列表)类型:{"L":[{"N":"0.123"},...]}。当您将查询向量传递给 SearchVectors 时,您使用的是不带 L 包装器的普通 N 值数组:[{"N":"0.123"},...]。因此,以下 jq 转换不同于您用于存储项目的转换。有关更多信息,请参阅 基本搜索

    jq '[.embedding[]|{"N":(.|tostring)}]' embedding-query.json > query-vector.json aws dynamodb search-vectors \ --table-name Products \ --index-name DescriptionIndex \ --search-vector file://query-vector.json \ --top-k 5 \ --projection-expression "ProductId, Title" \ --return-consumed-capacity TOTAL

    查询向量和存储的向量必须来自相同的嵌入模型,并且维度数量必须相同。使用不同的模型或维度数量会产生毫无意义的结果或验证错误。

  9. 读取结果。DynamoDB 返回按相似性排序的结果,最相似的项目排在最前面。每个结果都包含投影的 Item 属性和 Score

    { "SearchResults": [ { "Item": { "ProductId": { "S": "p11" }, "Title": { "S": "Ergonomic Mesh Office Chair" } }, "Score": 0.6130197048187256 }, { "Item": { "ProductId": { "S": "p12" }, "Title": { "S": "Sit Stand Desk Converter" } }, "Score": 0.781868577003479 }, { "Item": { "ProductId": { "S": "p14" }, "Title": { "S": "Under Desk Footrest" } }, "Score": 0.816369354724884 }, { "Item": { "ProductId": { "S": "p13" }, "Title": { "S": "Monitor Arm Mount" } }, "Score": 0.8283305168151855 }, { "Item": { "ProductId": { "S": "p15" }, "Title": { "S": "Wireless Split Keyboard" } }, "Score": 0.8469693064689636 } ], "ConsumedCapacity": { "VectorSearchRequestBytes": 31449.0 } }

    分数取决于嵌入模型和确切的输入文本,因此您的值会略有不同。重要的是选中了哪些项目以及按什么顺序选择。查询不包含“椅子”、“显示器”或“键盘”字样,但搜索结果返回了全部五个办公桌和办公用品,将它们排在目录中其他 45 个商品之前。没有显示咖啡、露营或宠物类别中的任何商品。

    尝试其他查询来查看不同组的相同行为。使用 "Something to brew fresh coffee at home" 重复上一个步骤,排在最前的结果是意式浓缩咖啡机、手冲滤杯和奶泡器。使用 "Keeping my dog safe on walks" 重复调用,牵引带和挽具排在第一位,然后是松散相关的商品,因为目录中只有两个紧密匹配的商品。最后一种情况需要注意:即使目录中没有那么多合适的匹配项,搜索也始终会返回您需要的商品数量。使用 Score 值(而不是结果计数)来判断匹配质量。

    如何解读分数取决于索引使用的距离函数:

    • COSINEEUCLIDEAN 返回分数最小的项目,所以分数越低越相似。余弦分数的范围从 0(方向完全相同)到 2(方向完全相反)。

    • DOT_PRODUCT 返回分数最高的项目,因此分数越高越相似。

    此索引使用 COSINE,因此第一个结果的分数最低。当搜索的本文与存储的描述完全相同时,会首先返回该项目并且分数等于或接近零。

清理

为避免持续产生费用,请删除您在本教程中创建的资源。只要索引存在,无论您是否对其执行搜索,都会持续产生向量索引存储的费用。

要删除向量索引但保留 Products 表及其项目,请使用 UpdateTable。这些项目保留在表中,仅移除索引。

aws dynamodb update-table \ --table-name Products \ --vector-index-updates \ "[ {\"Delete\": {\"IndexName\": \"DescriptionIndex\"}} ]"

要同时删除表及其向量索引,请删除表。

aws dynamodb delete-table --table-name Products

确认表已删除。在删除完成后,以下命令返回 ResourceNotFoundException

aws dynamodb describe-table --table-name Products

有关从要保留的表中移除向量索引的更多信息,请参阅删除向量索引

后续步骤

在您完成这项基本的向量搜索后,请浏览以下相关主题。