適用対象: すべての API Management レベル
この記事では、Amazon Bedrock 言語モデル API をパススルー API として API Management インスタンスにインポートします。 これは、Azure AI サービス以外の推論プロバイダーでホストされているモデルの例です。 API Management で AI ゲートウェイ ポリシーやその他の機能を使用して、統合を簡素化し、監視性を向上させ、モデル エンドポイントの制御を強化します。
API Management での AI API の管理の詳細については、以下を参照してください。
Amazon Bedrock の詳細については、以下をご覧ください。
[前提条件]
- 既存の API Management インスタンスがある。 まだない場合は、作成してください。
- Amazon Bedrock にアクセスし、1 つ以上の Amazon Bedrock 基盤モデルにアクセスできるアマゾン ウェブ サービス (AWS) アカウント。 詳細情報
IAM ユーザー アクセス キーを作成する
Amazon API Gateway に対して API Management インスタンスを認証するには、AWS IAM ユーザーのアクセスキーが必要です。
AWS マネジメントコンソールを使用して必要なアクセスキー ID と秘密鍵を生成するには、AWS ドキュメントの「自分用のアクセス キーを作成する」を参照してください。
安全な場所にアクセス キーを保存します。 次の手順では、名前付き値として格納します。
注意事項
アクセス キーは長期的な資格情報であり、パスワードと同じように安全に管理する必要があります。 アカウント キーの保護に関する詳細情報
IAM ユーザー アクセス キーを名前付き値として格納する
次の表で推奨される構成を使用して、2 つの IAM ユーザー アクセス キーをシークレット の名前付き値 として Azure API Management インスタンスに安全に格納します。
AWS シークレット | 名前 | シークレット値 |
---|---|---|
アクセス キー | accesskey | AWS から取得したアクセス キー ID |
シークレット アクセス キー | secretkey | AWS から取得したシークレット アクセス キー |
ポータルを使用して Bedrock API をインポートする
Amazon Bedrock API を API Management にインポートするには:
Azure portal で、API Management インスタンスに移動します。
左側のメニューの [API] で、[API]>[+ API の追加] を選択します。
[ 新しい API の定義] で、[ 言語モデル API] を選択します。
[ API の構成 ] タブで、次の手順を実行します。
API の表示名と、必要に応じて説明を入力します。
既定の Amazon Bedrock エンドポイントの URL として 、
https://bedrock-runtime.<aws-region>.amazonaws.com
を入力します。例:
https://bedrock-runtime.us-east-1.amazonaws.com
必要に応じて、API に関連付ける 1 つ以上の 製品 を選択します。
[パス] に、API Management インスタンスが LLM API エンドポイントへのアクセスに使用するパスを追加します。
[種類] で、[パススルー API の作成] を選択します。
Access キーの値は空白のままにします。
残りのタブで、必要に応じて、トークンの使用、セマンティック キャッシュ、AI コンテンツの安全性を管理するポリシーを構成します。 詳細については、「 言語モデル API のインポート」を参照してください。
[レビュー] を選択します。
設定が検証されたら、[作成] を選択します。
API Management は、API の監視と管理に役立つ API と (必要に応じて) ポリシーを作成します。
Amazon Bedrock API に対する要求を認証するポリシーを構成する
Amazon Bedrock API に要求に署名するように API Management ポリシーを構成します。 AWS API 要求への署名の詳細
次の例では、AWS アクセス キーとシークレット キーに対して以前に作成したアクセスキーとシークレットキーの名前付き値を使用します。 region
変数を Amazon Bedrock API の適切な値に設定します。 この例では、リージョンに us-east-1
を使用します。
Azure portal で、API Management インスタンスに移動します。
左側のメニューの [API] で、[API] を選択します。
前のセクションで作成した API を選択します。
左側のメニューの [ デザイン] で、[ すべての操作] を選択します。
[ 受信処理 ] タブを選択します。
受信処理ポリシー エディターで、</> を選択してポリシー エディターを開きます。
次のポリシーを構成します。
<policies> <inbound> <base /> <set-variable name="now" value="@(DateTime.UtcNow)" /> <set-header name="X-Amz-Date" exists-action="override"> <value>@(((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ"))</value> </set-header> <set-header name="X-Amz-Content-Sha256" exists-action="override"> <value>@{ var body = context.Request.Body.As<string>(preserveContent: true); using (var sha256 = System.Security.Cryptography.SHA256.Create()) { var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } }</value> </set-header> <set-header name="Authorization" exists-action="override"> <value>@{ var accessKey = "{{accesskey}}"; var secretKey = "{{secretkey}}"; var region = "us-east-1"; var service = "bedrock"; var method = context.Request.Method; var uri = context.Request.Url; var host = uri.Host; // Create canonical path var path = uri.Path; var modelSplit = path.Split(new[] { "model/" }, 2, StringSplitOptions.None); var afterModel = modelSplit.Length > 1 ? modelSplit[1] : ""; var parts = afterModel.Split(new[] { '/' }, 2); var model = System.Uri.EscapeDataString(parts[0]); var remainder = parts.Length > 1 ? parts[1] : ""; var canonicalPath = $"/model/{model}/{remainder}"; var amzDate = ((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ"); var dateStamp = ((DateTime)context.Variables["now"]).ToString("yyyyMMdd"); // Hash the payload var body = context.Request.Body.As<string>(preserveContent: true); string hashedPayload; using (var sha256 = System.Security.Cryptography.SHA256.Create()) { var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); hashedPayload = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } // Create canonical query string var queryDict = context.Request.Url.Query; var canonicalQueryString = ""; if (queryDict != null && queryDict.Count > 0) { var encodedParams = new List<string>(); foreach (var kvp in queryDict) { var encodedKey = System.Uri.EscapeDataString(kvp.Key); var encodedValue = System.Uri.EscapeDataString(kvp.Value.First() ?? ""); encodedParams.Add($"{encodedKey}={encodedValue}"); } canonicalQueryString = string.Join("&", encodedParams.OrderBy(p => p)); } // Create signed headers and canonical headers var headers = context.Request.Headers; var canonicalHeaderList = new List<string[]>(); // Add content-type if present var contentType = headers.GetValueOrDefault("Content-Type", "").ToLowerInvariant(); if (!string.IsNullOrEmpty(contentType)) { canonicalHeaderList.Add(new[] { "content-type", contentType }); } // Always add host canonicalHeaderList.Add(new[] { "host", host }); // Add x-amz-* headers (excluding x-amz-date, x-amz-content-sha256) foreach (var header in headers) { var name = header.Key.ToLowerInvariant(); if (string.Equals(name, "x-amz-content-sha256", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "x-amz-date", StringComparison.OrdinalIgnoreCase)) { continue; } if (name.StartsWith("x-amz-")) { var value = header.Value.First()?.Trim(); canonicalHeaderList.Add(new[] { name, value }); } } canonicalHeaderList.Add(new[] { "x-amz-content-sha256", hashedPayload }); canonicalHeaderList.Add(new[] { "x-amz-date", amzDate }); var canonicalHeadersOrdered = canonicalHeaderList.OrderBy(h => h[0]); var canonicalHeaders = string.Join("\n", canonicalHeadersOrdered.Select(h => $"{h[0]}:{h[1].Trim()}")) + "\n"; var signedHeaders = string.Join(";", canonicalHeadersOrdered.Select(h => h[0])); // Create and hash the canonical request var canonicalRequest = $"{method}\n{canonicalPath}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedPayload}"; string hashedCanonicalRequest = ""; using (var sha256 = System.Security.Cryptography.SHA256.Create()) { var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(canonicalRequest)); hashedCanonicalRequest = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } // Build string to sign var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request"; var stringToSign = $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n{hashedCanonicalRequest}"; // Sign it using secret key byte[] kSecret = System.Text.Encoding.UTF8.GetBytes("AWS4" + secretKey); byte[] kDate, kRegion, kService, kSigning; using (var h1 = new System.Security.Cryptography.HMACSHA256(kSecret)) { kDate = h1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(dateStamp)); } using (var h2 = new System.Security.Cryptography.HMACSHA256(kDate)) { kRegion = h2.ComputeHash(System.Text.Encoding.UTF8.GetBytes(region)); } using (var h3 = new System.Security.Cryptography.HMACSHA256(kRegion)) { kService = h3.ComputeHash(System.Text.Encoding.UTF8.GetBytes(service)); } using (var h4 = new System.Security.Cryptography.HMACSHA256(kService)) { kSigning = h4.ComputeHash(System.Text.Encoding.UTF8.GetBytes("aws4_request")); } // Auth header string signature; using (var hmac = new System.Security.Cryptography.HMACSHA256(kSigning)) { var sigBytes = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)); signature = BitConverter.ToString(sigBytes).Replace("-", "").ToLowerInvariant(); } return $"AWS4-HMAC-SHA256 Credential={accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}"; }</value> </set-header> <set-header name="Host" exists-action="override"> <value>@(context.Request.Url.Host)</value> </set-header> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
Bedrock API を呼び出す
API Management を使用して Bedrock API を呼び出すには、AWS Bedrock SDK を使用できます。 この例では .NET SDK を使用しますが、AWS Bedrock API をサポートする任意の言語を使用できます。
次の例では、付属のファイル BedrockHttpClientFactory.cs
で定義されているクラスをインスタンス化するカスタム HTTP クライアントを使用します。 カスタム HTTP クライアントは、要求を API Management エンドポイントにルーティングし、必要に応じて API Management サブスクリプション キーを要求ヘッダーに含めます。
using Amazon;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
using Amazon.Runtime;
using BedrockClient;
// Leave accessKey and secretKey values as empty strings. Authentication to AWS API is handled through policies in API Management.
var accessKey = "";
var secretKey = "";
var credentials = new BasicAWSCredentials(accessKey, secretKey);
// Create custom configuration to route requests through API Management
// apimUrl is the API Management endpoint, such as https://apim-hello-word.azure-api.net/bedrock
var apimUrl = "<api-management-endpoint">;
// Provide name and value for the API Management subscription key header.
var apimSubscriptionHeaderName = "api-key";
var apimSubscriptionKey = "<your-apim-subscription-key>";
var config = new AmazonBedrockRuntimeConfig()
{
HttpClientFactory = new BedrockHttpClientFactory(apimUrl, apimSubscriptionHeaderName, apimSubscriptionKey),
// Set the AWS region where your Bedrock model is hosted.
RegionEndpoint = RegionEndpoint.USEast1
};
var client = new AmazonBedrockRuntimeClient(credentials, config);
// Set the model ID, e.g., Claude 3 Haiku. Find the supported models in Amazon Bedrock documentation: https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html.
var modelId = "us.anthropic.claude-3-5-haiku-20241022-v1:0";
// Define the user message.
var userMessage = "Describe the purpose of a 'hello world' program in one line.";
// Create a request with the model ID, the user message, and an inference configuration.
var request = new ConverseRequest
{
ModelId = modelId,
Messages = new List<Message>
{
new Message
{
Role = ConversationRole.User,
Content = new List<ContentBlock> { new ContentBlock { Text = userMessage } }
}
},
InferenceConfig = new InferenceConfiguration()
{
MaxTokens = 512,
Temperature = 0.5F,
TopP = 0.9F
}
};
try
{
// Send the request to the Bedrock runtime and wait for the result.
var response = await client.ConverseAsync(request);
// Extract and print the response text.
string responseText = response?.Output?.Message?.Content?[0]?.Text ?? "";
Console.WriteLine(responseText);
}
catch (AmazonBedrockRuntimeException e)
{
Console.WriteLine($"ERROR: Can't invoke '{modelId}'. Reason: {e.Message}");
throw;
}
BedrockHttpClientFactory.cs
次のコードでは、API Management を介して Bedrock API に要求をルーティングするカスタム HTTP クライアントを作成するクラスを実装します。これには、ヘッダーに API Management サブスクリプション キーが含まれます。
using Amazon.Runtime;
namespace BedrockClient
{
public class BedrockHttpClientFactory : HttpClientFactory
{
readonly string subscriptionKey;
readonly string subscriptionHeaderName;
readonly string rerouteUrl;
public BedrockHttpClientFactory(string rerouteUrl, string subscriptionHeaderName, string subscriptionKey)
{
this.rerouteUrl = rerouteUrl;
this.subscriptionHeaderName = subscriptionHeaderName;
this.subscriptionKey = subscriptionKey;
}
public override HttpClient CreateHttpClient(IClientConfig clientConfig)
{
var handler = new RerouteHandler(rerouteUrl)
{
InnerHandler = new HttpClientHandler()
};
var httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Add(this.subscriptionHeaderName, this.subscriptionKey);
return httpClient;
}
}
public class RerouteHandler : DelegatingHandler
{
readonly string rerouteUrl;
readonly string host;
public RerouteHandler(string rerouteUrl)
{
this.rerouteUrl = rerouteUrl;
this.host = rerouteUrl.Split("/")[2].Split(":")[0];
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var originalUri = request.RequestUri;
request.RequestUri = new Uri($"{this.rerouteUrl}{originalUri.PathAndQuery}");
request.Headers.Host = this.host;
return base.SendAsync(request, cancellationToken);
}
}
}