跳到正文
AWS Machine Learning Blog· Jyothirmai Kottu·· 3 小时前AI 评分41

在 Amazon SageMaker AI 上用 llama.cpp 部署决策模型

Deploy decision models with llama.cpp on Amazon SageMaker AI

AI 导读

AWS 介绍了在 Amazon SageMaker AI 上使用 llama.cpp 部署决策模型的两种路径:通过 AWS DLC 提供 OpenAI 兼容的 chat completions,以及通过 SemIf(原 OpenJev)子镜像直接返回声明选项的概率,无需解码答案 token。

正文 · AI 翻译

在请求路由、智能体动作选择或策略结果分配等应用中,往往需要对固定的选项集合输出分数,而不是生成自由文本。通过 llama.cpp 和 Amazon SageMaker AI 部署这些决策模型,为您提供了在 CPU 或 GPU 基础设施上服务量化模型的托管路径。

本文是关于用于生成式 AI 推理的专用 AWS Deep Learning Containers(DLC)系列的延续。之前的文章涵盖了 WhisperX、vLLM-Omni – Part 1 和 vLLM-Omni – Part 2。

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

解决方案概览

图 1 展示了基于同一打包模型的两种部署路径。文本生成路径使用 AWS DLC for llama.cpp,并通过 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 上运行 AWS DLC for llama.cpp 或 SemIf 子镜像

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

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

了解输入和输出架构

AWS DLC for llama.cpp 接受 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 端点。

配置 用于 llama.cpp 的 AWS DLC 有效响应 准确率 客户端 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 命令行界面(AWS CLI)。
  • 用于可选 SemIf 扩展的专用 CodeBuild 服务角色。
  • 一个 SageMaker AI 可以代入的 AWS Identity and Access Management (IAM) 角色。
  • SageMaker AI、Amazon ECR、Amazon S3、CodeBuild、IAM 传递角色(pass-role)操作以及 Amazon CloudWatch Logs 的权限。
  • 兼容的 x86 CPU 或 NVIDIA GPU SageMaker 实时终端节点的配额。

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

部署决策模型并用 SemIf 进行扩展

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

  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、token 和提示元数据,以及固定的模型标识:

    {
      "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 角色。

结论

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

请查看代码示例、适用于 llama.cpp 的 AWS DLC 文档以及 SageMaker AI 自定义推理容器契约,以便为您的 workload 调整部署。


关于作者

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