Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este artigo mostra como usar modelos multimodais do Azure OpenAI para gerar respostas a mensagens do usuário e imagens carregadas em um aplicativo de bate-papo. Este exemplo de aplicativo de chat também inclui toda a infraestrutura e configuração necessárias para provisionar recursos do Azure OpenAI e implantar o aplicativo em Aplicativos de Contêiner do Azure usando a CLI do Desenvolvedor do Azure.
Seguindo as instruções neste artigo, você irá:
- Implante um aplicativo de chat de Contêiner do Azure que usa identidade gerenciada para autenticação.
- Carregue imagens para serem usadas como parte do fluxo de bate-papo.
- Converse com um Modelo de Linguagem Grande (LLM) multimodal do Azure OpenAI usando a biblioteca OpenAI.
Depois de concluir este artigo, você pode começar a modificar o novo projeto com seu código personalizado.
Nota
Este artigo usa um ou mais modelos de aplicativo de IA como base para os exemplos e orientações no artigo. Os modelos de aplicativos de IA fornecem implementações de referência bem mantidas e fáceis de implantar que ajudam a garantir um ponto de partida de alta qualidade para seus aplicativos de IA.
Descrição geral da arquitetura
Uma arquitetura simples do aplicativo de bate-papo é mostrada no diagrama a seguir:
O aplicativo de chat está sendo executado como um Aplicativo de Contêiner do Azure. O aplicativo usa identidade gerenciada por meio do Microsoft Entra ID para autenticar com o Azure OpenAI em produção, em vez de uma chave de API. Durante o desenvolvimento, o aplicativo dá suporte a vários métodos de autenticação, incluindo credenciais da CLI do Desenvolvedor do Azure, chaves de API e modelos do GitHub para testes sem recursos do Azure.
A arquitetura do aplicativo depende dos seguintes serviços e componentes:
- O Azure OpenAI representa o provedor de IA para o qual enviamos as consultas do usuário.
- Os Aplicativos de Contêiner do Azure são o ambiente de contêiner onde o aplicativo está hospedado.
- A Identidade Gerenciada nos ajuda a garantir a melhor segurança da categoria e elimina a necessidade de você, como desenvolvedor, gerenciar um segredo com segurança.
- Arquivos de bíceps para provisionar recursos do Azure, incluindo Azure OpenAI, Aplicativos de Contêiner do Azure, Registro de Contêiner do Azure, Análise de Log do Azure e funções de controle de acesso baseado em função (RBAC).
- O Microsoft AI Chat Protocol fornece contratos de API padronizados em soluções e idiomas de IA. O aplicativo de bate-papo está em conformidade com o Microsoft AI Chat Protocol.
- Um Python Quart que usa o
openaipacote para gerar respostas a mensagens do usuário com arquivos de imagem carregados. - Um frontend HTML/JavaScript básico que transmite respostas do back-end usando linhas JSON em um ReadableStream.
Custo
Na tentativa de manter o preço o mais baixo possível neste exemplo, a maioria dos recursos usa um nível de preço básico ou de consumo. Altere o nível da camada conforme necessário com base no uso pretendido. Para parar de incorrer em cobranças, exclua os recursos quando terminar o artigo.
Saiba mais sobre o custo no repositório de amostra.
Pré-requisitos
Um ambiente de contêiner de desenvolvimento está disponível com todas as dependências necessárias para concluir este artigo. Você pode executar o contêiner de desenvolvimento no GitHub Codespaces (em um navegador) ou localmente usando o Visual Studio Code.
Para usar este artigo, você precisa cumprir os seguintes pré-requisitos:
Uma assinatura do Azure - Crie uma gratuitamente
Permissões da conta do Azure - Sua Conta do Azure deve ter
Microsoft.Authorization/roleAssignments/writepermissões, como Administrador de Acesso de Usuário ou Proprietário.Conta do GitHub
Ambiente de desenvolvimento aberto
Use as instruções a seguir para implantar um ambiente de desenvolvimento pré-configurado contendo todas as dependências necessárias para concluir este artigo.
O GitHub Codespaces executa um contêiner de desenvolvimento gerenciado pelo GitHub com o Visual Studio Code for the Web como interface do usuário. Para o ambiente de desenvolvimento mais simples, use o GitHub Codespaces para que você tenha as ferramentas de desenvolvedor corretas e as dependências pré-instaladas para concluir este artigo.
Importante
Todas as contas do GitHub podem usar Codespaces por até 60 horas gratuitas por mês com duas instâncias principais. Para obter mais informações, consulte GitHub Codespaces mensalmente incluído armazenamento e horas principais.
Use as etapas a seguir para criar um novo espaço de código do GitHub na main ramificação do Azure-Samples/openai-chat-vision-quickstart repositório do GitHub.
Clique com o botão direito do mouse no botão a seguir e selecione Abrir link na nova janela. Esta ação permite que você tenha o ambiente de desenvolvimento e a documentação disponíveis para revisão.
Na página Criar espaço de código, revise e selecione Criar novo espaço de código
Aguarde até que o espaço de código inicie. Este processo de arranque pode demorar alguns minutos.
Entre no Azure com a CLI do Desenvolvedor do Azure no terminal na parte inferior da tela.
azd auth loginCopie o código do terminal e cole-o em um navegador. Siga as instruções para autenticar com sua conta do Azure.
As tarefas restantes neste artigo ocorrem no contexto desse contêiner de desenvolvimento.
Implantar e executar
O repositório de exemplo contém todos os arquivos de código e configuração para a implantação do aplicativo de chat do Azure. As etapas a seguir orientam você pelo processo de implantação do Azure do aplicativo de chat de exemplo.
Implantar o aplicativo de chat no Azure
Importante
Para manter os custos baixos, este exemplo usa níveis de preços básicos ou de consumo para a maioria dos recursos. Ajuste a camada conforme necessário e exclua recursos quando terminar para evitar cobranças.
Execute o seguinte comando da CLI do Desenvolvedor do Azure para provisionamento de recursos do Azure e implantação de código-fonte:
azd upUse a tabela a seguir para responder aos prompts:
Pedido Resposta Nome do ambiente Mantenha-o curto e minúsculo. Adicione o seu nome ou alias. Por exemplo, chat-vision. Ele é usado como parte do nome do grupo de recursos.Subscrição Selecione a assinatura na qual criar os recursos. Localização (para hospedagem) Selecione um local perto de você na lista. Local para o modelo OpenAI do Azure Selecione um local perto de você na lista. Se o mesmo local estiver disponível como seu primeiro local, selecione isso. Aguarde até que o aplicativo seja implantado. A implantação geralmente leva entre 5 e 10 minutos para ser concluída.
Use o aplicativo de bate-papo para fazer perguntas ao modelo de linguagem grande
O terminal exibe uma URL após a implantação bem-sucedida do aplicativo.
Selecione esse URL rotulado
Deploying service webpara abrir o aplicativo de bate-papo em um navegador.No navegador, carregue uma imagem clicando em Escolher arquivo e selecionando uma imagem.
Faça uma pergunta sobre a imagem carregada, como "O que é a imagem?".
A resposta vem do Azure OpenAI e o resultado é exibido.
Explorando o código de exemplo
Embora o OpenAI e o Serviço OpenAI do Azure dependam de uma biblioteca de cliente Python comum, pequenas alterações de código são necessárias ao usar pontos de extremidade do Azure OpenAI. Este exemplo usa um modelo multimodal do Azure OpenAI para gerar respostas a mensagens do usuário e imagens carregadas.
Base64 Codificando a imagem carregada no frontend
A imagem carregada precisa ser codificada em Base64 para que possa ser usada diretamente como um URI de dados como parte da mensagem.
No exemplo, o seguinte trecho de código de front-end na scripttag do arquivo manipula src/quartapp/templates/index.html essa funcionalidade. A toBase64 função de setareadAsDataURL usa o FileReader método do para ler assincronamente no arquivo de imagem carregado como uma cadeia de caracteres codificada em base64.
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
A toBase64 função é chamada por um ouvinte no evento do submit formulário.
O submit ouvinte de eventos lida com todo o fluxo de interação do chat. Quando o usuário envia uma mensagem, ocorre o seguinte fluxo:
- Oculta o elemento "no-messages-heading" para mostrar a conversa iniciada
- Obtém e Base64 codifica o arquivo de imagem carregado (se presente)
- Cria e exibe a mensagem do usuário no bate-papo, incluindo a imagem carregada
- Prepara uma caixa de mensagens do assistente com um indicador de "A escrever..."
- Adiciona a mensagem do usuário à matriz de histórico de mensagens
- Chama o método do AI Chat Protocol Client
getStreamedCompletion()com o histórico e o contexto da mensagem (incluindo a imagem codificada em Base64 e o nome do arquivo) - Processa os blocos de resposta transmitidos e converte Markdown em HTML usando Showdown.js
- Lida com quaisquer erros durante o streaming
- Adiciona um botão de saída de fala depois de receber a resposta completa para que os usuários possam ouvir a resposta
- Limpa o campo de entrada e retorna o foco para a próxima mensagem
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;
}
});
Manipulando a imagem com o back-end
No arquivo, o código de back-end para manipulação de imagem é iniciado após a configuração da src\quartapp\chat.py autenticação sem chave.
Nota
Para obter mais informações sobre como usar conexões sem chave para autenticação e autorização para o Azure OpenAI, confira o artigo Introdução ao bloco de construção de segurança do Azure OpenAI Microsoft Learn.
Configuração de autenticação
A configure_openai() função configura o cliente OpenAI antes que o aplicativo comece a atender solicitações. Ele usa o decorador do @bp.before_app_serving Quart para configurar a autenticação com base em variáveis de ambiente. Este sistema flexível permite que os desenvolvedores trabalhem em diferentes contextos sem alterar o código.
Modos de autenticação explicados
-
Desenvolvimento local (
OPENAI_HOST=local): Conecta-se a um serviço de API local compatível com OpenAI (como Ollama ou LocalAI) sem autenticação. Use este modo para testar sem custos de internet ou API. -
Modelos do GitHub (
OPENAI_HOST=github): Usa o mercado de modelos de IA do GitHub com umGITHUB_TOKENpara autenticação. Ao usar modelos do GitHub, prefixe o nome do modelo comopenai/(por exemplo,openai/gpt-4o). Esse modo permite que os desenvolvedores experimentem modelos antes de provisionar recursos do Azure. -
Azure OpenAI com chave de API (
AZURE_OPENAI_KEY_FOR_CHATVISIONvariável de ambiente): usa uma chave de API para autenticação. Evite esse modo de produção porque as chaves de API exigem rotação manual e representam riscos de segurança se expostas. Use-o para testes locais dentro de um contêiner do Docker sem credenciais da CLI do Azure. -
Produção com Identidade Gerenciada (
RUNNING_IN_PRODUCTION=true): UsaManagedIdentityCredentialpara autenticar com o Azure OpenAI por meio da identidade gerenciada do aplicativo de contêiner. Este método é recomendado para produção porque elimina a necessidade de gerir segredos. Os Aplicativos de Contêiner do Azure fornecem automaticamente a identidade gerenciada e concedem permissões durante a implantação via Bicep. -
Desenvolvimento com a CLI do Azure (modo padrão): usa
AzureDeveloperCliCredentialpara autenticar com o Azure OpenAI usando credenciais da CLI do Azure conectadas localmente. Esse modo simplifica o desenvolvimento local sem gerenciar chaves de API.
Principais detalhes da implementação
- A
get_bearer_token_provider()função atualiza as credenciais do Azure e as usa como tokens de portador. - O caminho do ponto de extremidade do Azure OpenAI inclui
/openai/v1/para corresponder aos requisitos da biblioteca de cliente OpenAI. - O registro em log mostra qual modo de autenticação está ativo.
- A função é assíncrona para dar suporte a operações de credenciais do Azure.
Aqui está o código de configuração de autenticação completo de 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)
Função de manipulador de bate-papo
A chat_handler() função processa solicitações de chat enviadas para o /chat/stream endpoint. Ele recebe uma solicitação POST com uma carga JSON que segue o Microsoft AI Chat Protocol.
A carga útil JSON inclui:
-
mensagens: uma lista do histórico de conversas. Cada mensagem tem um
role("usuário" ou "assistente") econtent(o texto da mensagem). -
contexto: Dados adicionais para processamento, incluindo:
-
arquivo: dados de imagem codificados em Base64 (por exemplo,
data:image/png;base64,...). - file_name: O nome do ficheiro original da imagem carregada (útil para registar ou identificar o tipo de imagem).
-
arquivo: dados de imagem codificados em Base64 (por exemplo,
- temperatura (opcional): um flutuador que controla a aleatoriedade da resposta (o padrão é 0,5).
O manipulador extrai o histórico de mensagens e os dados da imagem. Se nenhuma imagem for carregada, o valor da imagem será null, e o código lida com esse 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"]
# The context also includes the filename for reference
# file_name = request_json["context"]["file_name"]
Criando a matriz de mensagens para solicitações de visão
A response_stream() função prepara a matriz de mensagens que é enviada para a API OpenAI do Azure. O @stream_with_context decorador mantém o contexto do pedido enquanto transmite a resposta.
Lógica de preparação de mensagens
-
Comece com o histórico de conversas: A função começa com
all_messages, que inclui uma mensagem do sistema e todas as mensagens anteriores, exceto a mais recente (request_messages[0:-1]). -
Manipule a mensagem do usuário atual com base na presença da imagem:
-
Com imagem: formate a mensagem do usuário como uma matriz de conteúdo de várias partes com texto e objetos image_url. O
image_urlobjeto contém os dados de imagem codificados em Base64 e umdetailparâmetro. - Sem imagem: anexe a mensagem do usuário como texto sem formatação.
-
Com imagem: formate a mensagem do usuário como uma matriz de conteúdo de várias partes com texto e objetos image_url. O
-
O
detailparâmetro: Defina como "auto" para permitir que o modelo escolha entre detalhes "baixos" e "altos" com base no tamanho da imagem. O baixo detalhe é mais rápido e barato, enquanto o alto detalhe fornece uma análise mais precisa para imagens complexas.
@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])
Nota
Para obter mais informações sobre o parâmetro de imagem detail e configurações relacionadas, consulte a seção Configurações de parâmetros detalhados no artigo "Usar modelos de bate-papo habilitados para visão" do Microsoft Learn.
Em seguida, bp.openai_client.chat.completions obtém a conclusão do chat por meio de uma chamada de API do Azure OpenAI e transmite a resposta.
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),
)
Finalmente, a resposta é transmitida de volta para o cliente, com tratamento de erros para quaisquer exceções.
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())
Bibliotecas e recursos de front-end
O frontend usa APIs e bibliotecas modernas do navegador para criar uma experiência de bate-papo interativa. Os desenvolvedores podem personalizar a interface ou adicionar recursos entendendo estes componentes:
Entrada/saída de fala: os componentes da Web personalizados usam as APIs de fala do navegador:
<speech-input-button>: Converte fala em texto usando as APIs de Fala da Web.SpeechRecognitionEle fornece um botão de microfone que escuta a entrada de voz e emite umspeech-input-resultevento com o texto transcrito.<speech-output-button>: Lê texto em voz alta usando aSpeechSynthesisAPI. Ele aparece após cada resposta do assistente com um ícone de alto-falante, permitindo que os usuários ouçam a resposta.
Por que usar APIs de navegador em vez dos Serviços de Fala do Azure?
- Sem custos - funciona inteiramente no navegador
- Resposta instantânea - sem latência de rede
- Privacidade - os dados de voz permanecem no dispositivo do utilizador
- Não há necessidade de recursos extras do Azure
Esses componentes estão em
src/quartapp/static/speech-input.jsespeech-output.js.Visualização de imagem: Exibe a imagem carregada no chat antes do envio da análise para confirmação. A visualização é atualizada automaticamente quando um arquivo é selecionado.
fileInput.addEventListener("change", async function() { const file = fileInput.files[0]; if (file) { const fileData = await toBase64(file); imagePreview.src = fileData; imagePreview.style.display = "block"; } });Ícones de Bootstrap 5 e Bootstrap: Fornece componentes e ícones de interface do usuário responsivos. O aplicativo usa o tema Cosmo do Bootswatch para um visual moderno.
Renderização de mensagens baseada em modelo: usa elementos HTML
<template>para layouts de mensagens reutilizáveis, garantindo estilo e estrutura consistentes.
Outros exemplos de recursos a explorar
Além do exemplo de aplicativo de bate-papo, há outros recursos no repositório para explorar para aprendizado adicional. Confira os seguintes blocos de anotações no notebooks diretório:
| Bloco de Notas | Description |
|---|---|
| chat_pdf_images.ipynb | Este bloco de anotações demonstra como converter páginas PDF em imagens e enviá-las para um modelo de visão para inferência. |
| chat_vision.ipynb | Este notebook é fornecido para experimentação manual com o modelo de visão usado no aplicativo. |
Conteúdo Localizado: As versões em espanhol dos notebooks estão no diretório notebooks/Spanish/, oferecendo o mesmo aprendizado prático para desenvolvedores hispanofalantes. Os cadernos em inglês e espanhol mostram:
- Como chamar modelos de visão diretamente para experimentação
- Como converter páginas PDF em imagens para análise
- Como ajustar parâmetros e prompts de teste
Clean up resources (Limpar recursos)
Limpar recursos do Azure
Os recursos do Azure criados neste artigo são cobrados na sua assinatura do Azure. Se você não espera precisar desses recursos no futuro, exclua-os para evitar incorrer em mais cobranças.
Para excluir os recursos do Azure e remover o código-fonte, execute o seguinte comando da CLI do Desenvolvedor do Azure:
azd down --purge
Limpar espaços de código do GitHub
Excluir o ambiente do GitHub Codespaces garante que você possa maximizar a quantidade de direitos de horas gratuitas por núcleo que você obtém para sua conta.
Importante
Para obter mais informações sobre os direitos da sua conta do GitHub, consulte Codespaces do GitHub mensalmente incluídos armazenamento e horas principais.
Faça login no painel do GitHub Codespaces.
Localize seus Codespaces atualmente em execução provenientes do
Azure-Samples//openai-chat-vision-quickstartrepositório GitHub.Abra o menu de contexto do espaço de código e selecione Excluir.
Obter ajuda
Registre seu problema nos problemas do repositório.