Partager via


Développer des applications de raisonnement avec des modèles DeepSeek sur Azure AI Foundry à l’aide du Kit de développement logiciel (SDK) OpenAI

Découvrez comment utiliser des modèles de raisonnement comme DeepSeek dans Azure OpenAI avec le Kit de développement logiciel (SDK) OpenAI pour Python.

Cet article présente plusieurs bonnes pratiques pour intégrer des modèles de raisonnement :

  • Authentification sans clé : utilisez des identités managées ou des informations d’identification de développeur au lieu de clés API.
  • Opérations asynchrones : utilisez des fonctionnalités asynchrones pour améliorer les performances.
  • Réponses de diffusion en continu : fournissez des commentaires immédiats aux utilisateurs.
  • Séparation du raisonnement : séparez les étapes de raisonnement de la sortie finale.
  • Gestion des ressources : nettoyer les ressources après l’utilisation.

Le bloc de construction DeepSeek

Explorez l’exemple de bloc de construction DeepSeek. Il montre comment utiliser la bibliothèque cliente OpenAI pour appeler le modèle DeepSeek-R1 et générer des réponses aux messages utilisateur.

Vue d’ensemble de l’architecture

Le diagramme suivant montre l’architecture simple de l’exemple d’application : diagramme montrant l’architecture du client vers l’application back-end.

L’application de conversation s’exécute en tant qu’application de conteneur Azure. L’application utilise une identité managée avec l’ID Microsoft Entra pour s’authentifier auprès d’Azure OpenAI au lieu d’une clé API. L’application utilise Azure OpenAI pour générer des réponses aux messages utilisateur.

L’application s’appuie sur ces services et composants :

  • Application Python Quart qui utilise le package de bibliothèque de client OpenAI pour générer des réponses aux messages utilisateur
  • Frontend HTML/JS de base qui diffuse des réponses du serveur principal à l’aide de lignes JSON sur un readableStream
  • Fichiers Bicep pour l’approvisionnement de ressources Azure, notamment Azure AI Services, Azure Container Apps, Azure Container Registry, Azure Log Analytics et rôles RBAC.

Coûts

Pour réduire les coûts, cet exemple utilise des niveaux tarifaires de base ou de consommation pour la plupart des ressources. Ajustez le niveau en fonction des besoins, puis supprimez les ressources une fois terminé pour éviter des frais.

En savoir plus sur les coûts dans l’exemple de dépôt.

Conditions préalables

Un conteneur de développement inclut toutes les dépendances dont vous avez besoin pour cet article. Vous pouvez l’exécuter dans GitHub Codespaces (dans un navigateur) ou localement à l’aide de Visual Studio Code.

Pour suivre cet article, vérifiez que vous remplissez les conditions préalables suivantes :

Environnement de développement ouvert

Suivez ces étapes pour configurer un environnement de développement préconfiguré avec toutes les dépendances requises.

GitHub Codespaces exécute un conteneur de développement géré par GitHub avec Visual Studio Code pour le web comme interface. Utilisez GitHub Codespaces pour la configuration la plus simple, car il est fourni avec les outils et dépendances nécessaires préinstallés pour cet article.

Importante

Tous les comptes GitHub peuvent utiliser Codespaces jusqu'à 60 heures gratuitement chaque mois avec deux instances principales. Pour plus d'informations, consultez le stockage mensuel inclus et les heures de base de GitHub Codespaces.

Procédez comme suit pour créer un nouveau codespace GitHub sur la branche main du référentiel GitHub Azure-Samples/deepseek-python.

  1. Cliquez avec le bouton droit sur le bouton suivant et sélectionnez Ouvrir le lien dans la nouvelle fenêtre. Cette action vous permet d’avoir l’environnement de développement et la documentation ouverte côte à côte.

    Ouvrir dans un GitHub Codespaces

  2. Sur la page Créer un codespace (Create codespace), consultez les informations, puis sélectionnez Créer un nouveau codespace.

  3. Attendez que le codespace démarre. Cela peut prendre quelques minutes.

  4. Connectez-vous à Azure avec le CLI Azure Developer dans le terminal en bas de l’écran.

    azd auth login
    
  5. Copiez le code à partir du terminal, puis collez-le dans un navigateur. Suivez les instructions pour vous authentifier avec votre compte Azure.

Vous effectuez le reste des tâches dans ce conteneur de développement.

Déployer et exécuter

L’exemple de référentiel contient tous les fichiers de code et de configuration dont vous avez besoin pour déployer l’application de conversation sur Azure. Suivez ces étapes pour déployer l’application de conversation sur Azure.

Déployer une application de conversation sur Azure

Importante

Les ressources Azure créées dans cette section commencent à coûter de l’argent immédiatement. Ces ressources peuvent encore entraîner des coûts même si vous arrêtez la commande avant sa fin.

  1. Exécutez la commande CLI Azure Developer suivante pour provisionner les ressources Azure et déployer le code source :

    azd up
    
  2. Utilisez le tableau suivant pour répondre aux invites :

    Prompt Réponse
    Nom de l’environnement Gardez-le court et en minuscules. Ajoutez votre nom ou votre pseudo. Par exemple : chat-app. Il est utilisé comme partie du nom du groupe de ressources.
    Abonnement Sélectionnez l’abonnement pour créer les ressources.
    Emplacement (pour l’hébergement) Sélectionnez un emplacement près de chez vous dans la liste.
    Emplacement du modèle DeepSeek Sélectionnez un emplacement près de chez vous dans la liste. Si le premier emplacement choisi est également disponible, sélectionnez-le.
  3. Attendez que l’application se déploie. Le déploiement prend généralement 5 à 10 minutes.

Utiliser l'application de conversation pour poser des questions au modèle de langage volumineux

  1. Après le déploiement, le terminal affiche une URL.

  2. Sélectionnez l’URL étiquetée Deploying service web pour ouvrir l’application de conversation dans votre navigateur.

    Capture d’écran de l’application de conversation dans le navigateur avec une question dans la zone de texte de conversation, ainsi que la réponse.

  3. Dans le navigateur, posez une question sur l’image chargée, telle que « Qui a peint la Mona Lisa ? »

  4. Azure OpenAI fournit la réponse par le biais de l’inférence de modèle, et le résultat s’affiche dans l’application.

Explorer l’exemple de code

OpenAI et Le service Azure OpenAI utilisent toutes deux la bibliothèque de client Python commune, mais vous devez apporter quelques petites modifications de code pour les points de terminaison Azure OpenAI. Cet exemple utilise un modèle de raisonnement DeepSeek-R1 pour générer des réponses dans une application de conversation simple.

Configuration et authentification

Le src\quartapp\chat.py fichier commence par l’installation et la configuration de l’authentification sans clé.

Configuration de l’infrastructure

Le script utilise Quart, une infrastructure web asynchrone, pour créer un Blueprint nom chat. Cela Blueprint définit les itinéraires de l’application et gère ses hooks de cycle de vie.

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

Le Blueprint définit les itinéraires / et /chat/stream, ainsi que les hooks de cycle de vie @bp.before_app_serving et @bp.after_app_serving.

Initialisation avec l’authentification sans clé

L’extrait de code suivant gère l’authentification.

Remarque

Le @bp.before_app_serving hook initialise le client OpenAI et gère l’authentification. Cette approche est essentielle pour accéder en toute sécurité aux modèles DeepSeek-R1 hébergés par Azure.

La stratégie d’authentification s’adapte à l’environnement :

  • En production : utilise les informations d’identification de l’identité managée avec un ID client Azure pour éviter de stocker des clés sensibles. Cette méthode est sécurisée et évolutive pour les applications natives cloud.
  • En développement : utilise les informations d'identification de l'Azure Developer CLI avec un ID de locataire Azure pour simplifier les tests locaux en utilisant la session de connexion Azure CLI du développeur.
@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)

Cette approche d’authentification sans clé fournit les éléments suivants :

  • Meilleure sécurité : aucune clé API stockée dans du code ou des variables d’environnement.
  • Gestion plus facile : il n’est pas nécessaire de faire pivoter des clés ou de gérer les secrets.
  • Transitions lisses : le même code fonctionne à la fois dans le développement et la production.

Configuration du fournisseur de jetons

Dans l’extrait de code suivant, le fournisseur de jetons crée un jeton du porteur pour authentifier les demandes auprès des services Azure OpenAI. Il génère et actualise automatiquement ces jetons à l’aide des informations d’identification configurées.

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

Configuration du client Azure OpenAI

Il existe deux clients possibles, AzureOpenAI et AsyncAzureOpenAI. L’extrait de code suivant utilise AsyncAzureOpenAI avec l’infrastructure asynchrone Quart pour améliorer les performances avec les utilisateurs simultanés :

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 : pointe vers le point de terminaison d’inférence DeepSeek hébergé par Azure
  • api_key : utilise une clé API générée dynamiquement à partir du fournisseur de jetons.
  • api-version : spécifie la version de l’API prenant en charge les modèles DeepSeek

Configuration du nom de déploiement du modèle

L’extrait de code suivant définit la version du modèle DeepSeek en obtenant le nom du déploiement à partir de la configuration de votre environnement. Il affecte le nom à la bp.model_deployment_name variable, ce qui le rend accessible dans l’application. Cette approche vous permet de modifier le déploiement du modèle sans mettre à jour le code.

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

Remarque

Dans Azure OpenAI, vous n’utilisez pas directement de noms de modèle comme gpt-4o ou deepseek-r1. Au lieu de cela, vous créez des déploiements, qui sont des instances nommées de modèles dans votre ressource Azure OpenAI. Cette approche offre les avantages suivants :

  • Abstraction : conserve les noms de déploiement hors du code à l’aide de variables d’environnement.
  • Flexibilité : vous permet de basculer entre différents déploiements DeepSeek sans modifier le code.
  • Configuration spécifique à l’environnement : permet d’utiliser différents déploiements pour le développement, les tests et la production.
  • Gestion des ressources : chaque déploiement Azure a son propre quota, limitation et supervision.

Gestion du cycle de vie

L’extrait de code suivant empêche les fuites de ressources en fermant le client Azure OpenAI asynchrone lorsque l’application s’arrête. Le hook @bp.after_app_serving garantit le nettoyage approprié des ressources.

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

Fonction de diffusion en continu du gestionnaire de conversation

La chat_handler() fonction gère les interactions utilisateur avec le DeepSeek-R1 modèle via l’itinéraire chat/stream . Il diffuse les réponses au client en temps réel et les traite. La fonction extrait les messages de la charge utile JSON.

Implémentation de streaming

  1. La response_stream fonction commence par accepter les messages du client.

    • request_messages : l’itinéraire attend une charge utile JSON contenant des messages utilisateur.
    @bp.post("/chat/stream")
    async def chat_handler():
       request_messages = (await request.get_json())["messages"]
    
  2. Ensuite, la fonction diffuse les réponses de l’API OpenAI. Il combine les messages système comme « Vous êtes un assistant utile » avec des messages fournis par l’utilisateur.

    @stream_with_context
    async def response_stream():
        all_messages = [
            {"role": "system", "content": "You are a helpful assistant."},
        ] + request_messages
    
  3. Ensuite, la fonction crée une demande de complétion de conversation en streaming.

    La chat.completions.create méthode envoie les messages au DeepSeek-R1 modèle. Le stream=True paramètre active le streaming de réponse en temps réel.

      chat_coroutine = bp.openai_client.chat.completions.create(
          model=bp.openai_model,
          messages=all_messages,
          stream=True,
      )
    
  4. L’extrait de code suivant traite les réponses de streaming à partir du DeepSeek-R1 modèle et gère les erreurs. Il effectue une itération via les mises à jour, vérifie les choix valides et envoie chaque bloc de réponse en tant que lignes JSON. Si une erreur se produit, elle enregistre l’erreur et envoie un message d’erreur JSON au client tout en continuant le flux.

    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())
    

Gestion du contenu de raisonnement

Bien que les modèles de langage traditionnels fournissent uniquement des sorties finales, les modèles de raisonnement comme DeepSeek-R1 montrent leurs étapes intermédiaires de raisonnement. Ces étapes les rendent utiles pour :

  • Résolution de problèmes complexes
  • Exécution de calculs mathématiques
  • Gestion du raisonnement logique en plusieurs étapes
  • Prise de décisions transparentes

Le gestionnaire d’événements submit dans index.html traite la réponse de diffusion en continu sur le front-end. Cette approche vous permet d’accéder aux étapes de raisonnement du modèle et de les afficher en même temps que la sortie finale.

Le serveur frontal utilise un ReadableStream pour traiter les réponses de streaming à partir du serveur principal. Il sépare le contenu du raisonnement du contenu normal, montrant le raisonnement dans une section extensible et la réponse finale dans la zone de conversation principale.

Répartition pas à pas

  1. Lancer une demande de diffusion en continu

    Cet extrait de code crée une connexion entre le frontend JavaScript et le backend Python, permettant ainsi l'intégration Azure OpenAI de DeepSeek-R1 avec une authentification sans clé.

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

    L’extrait de code suivant initialise les variables pour stocker les réponses et les pensées séparément. Cette séparation permet de gérer efficacement le contenu du raisonnement.

    let answer = "";
    let thoughts = "";    
    
  3. Traiter chaque mise à jour

    L’extrait de code suivant itère de façon asynchrone les blocs de la réponse du modèle.

    for await (const event of readNDJSONStream(response.body)) {
    
  4. Détecter et router le type de contenu

    Le script vérifie si l’événement contient un delta champ. S'il le fait, il traite le contenu en fonction de s'il s'agit de contenu de raisonnement ou de contenu régulier.

    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);
     }
    
    • Si le type de contenu est reasoning_content, le contenu est ajouté à thoughts et affiché dans la section .thoughts-content.
    • Si le type de contenu est content, le contenu est ajouté à answer et affiché dans la section .answer-content.
    • L'élément .loading-bar est masqué une fois que le contenu commence à diffuser en continu, et la section .thoughts s’affiche s’il y a des avis.
  5. Gestion des erreurs :

    Les erreurs sont consignées dans le back-end et retournées au client au format JSON.

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

    Cet extrait de code frontal affiche le message d’erreur dans l’interface de conversation.

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

Nettoyer GitHub Codespaces

Supprimez l’environnement GitHub Codespaces pour optimiser vos heures de base gratuites.

Importante

Pour plus d’informations sur le stockage gratuit et les heures de base de votre compte GitHub, consultez GitHub Codespaces, stockage et heures principales inclus mensuellement.

  1. Connectez-vous au tableau de bord GitHub Codespaces.

  2. Recherchez vos espaces de code actifs créés à partir du Azure-Samples//deepseek-python dépôt GitHub.

  3. Ouvrez le menu contextuel du codespace et sélectionnez Supprimer.

Obtenir de l’aide

Consignez votre problème dans les problèmes du référentiel.

Étapes suivantes