Partager via


Comment utiliser des points de terminaison de streaming déployés à partir du flux d’invite

Dans le flux d’invite, vous pouvez déployer un flux sur un point de terminaison en ligne managé Azure Machine Learning pour une inférence en temps réel.

Lorsque vous consommez le point de terminaison en envoyant une requête, le comportement par défaut est que le point de terminaison en ligne continue d’attendre que l’ensemble de la réponse soit prête, avant de la renvoyer au client. Cela peut entraîner un long délai pour le client et une expérience utilisateur médiocre.

Pour éviter cela, vous pouvez utiliser la diffusion en continu lorsque vous consommez les points de terminaison. Une fois la diffusion en continu activée, vous n’avez pas besoin d’attendre que toute la réponse soit prête. Au lieu de cela, le serveur renvoie la réponse en blocs à mesure qu’ils sont générés. Le client peut ensuite afficher la réponse progressivement, avec moins de temps d’attente et plus d’interactivité.

Cet article décrit l’étendue et le fonctionnement de la diffusion en continu, ainsi que la façon de consommer des points de terminaison de streaming.

Créer un flux avec diffusion en continu

Si vous souhaitez utiliser le mode de diffusion en continu, vous devez créer un flux avec un nœud qui produit un générateur de chaînes comme sortie de flux. Un générateur de chaînes est un objet qui peut renvoyer une chaîne à la fois lors de requêtes. Vous pouvez utiliser les types de nœuds suivants pour créer un générateur de chaînes :

  • Nœud LLM : ce nœud utilise un modèle de langage volumineux pour générer des réponses en langage naturel en fonction de l’entrée.

    {# Sample prompt template for LLM node #}
    
    system:
    You are a helpful assistant.
    
    user:
    {{question}}
    
  • Nœud Python : ce nœud vous permet d’écrire du code Python personnalisé qui peut générer des sorties de chaîne. Vous pouvez utiliser ce nœud pour appeler des bibliothèques ou des API externes qui prennent en charge la diffusion en continu. Par exemple, vous pouvez utiliser ce code pour répéter l’entrée mot par mot :

    from promptflow import tool
    
    # Sample code echo input by yield in Python tool node
    
    @tool
    def my_python_tool(paragraph: str) -> str:
        yield "Echo: "
        for word in paragraph.split():
            yield word + " "
    

Important

Seule la sortie du dernier nœud du flux peut prendre en charge la diffusion en continu.

« Dernier nœud » signifie que la sortie du nœud n’est pas consommée par d’autres nœuds.

Dans ce guide, nous allons utiliser l’exemple de flux « Conversation avec Wikipédia ». Ce flux traite la question de l’utilisateur, recherche des articles pertinents sur Wikipédia et répond à la question avec des informations provenant des articles. Il utilise le mode de diffusion en continu pour afficher la progression de la génération de réponses.

Pour savoir comment créer un flux de conversation, consultez comment développer un flux de conversation dans un flux d’invite.

Capture d’écran du flux de conversation avec Wikipédia.

Déployer le flux en tant que point de terminaison en ligne

Pour utiliser le mode de diffusion en continu, vous devez déployer votre flux en tant que point de terminaison en ligne. Cela vous permettra d’envoyer des requêtes et de recevoir des réponses de votre flux en temps réel.

Pour savoir comment déployer votre flux en tant que point de terminaison en ligne, consultez Déployer un flux vers un point de terminaison en ligne pour l’inférence en temps réel avec l’interface CLI.

Remarque

Déployez avec une version de l’environnement d’exécution ultérieure à la version 20230816.v10.

Vous pouvez vérifier votre version d’exécution et la mettre à jour sur la page des détails de l’exécution.

Capture d’écran d’Azure Machine Learning studio montrant l’environnement d’exécution.

Comprendre le processus de diffusion en continu

Lorsque vous disposez d’un point de terminaison en ligne, le client et le serveur doivent suivre des principes spécifiques pour la négociation du contenu afin d’utiliser le mode de diffusion en continu :

La négociation de contenu ressemble à une conversation entre le client et le serveur au sujet du format préféré des données qu’ils veulent envoyer et recevoir. Elle garantit une communication et un accord efficaces sur le format des données échangées.

Pour comprendre le processus de diffusion en continu, procédez comme suit :

  • Tout d’abord, le client construit une requête HTTP avec le type de média souhaité inclus dans l’en-tête Accept. Le type de média indique au serveur le type de format de données attendu par le client. C’est comme si le client disait : « Je recherche un format spécifique pour les données que vous allez m’envoyer. Il peut s’agir de JSON, de texte ou d’autre chose. » Par exemple, application/json indique une préférence pour les données JSON, text/event-stream indique un souhait de données de diffusion en continu et */* signifie que le client accepte n’importe quel format de données.

    Remarque

    Si une requête n’a pas d’en-tête Accept ou a un en-tête Accept vide, cela implique que le client acceptera n’importe quel type de média en réponse. Le serveur le traite comme */*.

  • Ensuite, le serveur répond en fonction du type de média spécifié dans l’en-tête Accept. Il est important de noter que le client est susceptible de demander plusieurs types de médias dans l’en-tête Accept et que le serveur doit tenir compte de ses fonctionnalités et de ses priorités de format pour déterminer la réponse appropriée.

    • Tout d’abord, le serveur vérifie si text/event-stream est spécifié explicitement dans l’en-tête Accept :
      • Pour un flux avec diffusion en continu, le serveur retourne une réponse avec un Content-Type de text/event-stream, indiquant que les données sont en cours de diffusion.
      • Pour un flux sans diffusion en continu, le serveur passe à la vérification d’autres types de médias spécifiés dans l’en-tête.
    • Si text/event-stream n’est pas spécifié, le serveur vérifie alors si application/json ou */* est spécifié dans l’en-tête Accept :
      • Dans ce cas, le serveur retourne une réponse avec un Content-Type de application/json, en fournissant les données au format JSON.
    • Si l’en-tête Accept spécifie d’autres types de médias, tels que text/html :
      • Le serveur retourne une réponse 424 avec un code d’erreur d’exécution du flux d’invite UserError et un état HTTP d’exécution 406, indiquant que le serveur ne peut pas répondre à la requête avec le format de données demandé. Pour plus d’informations, consultez erreurs de traitement.
  • Enfin, le client vérifie l’en-tête de réponse Content-Type. S’il est défini sur text/event-stream, cela indique que les données sont en cours de diffusion.

Examinons de plus près le fonctionnement du processus de diffusion en continu. Les données de réponse en mode de diffusion en continu suivent le format des événements envoyés par le serveur (SSE).

Le processus global fonctionne comme suit :

0. Le client envoie un message au serveur

POST https://<your-endpoint>.inference.ml.azure.com/score
Content-Type: application/json
Authorization: Bearer <key or token of your endpoint>
Accept: text/event-stream

{
    "question": "Hello",
    "chat_history": []
}

Remarque

L’en-tête Accept est défini sur text/event-stream pour demander une réponse de diffusion en continu.

1. Le serveur renvoie la réponse en mode de diffusion en continu

HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Connection: close
Transfer-Encoding: chunked

data: {"answer": ""}

data: {"answer": "Hello"}

data: {"answer": "!"}

data: {"answer": " How"}

data: {"answer": " can"}

data: {"answer": " I"}

data: {"answer": " assist"}

data: {"answer": " you"}

data: {"answer": " today"}

data: {"answer": " ?"}

data: {"answer": ""}

Remarque

Le Content-Type est défini sur text/event-stream; charset=utf-8, indiquant que la réponse est un flux d’événements.

Le client doit décoder les données de réponse en tant qu’événements envoyés par le serveur et les afficher de manière incrémentielle. Le serveur ferme la connexion HTTP après l’envoi de toutes les données.

Chaque événement de réponse est le delta de l’événement précédent. Il est recommandé au client de suivre les données fusionnées en mémoire et de les renvoyer au serveur en tant qu’historique des conversations dans la requête suivante.

2. Le client envoie un autre message de conversation, avec l’historique complet des conversations, au serveur

POST https://<your-endpoint>.inference.ml.azure.com/score
Content-Type: application/json
Authorization: Bearer <key or token of your endpoint>
Accept: text/event-stream

{
    "question": "Glad to know you!",
    "chat_history": [
        {
            "inputs": {
                "question": "Hello"
            },
            "outputs": {
                "answer": "Hello! How can I assist you today?"
            }
        }
    ]
}

3. Le serveur renvoie la réponse en mode de diffusion en continu

HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Connection: close
Transfer-Encoding: chunked

data: {"answer": ""}

data: {"answer": "Nice"}

data: {"answer": " to"}

data: {"answer": " know"}

data: {"answer": " you"}

data: {"answer": " too"}

data: {"answer": "!"}

data: {"answer": " Is"}

data: {"answer": " there"}

data: {"answer": " anything"}

data: {"answer": " I"}

data: {"answer": " can"}

data: {"answer": " help"}

data: {"answer": " you"}

data: {"answer": " with"}

data: {"answer": "?"}

data: {"answer": ""}

La conversation continue ensuite de la même manière.

Gérer les erreurs

Le client doit d’abord vérifier le code de réponse HTTP. Consultez la table de codes d’état HTTP pour connaître les codes d’erreur courants retournés par les points de terminaison en ligne.

Si le code de réponse est « Erreur de modèle 424 », cela signifie que l’erreur est provoquée par le code du modèle. La réponse d’erreur d’un modèle de flux d’invite suit toujours ce format :

{
  "error": {
    "code": "UserError",
    "message": "Media type text/event-stream in Accept header is not acceptable. Supported media type(s) - application/json",
  }
}
  • Il s’agit toujours d’un dictionnaire JSON avec une seule clé « error » définie.
  • La valeur de la clé « error » est un dictionnaire contenant « code » et « message ».
  • « code » définit la catégorie d’erreur. Actuellement, il peut s’agir de « UserError » pour les entrées utilisateur incorrectes et de « SystemError » pour les erreurs internes du service.
  • « message » contient une description de l’erreur. Il peut être affiché à l’utilisateur final.

Comment utiliser les événements envoyés par le serveur

Consommer à l’aide de Python

Dans cet exemple d’utilisation, nous utilisons la classe SSEClient. Cette classe n’est pas une classe Python intégrée et doit être installée séparément. Vous pouvez l’installer via pip :

pip install sseclient-py

Un exemple d’utilisation serait :

import requests
from sseclient import SSEClient
from requests.exceptions import HTTPError

try:
    response = requests.post(url, json=body, headers=headers, stream=stream)
    response.raise_for_status()

    content_type = response.headers.get('Content-Type')
    if "text/event-stream" in content_type:
        client = SSEClient(response)
        for event in client.events():
            # Handle event, i.e. print to stdout
    else:
        # Handle json response

except HTTPError:
    # Handle exceptions

Consommer à l’aide de JavaScript

Il existe plusieurs bibliothèques pour utiliser les événements envoyés par le serveur dans JavaScript. Voici l’un d’entre eux comme exemple.

Exemple d’application de conversation utilisant Python

Voici un exemple d’application de conversation écrite en Python.

Fichier Gif d’un exemple d’application de conversation utilisant Python.

Utilisation avancée – Sortie de flux avec et sans diffusion hybride

Parfois, vous souhaiterez probablement obtenir des résultats avec et sans stream d’une sortie de flux. Par exemple, dans le flux « Conversation avec Wikipédia », vous souhaiterez probablement obtenir non seulement la réponse de LLM, mais aussi la liste des URL recherchées par le flux. Pour ce faire, vous devez modifier le flux pour générer une combinaison de la réponse de LLM avec diffusion et de la liste d’URL sans diffusion.

Dans l’exemple de flux « Conversation avec Wikipédia », la sortie est connectée au nœud LLM augmented_chat. Pour ajouter la liste d’URL à la sortie, vous devez ajouter un champ de sortie avec le nom url et la valeur ${get_wiki_url.output}.

Capture d’écran du flux hybride de conversation avec Wikipédia.

La sortie du flux sera un champ sans diffusion comme base et un champ de diffusion en tant que delta. Voici un exemple de requête et de réponse.

Utilisation avancée – 0. Le client envoie un message au serveur

POST https://<your-endpoint>.inference.ml.azure.com/score
Content-Type: application/json
Authorization: Bearer <key or token of your endpoint>
Accept: text/event-stream
{
    "question": "When was ChatGPT launched?",
    "chat_history": []
}

Utilisation avancée, 1. Le serveur renvoie la réponse en mode de diffusion en continu

HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Connection: close
Transfer-Encoding: chunked

data: {"url": ["https://en.wikipedia.org/w/index.php?search=ChatGPT", "https://en.wikipedia.org/w/index.php?search=GPT-4"]}

data: {"answer": ""}

data: {"answer": "Chat"}

data: {"answer": "G"}

data: {"answer": "PT"}

data: {"answer": " was"}

data: {"answer": " launched"}

data: {"answer": " on"}

data: {"answer": " November"}

data: {"answer": " "}

data: {"answer": "30"}

data: {"answer": ","}

data: {"answer": " "}

data: {"answer": "202"}

data: {"answer": "2"}

data: {"answer": "."}

data: {"answer": " \n\n"}

...

data: {"answer": "PT"}

data: {"answer": ""}

Utilisation avancée, 2. Le client envoie un autre message de conversation, avec l’historique complet des conversations, au serveur

POST https://<your-endpoint>.inference.ml.azure.com/score
Content-Type: application/json
Authorization: Bearer <key or token of your endpoint>
Accept: text/event-stream
{
    "question": "When did OpenAI announce GPT-4? How long is it between these two milestones?",
    "chat_history": [
        {
            "inputs": {
                "question": "When was ChatGPT launched?"
            },
            "outputs": {
                "url": [
                    "https://en.wikipedia.org/w/index.php?search=ChatGPT",
                    "https://en.wikipedia.org/w/index.php?search=GPT-4"
                ],
                "answer": "ChatGPT was launched on November 30, 2022. \n\nSOURCES: https://en.wikipedia.org/w/index.php?search=ChatGPT"
            }
        }
    ]
}

Utilisation avancée, 3. Le serveur renvoie la réponse en mode de diffusion en continu

HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Connection: close
Transfer-Encoding: chunked

data: {"url": ["https://en.wikipedia.org/w/index.php?search=Generative pre-trained transformer ", "https://en.wikipedia.org/w/index.php?search=Microsoft "]}

data: {"answer": ""}

data: {"answer": "Open"}

data: {"answer": "AI"}

data: {"answer": " released"}

data: {"answer": " G"}

data: {"answer": "PT"}

data: {"answer": "-"}

data: {"answer": "4"}

data: {"answer": " in"}

data: {"answer": " March"}

data: {"answer": " "}

data: {"answer": "202"}

data: {"answer": "3"}

data: {"answer": "."}

data: {"answer": " Chat"}

data: {"answer": "G"}

data: {"answer": "PT"}

data: {"answer": " was"}

data: {"answer": " launched"}

data: {"answer": " on"}

data: {"answer": " November"}

data: {"answer": " "}

data: {"answer": "30"}

data: {"answer": ","}

data: {"answer": " "}

data: {"answer": "202"}

data: {"answer": "2"}

data: {"answer": "."}

data: {"answer": " The"}

data: {"answer": " time"}

data: {"answer": " between"}

data: {"answer": " these"}

data: {"answer": " two"}

data: {"answer": " milestones"}

data: {"answer": " is"}

data: {"answer": " approximately"}

data: {"answer": " "}

data: {"answer": "3"}

data: {"answer": " months"}

data: {"answer": ".\n\n"}

...

data: {"answer": "Chat"}

data: {"answer": "G"}

data: {"answer": "PT"}

data: {"answer": ""}

Étapes suivantes