跳到正文
AWS Machine Learning Blog· Jyothirmai Kottu·· 2 小时前精选AI 评分65

AWS 教程:在 Amazon SageMaker AI 上用 llama.cpp 部署决策模型

Deploy decision models with llama.cpp on Amazon SageMaker AI

AI 导读

AWS 介绍如何在 Amazon SageMaker AI 上使用 llama.cpp 部署决策模型,提供 OpenAI-compatible 聊天补全接口,并可扩展 SemIf 以返回指定选项的概率和 logits。

推荐理由

文章对比了 llama.cpp 标准文本生成接口与 SemIf 结构化决策扩展,并用 CPU、GPU 基准数据展示延迟和准确率差异。

正文 · AI 翻译

需要对请求进行路由、选择代理操作或分配策略结果的应用,通常需要针对固定选项集输出分数,而不是自由格式的文本。使用 llama.cpp 在 Amazon SageMaker AI 上部署这些决策模型,可以通过托管方式在 CPU 或 GPU 基础设施上提供量化模型服务。

本文延续了关于用于生成式 AI 推理的专用 AWS 深度学习容器(DLC)的系列文章。此前的文章介绍了 WhisperX、vLLM-Omni – Part 1 和 vLLM-Omni – Part 2。

在本文中,你首先使用 AWS DLC for llama.cpp,通过 SageMaker AI 提供兼容 OpenAI 的聊天补全服务 InvokeEndpoint。随后,你使用 SemIf(原名 OpenJev)扩展同一运行时;它无需解码答案 token,即可返回针对声明选项的概率。配套的代码示例包含 CPU 和 GPU 部署选项。

解决方案概述

图 1 展示了从同一个已打包模型出发的两条部署路径。文本生成路径使用用于 llama.cpp 的 AWS DLC,并通过兼容 OpenAI 的 API 返回生成的响应。结构化决策路径则构建一个子镜像,用 SemIf 适配器替换默认服务器,并返回声明选项的概率。

An application invokes an Amazon SageMaker AI real-time endpoint that can use the AWS Deep Learning Container for llama.cpp for generated text or a SemIf child image for option probabilities. Either serving mode can run on CPU or GPU.

图 1:根据工作负载的 API 和延迟要求,在 CPU 或 GPU 上运行用于 llama.cpp 的 AWS DLC 或 SemIf 子镜像

对于标准路径,请将模型打包为 model.tar.gz 并存储在 Amazon Simple Storage Service (Amazon S3) 中,然后直接引用已发布的 DLC 镜像。容器会在 /opt/ml/model 下查找第一个 .gguf 文件,将 POST /invocations 映射到 llama-server 的 /v1/chat/completions,并将 SM_LLAMA_CPP_* 环境变量传递给 llama.cpp 参数。

llama-server HTTP API 不会公开 SemIf 所使用的最终位置 logits 和状态操作。因此,结构化决策路径使用 AWS CodeBuild 构建子镜像,并将其存储在 Amazon Elastic Container Registry (Amazon ECR) 中。子镜像保留 DLC 中已编译的 CPU 或 CUDA 库,同时将入口点替换为兼容 SageMaker 的适配器。

了解输入和输出架构

用于 llama.cpp 的 AWS DLC 接受兼容 OpenAI 的 Chat Completions 架构。发送一个包含 role 和 content 的 messages 数组,并附带 max_tokens 和 stream 等生成控制参数:

{
  "messages": [
    {
      "role": "user",
      "content": "A customer cannot receive a password reset email. Reply with the best support queue."
    }
  ],
  "max_tokens": 64,
  "stream": false
}

响应使用兼容 OpenAI 的 choices 数组。从 choices[0].message.content 中读取生成的文本。

SemIf 使用受约束的决策架构,而不是聊天消息。请求包含调用方定义的 ID、非结构化状态、一个问题以及按顺序排列的允许选项:

{
  "decision": {
    "id": "support-route",
    "state": "The customer cannot receive a password reset email.",
    "question": "Which queue should handle this request?",
    "options": [
      {"id": "account_access", "description": "Account access support."},
      {"id": "billing", "description": "Billing support."}
    ]
  }
}

响应按相同顺序返回 option_ids、probabilities 和原始 option_logits,同时提供提示词、计时信息和固定的模型元数据。借助稳定的选项 ID,你的应用可以应用阈值、回退机制和策略检查,而无需解析生成的自然语言文本。

SemIf 属于更广泛的决策模型类别:根据非结构化状态,对应用定义的结果进行评分。其他实现包括 Jev(TypeSafe 的决策模型和 /v1/systemone API);Kev(专为兼容 TypeSafe API 而设计的模型服务器);以及 Strands Decider 2B(专为代理工作流构建、具有校准输出的模型)。

选择合适的决策工作流

当您的应用在请求时定义允许的选项,并需要模型解释非结构化上下文时,请使用此模式。示例包括将案例路由到队列、从已知集合中提取一个值、分配事件优先级、检查策略结果或选择代理操作。请将授权和确定性业务规则置于模型之外,并在将模型概率视为运营置信度之前对其进行验证。

当您需要通过 InvokeEndpoint 使用与 OpenAI 兼容的聊天补全时,请使用标准的 SageMaker AI 文本生成路径。SageMaker AI 镜像将 POST /invocations 映射到 llama-server /v1/chat/completions 端点。仅当您的应用需要此接口不提供的行为时,才构建子镜像,例如 SemIf 的直接 logits 和状态访问。

移除答案 token 生成并不会移除模型推理。在 SemIf 读取最终 logits 之前,模型仍会处理完整的状态、问题和选项描述。请让提示词保持聚焦,并在投入生产前,使用来自您应用的带标签决策评估所选模型。

针对您的延迟和质量目标进行调优

以下基准测试适用于 SemIf 扩展,而非文本生成端点。模型大小、量化、提示词长度、运行时和 CPU 线程数都会影响每次决策。为量化这些权衡,我们将相同的 231 个公共 JevBench 任务依次发送到一个 CPU 端点和一个 GPU 端点。

配置 AWS DLC for llama.cpp 有效响应 准确率 客户端 p50 客户端 p95
Qwen3.5-4B Q4_K_M、ml.c6i.8xlarge,32 个线程 llama-cpp:server-sagemaker-cpu-v1 231/231 179/231 (77.5%) 1.715 秒 19.291 秒
Qwen3.5-4B Q4_K_M、ml.g6e.xlarge,完整 GPU 卸载 llama-cpp:server-sagemaker-cuda-v1 231/231 180/231 (77.9%) 0.352 秒 0.599 秒

与 CPU 配置相比,GPU 端点在 p50 时快了 4.9 倍,在 p95 时快了 32.2 倍。该示例使用 SageMaker AI 实例池功能按优先级顺序列出兼容的 GPU 实例类型,因此当首选项不可用时,SageMaker 可以选择替代项。该池按 g6e、g6、g5,然后 g4dn 的顺序排列。SageMaker 选择了 ml.g6e.xlarge,启动日志确认 llama.cpp 将全部 34 个模型层卸载到了 NVIDIA L40S。CPU 和 GPU 镜像使用了不同的 llama.cpp 构建版本,并在六个困难任务上产生了不同的预测结果。请将这一个答案的准确率差异视为运行时变化,而不是 GPU 卸载能够提升模型质量的证据。

较小的模型降低了中位延迟,但 2B 和 0.8B 候选模型的准确率分别为 66.7% 和 58.4%。因此,该示例将 4B Q4_K_M 模型作为质量优先的默认选项。这些测量结果包含网络延迟,并非服务保证。

请针对您自己的决策,对完整的模型和基础设施配置进行基准测试。对于后台、离线或低吞吐量工作负载,如果其对延迟不敏感且可以容忍数秒的响应时间,请使用 CPU。对于交互式决策、较长提示词或对尾延迟有严格目标的工作负载,请使用 GPU。保持状态和选项描述简洁,将共享相同状态的决策合并到一个请求中,并扩展端点实例以支持并发。共享状态评分会复用公共提示词前缀,而适配器会返回可重试的 HTTP 503 响应,而不是让有状态的 llama.cpp 上下文周围形成无界队列。

先决条件

您需要:

  • 部署 AWS 区域中的 AWS 账户和 S3 存储桶。
  • Python 3.10 或更高版本、Git,以及 AWS Command Line Interface (AWS CLI)。
  • 用于可选 SemIf 扩展的专用 CodeBuild 服务角色。
  • SageMaker AI 可以代入的 AWS Identity and Access Management (IAM) 角色。
  • SageMaker AI、Amazon ECR、Amazon S3、CodeBuild、IAM 传递角色操作以及 Amazon CloudWatch Logs 的权限。
  • 兼容的 x86 CPU 或 NVIDIA GPU SageMaker 实时终端节点配额。

文本生成示例使用 CPU DLC 和固定的 CPU 实例。SemIf 扩展将 CPU 作为对延迟不敏感工作负载的默认选项,并提供带有有序实例池的可选 CUDA 映像。在创建终端节点之前,请查看 SageMaker AI 定价。

部署决策模型并使用 SemIf 扩展

使用以下步骤部署标准文本生成终端节点,然后为 CPU 或 GPU 上的结构化决策构建可选的 SemIf 扩展。

  1. Clone the code sample and install its dependencies.
    git clone https://github.com/aws-samples/sagemaker-genai-hosting-examples.git
    cd sagemaker-genai-hosting-examples/03-features/semif-decision-model
    python3 -m venv .venv
    . .venv/bin/activate
    python3 -m pip install -r deploy/requirements.txt
  2. Set the deployment values for your account.
    export AWS_REGION="<your-region>"
    export ARTIFACT_BUCKET=amzn-s3-demo-semif-artifacts
    export CODEBUILD_ROLE_ARN=arn:aws:iam::111122223333:role/semif-codebuild-role
    export SAGEMAKER_ROLE_ARN=arn:aws:iam::111122223333:role/semif-sagemaker-role
    export STANDARD_ENDPOINT_NAME=llamacpp-gguf
    export SEMIF_ENDPOINT_NAME=semif-decision-workflows
  3. Package the pinned GGUF model and tokenizer with CodeBuild.
    python deploy/prepare_model_build.py \
      --region "$AWS_REGION" \
      --bucket "$ARTIFACT_BUCKET" \
      --codebuild-role-arn "$CODEBUILD_ROLE_ARN" \
      --model-s3-uri "s3://$ARTIFACT_BUCKET/semif-llamacpp/model.tar.gz"

    等待返回的构建 ID 达到 SUCCEEDED。构建过程会在 model-manifest.json 中记录源修订版本和校验和;如果 GGUF 校验和与经过验证的值不同,则停止。

  4. Deploy the GGUF model with the AWS DLC for llama.cpp.

    从 AWS DLC for llama.cpp 文档中解析固定摘要的 server-sagemaker-cpu-v1 映像,然后创建 SageMaker AI 模型、终端节点配置和终端节点:

    export STANDARD_IMAGE_URI="<published-llama-cpp-cpu-image>@sha256:<digest>"
    
    python deploy/deploy_standard_endpoint.py \
      --region "$AWS_REGION" \
      --image-uri "$STANDARD_IMAGE_URI" \
      --model-data-url "s3://$ARTIFACT_BUCKET/semif-llamacpp/model.tar.gz" \
      --role-arn "$SAGEMAKER_ROLE_ARN" \
      --instance-type ml.c6i.2xlarge \
      --name "$STANDARD_ENDPOINT_NAME"

    选择 server-sagemaker-cpu-v1 或 server-sagemaker-cuda-v1 映像,而不是对应的 EC2 映像。SageMaker AI 映像实现 /ping 和 /invocations 容器契约,并将调用负载映射到聊天补全。

    本演练通过 Amazon S3 中的 SageMaker AI 模型构件提供 GGUF 模型。或者,当终端节点具有网络出口时,可以配置 SM_LLAMA_CPP_HF_REPO 和 SM_LLAMA_CPP_HF_FILE,从 Hugging Face 下载 GGUF 文件。

    对于此路径,不需要 Dockerfile 或 ECR 存储库。等待终端节点进入 InService,然后发送兼容 OpenAI 的聊天请求:

    python deploy/invoke_standard_endpoint.py \
      --region "$AWS_REGION" \
      --endpoint-name "$STANDARD_ENDPOINT_NAME"
  5. Build the optional SemIf child image and resolve its immutable digest.

    对于 CPU:

    python deploy/prepare_build.py \
      --region "$AWS_REGION" \
      --bucket "$ARTIFACT_BUCKET" \
      --codebuild-role-arn "$CODEBUILD_ROLE_ARN"

    对于 GPU,请改为构建 CUDA 子映像:

    python deploy/prepare_build.py \
      --region "$AWS_REGION" \
      --bucket "$ARTIFACT_BUCKET" \
      --codebuild-role-arn "$CODEBUILD_ROLE_ARN" \
      --dockerfile Dockerfile.gpu \
      --image-tag "gpu-$(date +%Y%m%d%H%M%S)"

    构建成功后,构造不可变的映像 URI:

    ECR_REPOSITORY_URI=$(aws ecr describe-repositories \
      --region "$AWS_REGION" \
      --repository-names semif-llamacpp-dlc \
      --query 'repositories[0].repositoryUri' \
      --output text)
    IMAGE_DIGEST=$(aws ecr describe-images \
      --region "$AWS_REGION" \
      --repository-name semif-llamacpp-dlc \
      --query 'sort_by(imageDetails,&imagePushedAt)[-1].imageDigest' \
      --output text)
    export IMAGE_URI="${ECR_REPOSITORY_URI}@${IMAGE_DIGEST}"

    部署脚本会拒绝可变的映像标签。

  6. Deploy the SemIf endpoint.

    CPU 配置使用固定的实例类型:

    python deploy/deploy_endpoint.py \
      --region "$AWS_REGION" \
      --image-uri "$IMAGE_URI" \
      --model-data-url "s3://$ARTIFACT_BUCKET/semif-llamacpp/model.tar.gz" \
      --role-arn "$SAGEMAKER_ROLE_ARN" \
      --instance-type ml.c6i.8xlarge \
      --threads 32 \
      --name "$SEMIF_ENDPOINT_NAME"

    等待返回的终端节点名称进入 InService。此路径会设置 n_gpu_layers=0。适配器围绕一个有状态的 llama.cpp 上下文串行执行评分,因此应在测量工作负载后使用终端节点实例进行横向扩展,而不要假设单个容器内可以并行评分。

    对于 GPU 映像,请提供有序的实例池,并请求完整的模型卸载:

    python deploy/deploy_endpoint.py \
      --region "$AWS_REGION" \
      --image-uri "$IMAGE_URI" \
      --model-data-url "s3://$ARTIFACT_BUCKET/semif-llamacpp/model.tar.gz" \
      --role-arn "$SAGEMAKER_ROLE_ARN" \
      --instance-pool ml.g6e.xlarge \
      --instance-pool ml.g6.2xlarge \
      --instance-pool ml.g5.2xlarge \
      --instance-pool ml.g4dn.2xlarge \
      --gpu-layers=-1 \
      --provision-timeout 1200 \
      --name "$SEMIF_ENDPOINT_NAME"

    实例池是有序的放置回退选项,而不是推理负载均衡。SageMaker 会为变体选择一个可用类型。当 SageMaker AI 依次尝试这些实例池时,终端节点创建可能需要更长时间。检查启动日志,确认所选 GPU 以及所有模型层均已卸载的消息。

  7. Invoke the SemIf endpoint with the example decision.

    请求为每个允许的选项声明稳定 ID:

    {
      "decision": {
        "id": "support-route",
        "state": "Customer asks to reset a forgotten password and says the reset email never arrived.",
        "question": "Which queue should handle this request?",
        "options": [
          {"id": "account_access", "description": "Account access and authentication support."},
          {"id": "billing", "description": "Billing and payment support."},
          {"id": "sales", "description": "Sales and product evaluation."}
        ]
      }
    }
    python deploy/invoke_endpoint.py \
      --region "$AWS_REGION" \
      --endpoint-name "$SEMIF_ENDPOINT_NAME"

    响应会保留选项顺序,并包含条件概率、原始 logits、令牌和提示元数据,以及固定的模型身份:

    {
      "result": {
        "id": "support-route",
        "option_ids": ["account_access", "billing", "sales"],
        "probabilities": [0.99996, 0.00003, 0.00001],
        "option_logits": [28.47, 17.92, 17.04],
        "prompt_sha256": "016267542103ab91422c425d2234ec7e1b9aef77f9d3e481b96db2de7cca470a",
        "readout": "quantized last-position logits restricted to declared answer slots; no generated tokens"
      }
    }

    应用应用程序级阈值和回退机制。不要使用模型决策来替代授权。

清理

删除 SageMaker AI 终端节点、终端节点配置和模型:

python deploy/cleanup_endpoint.py \
  --region "$AWS_REGION" \
  --name "$STANDARD_ENDPOINT_NAME"

python deploy/cleanup_endpoint.py \
  --region "$AWS_REGION" \
  --name "$SEMIF_ENDPOINT_NAME"

该脚本仅删除明确命名的 SageMaker AI 资源。不再需要时,请删除 CodeBuild 项目、ECR 存储库、Amazon S3 中的模型和源对象、日志以及专用 IAM 角色。

结论

AWS 的 llama.cpp DLC 为您提供了一个托管基础,用于在 SageMaker AI 的 CPU 和 GPU 实例上服务决策模型。它可以从 SageMaker AI 模型构件或配置的 Hugging Face 存储库加载 GGUF 模型,然后通过 InvokeEndpoint 提供兼容 OpenAI 的聊天补全服务,而无需自定义容器。当您的应用需要直接获取 logits 并执行模型状态操作时,可以使用 SemIf 适配器扩展该 DLC。对于对延迟不敏感的工作负载,请选择 CPU;当提示预填充和尾延迟会影响应用体验时,请选择 GPU。

请查看代码示例、AWS 的 llama.cpp DLC 文档以及SageMaker AI 自定义推理容器契约,根据您的工作负载调整部署。


关于作者

Jyothirmai Kottu

Jyothirmai Kottu

Jyothirmai 是 AWS 深度学习容器团队的软件开发工程师。她负责构建 AI 和机器学习基础设施,专注于容器性能、可靠性和开发者易用性。工作之余,她喜欢咖啡、瑜伽,以及与家人和朋友探索新的地方。

Daniel Wirjo

Daniel Wirjo

Daniel 是 AWS 的解决方案架构师,专注于前沿 AI 初创公司。作为一名前初创公司 CTO,他喜欢与创始人和工程负责人合作,推动企业在 AWS 上实现增长与创新。工作之余,Daniel 喜欢手捧咖啡散步、欣赏大自然和学习新理念。

Daniel Quang

Daniel Quang

Daniel 是 AWS 前沿 AI 初创公司团队的解决方案架构师,负责帮助全球规模最大、最具战略意义的生成式 AI 初创公司在云端扩展其基础设施。他拥有加州大学尔湾分校计算机科学博士学位,研究方向为机器学习和生物信息学。

Dmitry Soldatkin

Dmitry Soldatkin

Dmitry 是 AWS 全球专业解决方案架构部门 SageMaker Inference 的负责人。他帮助客户设计、构建和优化生成式 AI 与机器学习解决方案,研究兴趣包括深度学习以及大规模部署机器学习。他热衷于持续创新,并利用数据推动业务成果。

来源:AWS Machine Learning Blog · aws.amazon.com