用 Amazon Nova 2 Lite 做目标检测:从 Prompt 到部署的全链路实践

2026-06-03 17 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:13 分钟

视觉大模型正在从"能看懂图"走向"能结构化地看懂图"。Amazon Nova 2 Lite 是 Bedrock 上新增的多模态模型,它不仅能识别图中的物体,还能按你要求的 JSON 格式输出检测结果——坐标、类别、置信度一应俱全。这意味着你不再需要单独跑一个 YOLO 推理服务,直接用一次 API 调用就能完成"看图 → 出结构"的闭环。

这篇文章把从 Prompt 设计到 Lambda + API Gateway 部署的完整链路拆开讲,并给出可以直接改造的代码。

Nova 2 Lite 的检测能力边界

Nova 2 Lite 的目标检测不是传统 CV 模型那种"训练 → 推理"范式,而是基于多模态理解的 Prompt 驱动检测。你告诉它要找什么,它就在图中定位并返回结构化结果。

这种方式的优劣很明确:

  • 优势:零训练、灵活定义类别、天然支持开放词汇检测。制造业想找"螺丝缺失"、农业想找"病斑"、物流想找"破损纸箱",只需要改 Prompt,不用改模型。
  • 边界:对极小目标(像素级缺陷)的定位精度不如专用检测模型;高频实时场景(>10 FPS)不适合走 Bedrock API 调用链路。

理解了边界,就知道该在什么场景用它:低频、多类别、需要快速迭代的检测任务是它的甜区。

Prompt 设计:检测质量的关键杠杆

Nova 2 Lite 的检测结果质量,很大程度上取决于你怎么写 Prompt。以下是三条实测有效的原则:

1. 明确列出目标类别,不要用模糊描述。

差的写法:

找出图中所有有问题的地方

好的写法:

检测图中以下类别的物体:缺失螺丝、错位卡扣、表面裂纹。对每个检测到的物体,返回其类别名称、在图中的位置(用 bounding box 的左上角和右下角坐标表示,坐标系以像素为单位,原点在图片左上角),以及置信度(0-1之间的浮点数)。

2. 锁定输出格式,减少后端解析成本。

在 Prompt 中直接给出 JSON schema,比说"请用 JSON 格式输出"要稳定得多:

请严格按照以下 JSON 格式输出,不要添加任何额外字段或解释:
{
  "detections": [
    {
      "label": "string - 类别名称",
      "box": {"x1": "int", "y1": "int", "x2": "int", "y2": "int"},
      "confidence": "float"
    }
  ]
}
如果某个类别未检测到,该类别不出现在数组中。

3. 给坐标系定义,避免歧义。

坐标原点在哪、单位是像素还是比例、box 是 [x,y,w,h] 还是 [x1,y1,x2,y2]——这些细节不写清楚,模型可能每次格式都不一样。上面示例中的"原点在图片左上角、像素单位、左上右下角坐标"就是一次明确的约定。

Lambda 函数:一次调用的完整实现

下面是一个可以直接部署的 Lambda 函数,它接收图片(Base64 编码)和检测类别列表,调用 Bedrock 上的 Nova 2 Lite,解析 JSON 结果并返回。

import json
import base64
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
MODEL_ID = "amazon.nova-2-lite"

def build_prompt(categories: list[str]) -> str:
    """根据目标类别列表构建检测 Prompt"""
    category_str = ", ".join(categories)
    return (
        f"检测图中以下类别的物体:{category_str}\n"
        "对每个检测到的物体,返回其类别名称、在图中的位置"
        "(bounding box 左上角和右下角坐标,像素单位,原点在图片左上角),"
        "以及置信度(0-1 浮点数)。\n\n"
        "请严格按照以下 JSON 格式输出,不要添加额外字段或解释:\n"
        '{"detections":[{"label":"string","box":{"x1":"int","y1":"int","x2":"int","y2":"int"},"confidence":"float"}]}\n'
        "如果某个类别未检测到,该类别不出现在数组中。"
    )

def call_nova(image_b64: str, prompt: str) -> dict:
    """调用 Bedrock Nova 2 Lite 并解析 JSON 结果"""
    # Nova 系列的请求格式:image 放在 content 里,text 作为消息
    payload = {
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/jpeg",
                            "data": image_b64,
                        },
                    },
                    {"type": "text", "text": prompt},
                ],
            }
        ],
        "inferenceConfig": {"maxTokens": 2048, "temperature": 0.1},
    }

    response = bedrock.invoke_model(
        modelId=MODEL_ID,
        body=json.dumps(payload),
        contentType="application/json",
        accept="application/json",
    )

    result = json.loads(response["body"].read())
    # Nova 返回的结构中,文本在 output.message.content 里
    text_output = result["output"]["message"]["content"][0]["text"]

    # 模型可能在外面包一层 markdown code block,做一下清洗
    text_output = text_output.strip()
    if text_output.startswith("```json"):
        text_output = text_output[len("```json"):]
    if text_output.startswith("```"):
        text_output = text_output[len("```"):]
    if text_output.endswith("```"):
        text_output = text_output[:-len("```")]
    text_output = text_output.strip()

    return json.loads(text_output)

def lambda_handler(event, context):
    """API Gateway 入口"""
    try:
        body = json.loads(event.get("body", "{}"))
        image_b64 = body["image_base64"]
        categories = body.get("categories", ["defect", "missing_part"])

        prompt = build_prompt(categories)
        detections = call_nova(image_b64, prompt)

        return {
            "statusCode": 200,
            "body": json.dumps({
                "status": "ok",
                "detections": detections["detections"],
                "count": len(detections["detections"]),
            }),
        }
    except json.JSONDecodeError as e:
        logger.error(f"JSON parse error: {e}")
        return {"statusCode": 502, "body": json.dumps({"status": "error", "message": "模型输出无法解析为 JSON"})}
    except Exception as e:
        logger.error(f"Unexpected error: {e}")
        return {"statusCode": 500, "body": json.dumps({"status": "error", "message": str(e)})}

部署前需要改的地方:region_name 换成你 Bedrock 开启的区域;MODEL_ID 确认模型标识符(Bedrock 模型列表中可查);Lambda 的执行角色需要 bedrock:InvokeModel 权限。

API Gateway 配置:把检测变成 HTTP 接口

用 API Gateway 把上面的 Lambda 暴露为 POST 接口,前端或边缘设备就能直接调用。以下是 SAM 模板的核心片段:

Resources:
  DetectFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Timeout: 30
      MemorySize: 256
      Policies:
        - Statement:
            Effect: Allow
            Action: bedrock:InvokeModel
            Resource: !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/amazon.nova-2-lite"
      Events:
        DetectApi:
          Type: Api
          Properties:
            Path: /detect
            Method: post

部署命令:

sam build && sam deploy --guided

部署完成后,调用方式:

# 把图片转 base64 并调用
IMAGE_B64=$(base64 -i factory_photo.jpg)

curl -X POST "$API_ENDPOINT/detect" \
  -H "Content-Type: application/json" \
  -d "{\"image_base64\": \"$IMAGE_B64\", \"categories\": [\"missing_screw\", \"misaligned_clip\", \"surface_crack\"]}"

返回示例:

{
  "status": "ok",
  "detections": [
    {"label": "missing_screw", "box": {"x1": 320, "y1": 180, "x2": 380, "y2": 220}, "confidence": 0.87},
    {"label": "surface_crack", "box": {"x1": 510, "y1": 400, "x2": 620, "y2": 440}, "confidence": 0.72}
  ],
  "count": 2
}

结果可视化:快速验证检测效果

拿到坐标后,最直接的验证方式是在原图上画框。以下脚本可以在本地跑,也可以嵌入 Lambda 做预处理:

from PIL import Image, ImageDraw
import json

def visualize(image_path: str, detections: list, output_path: str = "result.jpg"):
    img = Image.open(image_path)
    draw = ImageDraw.Draw(img)

    for det in detections:
        box = det["box"]
        x1, y1, x2, y2 = box["x1"], box["y1"], box["x2"], box["y2"]
        label = f"{det['label']} {det['confidence']:.2f}"
        draw.rectangle([x1, y1, x2, y2], outline="red", width=3)
        draw.text((x1, y1 - 15), label, fill="red")

    img.save(output_path)
    print(f"可视化结果已保存到 {output_path}")

# 使用示例
with open("api_response.json") as f:
    resp = json.load(f)

visualize("factory_photo.jpg", resp["detections"])

三个行业的落地切入点

源文中提到的制造业、农业、物流场景,用 Nova 2 Lite 的检测方式各有不同的适配策略:

制造业——质检巡检:产线上拍一张组装完成图,Prompt 列出所有应存在的零件类别。检测到 missing 类别即触发告警。优势是换产品线只改 Prompt 里的类别列表,不用重新训练模型。

农业——病害识别:田间摄像头定期拍摄叶片照片,Prompt 指定"锈斑、霉层、虫蛀孔"。输出 JSON 后直接写入时序数据库,跟踪病害扩散趋势。注意:病害检测对颜色敏感,输入图片尽量保留原始色彩,不要做灰度化预处理。

物流——破损分拣:分拣线上的包裹图片送入检测接口,类别设为"外箱破损、标签撕裂、液体泄漏"。检测到任一类别就标记该包裹进入人工复核通道。这类场景图片分辨率通常不高,Prompt 中可以加一句"注意低分辨率下的边缘模糊特征"来提升检测敏感度。

上线前的检查清单

把 Nova 2 Lite 的检测服务推到生产环境前,逐项确认:

  • Prompt 稳定性:同一张图跑 5 次,检测结果(类别 + 坐标)的偏差是否在业务容忍范围内。temperature 设为 0.1 可以降低随机性。
  • 延迟预算:Bedrock 调用 + Lambda 执行的端到端延迟通常在 2-5 秒。如果业务要求 <1 秒响应,这个链路不合适。
  • 图片尺寸:Nova 2 Lite 对输入图片有尺寸限制(具体上限查 Bedrock 文档)。超过限制的图片需要先缩放,缩放后坐标要按比例还原。
  • JSON 解析兜底:模型偶尔会在 JSON 外加解释文字或 markdown 包裹。上面的 Lambda 代码已经做了清洗,但建议再加一层 try-catch,解析失败时返回原始文本供人工排查。
  • 成本估算:Bedrock 按 request 计费,每次检测是一次 multimodal invoke。高频场景(每分钟数十次)下,成本可能超过自托管检测模型,需要做算账。

Nova 2 Lite 的 Prompt 驱动检测不是万能替代,但在"类别多变、迭代快速、调用低频"的场景下,它省掉了训练管线和模型运维,用一次 API 调用就把"看图出结构"这件事做完了。从 Prompt 到 Lambda 到 API Gateway,整条链路的代码量不到 200 行——这才是它最大的价值。


相关推荐