Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come utilizzare i modelli multimodali di Azure OpenAI per generare risposte ai messaggi degli utenti e alle immagini caricate in un'app di chat. Questo esempio di app chat include anche tutta l'infrastruttura e la configurazione necessarie per effettuare il provisioning delle risorse OpenAI Azure e distribuire l'app in App contenitore di Azure usando l'interfaccia della riga di comando per sviluppatori Azure.
Seguendo le istruzioni riportate in questo articolo, potrai:
- Distribuire un'applicazione di chat in contenitore di Azure che usa l'Identità gestita per l'autenticazione.
- Caricare immagini da usare come parte del flusso di chat.
- Chattare con un Azure OpenAI large Language Model (LLM) multilingue usando l'API Risposte della libreria OpenAI.
Dopo aver completato questo articolo, è possibile iniziare a modificare il nuovo progetto con il codice personalizzato.
Nota
Questo articolo usa uno o più modelli di app di intelligenza artificiale come base per gli esempi e le linee guida nell’articolo. I modelli di app di intelligenza artificiale offrono implementazioni di riferimento ben gestite e facili da distribuire per garantire un punto di partenza di alta qualità per le app di intelligenza artificiale.
Panoramica dell'architettura
Un'architettura semplice dell'app di chat è illustrata nel diagramma seguente:
L'applicazione di chat è in esecuzione come Azure Container App. L'app usa l'identità gestita tramite Microsoft Entra ID per eseguire l'autenticazione con Azure OpenAI nell'ambiente di produzione, anziché con una chiave API. Durante lo sviluppo, l'app supporta più metodi di autenticazione, tra cui le credenziali dell'interfaccia della riga di comando per sviluppatori Azure e le chiavi API.
L'architettura dell'applicazione si basa sui servizi e sui componenti seguenti:
- Azure OpenAI rappresenta il provider di intelligenza artificiale a cui si inviano le query dell'utente.
- App contenitore di Azure è l'ambiente contenitore in cui è ospitata l'applicazione.
- L'identità gestita consente di garantire la sicurezza ottimale e di eliminare i requisiti per gli sviluppatori per gestire in modo sicuro un segreto.
- Bicep file per il provisioning di risorse Azure, tra cui Azure OpenAI, App contenitore di Azure, Registro Azure Container, Log Analytics di Azure e ruoli di controllo degli accessi in base al ruolo.
- Un'app Python Quart che usa il pacchetto
openaiper generare risposte ai messaggi utente con file di immagine caricati. - Front-end HTML/JavaScript di base che trasmette le risposte dal back-end usando righe JSON su un flusso leggibile.
Costo
Nel tentativo di mantenere i prezzi il più basso possibile in questo esempio, la maggior parte delle risorse usa un piano tariffario di base o a consumo. Modificare il livello di categoria in base all'utilizzo previsto. Per interrompere l'accumulo di costi, eliminare le risorse una volta terminato l'uso dell'articolo.
Altre informazioni sul costo nell'esempio di repository.
Prerequisiti
Per completare questo articolo è disponibile un ambiente contenitore di sviluppo con tutte le dipendenze necessarie. È possibile eseguire il contenitore di sviluppo in GitHub Codespaces (in un browser) o in locale usando Visual Studio Code.
Per usare questo articolo, è necessario soddisfare i prerequisiti seguenti:
Una sottoscrizione Azure - Crearne una gratuitamente
Azure autorizzazioni dell'account: l'account Azure deve avere autorizzazioni
Microsoft.Authorization/roleAssignments/write, come ad esempio Amministratore accesso utente o Proprietario.account GitHub
Ambiente di sviluppo aperto
Usare le istruzioni seguenti per distribuire un ambiente di sviluppo preconfigurato contenente tutte le dipendenze necessarie per completare questo articolo.
GitHub Codespaces esegue un contenitore di sviluppo gestito da GitHub con Visual Studio Code per web come interfaccia utente. Per l'ambiente di sviluppo più semplice, usare GitHub Codespaces in modo che siano preinstallati gli strumenti di sviluppo e le dipendenze corretti per completare questo articolo.
Importante
Tutti gli account GitHub possono usare Codespaces per un massimo di 60 ore gratuite ogni mese con due istanze principali. Per ulteriori informazioni, consulta lo spazio di archiviazione mensile incluso e le ore core di GitHub Codespaces.
Usare la procedura seguente per creare un nuovo GitHub Codespace nel ramo main del repository Azure-Samples/openai-chat-vision-quickstart GitHub.
Fare clic con il pulsante destro del mouse sul pulsante seguente e scegliere Apri collegamento in una nuova finestra. Questa azione consente di avere l'ambiente di sviluppo e la documentazione disponibile per la revisione.
Nella pagina Crea spazio codici esaminare e quindi selezionare Crea nuovo spazio di codice
Attendere l'avvio del codespace. Questo processo di avvio può richiedere alcuni minuti.
Accedi ad Azure con l'Azure Developer CLI nel terminale in basso nella schermata.
azd auth loginCopia il codice dal terminale e incollalo in un browser. Seguire le istruzioni per eseguire l'autenticazione con l'account Azure.
Le attività rimanenti in questo articolo vengono eseguite nel contesto di questo contenitore di sviluppo.
Implementare ed eseguire
Il repository di esempio contiene tutto il codice sorgente e i file di configurazione per la distribuzione dell'applicazione chat su Azure. La procedura seguente illustra il processo di distribuzione dell'app di chat di esempio su Azure.
Distribuire l'applicazione di chat su Azure
Importante
Per ridurre i costi, questo esempio usa piani tariffari di base o a consumo per la maggior parte delle risorse. Modificare il livello in base alle esigenze ed eliminare le risorse al termine per evitare addebiti.
Esegui il seguente comando della CLI per sviluppatori di Azure per il provisioning delle risorse e la distribuzione del codice sorgente:
azd upUsare la tabella seguente per rispondere alle richieste:
Richiesta Risposta Nome ambiente Mantienilo breve e in minuscolo. Aggiungere il nome o l'alias. Ad esempio: chat-vision. Viene usato come parte del nome del gruppo di risorse.Abbonamento Selezionare la sottoscrizione in cui creare le risorse. Localizzazione (per hosting) Seleziona una località vicina dall'elenco. Posizione per il modello OpenAI Azure Seleziona una località vicina dall'elenco. Se la stessa posizione è disponibile come prima posizione, selezionala. Attendi la distribuzione dell'app. Il completamento della distribuzione richiede in genere tra 5 e 10 minuti.
Usare l'app chat per porre domande al modello linguistico di grandi dimensioni
Il terminale visualizza un URL dopo la corretta distribuzione dell'applicazione.
Seleziona l'URL etichettato
Deploying service webper aprire l'applicazione di chat in un browser.Nel browser caricare un'immagine facendo clic su Scegli file e selezionando un'immagine.
Porre una domanda sull'immagine caricata, ad esempio "Informazioni sull'immagine?".
La risposta proviene da Azure OpenAI e il risultato viene visualizzato.
Esplorazione del codice di esempio
In questo esempio viene usato un modello Azure OpenAI multimodale per generare risposte ai messaggi utente e alle immagini caricate.
Base64 Codifica dell'immagine caricata nel front-end
L'immagine caricata deve essere codificata in Base64 in modo che possa essere usata direttamente come URI dati come parte del messaggio.
Nell'esempio, il frammento di codice front-end seguente nel scripttag del src/quartapp/templates/index.html file gestisce tale funzionalità. La toBase64 funzione freccia usa il metodo readAsDataURL di FileReader per leggere in modo asincrono il file immagine caricato come stringa codificata in Base64.
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
La funzione toBase64 viene chiamata da un listener sull'evento submit del form.
Il submit listener di eventi gestisce il flusso completo dell'interazione in chat. Quando l'utente invia un messaggio, si verifica il flusso seguente:
- Ottiene il file di immagine caricato (se presente) e codifica come Base64
- Crea e visualizza il messaggio dell'utente nella chat, inclusa l'immagine caricata
- Prepara un contenitore di messaggi per l'assistente con un indicatore di scrittura in corso
- Aggiunge il messaggio dell'utente alla matrice di cronologia messaggi in formato API Risposte
- Invia una
fetchrichiesta POST all'endpoint/chat/streamcon la cronologia e il contesto dei messaggi (inclusa l'immagine e il nome file con codifica Base64) - Elabora la risposta alle righe JSON in streaming per visualizzare ogni delta del testo in modo incrementale
- Gestisce eventuali errori durante lo streaming
- Aggiunge un pulsante di output vocale dopo aver ricevuto la risposta completa in modo che gli utenti possano ascoltare la risposta
- Cancella il campo di input e riporta il focus per il messaggio successivo
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": [{"type": "input_text", "text": message}]
});
try {
messageDiv.scrollIntoView();
const response = await fetch("/chat/stream", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
messages: messages,
context: {
file: fileData,
file_name: file ? file.name : null
}
})
});
if (!response.ok || !response.body) {
throw new Error(`Request failed (${response.status})`);
}
let answer = "";
for await (const chunk of readNDJSONStream(response.body)) {
if (chunk.type === "error" || chunk.type === "response.failed") {
messageDiv.innerHTML = "Error: " + (chunk.error || "Unknown error");
break;
}
if (chunk.type === "response.output_text.delta") {
// Clear out the DIV if its the first answer chunk we've received
if (answer == "") {
messageDiv.innerHTML = "";
}
answer += chunk.delta;
messageDiv.innerHTML = converter.makeHtml(answer);
messageDiv.scrollIntoView();
}
}
messages.push({
"role": "assistant",
"content": [{"type": "output_text", "text": 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;
}
});
Gestione dell'immagine con il back-end
src\quartapp\chat.py Nel file il codice back-end per la gestione delle immagini inizia dopo la configurazione dell'autenticazione senza chiave.
Nota
Per ulteriori informazioni su come utilizzare connessioni senza chiavi per l'autenticazione e l'autorizzazione con Azure OpenAI, vedere l'articolo Guida introduttiva al blocco predefinito di sicurezza di Azure OpenAI su Microsoft Learn.
Configurazione dell'autenticazione
La configure_openai() funzione configura il client OpenAI prima che l'app inizi a gestire le richieste. Utilizza il decoratore di Quart @bp.before_app_serving per configurare l'autenticazione in base alle variabili di ambiente. Questo sistema flessibile consente agli sviluppatori di lavorare in contesti diversi senza modificare il codice.
Modalità di autenticazione illustrate
-
Sviluppo locale (
OPENAI_HOST=local): si connette a un servizio API compatibile con OpenAI locale (ad esempio Ollama o LocalAI) senza autenticazione. Usare questa modalità per il test senza costi di Internet o API. -
Azure OpenAI con chiave API (variabile di ambiente
AZURE_OPENAI_KEY_FOR_CHATVISION): usa una chiave API per l'autenticazione. Evitare questa modalità nell'ambiente di produzione perché le chiavi API richiedono rotazione manuale e comportano rischi per la sicurezza, se esposti. Usarlo per i test locali all'interno di un contenitore Docker senza interfaccia della riga di comando di Azure credenziali. -
Production con identità gestita (
RUNNING_IN_PRODUCTION=true): UtilizzaManagedIdentityCredentialper autenticarsi con Azure OpenAI tramite l'identità gestita dell'app contenitore. Questo metodo è consigliato per la produzione perché rimuove la necessità di gestire i segreti. App contenitore di Azure forniscono automaticamente l'identità gestita e concedono le autorizzazioni durante la distribuzione attraverso Bicep. -
Development con interfaccia della riga di comando di Azure (modalità predefinita): usa
AzureDeveloperCliCredentialper eseguire l'autenticazione con Azure OpenAI usando le credenziali di interfaccia della riga di comando di Azure con accesso locale. Questa modalità semplifica lo sviluppo locale senza gestire le chiavi API.
Dettagli chiave dell'implementazione
- La funzione
get_bearer_token_provider()aggiorna le credenziali Azure e le usa come token di connessione. - Il percorso dell'endpoint di Azure OpenAI termina con
/openai/v1/, l'endpoint compatibile con OpenAI generalmente disponibile per i modelli Foundry di Microsoft. - La funzione è asincrona, poiché Quart è un framework asincrono per applicazioni web. Quart consente ai gestori delle richieste di essere asincroni, quindi mentre l'app è in attesa di risposte lente all'API LLM, il server può continuare a gestire altre richieste.
Ecco il codice di configurazione completo dell'autenticazione da 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", "azure")
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 os.getenv("AZURE_OPENAI_KEY_FOR_CHATVISION"):
bp.openai_client = AsyncOpenAI(
base_url=os.environ["AZURE_OPENAI_ENDPOINT"].rstrip("/") + "/openai/v1",
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"].rstrip("/") + "/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"].rstrip("/") + "/openai/v1",
api_key=token_provider,
)
current_app.logger.info("Using Azure OpenAI with az CLI credential for tenant ID: %s", tenant_id)
Funzione del gestore di chat
La chat_handler() funzione elabora le richieste di chat inviate all'endpoint /chat/stream . Riceve una richiesta POST con un payload JSON.
Il payload JSON include:
-
messages: elenco della cronologia delle conversazioni. Ogni messaggio ha un
role("utente" o "assistente") econtent(una matrice di parti di contenuto usando il formato di input dell'API Risposte). -
context: dati aggiuntivi per l'elaborazione, tra cui:
-
file: dati immagine con codifica Base64 (ad esempio,
data:image/png;base64,...). - file_name: nome file originale dell'immagine caricata (utile per la registrazione o l'identificazione del tipo di immagine).
-
file: dati immagine con codifica Base64 (ad esempio,
Il gestore estrae la cronologia dei messaggi e i dati dell'immagine. Se non viene caricata alcuna immagine, il valore dell'immagine è nulle il codice gestisce questo caso.
@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"]
Compilazione della matrice di input per le richieste di visione
La funzione response_stream() prepara la matrice di input inviata all'API Risposte OpenAI Azure. L'elemento @stream_with_context Decorator mantiene il contesto della richiesta durante lo streaming della risposta.
Logica di preparazione dell'input
-
Iniziare con la cronologia delle conversazioni: la funzione inizia con
all_input, che include tutti i messaggi precedenti tranne quello più recente (request_messages[0:-1]). I messaggi sono già in formato API Risposte dal front-end. -
Gestire il messaggio utente corrente in base alla presenza dell'immagine:
-
Con l'immagine: aggiungere una
input_imageparte del contenuto alla matrice di contenuto esistente dell'utente. - Senza immagine: aggiungere il messaggio dell'utente as-is.
-
Con l'immagine: aggiungere una
@stream_with_context
async def response_stream():
# This sends all messages, so API request may exceed token limits
all_input = list(request_messages[0:-1])
# Add the current user message, appending image if provided
if image:
user_content = request_messages[-1]["content"] + [{"type": "input_image", "image_url": image}]
all_input.append({"role": "user", "content": user_content})
else:
all_input.append(request_messages[-1])
Successivamente, bp.openai_client.responses.create chiama l'API Risposte OpenAI Azure e trasmette la risposta. Il store=False parametro indica all'API di non archiviare le risposte nel server, rendendo la chiamata senza stato.
openai_stream = await bp.openai_client.responses.create(
model=bp.model_name,
input=all_input,
stream=True,
temperature=0.3,
store=False,
)
Infine, la risposta viene trasmessa nuovamente al client. L'API Risposte genera molti tipi di evento, ma è necessario solo l'evento per lo response.output_text.delta streaming di testo generato. Gli eventi di errore vengono registrati e inoltrati al front-end.
try:
async for event in openai_stream:
if event.type == "response.output_text.delta":
yield json.dumps({"type": event.type, "delta": event.delta}, ensure_ascii=False) + "\n"
elif event.type in ("response.failed", "error"):
current_app.logger.error("Responses API error: %s", event)
yield json.dumps({"type": event.type}, ensure_ascii=False) + "\n"
except Exception as e:
current_app.logger.exception("Error in response stream")
yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
return Response(response_stream())
Librerie front-end e funzionalità
Il front-end usa api e librerie moderne del browser per creare un'esperienza di chat interattiva. Gli sviluppatori possono personalizzare l'interfaccia o aggiungere funzionalità comprendendo questi componenti:
Input vocale/Output: i componenti Web personalizzati usano le API Voce del browser:
<speech-input-button>: converte la voce in testo utilizzando l'API Web SpeechSpeechRecognition. Fornisce un pulsante del microfono che ascolta l'input vocale ed emette un eventospeech-input-resultcon il testo trascritto.<speech-output-button>: legge il testo ad alta voce usando l'APISpeechSynthesis. Viene visualizzato dopo ogni risposta dell'assistente con un'icona del parlante, consentendo agli utenti di ascoltare la risposta.
Perché usare le API del browser invece di Servizi Vocale di Azure?
- Nessun costo: viene eseguito interamente nel browser
- Risposta immediata: nessuna latenza di rete
- Privacy: i dati vocali rimangono nel dispositivo dell'utente
- Nessuna necessità di risorse aggiuntive Azure
Questi componenti si trovano in
src/quartapp/static/speech-input.jsespeech-output.js.Anteprima immagine: visualizza l'immagine caricata nella chat prima dell'invio di analisi per la conferma. L'anteprima viene aggiornata automaticamente quando viene selezionato un file.
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 e Bootstrap Icons: fornisce componenti dell'interfaccia utente reattivi e icone. L'app usa il tema Cosmo di Bootswatch per un aspetto moderno.
Rendering dei messaggi basato sul modello: uso di elementi HTML per i layout dei messaggi riutilizzabili, garantendo stili e struttura coerenti.
Altre risorse di esempio da esplorare
Oltre all'esempio di app di chat, nel repository sono disponibili altre risorse da esplorare per altre informazioni. Consulta i seguenti notebook nella directory notebooks:
| Notebook | Descrizione |
|---|---|
| chat_pdf_images.ipynb | Questo notebook illustra come convertire le pagine PDF in immagini e inviarle a un modello di visione per l'inferenza. |
| chat_vision.ipynb | Questo notebook viene fornito per la sperimentazione manuale con il modello di visione usato nell'app. |
Contenuto localizzato: le versioni spagnole dei notebook si trovano nella notebooks/Spanish/ directory, offrendo lo stesso apprendimento pratico per gli sviluppatori di lingua spagnola. I notebook in inglese e in spagnolo mostrano:
- Come chiamare direttamente i modelli di visione per la sperimentazione
- Come convertire pagine PDF in immagini per l'analisi
- Come modificare i parametri e i prompt dei test
Pulire le risorse
Pulire le risorse Azure
Le risorse di Azure create in questo articolo vengono fatturate al tuo abbonamento Azure. Se prevedi che queste risorse non ti servano in futuro, eliminale per evitare di incorrere in costi aggiuntivi.
Per eliminare le risorse Azure e rimuovere il codice sorgente, eseguire il comando della Azure Developer CLI seguente:
azd down --purge
Riordinare GitHub Codespaces
L'eliminazione dell'ambiente GitHub Codespaces garantisce che sia possibile massimizzare la quantità di ore gratuite per core che si ottiene per l'account.
Importante
Per ulteriori informazioni sui diritti del tuo account GitHub, consulta Archiviazione mensile inclusa e ore core di GitHub Codespaces.
Accedere al dashboard GitHub Codespaces.
Trova i tuoi Codespaces attualmente in esecuzione originati dal repository
Azure-Samples//openai-chat-vision-quickstartGitHub.Aprire il menu di scelta rapida per il codespace e scegliere Elimina.
Come ottenere assistenza
Segnala il tuo problema nel repository delle Issues.