Behandlung von Eingabeereignissen mit der bidirektionalen API
Die bidirektionale Stream-API verwendet eine ereignisgesteuerte Architektur mit strukturierten Eingabe- und Ausgabeereignissen. Das Verständnis der richtigen Reihenfolge der Ereignisse ist entscheidend für die Implementierung erfolgreicher Konversationsanwendungen und die Aufrechterhaltung des richtigen Konversationsstatus während der gesamten Interaktionen.
Die Nova-Sonic-Konversation folgt einer strukturierten Ereignissequenz. Sie senden zunächst ein sessionStart-Ereignis, das die Konfigurationsparameter der Inferenz wie Temperatur- und Token-Grenzwerte enthält. Anschließend senden Sie promptStart, um das Audioausgabeformat und die Toolkonfigurationen zu definieren, und weisen eine eindeutige promptName-ID zu, die in allen nachfolgenden Ereignissen enthalten sein muss.
Für jeden Interaktionstyp (System-Prompt, Audio usw.) befolgen Sie ein dreiteiliges Muster: Verwenden Sie contentStart, um den Inhaltstyp und die Rolle des Inhalts zu definieren (SYSTEM, USER, ASSISTANT, TOOL), geben Sie dann das eigentliche Inhaltsereignis an und schließen Sie das Segment mit contentEnd ab. Das contentStart-Ereignis gibt an, ob Sie Toolergebnisse, Streaming-Audio oder einen System-Prompt senden. Das contentStart-Ereignis enthält eine eindeutige contentName-ID.
Ein Konversationsverlauf kann nur einmal hinzugefügt werden, nämlich nach dem System-Prompt und bevor das Audio-Streaming beginnt. Er folgt demselben contentStart/textInput/contentEnd-Muster. Die Rollen USER und ASSISTANT müssen im contentStart-Ereignis für jede historische Nachricht definiert werden. Dies bietet einen wichtigen Kontext für die aktuelle Konversation, muss jedoch abgeschlossen sein, bevor eine neue Benutzereingabe beginnt.
Das Audio-Streaming arbeitet mit kontinuierlicher Mikrofonabtastung. Nach dem Senden eines ersten contentStart, werden Audioframes (jeweils ca. 32 ms) direkt vom Mikrofon aufgenommen und mit demselben contentName sofort als audioInput-Ereignisse gesendet. Diese Audiosamples sollten in Echtzeit gestreamt werden, sobald sie aufgenommen werden, wobei die natürliche Abtastkadenz des Mikrofons während des gesamten Gesprächs beibehalten werden sollte. Alle Audioframes teilen sich einen einzigen Inhaltscontainer, bis die Konversation endet und dieser explizit geschlossen wird.
Nach Beendigung oder Abbruch des Gesprächs ist es unerlässlich, alle offenen Streams ordnungsgemäß zu schließen und die Sitzung in der richtigen Reihenfolge zu beenden. Um eine Sitzung ordnungsgemäß zu beenden und Ressourcenlecks zu vermeiden, müssen Sie eine bestimmte Schlusssequenz befolgen:
-
Schließen Sie alle geöffneten Audio-Streams mit dem
contentEnd-Ereignis. -
Senden Sie ein
promptEnd-Ereignis, das auf den Original-promptNameverweist. -
Senden Sie das
sessionEnd-Ereignis.
Wenn Sie eines dieser abschließenden Ereignisse überspringen, kann dies zu unvollständigen Konversationen oder verwaisten Ressourcen führen.
Diese Identifikatoren bilden eine hierarchische Struktur: Der promptName verbindet alle Konversationsereignisse miteinander, während jeder einzelne contentName die Grenzen bestimmter Inhaltsblöcke markiert. Diese Hierarchie stellt sicher, dass das Modell während der gesamten Interaktion den richtigen Kontext beibehält.
Eingabeereignis-Ablauf
In diesem Abschnitt wird die Struktur des Ablaufs der Eingabeereignisse erläutert.
-
RequestStartEvent{ "event": { "sessionStart": { "inferenceConfiguration": { "maxTokens": "int", "topP": "float", "temperature": "float" } } } } -
PromptStartEvent{ "event": { "promptStart": { "promptName": "string", // unique identifier same across all events i.e. UUID "textOutputConfiguration": { "mediaType": "text/plain" }, "audioOutputConfiguration": { "mediaType": "audio/lpcm", "sampleRateHertz": 8000 | 16000 | 24000, "sampleSizeBits": 16, "channelCount": 1, "voiceId": "matthew" | "tiffany" | "amy" | "lupe" | "carlos" | "ambre" | "florian" | "greta" | "lennart" | "beatrice" | "lorenzo", "encoding": "base64", "audioType": "SPEECH", }, "toolUseOutputConfiguration": { "mediaType": "application/json" }, "toolConfiguration": { "tools": [{ "toolSpec": { "name": "string", "description": "string", "inputSchema": { "json": "{}" } } }] } } } } -
InputContentStartEvent-
Text{ "event": { "contentStart": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // unique identifier for the content block "type": "TEXT", "interactive": false, "role": "SYSTEM" | "USER" | "ASSISTANT", "textInputConfiguration": { "mediaType": "text/plain" } } } } -
Audio{ "event": { "contentStart": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // unique identifier for the content block "type": "AUDIO", "interactive": true, "role": "USER", "audioInputConfiguration": { "mediaType": "audio/lpcm", "sampleRateHertz": 8000 | 16000 | 24000, "sampleSizeBits": 16, "channelCount": 1, "audioType": "SPEECH", "encoding": "base64" } } } } -
Tool{ "event": { "contentStart": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // unique identifier for the content block "interactive": false, "type": "TOOL", "role": "TOOL", "toolResultInputConfiguration": { "toolUseId": "string", // existing tool use id "type": "TEXT", "textInputConfiguration": { "mediaType": "text/plain" } } } } }
-
-
TextInputContent{ "event": { "textInput": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // unique identifier for the content block "content": "string" } } } -
AudioInputContent{ "event": { "audioInput": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // same unique identifier from its contentStart "content": "base64EncodedAudioData" } } } -
ToolResultContentEvent"event": { "toolResult": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string", // same unique identifier from its contentStart "content": "{\"key\": \"value\"}" // stringified JSON object as a tool result } } -
InputContentEndEvent{ "event": { "contentEnd": { "promptName": "string", // same unique identifier from promptStart event "contentName": "string" // same unique identifier from its contentStart } } } -
PromptEndEvent{ "event": { "promptEnd": { "promptName": "string" // same unique identifier from promptStart event } } } -
RequestEndEvent{ "event": { "sessionEnd": {} } }