目 录CONTENT

文章目录

使用托管在 SageMaker AI 端点上的 LLM 为 Strands Agent 构建自定义模型提供程序

Administrator
2026-03-10 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

📢 转载信息

原文链接:https://aws.amazon.com/blogs/machine-learning/building-custom-model-provider-for-strands-agents-with-llms-hosted-on-sagemaker-ai-endpoints/

原文作者:Dan Ferguson


组织越来越多地在 Amazon SageMaker AI 实时端点上部署自定义大型语言模型(LLM),使用他们首选的服务框架——例如 SGLang、vLLM 或 TorchServe——以帮助更好地控制部署、优化成本并符合合规性要求。然而,这种灵活性带来了一个关键的技术挑战:响应格式与 Strands Agent 不兼容。虽然这些自定义服务框架通常以 OpenAI 兼容的格式返回响应,以促进广泛的环境支持,但 Strands Agent 期望模型响应与 Bedrock Messages API 格式保持一致。

这个挑战尤为突出,因为在 SageMaker AI 实时端点上托管的模型并不保证支持 Messages API。虽然 Amazon Bedrock Mantle 分布式推理引擎自 2025 年 12 月起支持 OpenAI 消息格式,但 SageMaker AI 的灵活性允许客户托管各种基础模型——有些需要不符合标准 API 的深奥提示和响应格式。这在服务框架的输出结构与 Strands 所期望的之间造成了差距,尽管两个系统在技术上都能正常工作,但阻止了无缝集成。解决方案在于实施自定义模型解析器,扩展 SageMakerAIModel 并将模型服务器的响应格式转换为 Strands 所期望的格式,从而使组织能够在不牺牲与 Strands Agents SDK 兼容性的情况下,利用他们首选的服务框架。

本文演示了当使用托管在 SageMaker 上的 LLM 且这些模型不原生支持 Bedrock Messages API 格式时,如何为 Strands Agent 构建自定义模型解析器。我们将介绍使用 awslabs/ml-container-creator 在 SageMaker 上部署 Llama 3.1(使用 SGLang),然后实现一个自定义解析器将其与 Strands Agent 集成。

Strands 自定义解析器

Strands Agent 期望模型响应采用与 Bedrock Messages API 对齐的特定格式。当您使用 SGLang、vLLM 或 TorchServe 等自定义服务框架部署模型时,它们通常会以自己的格式返回响应——通常是 OpenAI 兼容的格式,以支持广泛的环境。如果没有自定义解析器,您将遇到类似以下的错误:

TypeError: 'NoneType' object is not subscriptable

发生这种情况是因为 Strands Agents 默认的 SageMakerAIModel 类试图解析响应时,假设了您的自定义端点未提供的特定结构。在本文及配套代码库中,我们说明了如何通过自定义解析逻辑扩展 SageMakerAIModel 类,将模型服务器的响应格式转换为 Strands 所期望的格式。

实施概述

我们的实施包括三个层次:

  1. 模型部署层:由 SGLang 在 SageMaker 上服务的 Llama 3.1,返回 OpenAI 兼容的响应
  2. 解析器层:自定义 LlamaModelProvider 类,扩展 SageMakerAIModel 以处理 Llama 3.1 的响应格式
  3. Agent 层:使用自定义提供程序进行对话式 AI 的 Strands Agent,正确解析模型的响应

custom-parser-process-flow

我们首先使用 awslabs/ml-container-creator,这是一个 AWS Labs 的开源 Yeoman 生成器,它自动化了 SageMaker BYOC(自带容器)部署项目的创建。它生成构建 LLM 服务容器所需的工件,包括 Dockerfile、CodeBuild 配置和部署脚本。

安装 ml-container-creator

我们需要采取的第一步是为我们的模型构建服务容器。我们使用一个开源项目来构建容器并为该容器生成部署脚本。以下命令说明了如何安装 awslabs/ml-container-creator 及其依赖项,其中包括 npmYeoman。有关更多信息,请查阅项目的 READMEWiki 以开始使用。

# 全局安装 Yeoman npm install -g yo # 克隆并安装 ml-container-creator git clone https://github.com/awslabs/ml-container-creator cd ml-container-creator npm install && npm link # 验证安装 yo --generators # 应显示 ml-container-creator

生成部署项目

安装并链接后,yo 命令允许您运行已安装的生成器,yo ml-container-creator 允许您运行本次练习所需的生成器。

# 运行生成器 yo ml-container-creator # 配置选项: # - Framework: transformers # - Model Server: sglang # - Model: meta-llama/Llama-3.1-8B-Instruct # - Deploy Target: codebuild # - Instance Type: ml.g6.12xlarge (GPU) # - Region: us-east-1

生成器创建一个完整的项目结构:

<project-directory>/ ├── Dockerfile # 包含 SGLang 和依赖项的容器 ├── buildspec.yml # CodeBuild 配置 ├── code/ │ └── serve # SGLang 服务器启动脚本 ├── deploy/ │ ├── submit_build.sh # 触发 CodeBuild │ └── deploy.sh # 部署到 SageMaker └── test/ └── test_endpoint.sh # 端点测试脚本

构建和部署

awslabs/ml-container-creator 构建的项目包含模板化的构建和部署脚本。./deploy/submit_build.sh./deploy/deploy.sh 脚本用于构建镜像、将镜像推送到 Amazon Elastic Container Registry (ECR) 并部署到 Amazon SageMaker AI 实时端点。

cd llama-31-deployment # 使用 CodeBuild 构建容器 (无需本地 Docker) ./deploy/submit_build.sh # 部署到 SageMaker ./deploy/deploy.sh arn:aws:iam::ACCOUNT:role/SageMakerExecutionRole

部署过程:

  1. CodeBuild 使用 SGLang 和 Llama 3.1 构建 Docker 镜像
  2. 镜像推送到 Amazon ECR
  3. SageMaker 创建一个实时端点
  4. SGLang 从 HuggingFace 下载模型并将其加载到 GPU 内存中
  5. 端点达到 InService 状态(大约 10-15 分钟)

我们可以使用 ./test/test_endpoint.sh 或直接调用来测试端点:

import boto3 import json runtime_client = boto3.client('sagemaker-runtime', region_name='us-east-1') payload = { "messages": [ {"user", "content": "Hello, how are you?"} ], "max_tokens": 100, "temperature": 0.7 } response = runtime_client.invoke_endpoint( EndpointName='llama-31-deployment-endpoint', ContentType='application/json', Body=json.dumps(payload) ) result = json.loads(response['Body'].read().decode('utf-8')) print(result['choices'][0]['message']['content'])

理解响应格式

Llama 3.1 返回 OpenAI 兼容的响应。Strands 期望模型响应遵循 Bedrock Messages API 格式。直到去年底,这是一种标准的兼容性不匹配。自 2025 年 12 月以来,Amazon Bedrock Mantle 分布式推理引擎支持 OpenAI 消息格式

{ "id": "cmpl-abc123", "object": "chat.completion", "created": 1704067200, "model": "meta-llama/Llama-3.1-8B-Instruct", "choices": [{ "index": 0, "message": {"role": "assistant", "content": "I'm doing well, thank you for asking!"}, "finish_reason": "stop" }], "usage": { "prompt_tokens": 23, "completion_tokens": 12, "total_tokens": 35 } }

然而,对 Messages API 的支持并不保证存在于 SageMaker AI 实时端点上托管的模型中。SageMaker AI 允许客户在托管的 GPU 加速基础设施上托管许多类型的底层模型,其中一些可能需要深奥的提示/响应格式。例如,默认的 SageMakerAIModel 使用旧版 Bedrock Messages API 格式,并尝试访问标准 OpenAI Messages 格式中不存在的字段,从而导致 TypeError 类型的故障。

实现自定义模型解析器

自定义模型解析器是 Strands Agents SDK 的一项功能,它为构建由 SageMaker AI 上托管的 LLM 驱动的代理的客户提供了强大的兼容性和灵活性。在这里,我们描述了如何创建扩展 SageMakerAIModel 的自定义提供程序:

def stream(self, messages: List[Dict[str, Any]], tool_specs: list, system_prompt: Optional[str], **kwargs): # 构建负载 messages payload_messages = [] if system_prompt: payload_messages.append({"role": "system", "content": system_prompt}) # 从 Strands 格式中提取消息内容 for msg in messages: payload_messages.append({"role": "user", "content": msg['content'][0]['text']}) # 构建带有流式传输启用的完整负载 payload = { "messages": payload_messages, "max_tokens": kwargs.get('max_tokens', self.max_tokens), "temperature": kwargs.get('temperature', self.temperature), "top_p": kwargs.get('top_p', self.top_p), "stream": True } try: # 使用流式响应调用 SageMaker 端点 response = self.runtime_client.invoke_endpoint_with_response_stream( EndpointName=self.endpoint_name, ContentType='application/json', Accept='application/json', Body=json.dumps(payload) ) # 处理流式响应 accumulated_content = "" for event in response['Body']: chunk = event['PayloadPart']['Bytes'].decode('utf-8') if not chunk.strip(): continue # 解析 SSE 格式: "data: {json}\n" for line in chunk.split('\n'): if line.startswith('data: '): try: json_str = line.replace('data: ', '').strip() if not json_str: continue chunk_data = json.loads(json_str) if 'choices' in chunk_data and chunk_data['choices']: delta = chunk_data['choices'][0].get('delta', {}) # 以 Strands 格式 Yield 内容块 if 'content' in delta: content_chunk = delta['content'] accumulated_content += content_chunk yield { "type": "contentBlockDelta", "delta": {"text": content_chunk}, "contentBlockIndex": 0 } # 检查完成 finish_reason = chunk_data['choices'][0].get('finish_reason') if finish_reason: yield { "type": "messageStop", "stopReason": finish_reason } # Yield 使用元数据 if 'usage' in chunk_data: yield { "type": "metadata", "usage": chunk_data['usage'] } except json.JSONDecodeError: continue except Exception as e: yield { "type": "error", "error": { "message": f"Endpoint invocation failed: {str(e)}", "type": "EndpointInvocationError" } }

stream 方法覆盖了 SageMakerAIModel 的行为,允许代理根据底层模型的需要解析响应,这一点至关重要。虽然绝大多数模型确实支持 OpenAI 的 Message API 协议,但此功能使用户能够利用 SageMaker AI 上高度定制化的 LLM,通过 Strands Agents SDK 为代理工作负载提供动力。构建自定义模型响应逻辑后,Strands Agents SDK 使使用自定义模型提供程序初始化代理变得简单:

from strands.agent import Agent # 初始化自定义提供程序 provider = LlamaModelProvider( endpoint_name="llama-31-deployment-endpoint", region_name="us-east-1", max_tokens=1000, temperature=0.7 ) # 使用自定义提供程序创建代理 agent = Agent( name="llama-assistant", model=provider, system_prompt=( "You are a helpful AI assistant powered by Llama 3.1, " "deployed on Amazon SageMaker. You provide clear, accurate, " "and friendly responses to user questions." ) ) # 测试代理 response = agent("What are the key benefits of deploying LLMs on SageMaker?") print(response.content)

此自定义解析器的完整实现,包括带有详细解释的 Jupyter 笔记本和 ml-container-creator 部署项目,可在配套的 GitHub 存储库中找到。

结论

为 Strands Agent 构建自定义模型解析器有助于用户利用 SageMaker 上的不同 LLM 部署,无论其响应格式如何。通过扩展 SageMakerAIModel 并实现 stream() 方法,您可以集成自定义托管的模型,同时保持 Strands 简洁的代理接口。

关键要点

  1. awslabs/ml-container-creator 简化了 SageMaker BYOC 部署,提供了生产就绪的基础设施代码
  2. 自定义解析器弥合了模型服务器响应格式与 Strands 期望之间的差距
  3. stream() 方法是自定义提供程序的关键集成点

关于作者

Dan Ferguson 是 AWS 的高级解决方案架构师,常驻美国纽约。作为一名机器学习服务专家,Dan 致力于支持客户高效、有效地可持续地整合机器学习工作流程。




🚀 想要体验更好更全面的AI调用?

欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。

0

评论区