Streaming απαντήσεων AI agent: cancel, partial output και tool calls χωρίς διπλές ενέργειες
Όταν μια AI απάντηση χρειάζεται αρκετά δευτερόλεπτα, το streaming δίνει άμεση αίσθηση προόδου. Ο χρήστης βλέπει το κείμενο καθώς παράγεται αντί να περιμένει μια κενή οθόνη. Σε agentic workflow όμως δεν μεταδίδεται μόνο κείμενο: υπάρχουν tool calls, statuses, approvals και πιθανές επιχειρησιακές ενέργειες.
Το κουμπί Stop δεν πρέπει να σημαίνει απλώς «σταμάτα να ζωγραφίζεις tokens». Πρέπει να ορίζει αν ακυρώνεται η παραγωγή, αν συνεχίζουν tools που έχουν ήδη ξεκινήσει και τι θεωρείται τελικό αποτέλεσμα. Χωρίς σαφές protocol, reconnect ή retry μπορεί να διπλασιάσει email, CRM updates ή πληρωμές.
Transport και event protocol
Για one-way token streaming, Server-Sent Events ή streaming HTTP response είναι συχνά αρκετά. Για αμφίδρομη realtime αλληλεπίδραση, WebSocket ή WebRTC μπορεί να ταιριάζει καλύτερα. Η επιλογή εξαρτάται από audio, latency, proxies και ανάγκη client-to-server events.
Ανεξάρτητα από transport, η εφαρμογή χρειάζεται typed events, όχι απλά text chunks:
response.startedoutput_text.deltatool.startedtool.completedήtool.failedapproval.requiredresponse.completed,cancelledήfailed
Κάθε event έχει response ID, monotonically increasing sequence και timestamp. Έτσι το client μπορεί να αγνοήσει duplicates και να ανακατασκευάσει state μετά από reconnect.
Partial text δεν είναι τελικό record
Τα streamed deltas συνθέτουν draft output. Δεν αποθηκεύουμε κάθε χαρακτήρα ως ξεχωριστό chat message. Το backend κρατά response state και, όταν ολοκληρωθεί, δημιουργεί canonical final message.
Αν το stream διακοπεί, το partial text επισημαίνεται ως incomplete. Δεν παρουσιάζεται σαν ολοκληρωμένη οδηγία και δεν τροφοδοτείται αυτόματα σε επόμενο business workflow. Ο χρήστης μπορεί να το κρατήσει οπτικά, αλλά το data model γνωρίζει incomplete_reason.
Structured outputs χρειάζονται ακόμη μεγαλύτερη προσοχή. Ένα partial JSON δεν είναι valid payload. Το UI μπορεί να δείχνει προοδευτικό κείμενο, αλλά το backend κάνει schema validation μόνο όταν λάβει final structured item.
Cancel στο model και cancel στο UI
Όταν ο χρήστης πατήσει Stop, το client στέλνει cancel request για συγκεκριμένο response ID. Η επίσημη OpenAI Realtime API, για παράδειγμα, ορίζει response.cancel και τελικό response.done με cancelled status.
Το client σταματά να εμφανίζει νέα deltas μόνο αφού σημειώσει local cancel intent, αλλά το backend εξακολουθεί να επεξεργάζεται late events για να διατηρήσει συνεπή κατάσταση. Δεν θεωρεί ότι η σύνδεση έκλεισε άρα το provider σταμάτησε.
Το cancel είναι idempotent. Διπλό click ή retry δεν δημιουργεί error που χαλάει το session. Αν η απάντηση έχει ήδη ολοκληρωθεί, το API επιστρέφει την τρέχουσα κατάσταση χωρίς να αλλάζει παλιό αποτέλεσμα.
Τα tool calls δεν ακυρώνονται πάντα
Ένα search request μπορεί να ακυρωθεί εύκολα. Ένα email που στάλθηκε ή ένα refund που καταχωρήθηκε δεν μπορεί να «ξεσταλεί». Το response cancellation και το tool cancellation είναι διαφορετικές έννοιες.
Κάθε tool δηλώνει cancellation semantics:
- cancellable πριν ξεκινήσει
- cooperative cancel όσο τρέχει
- non-cancellable μετά το commit
- compensatable με ξεχωριστή αντίστροφη ενέργεια
Το UI δείχνει την αλήθεια: «Η απάντηση σταμάτησε, αλλά η εξαγωγή συνεχίζει» ή «Το email είχε ήδη σταλεί». Δεν εμφανίζει γενικό Cancelled όταν side effect ολοκληρώθηκε.
Τα side-effecting tools χρησιμοποιούν operation ID και idempotency key. Αν το agent resume ή retry ξαναζητήσει την ίδια ενέργεια, το tool επιστρέφει το προηγούμενο αποτέλεσμα.
Approvals πριν από side effects
Για επικίνδυνες ενέργειες, ο agent μπορεί να streamάρει preview και να εκπέμψει approval.required. Η παραγωγή περιμένει, χωρίς να εκτελεί το tool. Η έγκριση συνδέεται με ακριβές payload hash και expiry.
Αν ο χρήστης ακυρώσει όσο περιμένει approval, το tool δεν εκτελείται. Αν αλλάξει recipient, amount ή άλλο κρίσιμο argument, απαιτείται νέα έγκριση.
Το approval event είναι μέρος του persisted workflow, όχι προσωρινό modal state. Αν ο browser κλείσει, ο χρήστης μπορεί να επιστρέψει και να δει τι ακριβώς περιμένει.
Reconnect και resume
Η σύνδεση μπορεί να χαθεί ενώ ο server συνεχίζει. Το client επανασυνδέεται με response ID και τελευταίο sequence. Ο server είτε replayάρει events από durable buffer είτε επιστρέφει current snapshot και συνεχίζει.
Δεν ξεκινά νέα model response μόνο επειδή έκλεισε το stream. Αυτό θα μπορούσε να διπλασιάσει κόστος και tool calls. Νέα προσπάθεια δημιουργείται μόνο με ρητή user action και νέο operation ID.
Το event retention έχει όριο. Αν το reconnect γίνει πολύ αργά, το client φορτώνει final persisted state αντί να περιμένει πλήρες replay.
Backpressure και αργοί clients
Το model μπορεί να παράγει events γρηγορότερα από όσο τα επεξεργάζεται mobile browser ή δίκτυο. Χρειάζεται bounded buffer και batching μικρών deltas. Αν ο client καθυστερεί υπερβολικά, ο server μπορεί να συμπτύξει text deltas σε snapshot χωρίς να χάσει tool/status events.
Δεν επιτρέπουμε unbounded memory queue ανά connection. Το transport έχει heartbeat, timeout και σαφή cleanup όταν client φύγει, χωρίς αυτό να ακυρώνει αυτόματα το workflow αν η πολιτική λέει ότι πρέπει να συνεχίσει.
Moderation και streamed content
Το streaming δυσκολεύει τον έλεγχο επειδή ο χρήστης βλέπει content πριν ολοκληρωθεί. Αν απαιτείται strict post-generation moderation, μπορεί να μη γίνει token-by-token display ή να χρησιμοποιηθεί buffered chunks.
Για χαμηλότερο ρίσκο, εφαρμόζονται input controls, model safety settings και incremental checks, αλλά η ομάδα πρέπει να κατανοεί τα trade-offs latency και πιθανής έκθεσης partial content. Το τελικό μήνυμα μπορεί να διαφέρει από το partial draft αν αφαιρεθεί ή διορθωθεί unsafe τμήμα.
UI states και προσβασιμότητα
Το interface ξεχωρίζει thinking/progress από τελικό answer χωρίς να εκθέτει εσωτερικά chain-of-thought. Δείχνει συγκεκριμένα statuses όπως «Αναζήτηση εγγράφων», «Αναμονή έγκρισης» και «Δημιουργία αναφοράς».
Το Stop είναι σταθερό icon button με label για screen readers. Τα νέα tokens δεν κλέβουν focus και οι live regions δεν ανακοινώνουν κάθε λέξη. Προτιμάται περιοδική ανακοίνωση προόδου.
Μετά από cancel, εμφανίζεται αν το partial output διατηρήθηκε και ποιες ενέργειες ολοκληρώθηκαν. Το Retry δεν είναι ίδιο με Continue: το πρώτο δημιουργεί νέα απόκριση, το δεύτερο συνεχίζει από checkpoint όπου υποστηρίζεται.
Παρακολούθηση
Μετράμε time to first event, time to first text, total duration, cancel latency, reconnect rate, dropped connections και tool duration. Καταγράφουμε response ID, sequence gaps και terminal status.
Ιδιαίτερα σημαντικό είναι το ποσοστό cancellations μετά από tool commit και τα duplicate operation attempts. Έτσι φαίνεται αν το UI επιτρέπει στον χρήστη να σταματήσει πολύ αργά ή αν τα tools χρειάζονται καλύτερο preview.
Τα logs δεν αποθηκεύουν αδιακρίτως όλο το streamed content ή tool arguments με προσωπικά δεδομένα. Χρησιμοποιούνται redaction και role-based πρόσβαση.
Πώς το υλοποιεί η Ai Foundry
Στην Ai Foundry σχεδιάζουμε το streaming ως event protocol με response IDs, sequences και σαφείς terminal states. Διαχωρίζουμε partial text από canonical final message και response cancellation από την κατάσταση κάθε tool.
Στους AI agents που αναπτύσσουμε, τα side effects έχουν idempotency keys, checkpoints και approvals δεμένα στο ακριβές payload. Υλοποιούμε reconnect χωρίς να ξεκινά δεύτερη απάντηση, bounded buffers και UI που δείχνει τι σταμάτησε και τι ολοκληρώθηκε. Δοκιμάζουμε disconnects, διπλό cancel, late events και cancel κατά τη διάρκεια tool call ώστε η γρήγορη εμπειρία να παραμένει συνεπής.
Checklist
- Υπάρχουν typed events, response ID και sequence;
- Ξεχωρίζει το partial draft από το final message;
- Είναι idempotent το cancel;
- Έχει κάθε tool σαφή cancellation semantics;
- Προστατεύονται τα side effects με operation IDs;
- Επιμένουν τα approvals μετά από browser close;
- Γίνεται reconnect χωρίς νέα model call;
- Υπάρχει backpressure και bounded buffering;
- Είναι προσβάσιμα τα live updates και το Stop;
- Μετρώνται cancel latency, reconnects και duplicate attempts;
Το streaming δεν είναι μόνο animation πληκτρολόγησης. Είναι distributed workflow ανάμεσα σε client, model και εργαλεία. Όταν τα events και οι ακυρώσεις έχουν σαφή σημασία, ο agent φαίνεται γρήγορος χωρίς να γίνεται απρόβλεπτος.