sone centralgeliştirici

Sohbet sürekliliği

Anonim bir ziyaretçinin sohbeti sayfa yenilense de nasıl sürer, Sonecentral bir isteği hangi sohbete nasıl bağlar ve yeniden gönderilen bir mesajı nasıl tanır.

API'den gelen ziyaretçinin hesabı ya da kimliği yoktur. Sohbetin sürmesi için iki şey yeterli: sohbetin kimliği ve gösterdiğiniz mesajlar. İkisini de ziyaretçinin tarayıcısında saklarsınız.

Gerçek kayıt sunucudadır

Her sohbet Sonecentral'da tutulur; asistan geçmişi oradan okur. Sizin gönderdiğiniz geçmiş yalnız şu işler için kullanılır:

  • yeni mesajı ya da mesajları ayırmak,
  • kimlik gelmediğinde sohbeti bulmak,
  • yeniden gönderilen bir mesajı tanımak,
  • hiç sohbet yoksa yenisini bu geçmişle başlatmak.

Yani geçmişi değiştirerek asistanın hatırladığını değiştiremezsiniz. Ekibinizin Mesajlar ekranında gördüğü konuşma, asistanın gördüğü konuşmadır.

Sohbet nasıl bulunur

Sonecentral her istekte sohbeti şu sırayla arar ve ilk bulduğunu kullanır:

  1. Sohbetin kimliği, conv_ ile başlayan değer.
    • Chat Completions: gövdedeki conversation_id, gövdedeki sone.conversation_id, X-Sone-Conversation-Id başlığı, metadata.conversation_id, son olarak geçmişe olduğu gibi eklediğiniz asistan mesajının sone.conversation_id alanı. İlk dolu olan kullanılır.
    • Responses: conversation, yoksa X-Sone-Conversation-Id başlığı.
    • Vercel AI SDK: gövdedeki conversationId, X-Sone-Conversation-Id başlığı, son olarak geri gönderilen asistan mesajının metadata.conversationId alanı.
  2. Responses'ta previous_response_id.
  3. Responses'ta önceki çıktıların kimlikleri: item_reference öğeleri ya da msg_… kimliğiyle geri gönderilen asistan mesajları.
  4. Vercel AI SDK'da useChat'in id değeri, aşağıdaki dört koşul tutarsa.
  5. user ya da safety_identifier. Yalnız sunucu anahtarıyla; Vercel AI SDK'da gövdedeki user. Aynı değer, aynı asistanla hep aynı sohbeti bulur.
  6. Önceki mesajların parmak izi. İsteğin geçmişi sohbetin ilk mesajından başlıyorsa Sonecentral turdan sonra o ana kadarki mesajların özetini kaydeder; aynı geçmişle gelen istek, aşağıdaki dört koşul tutarsa o sohbete bağlanır. Sohbete kimlikle ulaşıp yalnız yeni mesajını gönderen bir istek parmak izi kaydetmez.
  7. İlk mesajın yeniden denemesi. Sohbetin ilk mesajı aynı Idempotency-Key ile ya da Vercel AI SDK'da aynı mesaj kimliğiyle 10 dakika içinde yeniden gelirse, mesaj o sohbetin hâlâ son ziyaretçi mesajıysa, açtığı sohbete döner. Tarayıcı anahtarıyla deneme ayrıca aynı ağ adresinden, Vercel AI SDK'da aynı useChat kimliğiyle gelmelidir: Tekrar denemek.
  8. Hiçbiri tutmazsa yeni sohbet açılır. Gönderdiğiniz önceki mesajların son 40'ı yeni sohbete geçmiş olarak aktarılır, her biri en fazla 4.000 karakter. Tarayıcı anahtarıyla yalnız ziyaretçinin kendi satırları aktarılır; asistan satırları alınmaz, çünkü tarayıcıda onları herkes değiştirebilir.

İlk üç yoldaki kimlikleri Sonecentral üretir; beşinci yoldaki değeri yalnız sunucunuz bilir. Bunlar olduğu gibi kabul edilir:

  • Bir sohbet kimliği ya da previous_response_id bu çalışma alanında bir API sohbeti göstermiyorsa istek yeni sohbet açmaz; 404 conversation_not_found ya da 404 previous_response_not_found alır.
  • Sohbet başka bir asistanla açılmışsa istek 400 conversation_model_mismatch alır.
  • previous_response_id ile conversation birlikte gönderilemez: 400 conflicting_parameters.

İlk iki durumda o kimliği bırakıp yeni sohbet başlatın.

Dördüncü ve altıncı yol, yani useChat'in id değeri ve parmak izi, tarayıcının seçtiği değerlere dayanır. Bu yüzden sohbeti yalnız şu dört koşul birlikte tutarsa sürdürür:

  1. Sohbet asistandadır. Ekibinizin devraldığı bir sohbete bu iki yolla hiç ulaşılmaz.
  2. Gönderdiğiniz geçmiş kayıttaki en yeni karşılıklaşmayı kelimesi kelimesine taşır: ziyaretçinin son mesajı ve ona verilen yanıtın tamamı. Yanıt, ekibinizin o arada yazdıkları da dahil, kayıtta nasılsa öyle karşılaştırılır.
  3. O yanıtın en az bir mesajını asistan bu sohbette kendisi yazmıştır. Platformun her sohbette aynı olan hazır cümleleri (örneğin asistan yanıt üretemediğinde giden "Kusura bakmayın, bunu ekip arkadaşıma soruyorum. Size buradan dönüş yapacağız."), ekibinizin mesajları ve paylaşılan ürün, hizmet ya da dosya kartları tek başına yetmez, çünkü başka biri de onları bilebilir. Asistanın ayarlarındaki karşılama mesajı da yetmez: asistan onu her ziyaretçiye aynı yazar.
  4. Gönderdiğiniz geçmiş sohbetin ilk mesajından başlar: sohbette kayıtlı bütün ziyaretçi mesajlarını sırasıyla taşır. Yalnız son karşılıklaşma yetmez, çünkü o çoğu zaman tahmin edilebilir: "Teşekkürler" ve asistanın her seferinde yazdığı aynı nezaket cümlesi gibi. Yeni sohbet geçmişinizin yalnız son 40 mesajını aktardıysa, daha eski mesajlarınız geçmişin başında kalabilir.

Karşılaştırma boşlukları ve satır sonlarını yok sayar, art arda gelen aynı roldeki mesajları tek mesaj sayar. Tarayıcı anahtarıyla yeni sohbete aktarılan önceki satırlar bu karşılaştırmayı bozmaz. Tek istisna, aynı ziyaretçinin sohbetin son mesajını kendi adıyla, yani aynı Idempotency-Key ya da Vercel AI SDK'da aynı mesaj kimliğiyle yeniden denemesidir: Tekrar denemek.

Bir koşul tutmazsa istek yeni bir sohbet açar ve asistan orada yanıt verir. Şu durumlar da buna girer:

  • Sohbet ekipte.
  • Son yanıt yalnız hazır bir cümleden, ekibinizin bir mesajından ya da bir paylaşımdan oluşuyor.
  • Ziyaretçinin son mesajlarına henüz yanıt verilmedi, örneğin tur sessiz kaldı veya reddedildi. Geçmişiniz o mesajları ister arkalarında boş bir asistan mesajıyla (Chat Completions'ta content değeri "" ya da null, Vercel AI SDK'da metni olmayan bir asistan mesajı), ister yeni mesajlar olarak taşısın.
  • Ekibiniz son mesaja, sizin geçmişe eklemediğiniz bir yanıt yazdı.
  • Son yanıt yalnız asistanın karşılama mesajı.
  • Geçmişiniz sohbetin ilk mesajından başlamıyor, örneğin sohbete bir süre kimlikle ulaşıp yalnız yeni mesajları gönderdiniz, sonra kimliği bırakıp kısaltılmış bir geçmiş gönderiyorsunuz.
  • Yanıtını alamadığınız bir isteği adı olmadan, aynen yeniden gönderiyorsunuz.

Bunların hepsi başka biri tarafından tahmin edilebilir. Sohbeti yalnız onlarla bulmak başka bir ziyaretçiyi bu sohbete sokabilir ve ekibinizin ona yazdıklarını gösterebilirdi. İki sohbetin paylaştığı bir useChat kimliği ya da parmak izi bırakılır ve bir daha kullanılmaz. Bir belirsizlikte sohbet ikiye bölünür.

Kalan tek sınır: başka biri bir sohbetin bütün geçmişini kelimesi kelimesine yazabilirse bu iki yol onu o sohbete bağlayabilir. Bu pratikte henüz tek karşılıklaşması olan bir sohbette olur: tahmin edilebilir bir ilk mesaj, örneğin "Merhaba", ve asistanın ona her ziyaretçiye kelimesi kelimesine aynı yazdığı bir yanıt. Ziyaretçi ikinci mesajını gönderdiğinde bu yol kapanır. Tarayıcı anahtarında geçmişin parmak izi ziyaretçinin ağ adresine de bağlıdır: bunu ancak aynı ağdaki biri yapabilir, adresi değişen ziyaretçi ise yeni bir sohbet açar. Bu yüzden her istekte conversation_id gönderin.

Bir sohbette kalmak için her istekte sohbetin kimliğini gönderin, her zaman. Her mesaja da bir Idempotency-Key ekleyin. Kimlik her yanıtın X-Sone-Conversation-Id başlığında ve gövdesinde gelir: Chat Completions'ta sone.conversation_id, Responses'ta conversation.id, Vercel AI SDK'da asistan mesajının metadata.conversationId alanı.

Tekrar denemek

Ağ hatasından, zaman aşımından ya da bir 5xx yanıtından sonra aynı mesajı yeniden göndermek güvenli olmalı. Yeniden deneme sohbete ulaştığında Sonecentral bir ziyaretçi mesajını iki kez kaydetmez ve asistan aynı mesaja iki kez yanıt vermez. Deneme sohbete sohbetin kimliğiyle, gösterilen yanıtla (Responses'ta previous_response_id ya da item_reference) ya da mesajın adıyla ulaşır; ne kimlik ne ad taşıyan bir deneme ulaşmaz (aşağıda). Mesajı nasıl tanıdığı, isteğin ne taşıdığına bağlıdır:

  • Idempotency-Key başlığı isteğin en yeni ziyaretçi mesajına bir ad verir, en fazla 200 karakter. Aynı anahtar aynı metinle gelirse mesaj yeniden kaydedilmez: yanıt verilmişse aynı yanıt yeniden döner, verilmemişse, örneğin önceki deneme reddedildiyse, şimdi yanıtlanır. Aynı anahtar başka bir metinle gelirse istek 422 idempotency_key_reused alır ve x-should-retry: false taşır. Her yeni mesajda yeni bir anahtar üretin.
  • Vercel AI SDK'da her ziyaretçi mesajı kendi id'si ve metniyle adlandırılır; ayrıca bir şey göndermeniz gerekmez. Kimliği aynı kalıp metni değişen, yani düzenlenen bir mesaj yeni bir mesajdır.
  • Adı olmayan mesajlar için, sohbete kimlikle ulaşan istek gönderdiğiniz geçmişe göre sohbetin sonuna yerleştirilir. Yanıtı verilmiş bir mesajın yeniden denemesi kayıtlı yanıtı yeniden alır. Reddedilen bir önceki isteğin kaydettiği mesajlar ikinci kez kaydedilmez. Ziyaretçi bir mesajı yanıtını aldıktan sonra yeniden yazarsa, örneğin yine "evet" derse, bu yeni bir mesajdır. Sohbetin kimliğiyle gelen ve henüz yanıtlanmamış mesajları taşıyan bir isteğin yeniden denemesi de onları ikinci kez kaydetmez; o arada yanıtlandılarsa yanıtları yeniden gelir. Tek istisna: siz son mesajı gönderdikten sonra ekibinizden biri yazdıysa, adı olmayan bir yeniden deneme mesajı ikinci kez kaydeder. Bunun için her mesaja bir Idempotency-Key ekleyin. Kimlik ve ad olmadan, yalnız geçmişle gelen bir yeniden deneme sohbeti bulmaz: yeni bir sohbet açar ve asistan mesaja yeniden yanıt verir. Reddedilen ya da ekipte bekleyen bir mesajı yeni mesajlarla birlikte yeniden gönderen istek de öyle. Responses'ta yalnız yeni mesajı previous_response_id ya da item_reference ile gönderen istemci için, gösterilen yanıtın karşılıklaşması geçmişin yerine geçer.
  • Ne geçmiş ne anahtar gönderen bir istemcinin, yani yalnız sohbet kimliğini ve yeni mesajı gönderenin yeniden denemesi yeni bir mesajdan ayırt edilemez. Böyle çalışıyorsanız her mesajda Idempotency-Key gönderin.
  • Tarayıcı anahtarıyla, istek sohbetin kimliğini ya da gösterilen yanıtı taşımıyorsa, örneğin ilk mesajda, mesajın adı ziyaretçinin ağ adresine ve Vercel AI SDK'da useChat kimliğine de bağlıdır. Başka bir adresten gelen deneme, örneğin Wi-Fi'dan mobil veriye geçince, yeni bir sohbet açar ve yeniden yanıtlanır. Aynı adresi paylaşan iki ziyaretçi, örneğin aynı ofis ağındakiler, aynı Idempotency-Key'i ya da aynı useChat kimliğiyle aynı mesaj kimliğini aynı metinle gönderirse ilk mesajları aynı sohbete düşebilir. Bu değerleri her ziyaretçi için rastgele üretin; Vercel AI SDK'nın varsayılanı budur. Sohbetin kimliğiyle ya da gösterilen yanıtla gelen deneme her adresten tanınır.

Ayrıca:

  • Reddedilen bir ilk mesaj, örneğin ai_turn_ceiling, plan_required ya da assistant_paused alan, sohbet açmaz ve hiçbir şey kaydetmez. Biraz sonra yeniden gönderebilirsiniz.
  • Önceki deneme hâlâ yanıtlanıyorsa istek 409 conversation_busy alır. Retry-After kadar bekleyip aynı anahtarla yeniden gönderin.
  • OpenAI SDK'ları Idempotency-Key başlığını kendileri eklemez. Node'da isteğin ikinci argümanında headers, Python'da extra_headers ile verin: Chat Completions. SDK'nın kendi yeniden denemeleri aynı başlığı gönderir.

fetch ile çalışıyorsanız:

gonder.js
// Aynı mesajı en fazla dört kez dener; her denemede aynı Idempotency-Key gider.
export async function sendOnce(url, key, body) {
  const name = crypto.randomUUID(); // bu mesajın adı
  let response;
  for (let attempt = 0; attempt < 4; attempt += 1) {
    if (attempt > 0) await new Promise((done) => setTimeout(done, waitSeconds(response, attempt) * 1000));
    try {
      response = await fetch(url, {
        method: 'POST',
        headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json', 'Idempotency-Key': name },
        body: JSON.stringify(body),
      });
    } catch {
      response = undefined; // ağ hatası: yeniden dene
      continue;
    }
    const retry =
      response.headers.get('x-should-retry') !== 'false' &&
      (response.status === 409 || response.status === 429 || response.status >= 500);
    if (!retry) return response;
  }
  if (response) return response;
  throw new Error('Mesaj gönderilemedi.');
}

function waitSeconds(response, attempt) {
  const after = Number(response?.headers.get('retry-after'));
  return after > 0 && after <= 60 ? after : 2 ** (attempt - 1);
}

Tarayıcıda saklamak

Sitenizin köşesinde açılan bir sohbet penceresinde sohbetin kimliğini ve gösterdiğiniz mesajları localStorage'da tutun. Ziyaretçi sayfayı yenilese ya da başka bir sayfaya geçse de sohbet kaldığı yerden sürer.

sohbet-kaydi.js
const STORE = 'sone-chat-v1';

export function loadChat() {
  try {
    return JSON.parse(localStorage.getItem(STORE)) ?? { conversationId: null, messages: [] };
  } catch {
    return { conversationId: null, messages: [] };
  }
}

export function saveChat(chat) {
  try {
    localStorage.setItem(STORE, JSON.stringify(chat));
  } catch {
    // Gizli pencere ya da dolu depo: sohbet bu sekmede sürer, yenilenince baştan başlar.
  }
}

export function forgetChat() {
  try {
    localStorage.removeItem(STORE);
  } catch {}
}

Her turdan sonra:

const chat = loadChat();
chat.conversationId = sone.conversation_id; // yanıttan
chat.messages.push({ role: 'user', content: text }, ...sone.messages.map((m) => ({ role: 'assistant', message: m })));
saveChat(chat);

Sonraki istekte kimliği gönderin:

headers: {
  Authorization: `Bearer ${KEY}`,
  'Content-Type': 'application/json',
  'Idempotency-Key': crypto.randomUUID(), // yeniden denerken aynısını kullanın
},
body: JSON.stringify({
  model: 'default',
  conversation_id: chat.conversationId ?? undefined,
  messages: [{ role: 'user', content: text }],
}),

Kimlik gönderdiğinizde yalnız yeni mesajı yollamanız yeterli. Yalnız yeni mesajı gönderdiğiniz için Idempotency-Key burada gereklidir.

"Yeni sohbet" düğmesi forgetChat() çağırır; sonraki mesaj yeni bir sohbet açar. İstek 404 conversation_not_found ya da 400 conversation_model_mismatch alırsa da saklanan kimliği silin. Bu tarifin tamamı: Site içine sohbet penceresi.

Vercel AI SDK'da

useChat'te sohbetin kimliği asistan mesajlarının metadata.conversationId alanındadır ve mesajlarla birlikte geri gider. Bu yüzden hem useChat'in id'sini hem mesajları, asistan mesajlarının metadata alanıyla birlikte saklayıp geri verin. Yalnız id'yi saklamak yetmez: tarayıcının seçtiği kimlik sohbeti yalnız dört koşul tutarsa sürdürür, sohbet ekipteyken hiç sürdürmez.

'use client';

import { useChat } from '@ai-sdk/react';
import type { ChatTransport } from 'ai';
import { useEffect, useState } from 'react';
import type { SoneUIMessage } from '@/lib/sone';

function storedChat(): { id: string; messages: SoneUIMessage[] } {
  try {
    const saved = JSON.parse(localStorage.getItem('sone-chat-v1') ?? 'null');
    if (saved?.id) return saved;
  } catch {}
  return { id: crypto.randomUUID(), messages: [] };
}

export function useStoredChat(transport: ChatTransport<SoneUIMessage>) {
  const [initial] = useState(storedChat);
  const chat = useChat<SoneUIMessage>({ id: initial.id, messages: initial.messages, transport });

  useEffect(() => {
    if (chat.status === 'submitted' || chat.status === 'streaming') return;
    try {
      localStorage.setItem('sone-chat-v1', JSON.stringify({ id: initial.id, messages: chat.messages }));
    } catch {}
  }, [chat.status, chat.messages, initial.id]);

  return chat;
}

Bu kanca yalnız tarayıcıda çalışmalı. Next.js'te onu kullanan bileşeni bir 'use client' dosyasında dynamic(() => import(...), { ssr: false }) ile yükleyin. Uzun sohbetlerde yalnız son mesajı göndermek için: Vercel AI SDK. Örneğin tamamı: Site içine sohbet penceresi.

Sunucu anahtarı ve user

Kendi sisteminizde oturum açmış kullanıcılar için user alanına kendi kullanıcı kimliğinizi koyabilirsiniz. Aynı değer aynı asistanla aynı sohbeti bulur, böylece kullanıcı farklı cihazlardan aynı sohbeti sürdürür.

user yalnız sohbeti bulmak içindir, kimlik kanıtı değildir: asistan bu kişiyi yine anonim ziyaretçi sayar ve kişisel kayıtlarını paylaşmaz. Ayrıntılar: Siparişler, randevular ve gizlilik. Tarayıcı anahtarıyla gönderilen user yok sayılır, çünkü ziyaretçi onu değiştirebilir.

E-posta ya da telefon gibi kişisel bir veriyi user olarak göndermeyin. Kendi sisteminizdeki anlamsız bir kimlik yeterli.

Sınırlar

  • Bir sohbette en fazla 200 ziyaretçi mesajı olur. Sonrası 400 conversation_full alır; yeni sohbet başlatın.
  • Tek istekte en fazla 12 yeni ziyaretçi mesajı gönderilir.
  • Yeni bir mesaj en fazla 4.000 karakterdir.
  • Yeni bir sohbete en fazla son 40 önceki mesaj aktarılır.
  • İstek gövdesi en fazla 1 MB'dir. Uzun geçmişleri her istekte göndermek yerine sohbetin kimliğini gönderin.

Bu sayfada