Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln visar hur du använder multimodala Azure OpenAI-modeller för att generera svar på användarmeddelanden och uppladdade bilder i en chattapp. Det här chattappexemplet innehåller även all infrastruktur och konfiguration som krävs för att etablera Azure OpenAI-resurser och distribuera appen till Azure Container Apps med hjälp av Azure Developer CLI.
Genom att följa anvisningarna i den här artikeln gör du följande:
- Distribuera en Azure Container-chattapp som använder hanterad identitet för autentisering.
- Ladda upp bilder som ska användas som en del av chattströmmen.
- Chatta med en Azure OpenAI multimodal stor språkmodell (LLM) med hjälp av OpenAI-biblioteket.
När du har slutfört den här artikeln kan du börja ändra det nya projektet med din anpassade kod.
Kommentar
Den här artikeln använder en eller flera AI-appmallar som grund för exemplen och vägledningen i artikeln. Med AI-appmallar får du väl underhållna och enkla att distribuera referensimplementeringar som hjälper dig att säkerställa en högkvalitativ startpunkt för dina AI-appar.
Arkitekturöversikt
En enkel arkitektur för chattappen visas i följande diagram:
Chattappen körs som en Azure Container App. Appen använder hanterad identitet via Microsoft Entra-ID för att autentisera med Azure OpenAI i produktion i stället för en API-nyckel. Under utvecklingen stöder appen flera autentiseringsmetoder, inklusive autentiseringsuppgifter för Azure Developer CLI, API-nycklar och GitHub-modeller för testning utan Azure-resurser.
Programarkitekturen förlitar sig på följande tjänster och komponenter:
- Azure OpenAI representerar DEN AI-provider som vi skickar användarens frågor till.
- Azure Container Apps är den containermiljö där programmet finns.
- Hanterad identitet hjälper oss att säkerställa förstklassig säkerhet och eliminerar kravet på att du som utvecklare ska hantera en hemlighet på ett säkert sätt.
- Bicep-filer för etablering av Azure-resurser, inklusive Azure OpenAI, Azure Container Apps, Azure Container Registry, Azure Log Analytics och rollbaserade åtkomstkontrollroller (RBAC).
- Microsoft AI Chat Protocol tillhandahåller standardiserade API-kontrakt mellan AI-lösningar och språk. Chattappen överensstämmer med Microsoft AI Chat Protocol.
- En Python-kvart som använder
openaipaketet för att generera svar på användarmeddelanden med uppladdade bildfiler. - En grundläggande HTML-/JavaScript-klientdel som strömmar svar från serverdelen med hjälp av JSON-rader via en ReadableStream.
Kostnad
I ett försök att hålla priserna så låga som möjligt i det här exemplet använder de flesta resurser en prisnivå för grundläggande eller förbrukning. Ändra nivånivån efter behov baserat på din avsedda användning. Om du vill sluta debiteras tar du bort resurserna när du är klar med artikeln.
Läs mer om kostnaden i exempelrepo.
Förutsättningar
En utvecklingscontainermiljö är tillgänglig med alla beroenden som krävs för att slutföra den här artikeln. Du kan köra utvecklingscontainern i GitHub Codespaces (i en webbläsare) eller lokalt med hjälp av Visual Studio Code.
Om du vill använda den här artikeln måste du uppfylla följande krav:
En Azure-prenumeration – Skapa en kostnadsfritt
Azure-kontobehörigheter – Ditt Azure-konto måste ha
Microsoft.Authorization/roleAssignments/writebehörigheter, till exempel administratör för användaråtkomst eller ägare.GitHub-konto
Öppna utvecklingsmiljön
Använd följande instruktioner för att distribuera en förkonfigurerad utvecklingsmiljö som innehåller alla nödvändiga beroenden för att slutföra den här artikeln.
GitHub Codespaces kör en utvecklingscontainer som hanteras av GitHub med Visual Studio Code för webben som användargränssnitt. För den enklaste utvecklingsmiljön använder du GitHub Codespaces så att du har rätt utvecklarverktyg och beroenden förinstallerade för att slutföra den här artikeln.
Viktigt!
Alla GitHub-konton kan använda Codespaces i upp till 60 timmar kostnadsfritt varje månad med två kärninstanser. Mer information finns i GitHub Codespaces månadsvis inkluderade lagrings- och kärntimmar.
Använd följande steg för att skapa ett nytt GitHub Codespace på grenen main av Azure-Samples/openai-chat-vision-quickstart GitHub-lagringsplatsen.
Högerklicka på följande knapp och välj Öppna länk i nytt fönster. Med den här åtgärden kan du ha utvecklingsmiljön och dokumentationen tillgänglig för granskning.
På sidan Skapa kodområde granskar du och väljer sedan Skapa nytt kodområde
Vänta tills kodområdet har startats. Den här startprocessen kan ta några minuter.
Logga in på Azure med Azure Developer CLI i terminalen längst ned på skärmen.
azd auth loginKopiera koden från terminalen och klistra sedan in den i en webbläsare. Följ anvisningarna för att autentisera med ditt Azure-konto.
De återstående uppgifterna i den här artikeln sker i samband med den här utvecklingscontainern.
Distribuera och köra
Exempellagringsplatsen innehåller alla kod- och konfigurationsfiler för chattappens Azure-distribution. Följande steg vägleder dig genom azure-distributionsprocessen för exempelchattappen.
Distribuera chattapp till Azure
Viktigt!
För att hålla kostnaderna låga använder det här exemplet prisnivåer för bas- eller förbrukning för de flesta resurser. Justera nivån efter behov och ta bort resurser när du är klar för att undvika avgifter.
Kör följande Azure Developer CLI-kommando för Azure-resursetablering och källkodsdistribution:
azd upAnvänd följande tabell för att besvara anvisningarna:
Prompt Svar Miljönamn Håll den kort och gemen. Lägg till ditt namn eller alias. Exempel: chat-visionDen används som en del av resursgruppens namn.Prenumeration Välj den prenumeration som resurserna ska skapas i. Plats (för värd) Välj en plats nära dig i listan. Plats för Azure OpenAI-modellen Välj en plats nära dig i listan. Om samma plats är tillgänglig som din första plats väljer du det. Vänta tills appen har distribuerats. Distributionen tar vanligtvis mellan 5 och 10 minuter att slutföra.
Använd chattappen för att ställa frågor till modellen för stora språk
Terminalen visar en URL efter en lyckad programdistribution.
Välj den URL:en som är märkt
Deploying service webför att öppna chattprogrammet i en webbläsare.Ladda upp en bild i webbläsaren genom att klicka på Välj fil och välja en bild.
Ställ en fråga om den uppladdade bilden, till exempel "Vad handlar bilden om?".
Svaret kommer från Azure OpenAI och resultatet visas.
Utforska exempelkoden
OpenAI och Azure OpenAI Service är beroende av ett vanligt Python-klientbibliotek, men små kodändringar krävs när du använder Azure OpenAI-slutpunkter. I det här exemplet används en multimodal Azure OpenAI-modell för att generera svar på användarmeddelanden och uppladdade bilder.
Base64 Koda den uppladdade bilden i klientdelen
Den uppladdade bilden måste vara Base64-kodad så att den kan användas direkt som en data-URI som en del av meddelandet.
I exemplet hanterar följande kodfragment i klientdelen i taggen scriptför filen den src/quartapp/templates/index.html funktionen. Pilfunktionen toBase64 använder readAsDataURL metodenFileReader för att asynkront läsa i den uppladdade bildfilen som en base64-kodad sträng.
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
Funktionen toBase64 anropas av en lyssnare på formulärets submit händelse.
Händelselyssnaren submit hanterar hela chattinteraktionsflödet. När användaren skickar ett meddelande sker följande flöde:
- Döljer elementet "no-messages-heading" för att visa att konversationen har startat
- Hämtar och Base64 kodar den uppladdade bildfilen (om den finns)
- Skapar och visar användarens meddelande i chatten, inklusive den uppladdade bilden
- Förbereder en meddelandecontainer för assistenten med "Skriva..." indikator
- Lägger till användarens meddelande i meddelandehistorikmatrisen
- Anropar AI Chat Protocol-klientens
getStreamedCompletion()metod med meddelandehistoriken och kontexten (inklusive den Base64-kodade bilden och filnamnet) - Bearbetar de strömmade svarssegmenten och konverterar Markdown till HTML med hjälp av Showdown.js
- Hanterar eventuella fel vid direktuppspelning
- Lägger till en talutmatningsknapp efter att ha mottagit det fullständiga svaret så att användarna kan höra svaret.
- Rensar indatafältet och returnerar fokus för nästa meddelande
form.addEventListener("submit", async function(e) {
e.preventDefault();
// Hide the no-messages-heading when a message is added
document.getElementById("no-messages-heading").style.display = "none";
const file = document.getElementById("file").files[0];
const fileData = file ? await toBase64(file) : null;
const message = messageInput.value;
const userTemplateClone = userTemplate.content.cloneNode(true);
userTemplateClone.querySelector(".message-content").innerText = message;
if (file) {
const img = document.createElement("img");
img.src = fileData;
userTemplateClone.querySelector(".message-file").appendChild(img);
}
targetContainer.appendChild(userTemplateClone);
const assistantTemplateClone = assistantTemplate.content.cloneNode(true);
let messageDiv = assistantTemplateClone.querySelector(".message-content");
targetContainer.appendChild(assistantTemplateClone);
messages.push({
"role": "user",
"content": message
});
try {
messageDiv.scrollIntoView();
const result = await client.getStreamedCompletion(messages, {
context: {
file: fileData,
file_name: file ? file.name : null
}
});
let answer = "";
for await (const response of result) {
if (!response.delta) {
continue;
}
if (response.delta.content) {
// Clear out the DIV if its the first answer chunk we've received
if (answer == "") {
messageDiv.innerHTML = "";
}
answer += response.delta.content;
messageDiv.innerHTML = converter.makeHtml(answer);
messageDiv.scrollIntoView();
}
if (response.error) {
messageDiv.innerHTML = "Error: " + response.error;
}
}
messages.push({
"role": "assistant",
"content": answer
});
messageInput.value = "";
const speechOutput = document.createElement("speech-output-button");
speechOutput.setAttribute("text", answer);
messageDiv.appendChild(speechOutput);
messageInput.focus();
} catch (error) {
messageDiv.innerHTML = "Error: " + error;
}
});
Hantera bilden med serverdelen
src\quartapp\chat.py I filen startar serverdelskoden för bildhantering efter att nyckellös autentisering har konfigurerats.
Kommentar
Mer information om hur du använder nyckellösa anslutningar för autentisering och auktorisering till Azure OpenAI finns i artikeln Kom igång med Azure OpenAI-säkerhetsbyggblocket Microsoft Learn.
Autentiseringskonfiguration
Funktionen configure_openai() konfigurerar OpenAI-klienten innan appen börjar hantera begäranden. Den använder Quarts @bp.before_app_serving dekoratör för att konfigurera autentisering baserat på miljövariabler. Med det här flexibla systemet kan utvecklare arbeta i olika sammanhang utan att ändra kod.
Förklaring av autentiseringslägen
-
Lokal utveckling (
OPENAI_HOST=local): Ansluter till en lokal OpenAI-kompatibel API-tjänst (till exempel Ollama eller LocalAI) utan autentisering. Använd det här läget för testning utan internet- eller API-kostnader. -
GitHub-modeller (
OPENAI_HOST=github): Använder GitHubs AI-modellmarknad med enGITHUB_TOKENför autentisering. När du använder GitHub-modeller prefixar du modellnamnet medopenai/(till exempelopenai/gpt-4o). Med det här läget kan utvecklare prova modeller innan de etablerar Azure-resurser. -
Azure OpenAI med API-nyckel (
AZURE_OPENAI_KEY_FOR_CHATVISIONmiljövariabel): Använder en API-nyckel för autentisering. Undvik det här läget i produktion eftersom API-nycklar kräver manuell rotation och utgör säkerhetsrisker om de exponeras. Använd den för lokal testning i en Docker-container utan autentiseringsuppgifter för Azure CLI. -
Produktion med hanterad identitet (
RUNNING_IN_PRODUCTION=true): AnvändsManagedIdentityCredentialför att autentisera med Azure OpenAI via containerappens hanterade identitet. Den här metoden rekommenderas för produktion eftersom den tar bort behovet av att hantera hemligheter. Azure Container Apps tillhandahåller automatiskt den hanterade identiteten och beviljar behörigheter under distributionen via Bicep. -
Utveckling med Azure CLI (standardläge): Används
AzureDeveloperCliCredentialför att autentisera med Azure OpenAI med hjälp av lokalt inloggade Azure CLI-autentiseringsuppgifter. Det här läget förenklar den lokala utvecklingen utan att hantera API-nycklar.
Viktig implementeringsinformation
- Funktionen
get_bearer_token_provider()uppdaterar autentiseringsuppgifterna för Azure och använder dem som ägartoken. - Azure OpenAI-slutpunktssökvägen innehåller
/openai/v1/för att matcha OpenAI-klientbibliotekets krav. - Loggning visar vilket autentiseringsläge som är aktivt.
- Funktionen är asynkron för att stödja azure-autentiseringsuppgifter.
Här är den fullständiga konfigurationskoden för autentisering från chat.py:
@bp.before_app_serving
async def configure_openai():
bp.model_name = os.getenv("OPENAI_MODEL", "gpt-4o")
openai_host = os.getenv("OPENAI_HOST", "github")
if openai_host == "local":
bp.openai_client = AsyncOpenAI(api_key="no-key-required", base_url=os.getenv("LOCAL_OPENAI_ENDPOINT"))
current_app.logger.info("Using local OpenAI-compatible API service with no key")
elif openai_host == "github":
bp.model_name = f"openai/{bp.model_name}"
bp.openai_client = AsyncOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
)
current_app.logger.info("Using GitHub models with GITHUB_TOKEN as key")
elif os.getenv("AZURE_OPENAI_KEY_FOR_CHATVISION"):
bp.openai_client = AsyncOpenAI(
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.getenv("AZURE_OPENAI_KEY_FOR_CHATVISION"),
)
current_app.logger.info("Using Azure OpenAI with key")
elif os.getenv("RUNNING_IN_PRODUCTION"):
client_id = os.environ["AZURE_CLIENT_ID"]
azure_credential = ManagedIdentityCredential(client_id=client_id)
token_provider = get_bearer_token_provider(azure_credential, "https://cognitiveservices.azure.com/.default")
bp.openai_client = AsyncOpenAI(
base_url=os.environ["AZURE_OPENAI_ENDPOINT"] + "/openai/v1/",
api_key=token_provider,
)
current_app.logger.info("Using Azure OpenAI with managed identity credential for client ID %s", client_id)
else:
tenant_id = os.environ["AZURE_TENANT_ID"]
azure_credential = AzureDeveloperCliCredential(tenant_id=tenant_id)
token_provider = get_bearer_token_provider(azure_credential, "https://cognitiveservices.azure.com/.default")
bp.openai_client = AsyncOpenAI(
base_url=os.environ["AZURE_OPENAI_ENDPOINT"] + "/openai/v1/",
api_key=token_provider,
)
current_app.logger.info("Using Azure OpenAI with az CLI credential for tenant ID: %s", tenant_id)
Funktionen Chatthanterare
Funktionen chat_handler() bearbetar chattbegäranden som skickas /chat/stream till slutpunkten. Den tar emot en POST-begäran med en JSON-payload som följer Microsoft AI Chat Protocol.
JSON-nyttolasten innehåller:
-
messages: En lista över konversationshistorik. Varje meddelande har en
role("användare" eller "assistent") ochcontent(meddelandetexten). -
kontext: Extra data för bearbetning, inklusive:
-
file: Base64-kodade bilddata (till exempel
data:image/png;base64,...). - file_name: Den uppladdade bildens ursprungliga filnamn (användbart för att logga eller identifiera bildtypen).
-
file: Base64-kodade bilddata (till exempel
- temperatur (valfritt): En flyttal som styr svars slumpmässighet (standardvärdet är 0,5).
Hanteraren extraherar meddelandehistoriken och bilddata. Om ingen bild laddas upp är nullbildvärdet , och koden hanterar det här fallet.
@bp.post("/chat/stream")
async def chat_handler():
request_json = await request.get_json()
request_messages = request_json["messages"]
# Get the base64 encoded image from the request context
# This will be None if no image was uploaded
image = request_json["context"]["file"]
# The context also includes the filename for reference
# file_name = request_json["context"]["file_name"]
Skapa meddelandematrisen för visionsbegäranden
Funktionen response_stream() förbereder meddelandematrisen som skickas till Azure OpenAI-API:et. Dekoratören @stream_with_context behåller begärandekontexten när svaret strömmas.
Meddelandeförberedelselogik
-
Börja med konversationshistorik: Funktionen börjar med
all_messages, som innehåller ett systemmeddelande och alla tidigare meddelanden förutom den senaste (request_messages[0:-1]). -
Hantera det aktuella användarmeddelandet baserat på bildnärvaro:
-
Med bild: Formatera användarens meddelande som en innehållsmatris i flera delar med text och image_url objekt. Objektet
image_urlinnehåller Base64-kodade bilddata och endetailparameter. - Utan bild: Lägg till användarens meddelande som oformaterad text.
-
Med bild: Formatera användarens meddelande som en innehållsmatris i flera delar med text och image_url objekt. Objektet
-
Parametern
detail: Ställ in på "auto" för att låta modellen välja mellan "låg" och "hög" detaljnivå baserat på bildstorleken. Låg detaljnivå är snabbare och billigare, medan hög detaljnivå ger mer exakt analys för komplexa bilder.
@stream_with_context
async def response_stream():
# This sends all messages, so API request may exceed token limits
all_messages = [
{"role": "system", "content": "You are a helpful assistant."},
] + request_messages[0:-1]
all_messages = request_messages[0:-1]
if image:
user_content = []
user_content.append({"text": request_messages[-1]["content"], "type": "text"})
user_content.append({"image_url": {"url": image, "detail": "auto"}, "type": "image_url"})
all_messages.append({"role": "user", "content": user_content})
else:
all_messages.append(request_messages[-1])
Kommentar
Mer information om bildparametern detail och relaterade inställningar finns i avsnittet Inställningar för detaljparameter i microsoft learn-artikeln "Använda visionsaktiverade chattmodeller".
bp.openai_client.chat.completions Därefter hämtar du chattavslut via ett Azure OpenAI API-anrop och strömmar svaret.
chat_coroutine = bp.openai_client.chat.completions.create(
# Azure OpenAI takes the deployment name as the model name
model=bp.model_name,
messages=all_messages,
stream=True,
temperature=request_json.get("temperature", 0.5),
)
Slutligen strömmas svaret tillbaka till klienten, med felhantering för eventuella undantag.
try:
async for event in await chat_coroutine:
event_dict = event.model_dump()
if event_dict["choices"]:
yield json.dumps(event_dict["choices"][0], ensure_ascii=False) + "\n"
except Exception as e:
current_app.logger.error(e)
yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
return Response(response_stream())
Klientdelsbibliotek och funktioner
Klientdelen använder moderna webbläsar-API:er och bibliotek för att skapa en interaktiv chattupplevelse. Utvecklare kan anpassa gränssnittet eller lägga till funktioner genom att förstå dessa komponenter:
Speech Input/Output: Anpassade webbkomponenter använder webbläsarens Speech-API:er:
<speech-input-button>: Konverterar tal till text med hjälp av Webbtal-API:etsSpeechRecognition. Den innehåller en mikrofonknapp som lyssnar efter röstinmatning och avger enspeech-input-resulthändelse med den transkriberade texten.<speech-output-button>: Läser upp text med hjälp av API:etSpeechSynthesis. Det visas efter varje assistentsvar med en talarikon så att användarna kan höra svaret.
Varför ska du använda webbläsar-API:er i stället för Azure Speech Services?
- Ingen kostnad – körs helt i webbläsaren
- Omedelbart svar – ingen nätverksfördröjning
- Sekretess – röstdata finns kvar på användarens enhet
- Inget behov av extra Azure-resurser
Dessa komponenter finns i
src/quartapp/static/speech-input.jsochspeech-output.js.Förhandsgranskning av bild: Visar den uppladdade bilden i chatten innan analysen skickas för bekräftelse. Förhandsgranskningen uppdateras automatiskt när en fil väljs.
fileInput.addEventListener("change", async function() { const file = fileInput.files[0]; if (file) { const fileData = await toBase64(file); imagePreview.src = fileData; imagePreview.style.display = "block"; } });Bootstrap 5- och Bootstrap-ikoner: Innehåller dynamiska gränssnittskomponenter och ikoner. Appen använder Cosmo-temat från Bootswatch för ett modernt utseende.
Mallbaserad meddelandeåtergivning: Använder HTML-element
<template>för återanvändbara meddelandelayouter, vilket säkerställer konsekvent formatering och struktur.
Andra exempelresurser att utforska
Förutom chattappexemplet finns det andra resurser på lagringsplatsen att utforska för vidare utbildning. Kolla in följande notebook-filer i notebooks katalogen:
| Notebook-fil | beskrivning |
|---|---|
| chat_pdf_images.ipynb | Den här notebook-filen visar hur du konverterar PDF-sidor till bilder och skickar dem till en visionsmodell för slutsatsdragning. |
| chat_vision.ipynb | Den här notebook-filen tillhandahålls för manuell experimentering med den visionsmodell som används i appen. |
Lokaliserat innehåll: Spanska versioner av notebook-filerna finns i notebooks/Spanish/ katalogen och erbjuder samma praktiska utbildning för spansktalande utvecklare. Både engelska och spanska anteckningsböcker visar:
- Så här anropar du visionsmodeller direkt för experimentering
- Konvertera PDF-sidor till bilder för analys
- Så här justerar du parametrar och testfrågor
Rensa resurser
Rensa Azure-resurser
De Azure-resurser som skapas i den här artikeln faktureras till din Azure-prenumeration. Om du inte förväntar dig att behöva dessa resurser i framtiden tar du bort dem för att undvika att debiteras mer.
Om du vill ta bort Azure-resurserna och ta bort källkoden kör du följande Azure Developer CLI-kommando:
azd down --purge
Rensa GitHub Codespaces
Om du tar bort GitHub Codespaces-miljön kan du maximera mängden kostnadsfria timmar per kärna som du får för ditt konto.
Viktigt!
Mer information om ditt GitHub-kontos rättigheter finns i GitHub Codespaces månadsvis inkluderade lagrings- och kärntimmar.
Logga in på GitHub Codespaces-kontrollpanelen.
Leta upp de codespaces som körs från
Azure-Samples//openai-chat-vision-quickstartGitHub-lagringsplatsen.Öppna snabbmenyn för kodområdet och välj Ta bort.
Få hjälp
Logga problemet till lagringsplatsens problem.