Akış
Akışlı yanıtlar nasıl gelir, bağlantı nasıl canlı tutulur ve metin neden kelime kelime değil mesaj mesaj ilerler.
Üç biçim de akışı destekler: Chat Completions ve Responses'ta "stream": true, Vercel AI SDK'da /chat her zaman akar. Hepsi sunucu tarafından gönderilen olaylar, yani SSE kullanır.
Bağlantı
Akışlı yanıtın başlıkları:
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache, no-transform
Connection: keep-alive
X-Accel-Buffering: no
X-Sone-Conversation-Id: conv_x8k2m9p4q7w1r3tVercel AI SDK akışında ayrıca x-vercel-ai-ui-message-stream: v1 gelir.
- Önce kontroller. Anahtar, istek sınırı, asistan, sohbet ve paket kontrolleri akış başlamadan yapılır. Geçmeyen istek akış yerine sıradan bir HTTP hatası alır: Hatalar.
- İlk olay asistan başlarken gelir. Kontroller geçince Chat Completions'ta rolü taşıyan ilk parça, Responses'ta
response.created, AI SDK'dastarthemen gelir. Asistan daha düşünürken bağlantının kurulduğunu ve sohbetin kimliğini bilirsiniz. - On saniyede bir kalp atışı. Uzun süren turlarda bağlantı
: pingsatırlarıyla canlı tutulur. İki nokta üst üste ile başlayan satırlar SSE yorumudur; SDK'lar onları zaten atlar, kendi ayrıştırıcınızı yazıyorsanız siz de atlayın. - Bitiş. Chat Completions ve AI SDK
data: [DONE]ile biter. Responsesresponse.completedya da, bir hatada,response.failedolayıyla biter; orada[DONE]yoktur.
Metin neden mesaj mesaj gelir
Genel bir dil modeli API'sinde metin model ürettikçe, kelime kelime gelir. Burada öyle değil; bu bilerek böyle.
Asistanın yazdığı yanıt ziyaretçiye gitmeden önce işletmenin güvenlik kontrollerinden geçer: fiyat ve stok doğru mu, söz verilmemesi gereken bir şey var mı, mesaj gerçekten gönderilmeli mi. Bu kontroller yanıtın tamamına bakar. Bu yüzden bir mesaj ancak bitip kontrolden geçtikten sonra yola çıkar.
Sonuçta akış şöyle ilerler:
- Kontroller geçer, bağlantı açılır, ilk olay gelir.
- Asistan düşünür ve araçlarını kullanır. Bu birkaç saniye sürebilir. Bu sırada "Ürünlere bakılıyor" gibi kısa durumlar gelir.
- İlk mesaj kontrolden geçer ve gelir. Daktilo etkisi bozulmasın diye kelime gruplarına bölünerek hızla akar.
- Varsa sonraki mesajlar, ürün kartları ve düğmeler aynı şekilde sırayla gelir.
Kısa durumlar her biçimde başka bir yerde gelir:
| Biçim | Durum |
|---|---|
| Chat Completions | delta'sı boş bir parçanın üst düzeyindeki sone.activity |
| Responses | Bir olay değil, SSE yorum satırı: : sone.activity {"label":"Ürünlere bakılıyor"}. sequence_number taşımaz; SDK'lar onu atlar, okumak için akışı kendiniz ayrıştırın. |
| Vercel AI SDK | Geçici (transient: true) data-sone-status parçasının data.label alanı; yalnız onData'ya gelir |
Durum metni bir araç adı değil, ziyaretçinin anlayacağı Türkçe bir cümledir. İki mesaj arasında kısa bir bekleme olabilir. Arayüzünüzde bir "yazıyor" göstergesi tutmak iyi olur.
Aynı mesajı aynı Idempotency-Key ile, ya da sohbetin kimliği ve aynı geçmişle yeniden gönderdiğinizde asistan yeniden yanıt vermez; kayıtlı yanıt akışın sonunda bir kerede gelir. Ayrıntılar: Tekrar denemek.
Bağlantı koparsa
Asistanın gönderdiği her mesaj, gönderildiği anda sohbete kaydedilir. Akış yarıda koparsa kaçırdığınız mesajları GET /conversations/{id}/messages ile okuyabilirsiniz. Sohbetin kimliği akışın başında X-Sone-Conversation-Id başlığında gelir. Ayrıntılar: İnsan devri ve ekip yanıtları.
Aynı mesajı yeniden göndermeniz gerekirse ilk gönderimdeki Idempotency-Key ile gönderin; asistan iki kez yanıt vermez. Önceki tur hâlâ yanıtlanıyorsa istek 409 conversation_busy alır.
Proxy arkasında
Akışı kendi sunucunuzdan geçiriyorsanız ara katmanın yanıtı tamponlamadığından emin olun. Nginx'te proxy_buffering off; yeterlidir. Next.js route handler'larında yanıt gövdesini olduğu gibi döndürün:
export async function POST(request: Request) {
const upstream = await fetch('https://pb.sonecentral.com/api/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SONE_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': request.headers.get('Idempotency-Key') ?? crypto.randomUUID(),
},
body: await request.text(),
});
return new Response(upstream.body, {
status: upstream.status,
headers: {
'Content-Type': upstream.headers.get('Content-Type') ?? 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Sone-Conversation-Id': upstream.headers.get('X-Sone-Conversation-Id') ?? '',
},
});
}Bu şekilde sunucu anahtarını kullanırsanız, isteği gönderen ziyaretçiyi kendi tarafınızda doğrulamak ve sınırlamak sizin işinizdir. Sitenizdeki sohbet penceresi için tarayıcı anahtarı daha basittir.
Vercel AI SDK
useChat ile tarayıcıdan doğrudan /chat uç noktasına bağlanın, zengin mesajları tipli veri parçaları olarak çizin; ya da sunucuda OpenAI sağlayıcısıyla generateText ve streamText kullanın.
Zengin mesajlar
Ürün ve hizmet kartları, düğmeler, hızlı yanıtlar ve dosyalar SoneMessage nesnesi olarak gelir; her birinin düz metin karşılığı da vardır.