保险文档分类并不只是“读一段文字,然后输出一个标签”。保单、宣誓书、批单和理赔材料往往同时包含文本、表格、印章、签名以及固定版式。只依赖 OCR 文本,可能错过决定文档类型的视觉线索;只做图像相似度搜索,又难以理解条款内容。
一种更稳健的方案是使用 Amazon Bedrock 构建多智能体分类流程:让专门的智能体分别处理文本语义、视觉相似度和最终决策,再由协调智能体汇总结果。Strands Agents SDK 可以用来组织这些智能体,Claude Haiku 4.5 负责轻量文本分析,Amazon Titan Multimodal Embeddings 负责文本与图像的向量表示。
为什么要把分类任务拆成多个智能体
单个提示词通常能处理简单分类,但在保险文档场景中会遇到三个边界:
- 文本不完整:扫描件可能只有部分 OCR 结果,表格和页眉信息容易丢失。
- 视觉结构重要:宣誓书可能具有固定的签名区、见证人区和公证章;保单通常包含条款标题、保险责任和保费信息。
- 分类证据不同:一个智能体擅长解释文本,另一个智能体擅长比较版式或页面图像,最终结果需要合并而不是互相替代。
可以将流程拆成三个角色:
- 文本分析智能体:读取 OCR 文本,抽取标题、关键词、实体和候选类别。
- 视觉检索智能体:调用 Titan Multimodal Embeddings,将文档页面与已标注样本进行相似度比较。
- 决策智能体:结合文本证据、视觉证据和置信度,输出最终类别、理由以及是否需要人工复核。
这不是为了“让更多模型参与”而增加复杂度,而是为了让每种证据都有明确的来源。生产环境中还可以把每个智能体的输出保存下来,方便审计和回溯。
一个可落地的处理流程
典型流程可以表示为:
S3 文档
│
├── OCR / 文本抽取 ──> 文本分析智能体
│
├── 页面渲染 ────────> Titan Multimodal Embeddings ──> 向量检索
│
└────────────────────> 决策智能体 ──> 类别、理由、置信度
向量检索库中可以预先保存经过人工确认的样本文档,例如:
{
"document_id": "sample-policy-001",
"label": "insurance_policy",
"page": 1,
"embedding_model": "amazon.titan-embed-image-v1",
"metadata": {
"language": "en",
"source": "human_reviewed"
}
}
对于新文档,视觉智能体不应只返回“最相似的样本”,还应该返回相似度、命中的类别和样本 ID。决策智能体可以据此判断:视觉证据是否足够强,是否与文本证据冲突,以及是否必须转人工。
用 Strands Agents SDK 组织智能体
下面是一个可改造的 Python 示例。它展示了三个智能体的职责边界,以及如何把结构化结果交给最终决策智能体。示例中的模型 ID、索引查询函数和输入字段需要根据 AWS 账户所在区域及实际部署方式调整。
运行前安装依赖并配置凭证:
python -m venv .venv
source .venv/bin/activate
pip install strands-agents boto3
export AWS_REGION=us-east-1
export BEDROCK_TEXT_MODEL_ID=your-enabled-haiku-model-id
export BEDROCK_VISION_MODEL_ID=amazon.titan-embed-image-v1
示例代码:
import json
import os
from pathlib import Path
import boto3
from strands import Agent
from strands.models import BedrockModel
REGION = os.getenv("AWS_REGION", "us-east-1")
TEXT_MODEL_ID = os.environ["BEDROCK_TEXT_MODEL_ID"]
VISION_MODEL_ID = os.getenv("BEDROCK_VISION_MODEL_ID", "amazon.titan-embed-image-v1")
runtime = boto3.client("bedrock-runtime", region_name=REGION)
text_model = BedrockModel(
model_id=TEXT_MODEL_ID,
region_name=REGION,
)
text_agent = Agent(
model=text_model,
system_prompt=(
"You classify insurance documents from OCR text. "
"Return JSON with label, evidence, and confidence. "
"Allowed labels: insurance_policy, affidavit, endorsement, unknown."
),
)
decision_agent = Agent(
model=text_model,
system_prompt=(
"You are the final insurance-document classifier. "
"Combine text evidence and visual-search evidence. "
"Return strict JSON: label, confidence, reasons, needs_human_review. "
"If evidence conflicts or confidence is below 0.80, request human review."
),
)
def embed_image(image_path: str) -> list[float]:
"""Create a Titan multimodal embedding for one page image.
Confirm the request schema and model ID in the target AWS Region before use.
"""
image_bytes = Path(image_path).read_bytes()
body = json.dumps({
"inputImage": image_bytes.hex(),
"embeddingConfig": {"outputEmbeddingLength": 1024},
})
response = runtime.invoke_model(
modelId=VISION_MODEL_ID,
body=body,
contentType="application/json",
accept="application/json",
)
payload = json.loads(response["body"].read())
return payload["embedding"]
def search_visual_examples(embedding: list[float]) -> list[dict]:
"""Replace this adapter with OpenSearch Serverless, Aurora, or another vector store."""
# 生产实现中应使用向量数据库的 k-NN 查询,并返回 metadata 与 score。
return [
{
"sample_id": "sample-affidavit-004",
"label": "affidavit",
"score": 0.91,
"evidence": "signature and notary block are visually similar",
}
]
def classify_document(ocr_text: str, first_page_image: str) -> dict:
text_result = text_agent(
f"OCR text:\n{ocr_text}\n\nReturn JSON only."
)
embedding = embed_image(first_page_image)
visual_result = search_visual_examples(embedding)
decision_input = {
"text_analysis": str(text_result),
"visual_search": visual_result,
"policy": {
"human_review_threshold": 0.80,
"conflict_rule": "text and visual evidence conflict => review",
},
}
final_result = decision_agent(json.dumps(decision_input, ensure_ascii=False))
return {"result": str(final_result), "visual_matches": visual_result}
if __name__ == "__main__":
result = classify_document(
ocr_text="AFFIDAVIT OF LOSS ... subscribed and sworn before me ...",
first_page_image="page-001.jpg",
)
print(json.dumps(result, ensure_ascii=False, indent=2))
这个示例有两个重要的工程假设:
- Titan 的请求字段、输出维度和具体模型 ID 可能随模型版本或区域配置变化,部署前应以目标区域的 Bedrock 文档和控制台为准。
search_visual_examples只是向量数据库适配器。生产环境需要保存 embedding、类别、文档版本、数据来源和人工标注状态,并对查询结果做权限控制。
“向量提示”不只是相似度搜索
向量检索返回的是候选证据,不是最终答案。更可靠的决策提示应该把候选样本、分数、文本分析和业务规则一起传给决策智能体,例如:
You classify one insurance document.
Text evidence:
- OCR keywords: "subscribed", "sworn", "notary"
- Text candidate: affidavit
- Text confidence: 0.86
Visual evidence:
- Top match: sample-affidavit-004
- Similarity: 0.91
- Visual cues: signature area and notary block
Rules:
1. Choose only from the approved label set.
2. Do not treat similarity alone as proof.
3. If evidence conflicts or confidence < 0.80, set needs_human_review=true.
4. Return valid JSON only.
这种提示把“模型自由发挥”变成了可检查的证据合并过程。还可以要求输出 reasons、evidence_ids 和 missing_information,让下游系统知道为什么做出决定。
生产环境需要重点控制的风险
置信度不能直接当作准确率
模型输出的 0.91 不一定代表真实准确率为 91%。应该使用一批经过人工标注的验证集做校准,并分别统计每种文档类型的 precision、recall 和人工复核率。
多页文档不能只看第一页
第一页通常能提供强线索,但附加条款或签名可能出现在后续页面。可以对每页生成 embedding,再通过最大相似度、平均相似度或页面级投票汇总文档级结果。
OCR 和图像数据都要做隐私保护
保险文档可能包含姓名、地址、保单号和健康信息。应使用 S3 加密、最小 IAM 权限、日志脱敏和明确的数据保留策略。调试日志中不要直接输出完整 OCR 文本或原始图像。
低置信度路径必须真实可用
人工复核不是一个布尔字段就结束了。系统至少需要把原文档、候选标签、文本证据、视觉命中样本和模型版本一起交给审核人员,并保存最终修正结果,用于后续评估和样本库更新。
采用前的检查清单
- [ ] 定义有限且互斥的保险文档标签集合。
- [ ] 为每个标签准备经过人工确认的文本和页面图像样本。
- [ ] 明确 OCR、页面渲染、向量数据库和 Bedrock 的数据流向。
- [ ] 让文本智能体、视觉智能体和决策智能体输出可审计的结构化证据。
- [ ] 用验证集校准置信度,并设置人工复核阈值。
- [ ] 对多页文档、低质量扫描件、混合语言和类别未知样本单独测试。
- [ ] 记录模型版本、提示版本、embedding 模型和检索结果。
多智能体架构的价值不在于把一个分类器拆成三个黑盒,而在于把不同类型的证据显式组合起来。对保险文档这类既依赖语义又依赖版式的任务,Amazon Bedrock、Claude Haiku 4.5、Titan Multimodal Embeddings 和 Strands Agents SDK 可以组成一条清晰的工程链路;真正上线前,则要用数据集、阈值、审计和人工复核把它变成可控的业务系统。