Condividi tramite


Sviluppare app di ragionamento con modelli DeepSeek in Microsoft Foundry usando OpenAI SDK

Informazioni su come usare modelli di ragionamento come DeepSeek in Azure OpenAI con OpenAI SDK per Python.

Questo articolo illustra diverse procedure consigliate per l'integrazione dei modelli di ragionamento:

  • Autenticazione senza chiave: usare identità gestite o credenziali per sviluppatori anziché chiavi API.
  • Operazioni asincrone: usare le funzionalità asincrone per migliorare le prestazioni.
  • Risposte in streaming: fornire feedback immediato agli utenti.
  • Separazione dei motivi: separare i passaggi di ragionamento dall'output finale.
  • Gestione risorse: pulire le risorse dopo l'uso.

Il blocco di costruzione DeepSeek

Esplora l'esempio del blocco di costruzione DeepSeek. Illustra come usare la libreria client OpenAI per chiamare il modello di DeepSeek-R1 e generare risposte ai messaggi utente.

Panoramica dell'architettura

Il diagramma seguente illustra la semplice architettura dell'app di esempio: Diagramma che mostra l'architettura dal client all'app back-end.

L'app di chat viene eseguita come un'app contenitore di Azure. L'app usa l'identità gestita con Microsoft Entra ID per l'autenticazione con Azure OpenAI anziché con una chiave API. L'app usa Azure OpenAI per generare risposte ai messaggi utente.

L'app si basa su questi servizi e componenti:

  • Un'app Python Quart che usa la libreria client OpenAI per generare risposte ai messaggi utente
  • Front-end HTML/JS di base che trasmette le risposte dal back-end usando righe JSON su un flusso leggibile
  • File Bicep per il provisioning di risorse Azure, inclusi strumenti Foundry, Azure Container Apps, Azure Container Registry, Azure Log Analytics e ruoli RBAC.

Costo

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.

Per ulteriori informazioni sul costo nello repository di esempio.

Prerequisiti

Un contenitore di sviluppo include tutte le dipendenze necessarie per questo articolo. È possibile eseguirlo in GitHub Codespaces (in un browser) o in locale usando Visual Studio Code.

Per seguire questo articolo, assicurarsi di soddisfare questi prerequisiti:

  • Una sottoscrizione Azure: Crearne una gratuitamente
  • Azure autorizzazioni dell'account: l'account Azure deve avere autorizzazioni Microsoft.Authorization/roleAssignments/write, ad esempio Role Based Access Control Administrator, User Access Administrator o Owner. Se non si dispone delle autorizzazioni a livello di sottoscrizione, è necessario ricevere RBAC per un gruppo di risorse esistente e distribuire le risorse in tale gruppo.
    • L'account Azure richiede anche autorizzazioni Microsoft.Resources/deployments/write a livello di sottoscrizione.
  • account su GitHub

Ambiente di sviluppo aperto

Seguire questa procedura per configurare un ambiente di sviluppo preconfigurato con tutte le dipendenze necessarie.

GitHub Codespaces esegue un contenitore di sviluppo gestito da GitHub con Visual Studio Code per web come interfaccia. Usare GitHub Codespaces per la configurazione più semplice, perché include gli strumenti e le dipendenze necessari preinstallati per questo articolo.

Importante

Tutti gli account GitHub possono usare Codespaces per un massimo di 60 ore gratuite ogni mese con due istanze principali. Per altre informazioni, vedere l'archiviazione e le ore di base mensili incluse di GitHub Codespaces.

Usare la procedura seguente per creare un nuovo GitHub Codespace nel ramo main del repository Azure-Samples/deepseek-python GitHub.

  1. Fare clic con il pulsante destro del mouse sul pulsante seguente e scegliere Apri collegamento nella nuova finestra. Questa azione consente di avere l'ambiente di sviluppo e la documentazione aperta side-by-side.

    Apri in GitHub Codespaces

  2. Nella pagina Crea spazio codici esaminare e quindi selezionare Crea nuovo spazio di codice

  3. Attendere l'avvio dello spazio di codice. Potrebbe richiedere alcuni minuti.

  4. Accedi ad Azure con l'interfaccia della riga di comando Azure Developer nel terminale nella parte inferiore dello schermo.

    azd auth login
    
  5. Copiare il codice dal terminale e incollarlo in un browser. Seguire le istruzioni per eseguire l'autenticazione con l'account Azure.

Si eseguono le altre attività in questo contenitore di sviluppo.

Distribuire ed eseguire

Il repository di esempio include tutti i file di codice e configurazione necessari per distribuire l'app di chat in Azure. Seguire questa procedura per distribuire l'app di chat in Azure.

Distribuire l'applicazione di chat su Azure

Importante

Le risorse di Azure create in questa sezione iniziano a generare costi immediatamente. Queste risorse potrebbero comunque comportare costi anche se si arresta il comando prima del completamento.

  1. Eseguire il seguente comando della CLI sviluppatore di Azure per il provisioning delle risorse di Azure e la distribuzione del codice sorgente.

    azd up
    
  2. Usare la tabella seguente per rispondere alle richieste:

    Rapido Risposta
    Nome dell'ambiente Tienila breve e minuscola. Aggiungere il nome o l'alias. Ad esempio: chat-app. Viene usato come parte del nome del gruppo di risorse.
    Abbonamento Selezionare la sottoscrizione in cui creare le risorse.
    Località (per l'hosting) Seleziona una località vicino a te dall'elenco.
    Posizione del modello DeepSeek Seleziona una località vicino a te dall'elenco. Se la stessa località è disponibile come prima località, selezionare questa posizione.
  3. Attendere che l'app venga distribuita. La distribuzione richiede in genere da 5 a 10 minuti.

Usare l'app chat per porre domande al modello linguistico di grandi dimensioni

  1. Dopo la distribuzione, il terminale visualizza un URL.

  2. Selezionare l'URL etichettato Deploying service web per aprire l'app di chat nel browser.

    Screenshot dell'app chat nel browser con una domanda nella casella di testo della chat insieme alla risposta.

  3. Nel browser, porre una domanda sull'immagine caricata, ad esempio "Chi ha dipinto la Mona Lisa?"

  4. Azure OpenAI fornisce la risposta tramite l'inferenza del modello e il risultato viene visualizzato nell'app.

Esplorazione del codice di esempio

OpenAI e Azure OpenAI Service usano entrambi la comune libreria client Python, ma è necessario apportare alcune lievi modifiche al codice per gli endpoint di Azure OpenAI. Questo esempio usa un modello di ragionamento DeepSeek-R1 per generare risposte in una semplice app di chat.

Installazione e autenticazione

Il src\quartapp\chat.py file inizia con l'installazione e la configurazione dell'autenticazione senza chiave.

Configurazione dell'infrastruttura

Lo script usa Quart, un framework Web asincrono, per creare un Blueprint oggetto denominato chat. Questo Blueprint definisce le route dell'app e gestisce i relativi hook del ciclo di vita.

bp = Blueprint("chat", __name__, template_folder="templates", static_folder="static")

Definisce le route Blueprint e / e gli hook del ciclo di vita /chat/stream e @bp.before_app_serving.

Inizializzazione con autenticazione senza chiave

Il frammento di codice seguente gestisce l'autenticazione.

Annotazioni

L'hook @bp.before_app_serving inizializza il client OpenAI e gestisce l'autenticazione. Questo approccio è fondamentale per accedere in modo sicuro ai modelli di DeepSeek-R1 ospitati Azure.

La strategia di autenticazione si adatta all'ambiente:

  • In production: usa Managed Identity Credential con un ID client Azure per evitare di archiviare chiavi sensibili. Questo metodo è sicuro e scalabile per le app native del cloud.
  • In development: usa Azure Developer CLI Credential con un ID tenant Azure per semplificare i test locali usando la sessione di accesso Azure CLI dello sviluppatore.
@bp.before_app_serving
async def configure_openai():
    if os.getenv("RUNNING_IN_PRODUCTION"):
        client_id = os.environ["AZURE_CLIENT_ID"]
        bp.azure_credential = ManagedIdentityCredential(client_id=client_id)
    else:
        tenant_id = os.environ["AZURE_TENANT_ID"]
        bp.azure_credential = AzureDeveloperCliCredential(tenant_id=tenant_id)

Questo approccio di autenticazione senza chiave offre:

  • Maggiore sicurezza: nessuna chiave API archiviata nel codice o nelle variabili di ambiente.
  • Gestione più semplice: non è necessario ruotare le chiavi o gestire i segreti.
  • Transizioni uniformi: lo stesso codice funziona sia nello sviluppo che nell'ambiente di produzione.

Configurazione del provider di token

Nel frammento di codice seguente il provider di token crea un token di connessione per autenticare le richieste ai servizi OpenAI Azure. Genera e aggiorna automaticamente questi token usando le credenziali configurate.

bp.openai_token_provider = get_bearer_token_provider(
    bp.azure_credential, "https://cognitiveservices.azure.com/.default"
)

Configurazione del client Azure OpenAI

Esistono due possibili client, AzureOpenAI e AsyncAzureOpenAI. Il frammento di codice seguente usa AsyncAzureOpenAI insieme al framework asincrono Quart per migliorare le prestazioni con gli utenti simultanei:

bp.openai_client = AsyncAzureOpenAI(
    azure_endpoint=os.environ["AZURE_INFERENCE_ENDPOINT"],
    azure_ad_token_provider=openai_token_provider,
    api_version="2024-10-21",
  • base_url: si riferisce all'endpoint di inferenza DeepSeek ospitato su Azure
  • api_key: usa una chiave API generata dinamicamente dal provider di token.
  • api-version: specifica la versione dell'API che supporta i modelli DeepSeek

Configurazione del nome della distribuzione del modello

Il frammento di codice seguente imposta la versione del modello DeepSeek recuperando il nome della distribuzione dalla configurazione dell'ambiente. Assegna il nome alla bp.model_deployment_name variabile, rendendolo accessibile in tutta l'app. Questo approccio consente di modificare la distribuzione del modello senza aggiornare il codice.

bp.model_deployment_name = os.getenv("AZURE_DEEPSEEK_DEPLOYMENT")

Annotazioni

In Azure OpenAI non si usano direttamente nomi di modello come gpt-4o o deepseek-r1. È invece possibile creare deployments, ovvero istanze denominate di modelli nella risorsa Azure OpenAI. Questo approccio offre i vantaggi seguenti:

  • Astrazione: mantiene i nomi di distribuzione fuori dal codice usando le variabili di ambiente.
  • Flessibilità: consente di passare da distribuzioni DeepSeek diverse senza modificare il codice.
  • Configurazione specifica dell'ambiente: consente l'uso di distribuzioni diverse per sviluppo, test e produzione.
  • Gestione delle risorse: ogni distribuzione Azure ha una propria quota, limitazione e monitoraggio.

Gestione del ciclo di vita

Il frammento di codice seguente impedisce perdite di risorse chiudendo la Azure client OpenAI asincrona quando l'applicazione viene arrestata. L'hook @bp.after_app_serving garantisce una corretta pulizia delle risorse.

@bp.after_app_serving
async def shutdown_openai():
    await bp.openai_client.close()

Funzione di streaming del gestore chat

La chat_handler() funzione gestisce le interazioni utente con il DeepSeek-R1 modello attraverso la chat/stream route. Trasmette le risposte al client in tempo reale ed elabora le risposte. La funzione estrae messaggi dal payload JSON.

Implementazione di streaming

  1. La response_stream funzione inizia accettando messaggi dal client.

    • request_messages: la route prevede un payload JSON contenente i messaggi utente.
    @bp.post("/chat/stream")
    async def chat_handler():
       request_messages = (await request.get_json())["messages"]
    
  2. Successivamente, la funzione trasmette le risposte dall'API OpenAI. Combina messaggi di sistema come "Sei un assistente utile" con i messaggi forniti dall'utente.

    @stream_with_context
    async def response_stream():
        all_messages = [
            {"role": "system", "content": "You are a helpful assistant."},
        ] + request_messages
    
  3. Successivamente, la funzione crea una richiesta di completamento della chat in streaming.

    Il chat.completions.create metodo invia i messaggi al DeepSeek-R1 modello. Il stream=True parametro abilita lo streaming delle risposte in tempo reale.

      chat_coroutine = bp.openai_client.chat.completions.create(
          model=bp.openai_model,
          messages=all_messages,
          stream=True,
      )
    
  4. Il frammento di codice seguente elabora le risposte in streaming dal DeepSeek-R1 modello e gestisce gli errori. Scorre gli aggiornamenti, verifica la disponibilità di scelte valide e invia ogni blocco di risposta come righe JSON. Se si verifica un errore, registra l'errore e invia un messaggio di errore JSON al client durante la continuazione del flusso.

    try:
        async for update in await chat_coroutine:
            if update.choices:
                yield update.choices[0].model_dump_json() + "\n"
        except Exception as e:
            current_app.logger.error(e)
            yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
    
    return Response(response_stream())
    

Ragionamento della gestione dei contenuti

Mentre i modelli linguistici tradizionali forniscono solo output finali, i modelli di ragionamento come DeepSeek-R1 mostrano i passaggi intermedi di ragionamento. Questi passaggi li rendono utili per:

  • Risoluzione di problemi complessi
  • Esecuzione di calcoli matematici
  • Gestione del ragionamento logico in più passaggi
  • Prendere decisioni trasparenti

Il submit gestore eventi in index.html elabora la risposta di streaming nel front-end. Questo approccio consente di accedere e visualizzare i passaggi di ragionamento del modello insieme all'output finale.

Il frontend utilizza un ReadableStream per elaborare le risposte in streaming dal backend. Separa il contenuto di ragionamento dal contenuto normale, mostrando il ragionamento in una sezione espandibile e la risposta finale nell'area principale della chat.

Suddivisione dettagliata

  1. Avviare la richiesta di streaming

    Questo frammento di codice crea una connessione tra il front-end JavaScript e il back-end Python, abilitando l'integrazione di DeepSeek-R1 Azure OpenAI con l'autenticazione senza chiave.

    const response = await fetch("/chat/stream", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({messages: messages})
    });
    
  2. Inizializzare le variabili

    Il frammento di codice seguente inizializza le variabili per archiviare separatamente le risposte e i pensieri. Questa separazione consente di gestire in modo efficace il contenuto di ragionamento.

    let answer = "";
    let thoughts = "";    
    
  3. Elaborare ogni aggiornamento

    Il frammento di codice seguente itera in modo asincrono su porzioni della risposta del modello.

    for await (const event of readNDJSONStream(response.body)) {
    
  4. Identificare e indirizzare il tipo di contenuto

    Lo script controlla se l'evento contiene un delta campo. Se sì, elabora il contenuto in base al fatto che sia contenuto di ragionamento o contenuto normale.

    if (!event.delta) {
         continue;
    }
    if (event.delta.reasoning_content) {
         thoughts += event.delta.reasoning_content;
         if (thoughts.trim().length > 0) {
             // Only show thoughts if they are more than just whitespace
             messageDiv.querySelector(".loading-bar").style.display = "none";
             messageDiv.querySelector(".thoughts").style.display = "block";
             messageDiv.querySelector(".thoughts-content").innerHTML = converter.makeHtml(thoughts);
         }
     } else if (event.delta.content) {
         messageDiv.querySelector(".loading-bar").style.display = "none";
         answer += event.delta.content;
         messageDiv.querySelector(".answer-content").innerHTML = converter.makeHtml(answer);
     }
    
    • Se il tipo di contenuto è reasoning_content, il contenuto viene aggiunto a thoughts e visualizzato nella .thoughts-content sezione .
    • Se il tipo di contenuto è content, il contenuto viene aggiunto a answer e visualizzato nella .answer-content sezione .
    • L'oggetto .loading-bar viene nascosto una volta che il contenuto inizia a essere trasmesso in streaming, e la sezione .thoughts viene visualizzata se ci sono pensieri.
  5. Gestione degli errori:

    Gli errori vengono registrati nel back-end e restituiti al client in formato JSON.

    except Exception as e:
        current_app.logger.error(e)
        yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
    

    Questo frammento di codice front-end visualizza il messaggio di errore nell'interfaccia della chat.

    messageDiv.scrollIntoView();
    if (event.error) {
        messageDiv.innerHTML = "Error: " + event.error;
    }
    

Pulire GitHub Codespaces

Per ottimizzare le ore gratuite per singolo core, elimina l'ambiente Codespaces di GitHub.

Importante

Per ulteriori informazioni sull'archiviazione gratuita e sulle ore core incluse nel tuo account GitHub, consulta Archiviazione e ore core mensili incluse in GitHub Codespaces.

  1. Accedere al dashboard GitHub Codespaces.

  2. Trova il tuo spazio di codice attivo creato dal repository Azure-Samples//deepseek-python GitHub.

  3. Aprire il menu di scelta rapida per lo spazio di codice e selezionare Elimina.

Ottenere assistenza

Registrare il tuo problema nel repository Issues.

Passaggi successivi