
【Bug已解决】Getting Malformed input request when trying to use Claude Sonet Model by Bedrock 解决方案一、现象长什么样你在 Bedrock 上用 Claude Sonnet原文拼写为 Sonet模型请求被拒Malformed input request输入请求格式错误或ValidationException: Malformed input request你照着 Anthropic 官方文档写的 body带max_tokens、messages、system却在这报错用invoke_model直接传 JSON 时尤其容易踩有时是字段名不对Bedrock 用maxTokens而非max_tokens有时是缺anthropic_version同样的字段在直连 Anthropic 时没事一上 Bedrock 就 malformed。一句话Bedrock 对 Claude 的请求体有自己的一套字段命名/结构驼峰式maxTokens、通过inferenceConfig传参数、需要anthropic_version你用了 Anthropic 原生的蛇形命名max_tokens于是被判定为格式错误。二、背景Bedrock 的InvokeModel接受的是原始 JSON body而 Claude on Bedrock 的 body 格式与 Anthropic 官方 API 不同Anthropic 原生Bedrock (invoke_model)max_tokensmax_tokens其实也接受但inferenceConfig.maxTokens更标准system字符串system是文本块数组[{text: ...}]请求头anthropic-versionbody 里的anthropic_version: bedrock-2023-05-31最常见的malformed来源用了max_tokens但放在错误层级Bedrock 推荐放在inferenceConfig: { maxTokens: 256 }system写成字符串而非文本块数组漏了anthropic_version字段messages[].content结构不符合 Bedrock 的 content block 规范。而ConverseAPI 则更不同前面 930/933 已讲用inferenceConfig.maxTokens、system作为参数。三、根因根因是请求体字段不符合 Bedrock 的 Claude 格式契约// 错误Anthropic 原生风格Bedrock 不接受 { model: anthropic.claude-3-5-sonnet-..., max_tokens: 256, system: 你是助手, messages: [{role: user, content: hi}] } // - Malformed input request// 正确Bedrock Claude 格式 { anthropic_version: bedrock-2023-05-31, max_tokens: 256, system: [{text: 你是助手}], messages: [{role: user, content: [{text: hi}]}] }注意即便用invoke_model的bodymax_tokens顶层也可接受Claude on Bedrock 容忍但更稳妥的是按 Bedrock 约定。关键是system必须是文本块数组anthropic_version必填。四、最小可运行复现下面用 boto3 演示错误结构 vs 正确结构import boto3 import json client boto3.client(bedrock-runtime, region_nameus-west-2) MODEL anthropic.claude-3-5-sonnet-20241022-v2:0 def call(body: dict): try: client.invoke_model( modelIdMODEL, contentTypeapplication/json, acceptapplication/json, bodyjson.dumps(body).encode(), ) return ok except client.exceptions.ValidationException as e: return fMalformed: {e} # 错误system 是字符串 bad { anthropic_version: bedrock-2023-05-31, max_tokens: 256, system: 你是助手, # 应为 [{text:...}] messages: [{role: user, content: [{text: hi}]}], } print(call(bad)) # Malformed # 正确 good { anthropic_version: bedrock-2023-05-31, max_tokens: 256, system: [{text: 你是助手}], messages: [{role: user, content: [{text: hi}]}], } print(call(good)) # ok运行后system为字符串时报 Malformed改为数组后通过。五、解决方案第一层最小直接修复最小修复是按 Bedrock 的 Claude 格式构造 bodyimport boto3, json client boto3.client(bedrock-runtime, region_nameus-west-2) body { anthropic_version: bedrock-2023-05-31, max_tokens: 256, system: [{text: 你是乐于助人的助手}], # 文本块数组 messages: [ {role: user, content: [{text: 你好}]}, ], } resp client.invoke_model( modelIdanthropic.claude-3-5-sonnet-20241022-v2:0, contentTypeapplication/json, acceptapplication/json, bodyjson.dumps(body).encode(), ) print(json.loads(resp[body].read()))若改用ConverseAPI则参数走inferenceConfigclient.converse( modelIdMODEL, system[{text: 你是助手}], # Converse 的 system 也是数组 messages[{role: user, content: [{text: hi}]}], inferenceConfig{maxTokens: 256}, # 驼峰 maxTokens )六、解决方案第二层结构化改进把Bedrock Claude 请求体规范化做成策略统一system数组化、补全anthropic_versionfrom dataclasses import dataclass, field from typing import Dict, List dataclass(frozenTrue) class BedrockMalformedInputPolicy: Bedrock Claude 请求体策略消除 Malformed input request。 规则 - anthropic_version 必填 - system 必须是文本块数组非字符串 - messages content 必须是块数组 anthropic_version: str bedrock-2023-05-31 def normalize_system(self, system) - List[Dict]: if isinstance(system, str): return [{text: system}] if isinstance(system, list): return system return [{text: str(system)}] def build_body(self, *, system, messages, max_tokens: int 256) - Dict: if not messages: raise ValueError(messages 不能为空) return { anthropic_version: self.anthropic_version, max_tokens: max_tokens, system: self.normalize_system(system), messages: messages, } def demo() - None: policy BedrockMalformedInputPolicy() body policy.build_body( system你是助手, messages[{role: user, content: [{text: hi}]}], ) assert body[system] [{text: 你是助手}] print(body 规范化 OK) if __name__ __main__: demo()七、解决方案第三层断言 / CI 守护import pytest from your_module import BedrockMalformedInputPolicy def test_system_string_to_array(): policy BedrockMalformedInputPolicy() assert policy.normalize_system(hi) [{text: hi}] def test_system_array_passthrough(): policy BedrockMalformedInputPolicy() assert policy.normalize_system([{text: x}]) [{text: x}] def test_body_has_version(): policy BedrockMalformedInputPolicy() b policy.build_body(systemx, messages[{role: user, content: [{text: y}]}]) assert b[anthropic_version] bedrock-2023-05-31 def test_body_rejects_empty_messages(): policy BedrockMalformedInputPolicy() with pytest.raises(ValueError): policy.build_body(systemx, messages[]) def test_messages_content_is_blocks(): policy BedrockMalformedInputPolicy() b policy.build_body(systemx, messages[{role: user, content: [{text: y}]}]) assert isinstance(b[messages][0][content], list)CI 里加一条构造请求体后断言system是数组、anthropic_version存在避免Malformed input request。八、排查清单system是否写成了字符串Bedrock 要求是文本块数组[{text:...}]。是否漏了anthropic_version: bedrock-2023-05-31是否混用了 Anthropic 原生max_tokens与 BedrockinferenceConfig.maxTokens两者层级不同。messages[].content是否是块数组如[{text:...}]用Converse还是invoke_model两者参数位置不同。是否从 Anthropic 官方文档直接搬了 bodyBedrock 字段命名有差异。九、小结Bedrock 上用 Claude Sonnet 报Malformed input request根因是请求体不符合 Bedrock 的 Claude 格式契约——最常见是system写成字符串而非文本块数组、漏了anthropic_version、或max_tokens层级放错。最小修复是按 Bedrock 约定构造system数组化、补全anthropic_version、参数走inferenceConfig结构化做法是抽成BedrockMalformedInputPolicy统一规范化请求体最后用 pytest 守护system是数组、anthropic_version存在杜绝 malformed。