Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
588 lines
23 KiB
Markdown
588 lines
23 KiB
Markdown
# Request/Response Transformation for AI APIs
|
|
|
|
**Last updated:** 2026-06-24
|
|
**Status:** GA
|
|
**Category:** API Management & AI Gateway
|
|
**Type:** reference
|
|
**Source:** https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Model-agnostiske API-schemaer](#model-agnostiske-api-schemaer)
|
|
- [Header Rewriting](#header-rewriting)
|
|
- [Payload-transformasjon](#payload-transformasjon)
|
|
- [Error Response Normalization](#error-response-normalization)
|
|
- [Versjonstranslasjon](#versjonstranslasjon)
|
|
- [Policy Fragments for Reuse](#policy-fragments-for-reuse)
|
|
- [Bicep: Oppsett av transformasjons-API](#bicep-oppsett-av-transformasjons-api)
|
|
- [Referanser](#referanser)
|
|
- [For Cosmo](#for-cosmo)
|
|
|
|
## Introduksjon
|
|
|
|
Azure API Management (APIM) tilbyr over 75 innebygde policies for transformasjon av foresporsler og svar. Nar organisasjoner eksponerer AI-modeller gjennom APIM som AI gateway, blir transformasjon av request og response kritisk for a standardisere grensesnittet mellom ulike AI-backends (Azure OpenAI, Microsoft Foundry, tredjeparts LLM-er) og konsumerende applikasjoner. Ved a implementere model-agnostiske API-schemaer kan man bytte ut underliggende modeller uten a bryte klientkontrakter.
|
|
|
|
For norsk offentlig sektor er dette spesielt relevant: organisasjoner som Direktoratet for digital tjenesteutvikling, NAV og Skatteetaten kan etablere et standardisert AI-API-lag som abstraherer bort leverandoravhengigheter. Dette stotter prinsippet om leverandoruavhengighet fra Digitaliseringsdirektoratets arkitekturprinsipper, og gir fleksibilitet til a bytte mellom Azure OpenAI, Microsoft Foundry-modeller og fremtidige norske sprakmodeller uten endringer i klientapplikasjoner.
|
|
|
|
Transformasjonspolicies i APIM opererer i fire faser: inbound (request fra klient), backend (request til backend), outbound (response fra backend) og on-error. Denne referansen dekker praktiske monstre for a bygge et robust, model-agnostisk AI-API-lag med APIM-policies.
|
|
|
|
---
|
|
|
|
## Model-agnostiske API-schemaer
|
|
|
|
### Problemet med leverandorspesifikke API-er
|
|
|
|
Ulike AI-leverandorer bruker forskjellige API-formater:
|
|
|
|
| Leverandor | Endpoint-format | Auth-metode | Response-struktur |
|
|
|------------|----------------|-------------|-------------------|
|
|
| Azure OpenAI | `/openai/deployments/{id}/chat/completions` | API Key / Entra ID | `choices[].message.content` |
|
|
| Microsoft Foundry | `/models/chat/completions` | Managed Identity | `choices[].message.content` |
|
|
| Anthropic | `/v1/messages` | API Key | `content[].text` |
|
|
| Google Vertex AI | `/v1/projects/{id}/locations/{loc}/publishers/google/models/{model}:predict` | OAuth 2.0 | `predictions[]` |
|
|
| Open-source (vLLM) | `/v1/chat/completions` | Custom | `choices[].message.content` |
|
|
|
|
> **Innebygd alternativ — unified model API (Preview):** APIM tilbyr nå en _unified model API_ som gir facade-mønsteret nedenfor som innebygd funksjon: den eksponerer flere LLM-backends (OpenAI Chat Completions + Anthropic Messages) bak ett OpenAI-kompatibelt endepunkt, med automatisk format-translasjon, modell-aliaser, failover på tvers av leverandører og felles policy-governance. Vurder denne før du bygger custom transformasjon-policies manuelt. Foreløpig Preview (i classic-tiers via AI Gateway early-release-kanal). Se [Create and manage a unified model API](https://learn.microsoft.com/azure/api-management/unified-model-api).
|
|
|
|
### Designmonster: Facade API Schema
|
|
|
|
Definer et internt standardskjema som alle AI-API-er mapper til:
|
|
|
|
```json
|
|
{
|
|
"model": "string",
|
|
"messages": [
|
|
{
|
|
"role": "system | user | assistant",
|
|
"content": "string"
|
|
}
|
|
],
|
|
"parameters": {
|
|
"temperature": 0.7,
|
|
"max_tokens": 1000,
|
|
"top_p": 1.0
|
|
},
|
|
"metadata": {
|
|
"request_id": "string",
|
|
"tenant_id": "string",
|
|
"application": "string"
|
|
}
|
|
}
|
|
```
|
|
|
|
### APIM Policy: Route basert pa modellnavn
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<!-- Parse request body -->
|
|
<set-variable name="requestBody"
|
|
value="@(context.Request.Body.As<JObject>(preserveContent: true))" />
|
|
<set-variable name="modelName"
|
|
value="@(((JObject)context.Variables["requestBody"])["model"]?.ToString())" />
|
|
|
|
<!-- Route to correct backend based on model -->
|
|
<choose>
|
|
<when condition="@(((string)context.Variables["modelName"]).StartsWith("gpt-"))">
|
|
<set-backend-service backend-id="azure-openai-backend" />
|
|
<rewrite-uri template="/openai/deployments/{modelName}/chat/completions" />
|
|
<set-query-parameter name="api-version" exists-action="override">
|
|
<value>2024-08-01-preview</value>
|
|
</set-query-parameter>
|
|
</when>
|
|
<when condition="@(((string)context.Variables["modelName"]).StartsWith("claude-"))">
|
|
<set-backend-service backend-id="anthropic-backend" />
|
|
<rewrite-uri template="/v1/messages" />
|
|
</when>
|
|
<otherwise>
|
|
<set-backend-service backend-id="foundry-backend" />
|
|
<rewrite-uri template="/models/chat/completions" />
|
|
</otherwise>
|
|
</choose>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## Header Rewriting
|
|
|
|
### Autentiseringsheader-transformasjon
|
|
|
|
Nar APIM fungerer som AI gateway, ma den ofte transformere autentiseringsheadere mellom klientens format og backendets format:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<!-- Remove client API key and use managed identity -->
|
|
<set-header name="api-key" exists-action="delete" />
|
|
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
|
|
|
|
<!-- Authenticate with managed identity to Azure OpenAI -->
|
|
<authentication-managed-identity
|
|
resource="https://cognitiveservices.azure.com/"
|
|
output-token-variable-name="msi-access-token" />
|
|
<set-header name="Authorization" exists-action="override">
|
|
<value>@("Bearer " + (string)context.Variables["msi-access-token"])</value>
|
|
</set-header>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
### Tracking- og korrelasjonsheadere
|
|
|
|
For observability og sporbarhet, legg til standardiserte headere:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<!-- Add correlation headers -->
|
|
<set-header name="x-request-id" exists-action="skip">
|
|
<value>@(Guid.NewGuid().ToString())</value>
|
|
</set-header>
|
|
<set-header name="x-correlation-id" exists-action="skip">
|
|
<value>@(context.RequestId.ToString())</value>
|
|
</set-header>
|
|
<set-header name="x-tenant-id" exists-action="override">
|
|
<value>@(context.Subscription?.Name ?? "unknown")</value>
|
|
</set-header>
|
|
<set-header name="x-source-application" exists-action="override">
|
|
<value>@(context.Request.Headers.GetValueOrDefault("x-app-id", "unspecified"))</value>
|
|
</set-header>
|
|
</inbound>
|
|
<outbound>
|
|
<base />
|
|
<!-- Forward correlation headers to client -->
|
|
<set-header name="x-request-id" exists-action="override">
|
|
<value>@(context.Request.Headers.GetValueOrDefault("x-request-id", ""))</value>
|
|
</set-header>
|
|
<set-header name="x-model-used" exists-action="override">
|
|
<value>@{
|
|
var body = context.Response.Body.As<JObject>(preserveContent: true);
|
|
return body?["model"]?.ToString() ?? "unknown";
|
|
}</value>
|
|
</set-header>
|
|
</outbound>
|
|
</policies>
|
|
```
|
|
|
|
### Standard headere for AI-API-er
|
|
|
|
| Header | Retning | Formal |
|
|
|--------|---------|--------|
|
|
| `x-request-id` | Request/Response | Unik foresporsels-ID for sporing |
|
|
| `x-correlation-id` | Request/Response | Korrelasjon pa tvers av tjenester |
|
|
| `x-tenant-id` | Request | Identifiserer leietaker/abonnement |
|
|
| `x-model-used` | Response | Hvilken modell som behandlet foresporselen |
|
|
| `x-token-usage` | Response | Token-forbruk for fakturering |
|
|
| `x-processing-time-ms` | Response | Backend-behandlingstid |
|
|
| `x-rate-limit-remaining` | Response | Gjenverende rate limit |
|
|
|
|
---
|
|
|
|
## Payload-transformasjon
|
|
|
|
### Transformere request fra standardformat til leverandorspesifikt
|
|
|
|
Bruk `set-body` policy med Liquid-template eller C#-uttrykk:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<!-- Transform standard format to Anthropic API format -->
|
|
<set-body>@{
|
|
var inbound = context.Request.Body.As<JObject>();
|
|
var messages = (JArray)inbound["messages"];
|
|
string systemPrompt = "";
|
|
var userMessages = new JArray();
|
|
|
|
foreach (var msg in messages)
|
|
{
|
|
if (msg["role"]?.ToString() == "system")
|
|
{
|
|
systemPrompt = msg["content"]?.ToString();
|
|
}
|
|
else
|
|
{
|
|
userMessages.Add(msg);
|
|
}
|
|
}
|
|
|
|
var parameters = (JObject)inbound["parameters"] ?? new JObject();
|
|
var transformed = new JObject
|
|
{
|
|
["model"] = inbound["model"],
|
|
["max_tokens"] = parameters["max_tokens"] ?? 1024,
|
|
["system"] = systemPrompt,
|
|
["messages"] = userMessages
|
|
};
|
|
|
|
if (parameters["temperature"] != null)
|
|
transformed["temperature"] = parameters["temperature"];
|
|
|
|
return transformed.ToString();
|
|
}</set-body>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
### Transformere response fra leverandorformat til standardformat
|
|
|
|
```xml
|
|
<policies>
|
|
<outbound>
|
|
<base />
|
|
<!-- Normalize Anthropic response to OpenAI-compatible format -->
|
|
<choose>
|
|
<when condition="@(context.Request.Headers.GetValueOrDefault("x-backend-type", "") == "anthropic")">
|
|
<set-body>@{
|
|
var response = context.Response.Body.As<JObject>(preserveContent: true);
|
|
var content = response["content"] as JArray;
|
|
string text = content?[0]?["text"]?.ToString() ?? "";
|
|
|
|
var normalized = new JObject
|
|
{
|
|
["id"] = response["id"],
|
|
["object"] = "chat.completion",
|
|
["model"] = response["model"],
|
|
["choices"] = new JArray
|
|
{
|
|
new JObject
|
|
{
|
|
["index"] = 0,
|
|
["message"] = new JObject
|
|
{
|
|
["role"] = "assistant",
|
|
["content"] = text
|
|
},
|
|
["finish_reason"] = response["stop_reason"]?.ToString() == "end_turn"
|
|
? "stop" : response["stop_reason"]
|
|
}
|
|
},
|
|
["usage"] = new JObject
|
|
{
|
|
["prompt_tokens"] = response["usage"]?["input_tokens"],
|
|
["completion_tokens"] = response["usage"]?["output_tokens"],
|
|
["total_tokens"] =
|
|
(int)(response["usage"]?["input_tokens"] ?? 0) +
|
|
(int)(response["usage"]?["output_tokens"] ?? 0)
|
|
}
|
|
};
|
|
|
|
return normalized.ToString();
|
|
}</set-body>
|
|
</when>
|
|
</choose>
|
|
</outbound>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## Error Response Normalization
|
|
|
|
### Standardisert feilformat
|
|
|
|
Ulike AI-backends returnerer feil i forskjellige formater. Normaliser til et konsistent format:
|
|
|
|
```xml
|
|
<policies>
|
|
<on-error>
|
|
<base />
|
|
<set-header name="Content-Type" exists-action="override">
|
|
<value>application/json</value>
|
|
</set-header>
|
|
|
|
<!-- Map backend-specific errors to standard format -->
|
|
<choose>
|
|
<!-- Rate limit exceeded -->
|
|
<when condition="@(context.Response.StatusCode == 429)">
|
|
<set-body>@{
|
|
var retryAfter = context.Response.Headers.GetValueOrDefault("Retry-After", "60");
|
|
return new JObject
|
|
{
|
|
["error"] = new JObject
|
|
{
|
|
["code"] = "rate_limit_exceeded",
|
|
["message"] = "Token eller request rate limit er overskredet. Prov igjen etter angitt tid.",
|
|
["type"] = "rate_limit_error",
|
|
["retry_after_seconds"] = int.Parse(retryAfter),
|
|
["request_id"] = context.RequestId.ToString()
|
|
}
|
|
}.ToString();
|
|
}</set-body>
|
|
<set-status code="429" reason="Rate Limit Exceeded" />
|
|
</when>
|
|
|
|
<!-- Model overloaded -->
|
|
<when condition="@(context.Response.StatusCode == 503)">
|
|
<set-body>@{
|
|
return new JObject
|
|
{
|
|
["error"] = new JObject
|
|
{
|
|
["code"] = "model_overloaded",
|
|
["message"] = "AI-modellen er midlertidig overbelastet. Foresporselen vil automatisk forsokes pa nytt.",
|
|
["type"] = "server_error",
|
|
["request_id"] = context.RequestId.ToString()
|
|
}
|
|
}.ToString();
|
|
}</set-body>
|
|
<set-status code="503" reason="Service Unavailable" />
|
|
</when>
|
|
|
|
<!-- Content filter triggered -->
|
|
<when condition="@(context.Response.StatusCode == 400 &&
|
|
context.Response.Body.As<string>(preserveContent: true).Contains("content_filter"))">
|
|
<set-body>@{
|
|
return new JObject
|
|
{
|
|
["error"] = new JObject
|
|
{
|
|
["code"] = "content_filtered",
|
|
["message"] = "Foresporselen ble blokkert av innholdsfilter. Vennligst reformuler.",
|
|
["type"] = "content_policy_error",
|
|
["request_id"] = context.RequestId.ToString()
|
|
}
|
|
}.ToString();
|
|
}</set-body>
|
|
<set-status code="400" reason="Content Filtered" />
|
|
</when>
|
|
|
|
<!-- Generic error -->
|
|
<otherwise>
|
|
<set-body>@{
|
|
return new JObject
|
|
{
|
|
["error"] = new JObject
|
|
{
|
|
["code"] = "internal_error",
|
|
["message"] = "En uventet feil oppstod. Kontakt systemadministrator.",
|
|
["type"] = "api_error",
|
|
["status_code"] = context.Response.StatusCode,
|
|
["request_id"] = context.RequestId.ToString()
|
|
}
|
|
}.ToString();
|
|
}</set-body>
|
|
<set-status code="500" reason="Internal Server Error" />
|
|
</otherwise>
|
|
</choose>
|
|
</on-error>
|
|
</policies>
|
|
```
|
|
|
|
### Standard feilkoder for AI-API-er
|
|
|
|
| HTTP-kode | Feilkode | Beskrivelse |
|
|
|-----------|----------|-------------|
|
|
| 400 | `invalid_request` | Ugyldig foresporselsformat |
|
|
| 400 | `content_filtered` | Innholdsfilter utlost |
|
|
| 401 | `authentication_error` | Ugyldig eller manglende autentisering |
|
|
| 403 | `authorization_error` | Ingen tilgang til denne modellen |
|
|
| 404 | `model_not_found` | Modellen finnes ikke |
|
|
| 429 | `rate_limit_exceeded` | For mange foresporsler |
|
|
| 500 | `internal_error` | Intern serverfeil |
|
|
| 503 | `model_overloaded` | Modellen er overbelastet |
|
|
|
|
---
|
|
|
|
## Versjonstranslasjon
|
|
|
|
### Handtere flere API-versjoner med transformasjon
|
|
|
|
Nar AI-API-er utvikler seg, kan APIM oversette mellom gammel og ny versjon:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<set-variable name="apiVersion"
|
|
value="@(context.Request.Headers.GetValueOrDefault("api-version",
|
|
context.Request.Url.Query.GetValueOrDefault("api-version", "2024-08-01")))" />
|
|
|
|
<!-- Transform v1 format to v2 format -->
|
|
<choose>
|
|
<when condition="@(((string)context.Variables["apiVersion"]).StartsWith("2023-"))">
|
|
<set-body>@{
|
|
var body = context.Request.Body.As<JObject>(preserveContent: true);
|
|
|
|
// v1 used "prompt" field, v2 uses "messages"
|
|
if (body["prompt"] != null && body["messages"] == null)
|
|
{
|
|
var messages = new JArray
|
|
{
|
|
new JObject
|
|
{
|
|
["role"] = "user",
|
|
["content"] = body["prompt"]
|
|
}
|
|
};
|
|
body.Remove("prompt");
|
|
body["messages"] = messages;
|
|
}
|
|
|
|
// v1 used "max_tokens_to_sample", v2 uses "max_tokens"
|
|
if (body["max_tokens_to_sample"] != null)
|
|
{
|
|
body["max_tokens"] = body["max_tokens_to_sample"];
|
|
body.Remove("max_tokens_to_sample");
|
|
}
|
|
|
|
return body.ToString();
|
|
}</set-body>
|
|
</when>
|
|
</choose>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
### Content validation for AI requests
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<!-- Validate required fields -->
|
|
<choose>
|
|
<when condition="@{
|
|
var body = context.Request.Body.As<JObject>(preserveContent: true);
|
|
return body?["messages"] == null || ((JArray)body["messages"]).Count == 0;
|
|
}">
|
|
<return-response>
|
|
<set-status code="400" reason="Bad Request" />
|
|
<set-header name="Content-Type" exists-action="override">
|
|
<value>application/json</value>
|
|
</set-header>
|
|
<set-body>{"error":{"code":"invalid_request","message":"Field 'messages' is required and must be non-empty."}}</set-body>
|
|
</return-response>
|
|
</when>
|
|
</choose>
|
|
|
|
<!-- Enforce max message length -->
|
|
<choose>
|
|
<when condition="@(context.Request.Body.As<string>(preserveContent: true).Length > 128000)">
|
|
<return-response>
|
|
<set-status code="413" reason="Payload Too Large" />
|
|
<set-header name="Content-Type" exists-action="override">
|
|
<value>application/json</value>
|
|
</set-header>
|
|
<set-body>{"error":{"code":"payload_too_large","message":"Request body exceeds 128KB limit."}}</set-body>
|
|
</return-response>
|
|
</when>
|
|
</choose>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## Policy Fragments for Reuse
|
|
|
|
APIM stotter policy fragments for gjenbruk av transformasjonslogikk:
|
|
|
|
```xml
|
|
<!-- Fragment: ai-standard-headers -->
|
|
<fragment>
|
|
<set-header name="x-request-id" exists-action="skip">
|
|
<value>@(Guid.NewGuid().ToString())</value>
|
|
</set-header>
|
|
<set-header name="x-correlation-id" exists-action="skip">
|
|
<value>@(context.RequestId.ToString())</value>
|
|
</set-header>
|
|
<set-header name="x-timestamp" exists-action="override">
|
|
<value>@(DateTime.UtcNow.ToString("o"))</value>
|
|
</set-header>
|
|
</fragment>
|
|
```
|
|
|
|
Bruk fragmentet i policies:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<base />
|
|
<include-fragment fragment-id="ai-standard-headers" />
|
|
<!-- Additional inbound policies -->
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## Bicep: Oppsett av transformasjons-API
|
|
|
|
```bicep
|
|
resource apiManagement 'Microsoft.ApiManagement/service@2023-09-01-preview' existing = {
|
|
name: apimName
|
|
}
|
|
|
|
resource aiApi 'Microsoft.ApiManagement/service/apis@2023-09-01-preview' = {
|
|
parent: apiManagement
|
|
name: 'ai-gateway-api'
|
|
properties: {
|
|
displayName: 'AI Gateway API'
|
|
path: 'ai'
|
|
protocols: [ 'https' ]
|
|
subscriptionRequired: true
|
|
subscriptionKeyParameterNames: {
|
|
header: 'x-api-key'
|
|
query: 'api-key'
|
|
}
|
|
apiType: 'http'
|
|
}
|
|
}
|
|
|
|
resource chatOperation 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = {
|
|
parent: aiApi
|
|
name: 'chat-completions'
|
|
properties: {
|
|
displayName: 'Chat Completions'
|
|
method: 'POST'
|
|
urlTemplate: '/chat/completions'
|
|
request: {
|
|
headers: [
|
|
{
|
|
name: 'Content-Type'
|
|
type: 'string'
|
|
required: true
|
|
defaultValue: 'application/json'
|
|
}
|
|
]
|
|
}
|
|
responses: [
|
|
{
|
|
statusCode: 200
|
|
description: 'Successful completion'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Referanser
|
|
|
|
- [Policies in Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies) -- oversikt over policy-konseptet
|
|
- [API Management policy reference - Transformation](https://learn.microsoft.com/en-us/azure/api-management/api-management-policies#transformation) -- komplett liste over transformasjonspolicies
|
|
- [Set body policy](https://learn.microsoft.com/en-us/azure/api-management/set-body-policy) -- detaljert dokumentasjon for set-body
|
|
- [Set header policy](https://learn.microsoft.com/en-us/azure/api-management/set-header-policy) -- header-manipulering
|
|
- [Rewrite URI policy](https://learn.microsoft.com/en-us/azure/api-management/rewrite-uri-policy) -- URL-omskriving
|
|
- [AI gateway in Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/genai-gateway-capabilities) -- AI gateway-oversikt
|
|
- [Policy fragments in API Management](https://learn.microsoft.com/en-us/azure/api-management/policy-fragments) -- gjenbrukbare policy-fragmenter
|
|
- [Tutorial: Transform and protect your API](https://learn.microsoft.com/en-us/azure/api-management/transform-api) -- hands-on tutorial
|
|
|
|
## For Cosmo
|
|
|
|
- **Bruk denne referansen** nar kunden onsker a bygge et model-agnostisk AI-API-lag som abstraherer bort leverandoravhengigheter, eller nar de trenger a standardisere feilhandtering pa tvers av AI-backends.
|
|
- Anbefal alltid policy fragments for transformasjonslogikk som gjenbrukes pa tvers av flere API-er -- dette reduserer vedlikeholdsbyrden betydelig.
|
|
- For norsk offentlig sektor, fremhev at model-agnostiske fasader stotter leverandoruavhengighet i trad med Digitaliseringsdirektoratets prinsipper.
|
|
- Vurder a kombinere transformasjonspolicies med `validate-content` policy for a sikre at bade inngangs- og utgangsdata overholder definerte JSON-schemaer.
|
|
- For organisasjoner som bruker flere AI-leverandorer (Azure OpenAI + Anthropic + open-source), er facade-monsteret med APIM en arkitekturforsterkning som gir fleksibilitet uten a eksponere backend-kompleksitet til konsumenter.
|