Freigeben über


So verwenden Sie Streaming-Endpunkte, die von Prompt Flow aus bereitgestellt werden

In Prompt Flow können Sie Flows an einen von Azure Machine Learning verwalteten Online-Endpunkt für Echtzeit-Inferenz bereitstellen.

Wenn der Endpunkt durch Senden einer Anfrage konsumiert wird, wartet der Online-Endpunkt standardmäßig so lange, bis die gesamte Antwort fertig ist, und sendet sie dann an den Client zurück. Dies kann zu einer langen Verzögerung für den Kunden und einer schlechten Benutzererfahrung führen.

Um dies zu vermeiden, können Sie Streaming verwenden, wenn Sie die Endpunkte nutzen. Sobald das Streaming aktiviert ist, müssen Sie nicht mehr warten, bis die gesamte Antwort fertig ist. Stattdessen sendet der Server die Antwort in Stücken zurück, sobald sie generiert wurden. Der Client kann dann die Antwort schrittweise anzeigen, mit weniger Wartezeit und mehr Interaktivität.

Dieser Artikel beschreibt den Umfang des Streaming, die Funktionsweise des Streaming und die Nutzung von Streaming-Endpunkten.

Erstellen Sie einen streamingfähigen Flow

Wenn Sie den Streaming-Modus verwenden möchten, müssen Sie einen Flow erstellen, der einen Knoten hat, der einen String-Generator als Ausgabe des Flows erzeugt. Ein String-Generator ist ein Objekt, das auf Anfrage jeweils einen String zurückgeben kann. Sie können die folgenden Knotentypen verwenden, um einen String-Generator zu erstellen:

  • LLM-Knoten: Dieser Knoten verwendet ein umfangreiches Sprachmodell, um auf der Grundlage der Eingaben natürlichsprachliche Antworten zu generieren.

    {# Sample prompt template for LLM node #}
    
    system:
    You are a helpful assistant.
    
    user:
    {{question}}
    
  • Python-Knoten: Dieser Knoten ermöglicht es Ihnen, benutzerdefinierten Python-Code zu schreiben, der Zeichenketten ausgeben kann. Sie können diesen Knoten verwenden, um externe APIs oder Bibliotheken aufzurufen, die Streaming unterstützen. Sie können zum Beispiel diesen Code verwenden, um die Eingabe wortweise auszugeben:

    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 + " "
    

Wichtig

Nur die Ausgabe des letzten Knotens des Flows kann Streaming unterstützen.

„Letzter Knoten“ bedeutet, dass die Ausgabe des Knotens nicht von anderen Knoten verbraucht wird.

In diesem Leitfaden wird der Beispielablauf „Chat mit Wikipedia“ als Beispiel verwendet. Dieser Flow verarbeitet die Frage des Benutzers, durchsucht Wikipedia nach relevanten Artikeln und beantwortet die Frage mit Informationen aus den Artikeln. Es verwendet den Streaming-Modus, um den Fortschritt bei der Erstellung der Antwort anzuzeigen.

Informationen zum Erstellen eines Chatflows finden Sie unter Entwickeln eines Chatflows in Prompt Flow.

Screenshot des Flow „Chat mit Wikipedia“.

Stellen Sie den Flow als Online-Endpunkt bereit

Um den Streaming-Modus zu nutzen, müssen Sie Ihren Flow als Online-Endpunkt bereitstellen. So können Sie in Echtzeit Anfragen senden und Antworten von Ihrem Flow erhalten.

Wie Sie Ihren Flow als Online-Endpunkt bereitstellen können, erfahren Sie unter Bereitstellen eines Flows als Online-Endpunkt für Echtzeit-Inferenz mit CLI, um Ihren Flow als Online-Endpunkt bereitzustellen.

Hinweis

Bereitstellung mit einer Laufzeitumgebungsversion höher als Version 20230816.v10.

Sie können Ihre Laufzeitversion überprüfen und die Laufzeit auf der Laufzeit-Detailseite aktualisieren.

Screenshot der Runtimeumgebung in Azure Machine Learning Studio.

Verstehen des Streaming-Prozesses

Bei einem Online-Endpunkt müssen der Client und der Server bestimmte Prinzipien für die Inhaltsaushandlung befolgen, um den Streaming-Modus zu nutzen:

Die Aushandlung von Inhalten ist wie ein Gespräch zwischen dem Client und dem Server über das bevorzugte Format der Daten, die sie senden und empfangen wollen. Sie gewährleistet eine effektive Kommunikation und eine Einigung über das Format der ausgetauschten Daten.

Um den Streaming-Prozess zu verstehen, sollten Sie die folgenden Schritte beachten:

  • Zunächst erstellt der Client eine HTTP-Anfrage mit dem gewünschten Medientyp in der Accept-Kopfzeile. Der Medientyp teilt dem Server mit, welche Art von Datenformat der Client erwartet. Es ist, als würde der Kunde sagen: „Hey, ich brauche ein bestimmtes Format für die Daten, die Sie mir schicken. Das kann JSON, Text oder etwas anderes sein.“ Zum Beispiel bedeutet application/json, dass JSON-Daten bevorzugt werden, text/event-stream, dass Streaming-Daten gewünscht werden, und */*, dass der Client jedes Datenformat akzeptiert.

    Hinweis

    Wenn eine Anfrage keinen Accept-Header oder einen leeren Accept-Header hat, bedeutet dies, dass der Client jeden Medientyp als Antwort akzeptiert. Der Server behandelt es als */*.

  • Anschließend antwortet der Server auf der Grundlage des im Accept-Header angegebenen Medientyps. Es ist wichtig zu beachten, dass der Client im Accept-Header mehrere Medientypen anfordern kann, und der Server muss seine Fähigkeiten und Formatprioritäten berücksichtigen, um die geeignete Antwort zu bestimmen.

    • Zunächst prüft der Server, ob text/event-stream ausdrücklich in der Kopfzeile Accept angegeben ist:
      • Bei einem Stream-aktivierten DatenFlow gibt der Server eine Antwort mit einem Content-Type von text/event-stream zurück, was bedeutet, dass die Daten gestreamt werden.
      • Bei einem nicht Stream-fähigen DatenFlow prüft der Server, ob andere Medientypen im Header angegeben sind.
    • Wenn text/event-stream nicht angegeben ist, prüft der Server, ob application/json oder */* in der Kopfzeile Accept angegeben ist:
      • In solchen Fällen gibt der Server eine Antwort mit einem Content-Type von application/json zurück und stellt die Daten im JSON-Format bereit.
    • Wenn der Accept-Header andere Medientypen angibt, wie z. B. text/html:
      • Der Server gibt eine 424-Antwort mit einem Prompt-Flow-Laufzeitfehlercode UserError und einem Laufzeit-HTTP-Status 406 zurück, der anzeigt, dass der Server die Anforderung nicht mit dem angeforderten Datenformat erfüllen kann. Weitere Informationen finden Sie unter Fehlerbehandlung.
  • Schließlich prüft der Client den Antwort-Header Content-Type. Ist er auf text/event-stream gesetzt, bedeutet dies, dass die Daten gestreamt werden.

Schauen wir uns genauer an, wie der Streaming-Prozess funktioniert. Die Antwortdaten im Streaming-Modus haben das Format von server-sent events (SSE).

Der gesamte Prozess läuft folgendermaßen ab:

0. Der Client sendet eine Nachricht an den Server

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": []
}

Hinweis

Der Accept-Header wird auf text/event-stream gesetzt, um eine Stream-Antwort anzufordern.

1. Der Server sendet die Antwort im Streaming-Modus zurück

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": ""}

Hinweis

Die Content-Type wird auf text/event-stream; charset=utf-8 gesetzt, was bedeutet, dass die Antwort ein Ereignisstrom ist.

Der Client sollte die Antwortdaten als vom Server gesendete Ereignisse dekodieren und sie inkrementell anzeigen. Der Server schließt die HTTP-Verbindung, nachdem alle Daten gesendet wurden.

Jedes Antwortereignis ist das Delta zum vorherigen Ereignis. Es wird empfohlen, dass der Client die zusammengeführten Daten im Speicher behält und sie bei der nächsten Anfrage als Chatverlauf an den Server zurückschickt.

2. Der Client sendet eine weitere Chat-Nachricht zusammen mit dem gesamten Chat-Verlauf an den Server

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. Der Server sendet die Antwort im Streaming-Modus zurück

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": ""}

Das Gespräch wird dann in ähnlicher Weise fortgesetzt.

Umgang mit Fehlern

Der Client sollte zunächst den HTTP-Antwortcode überprüfen. Siehe HTTP-Statuscode-Tabelle für häufige Fehlercodes, die von Online-Endpunkten zurückgegeben werden.

Wenn der Antwortcode „424 Model Error“ lautet, bedeutet dies, dass der Fehler durch den Code des Modells verursacht wurde. Die Fehlerantwort eines Prompt-Flow-Modells hat immer dieses Format:

{
  "error": {
    "code": "UserError",
    "message": "Media type text/event-stream in Accept header is not acceptable. Supported media type(s) - application/json",
  }
}
  • Es handelt sich immer um ein JSON-Dictionary mit nur einem definierten Schlüssel „error“.
  • Der Wert für „error“ ist ein Wörterbuch, das „code“ und „message“" enthält.
  • „code“ definiert die Fehlerkategorie. Derzeit kann es sich um „UserError“ für fehlerhafte Benutzereingaben und „SystemError“ für Fehler innerhalb des Dienstes handeln.
  • „message“ ist eine Beschreibung des Fehlers. Sie kann dem Endbenutzer angezeigt werden.

Wie man die vom Server gesendeten Ereignisse konsumiert

Verbrauchen mit Python

In diesem Beispiel verwenden wir die SSEClient-Klasse. Diese Klasse ist keine integrierte Python-Klasse und muss separat installiert werden. Sie können Sie über „pip“ installieren:

pip install sseclient-py

Ein Beispiel für die Verwendung wäre:

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

Verbrauchen mit JavaScript

Es gibt mehrere Bibliotheken, um vom Server gesendete Ereignisse in JavaScript zu nutzen. Hier finden Sie ein Beispiel für eine dieser Bibliotheken.

Ein Beispiel für eine Chat-Anwendung mit Python

Das folgende Beispiel zeigt eine in Python geschriebene Chat-Anwendung.

GIF einer Beispiel-Chat-App mit Python.

Erweiterte Verwendung: Ausgabe von Hybridstreams und nicht streamingfähigen Flows

Es kann vorkommen, dass Sie aus einer Flowausgabe sowohl Stream- als auch Non-Stream-Ergebnisse erhalten möchten. Im Ablauf „Chat mit Wikipedia“ möchten Sie zum Beispiel nicht nur die Antwort von LLM erhalten, sondern auch die Liste der URLs, die der Ablauf durchsucht hat. Dazu müssen Sie den DatenFlow so ändern, dass er eine Kombination aus der Antwort des LLM-Streams und der URL-Liste für Nicht-Streams ausgibt.

Im Beispielflow „Chat mit Wikipedia“ ist die Ausgabe mit dem LLM-Knoten augmented_chat verbunden. Um die URL-Liste zur Ausgabe hinzuzufügen, müssen Sie ein Ausgabefeld mit dem Namen url und dem Wert ${get_wiki_url.output} hinzufügen.

Screenshot des Flow „Hybridchat mit Wikipedia“.

Die Ausgabe des Flows ist ein Nichtstromfeld als Basis und ein Stromfeld als Delta. Hier ist ein Beispiel für eine Anfrage und eine Antwort.

Erweiterte Verwendung – 0. Der Client sendet eine Nachricht an den Server

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": []
}

Vorzeitige Verwendung - 1. Der Server sendet die Antwort im Streaming-Modus zurück

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": ""}

Vorzeitige Verwendung - 2. Der Client sendet eine weitere Chat-Nachricht zusammen mit dem gesamten Chat-Verlauf an den Server

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"
            }
        }
    ]
}

Vorzeitige Verwendung - 3. Der Server sendet die Antwort im Streaming-Modus zurück

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": ""}

Nächste Schritte