航空制造领域的技术文档从来不只是文字——工程图纸、工艺流程图、材料规范表、检验照片交织在一起,构成一套复杂的多模态知识体系。传统文本检索只能"看懂"文字部分,图表里的关键信息往往被遗漏。Amazon Nova Multimodal Embeddings 的出现,让图像和文本在同一向量空间里对齐,检索终于能真正"读懂"一整份文档。
这篇文章拆解一个实际方案:在 Amazon Bedrock 上调用 Nova 多模态嵌入模型,把航空制造文档(含图片)编码为向量,存入 Amazon S3 Vectors,再对 26 条真实制造查询做检索增强生成(RAG),对比纯文本管线与多模态管线的回答质量。
为什么制造场景必须多模态
航空制造的典型文档长这样:
- 一份工艺规程,前半段是文字描述,后半段附有焊接顺序示意图;
- 一张检验记录表,既有数值列,又有缺陷位置标注照片;
- 材料规范 PDF 里,力学性能数据以表格呈现,微观金相图紧随其后。
纯文本嵌入只处理可提取的文字,图表被丢弃或仅靠 OCR 捕获零散字符。OCR 丢失空间关系和视觉语义——"裂纹位于焊缝热影响区"这句话,在图上是一条红色标注线指向特定区域,OCR 只能得到"裂纹"和"热影响区"两个词,丢失了位置对应关系。
Nova Multimodal Embeddings 的核心能力:同一模型同时编码文本和图像,输出到同一向量空间。这意味着一张焊接顺序图和一段描述焊接顺序的文字,在向量空间里天然靠近,无需额外对齐步骤。
系统架构拆解
整体管线分三层:
- 文档摄入层:从 S3 读取 PDF/图片,按页拆分,每页保留文字+图像对;
- 嵌入与存储层:调用 Bedrock 上的 Nova Multimodal Embeddings,生成向量,写入 S3 Vectors;
- 检索与生成层:用户查询同样走 Nova 嵌入,在 S3 Vectors 中做相似度搜索,取回 top-k 片段,送入 LLM 生成回答。
关键设计决策:
- 按页而非按段落切分:制造文档的图文强耦合,拆散会破坏语义完整性;
- 图像不单独索引:每页的图像与同页文字一起送入模型,模型内部做跨模态融合;
- S3 Vectors 作为向量库:直接在对象存储上做向量搜索,省去独立向量数据库的运维开销。
多模态嵌入调用实战
下面给出可改造运行的 Python 示例,展示如何调用 Nova Multimodal Embeddings 并写入 S3 Vectors。
前置准备
# 安装依赖
pip install boto3 Pillow pdf2image
# 确保 AWS 凭证已配置( ~/.aws/credentials 或环境变量)
# 需要的 IAM 权限:
# - bedrock:InvokeModel (Nova Multimodal Embeddings)
# - s3:PutObject / s3:GetObject
# - s3-vector:PutVectorIndex / s3-vector:QueryVectorIndex
生成多模态嵌入
import boto3
import json
import base64
from pathlib import Path
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
MODEL_ID = "amazon.nova-multimodal-embedding-v1"
EMBED_DIM = 1024 # 支持维度:256 / 512 / 1024,制造场景建议用 1024
def encode_image_to_base64(image_path: str) -> str:
"""将本地图片转为 base64 字符串,供 Bedrock 调用"""
with open(image_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
def get_multimodal_embedding(text: str, image_path: str = None) -> list[float]:
"""
调用 Nova Multimodal Embeddings,同时编码文字和图片。
如果没有图片,只传文字即可。
"""
content = [{"text": text}]
if image_path:
content.append({
"image": {
"format": Path(image_path).suffix.lstrip("."), # jpg / png
"source": {"bytes": encode_image_to_base64(image_path)}
}
})
body = {
"input": {"content": content},
"configuration": {"outputDimensionality": EMBED_DIM}
}
response = bedrock.invoke_model(
modelId=MODEL_ID,
body=json.dumps(body),
accept="application/json",
contentType="application/json"
)
result = json.loads(response["body"].read())
return result["embedding"]
# ---- 示例:编码一页制造文档 ----
page_text = "焊接顺序:先焊内环缝(G1),再焊外环缝(G2),层间温度控制在150°C以下。"
page_image = "weld_sequence_diagram.png" # 本地图片路径
embedding = get_multimodal_embedding(page_text, page_image)
print(f"向量维度: {len(embedding)}") # 1024
注意:
image_path为本地文件路径,实际生产中可从 S3 下载到临时文件再编码。如果文档页只有文字没有图片,image_path传None,模型退化为纯文本嵌入,但输出仍在同一向量空间。
写入 S3 Vectors 并检索
import boto3
s3v = boto3.client("s3-vector", region_name="us-east-1")
VECTOR_BUCKET = "aero-manufacturing-vectors"
INDEX_NAME = "doc-pages-index"
# 1. 创建向量索引(首次运行)
s3v.create_vector_index(
vectorBucketName=VECTOR_BUCKET,
indexName=INDEX_NAME,
dimension=EMBED_DIM,
distanceMetric="cosine"
)
# 2. 写入向量
def upsert_page_vector(page_id: str, embedding: list[float], metadata: dict):
s3v.put_vectors(
vectorBucketName=VECTOR_BUCKET,
indexName=INDEX_NAME,
vectors=[
{
"key": {"id": page_id},
"values": embedding,
"metadata": metadata # 如 {"doc": "WS-2024-037", "page": 5}
}
]
)
upsert_page_vector(
page_id="WS-2024-037_p5",
embedding=embedding,
metadata={"doc": "WS-2024-037", "page": 5, "type": "weld_procedure"}
)
# 3. 查询检索
def search_pages(query_text: str, top_k: int = 5) -> list[dict]:
query_emb = get_multimodal_embedding(query_text) # 纯文本查询
result = s3v.query_vector_index(
vectorBucketName=VECTOR_BUCKET,
indexName=INDEX_NAME,
queryVector={"values": query_emb},
topK=top_k,
returnMetadata=True
)
return result["results"]
# 实际查询
hits = search_pages("内环缝焊接的层间温度要求是多少?")
for hit in hits:
print(f"文档: {hit['metadata']['doc']}, 页码: {hit['metadata']['page']}, 距离: {hit['distance']}")
检索增强生成闭环
from anthropic import AnthropicBedrock
anthropic = AnthropicBedrock()
def generate_answer(query: str, top_k: int = 5) -> str:
hits = search_pages(query, top_k)
# 组装上下文:取回页面的文字部分(metadata 中可扩展存储原文)
context_blocks = []
for hit in hits:
context_blocks.append({
"type": "text",
"text": f"[文档 {hit['metadata']['doc']} 第{hit['metadata']['page']}页]\n"
+ hit["metadata"].get("page_text", "")
})
# 如果检索到的页面有图片,也可以把图片直接传给 LLM
img_path = hit["metadata"].get("page_image_path")
if img_path:
context_blocks.append({
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": encode_image_to_base64(img_path)
}
})
response = anthropic.messages.create(
model="anthropic.claude-3-5-sonnet-20241022-v2:0",
max_tokens=1024,
system="你是航空制造工艺专家,根据提供的文档内容准确回答问题。如果文档中没有相关信息,明确说明。",
messages=[{
"role": "user",
"content": context_blocks + [{"type": "text", "text": query}]
}]
)
return response.content[0].text
answer = generate_answer("内环缝焊接的层间温度要求是多少?")
print(answer)
26 条查询的对比结论
原文对 26 条航空制造真实查询做了系统评测,核心发现:
| 维度 | 纯文本管线 | 多模态管线 |
|---|---|---|
| 图表信息召回 | 低——图表内容几乎无法检索 | 高——图像语义被编码进向量 |
| 空间关系问答 | 无法回答"图中哪个位置"类问题 | 能定位到对应图像区域 |
| 纯文字问题 | 两者持平 | 持平,无退化 |
| 整体准确率 | 约 60% | 约 85% |
多模态管线在纯文字问题上没有退化,这是关键——同一向量空间意味着文字查询仍然能精准匹配文字片段,同时额外获得了图表理解能力。
值得注意的边界:
- 图像质量敏感:低分辨率扫描件或手写批注,嵌入质量会下降,建议预处理时做分辨率增强;
- 成本:多模态嵌入的调用费用高于纯文本嵌入,需评估文档中图片的实际密度——如果 90% 的页面只有文字,可以只对含图页面走多模态,其余走纯文本,混合索引仍有效;
- 维度选择:256 维适合大规模粗筛,1024 维适合最终精准检索,制造场景建议 1024。
上手清单
- 确认文档结构:统计制造文档中含图页面的比例,决定是否需要全量多模态还是混合策略;
- 开通 Bedrock Nova 模型:在 AWS Console 的 Bedrock 区域申请 Nova Multimodal Embeddings 访问权限;
- 创建 S3 Vectors 索引:选定维度和距离度量(制造场景推荐
cosine+ 1024 维); - 编写摄入脚本:按页拆分 PDF,提取文字 + 渲染页面为图片,批量调用嵌入并写入向量库;
- 评测检索质量:用 20-30 条真实业务查询对比纯文本 vs 多模态的召回率和生成准确率;
- 决定生产策略:根据评测结果选择全量多模态或混合索引,设置合理的 top-k 和 rerank 逻辑。
制造文档的智能检索,瓶颈从来不在文字理解,而在图表理解。Nova Multimodal Embeddings 把图文统一到同一向量空间,S3 Vectors 把向量存储拉回对象存储的简单运维模型——两个组件组合起来,制造场景的多模态 RAG 从"概念验证"进入了"可投产"阶段。