Udostępnij przez


Zaimportować interfejs API modelu językowego typu passthrough Amazon Bedrock

DOTYCZY: Wszystkich poziomów zarządzania API

W tym artykule zaimportujesz interfejs API modelu języka Amazon Bedrock do wystąpienia usługi API Management jako interfejs API przekazywania. Jest to przykład modelu hostowanego u dostawcy wnioskowania innego niż usługi Azure AI. Użyj zasad bramy sztucznej inteligencji i innych funkcji w usłudze API Management, aby uprościć integrację, zwiększyć czytelność i zwiększyć kontrolę nad punktami końcowymi modelu.

Dowiedz się więcej o zarządzaniu interfejsami API sztucznej inteligencji w usłudze API Management:

Dowiedz się więcej o amazon Bedrock:

Wymagania wstępne

Tworzenie kluczy dostępu użytkowników IAM

Aby uwierzytelnić wystąpienie usługi API Management w usłudze Amazon API Gateway, musisz mieć klucze dostępu dla użytkownika usługi AWS IAM.

Aby wygenerować wymagany identyfikator klucza dostępu i klucz tajny przy użyciu konsoli zarządzania platformy AWS, zobacz Tworzenie klucza dostępu dla siebie w dokumentacji platformy AWS.

Zapisz klucze dostępu w bezpiecznej lokalizacji. Zapiszesz je jako nazwane wartości w następnym kroku.

Ostrzeżenie

Klucze dostępu są poświadczeniami długoterminowymi i należy zarządzać nimi tak bezpiecznie, jak w przypadku hasła. Dowiedz się więcej o zabezpieczaniu kluczy dostępu

Przechowywanie kluczy dostępu użytkowników IAM jako nazwanych wartości

Bezpiecznie przechowuj dwa klucze dostępu użytkowników IAM jako sekretne nazwane wartości w instancji usługi Azure API Management przy użyciu konfiguracji zalecanej w poniższej tabeli.

Wpis tajny platformy AWS Nazwa Tajna wartość
Klucz dostępu accesskey Identyfikator klucza dostępu pobrany z platformy AWS
Tajny klucz dostępu secretkey Klucz dostępu tajnego pobrany z platformy AWS

Importowanie interfejsu API Bedrock przy użyciu portalu

Aby zaimportować interfejs API Amazon Bedrock do usługi API Management:

  1. Na portalu Azure przejdź do instancji zarządzania API.

  2. W menu po lewej stronie, w obszarze APIs, wybierz APIs>+ Dodaj API.

  3. W obszarze Definiowanie nowego interfejsu API wybierz pozycję Interfejs API modelu językowego.

    Zrzut ekranu przedstawiający tworzenie interfejsu API przejściowego modelu językowego w portalu.

  4. Na karcie Konfiguracja API :

    1. Wprowadź nazwę wyświetlaną i opcjonalny opis dla API.

    2. Wprowadź następujący adres URL domyślnego punktu końcowego Amazon Bedrock: https://bedrock-runtime.<aws-region>.amazonaws.com.

      Przykład: https://bedrock-runtime.us-east-1.amazonaws.com

    3. Opcjonalnie wybierz co najmniej jeden produkt do skojarzenia z interfejsem API.

    4. W polu Ścieżka dołącz ścieżkę używaną przez wystąpienie usługi API Management do uzyskiwania dostępu do punktów końcowych interfejsu API LLM.

    5. Wybierz opcję Typ, a następnie wybierz Utwórz API passthrough.

    6. Pozostaw puste wartości w polu Klucz dostępu .

    Zrzut ekranu przedstawiający konfigurację interfejsu API modelu językowego w portalu.

  5. Na pozostałych kartach opcjonalnie skonfiguruj zasady do zarządzania użyciem tokenów, buforowaniem semantycznym i bezpieczeństwem zawartości sztucznej inteligencji. Aby uzyskać szczegółowe informacje, zobacz Importowanie interfejsu API modelu językowego.

  6. Wybierz opcję Przejrzyj.

  7. Po zweryfikowaniu ustawień wybierz pozycję Utwórz.

Usługa API Management tworzy interfejs API i (opcjonalnie) zasady ułatwiające monitorowanie interfejsu API i zarządzanie nim.

Konfigurowanie zasad w celu uwierzytelniania żądań w interfejsie API Amazon Bedrock

Skonfiguruj polityki zarządzania API, aby podpisywać zapytania do interfejsu API Amazon Bedrock. Dowiedz się więcej o podpisywaniu żądań interfejsu API platformy AWS

W poniższym przykładzie użyto wcześniej utworzonych nazwanych wartości accesskey i secretkey dla klucza dostępu i tajnego klucza platformy AWS. Ustaw zmienną region na odpowiednią wartość dla interfejsu API Amazon Bedrock. W przykładzie użyto us-east-1 dla regionu.

  1. Na portalu Azure przejdź do instancji zarządzania API.

  2. W menu po lewej stronie w obszarze Interfejsy API wybierz pozycję Interfejsy API.

  3. Wybierz API, które utworzyłeś w poprzedniej sekcji.

  4. W menu po lewej stronie w obszarze Projekt wybierz pozycję Wszystkie operacje.

  5. Wybierz kartę Przetwarzanie przychodzące .

  6. W edytorze zasad przetwarzania przychodzącego wybierz /<>, aby otworzyć edytor zasad.

  7. Skonfiguruj następujące zasady:

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

Wywołaj interfejs API Bedrock

Aby wywołać interfejs API Bedrock za pomocą usługi API Management, możesz użyć zestawu AWS Bedrock SDK. W tym przykładzie użyto zestawu .NET SDK, ale możesz użyć dowolnego języka obsługującego interfejs API AWS Bedrock.

W poniższym przykładzie użyto niestandardowego klienta HTTP, który tworzy wystąpienia klas zdefiniowanych w towarzyszącym pliku BedrockHttpClientFactory.cs. Niestandardowy klient HTTP kieruje żądania do punktu końcowego usługi API Management i zawiera klucz subskrypcji usługi API Management (w razie potrzeby) w nagłówkach żądania.

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

Poniższy kod implementuje klasy w celu utworzenia niestandardowego klienta HTTP, który kieruje żądania do interfejsu API Bedrock przez zarządzanie interfejsem API, umieszczając klucz subskrypcji zarządzania interfejsem API w nagłówkach.

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