Delen via


Een Amazon Bedrock Passthrough-taalmodel-API importeren

Van toepassing op: Alle API Management-lagen

In dit artikel importeert u een Amazon Bedrock language model-API in uw API Management-exemplaar als een passthrough-API. Dit is een voorbeeld van een model dat wordt gehost op een andere deductieprovider dan Azure AI-services. Gebruik AI-gatewaybeleid en andere mogelijkheden in API Management om integratie te vereenvoudigen, waarneembaarheid te verbeteren en de controle over de modeleindpunten te verbeteren.

Meer informatie over het beheren van AI-API's in API Management:

Meer informatie over Amazon Bedrock:

Vereiste voorwaarden

Toegangssleutels voor IAM-gebruikers maken

Als u uw API Management-exemplaar wilt verifiëren bij Amazon API Gateway, hebt u toegangssleutels nodig voor een AWS IAM-gebruiker.

Zie Een toegangssleutel voor uzelf maken in de AWS-documentatie om de vereiste toegangssleutel en geheime sleutel te genereren met behulp van de AWS-beheerconsole.

Sla uw toegangssleutels op een veilige locatie op. U slaat ze op als benoemde waarden in de volgende stap.

Waarschuwing

Toegangssleutels zijn referenties voor de lange termijn en u moet deze net zo veilig beheren als een wachtwoord. Meer informatie over het beveiligen van toegangssleutels

Toegangssleutels voor IAM-gebruikers opslaan als benoemde waarden

Sla de twee IAM-gebruikerstoegangssleutels veilig op als geheim benoemde waarden in uw Azure API Management-exemplaar met behulp van de configuratie die wordt aanbevolen in de volgende tabel.

AWS-geheim Naam Geheime waarde
Toegangssleutel toegangssleutel Toegangssleutel-id opgehaald uit AWS
Geheime toegangssleutel secretkey Geheime toegangssleutel opgehaald uit AWS

Een Bedrock-API importeren met behulp van de portal

Een Amazon Bedrock-API importeren in API Management:

  1. Blader in Azure Portal naar uw API Management-exemplaar.

  2. Selecteer in het linkermenu onder API'sAPI's, >.

  3. Selecteer onder Een nieuwe API definiërende taalmodel-API.

    Schermopname van het maken van een passthrough-taalmodel-API in de portal.

  4. Op het tabblad API configureren :

    1. Voer een weergavenaam en een optionele beschrijving voor de API in.

    2. Voer de volgende URL in naar het standaard Amazon Bedrock-eindpunt: https://bedrock-runtime.<aws-region>.amazonaws.com.

      Voorbeeld: https://bedrock-runtime.us-east-1.amazonaws.com

    3. Selecteer eventueel een of meer producten die u aan de API wilt koppelen.

    4. Voeg in Pad een pad toe dat uw API Management-exemplaar gebruikt voor toegang tot de LLM API-eindpunten.

    5. Selecteer in Typeeen passthrough-API maken.

    6. Laat waarden in de Access-sleutel leeg.

    Schermopname van de API-configuratie van taalmodel in de portal.

  5. Configureer op de resterende tabbladen eventueel beleidsregels voor het beheren van tokenverbruik, semantische caching en beveiliging van AI-inhoud. Zie Een taalmodel-API importeren voor meer informatie.

  6. Selecteer Beoordelen.

  7. Nadat de instellingen zijn gevalideerd, selecteert u Maken.

API Management maakt het API- en (optioneel) beleid om u te helpen de API te bewaken en te beheren.

Beleid configureren voor het verifiëren van aanvragen voor de Amazon Bedrock-API

Configureer API Management-beleid om aanvragen te ondertekenen bij de Amazon Bedrock-API. Meer informatie over het ondertekenen van AWS API-aanvragen

In het volgende voorbeeld worden de waarden voor de toegangssleutel en geheime sleutel gebruikt die u eerder hebt gemaakt voor de AWS-toegangssleutel en geheime sleutel. Stel de region variabele in op de juiste waarde voor uw Amazon Bedrock-API. In het voorbeeld wordt gebruikgemaakt van de regio us-east-1.

  1. Blader in Azure Portal naar uw API Management-exemplaar.

  2. Selecteer API's in het linkermenu onder API's.

  3. Selecteer de API die u in de vorige sectie hebt gemaakt.

  4. Selecteer Alle bewerkingen in het linkermenu onder Ontwerpen.

  5. Selecteer het tabblad Binnenkomende verwerking .

  6. In de editor voor inkomend verwerkingsbeleid selecteer </> om de beleidseditor te openen.

  7. Configureer het volgende beleid:

    <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>
    

De Bedrock-API aanroepen

Als u de Bedrock-API wilt aanroepen via API Management, kunt u de AWS Bedrock SDK gebruiken. In dit voorbeeld wordt de .NET SDK gebruikt, maar u kunt elke taal gebruiken die ondersteuning biedt voor de AWS Bedrock-API.

In het volgende voorbeeld wordt een aangepaste HTTP-client gebruikt waarmee klassen worden geïnstitueerde die zijn gedefinieerd in het bijbehorende bestand BedrockHttpClientFactory.cs. De aangepaste HTTP-client routeert aanvragen naar het API Management-eindpunt en bevat de API Management-abonnementssleutel (indien nodig) in de aanvraagheaders.

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

Met de volgende code worden klassen geïmplementeerd om een aangepaste HTTP-client te maken waarmee aanvragen naar de Bedrock-API worden gerouteerd via API Management, inclusief een API Management-abonnementssleutel in de headers.

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);
        }
    }
}