sone centralgeliştirici

İnsan devri ve ekip yanıtları

Asistan sohbeti ekibinize devrettiğinde ne olur, ekibin yanıtları ziyaretçiye nasıl ulaşır.

API'den açılan her sohbet, ekibinizin Sonecentral'daki Mesajlar ekranında API kaynağıyla görünür. Ziyaretçinin adı orada API ziyaretçisi olarak geçer, çünkü kimliği bilinmez. Ekip sohbeti okuyabilir, devralabilir ve ziyaretçiye oradan yazabilir.

Devir nasıl olur

Asistan WhatsApp'ta nasıl davranıyorsa burada da öyle davranır: ziyaretçi bir insanla görüşmek isterse, asistan bir soruyu yanıtlayamazsa ya da işletmenin kuralları gerektirirse sohbeti ekibe devreder ve ziyaretçiye bunu söyler.

Ekibinizden biri Mesajlar ekranından sohbete yazdığında ya da bir ürün, hizmet veya belge paylaştığında da sohbet ekibe geçer. Asistan o andan sonra bu sohbette yanıt vermez; ekip sohbeti asistana geri verene kadar ekipte kalır.

Turun sonucu status alanında gelir:

statusNe olduNe yapmalı
answeredAsistan yanıt verdi.Bir şey gerekmez.
handoffSohbet bu turda ekibe geçti; ziyaretçiye bir mesaj, genellikle devir mesajı yazıldı.Mesajı gösterin, ekip yanıtlarını okumaya başlayın.
humanSohbet ekipte; asistan yanıt vermedi, mesaj ekibe ulaştı.Ekip yanıtlarını okumaya başlayın ya da sürdürün.
silentYanıt gerekmedi.Bir şey gerekmez.

status her biçimde gelir:

  • Chat Completions: akışsız yanıtta choices[0].message.sone.status, akışta son parçanın üst düzeyindeki sone.status.
  • Responses: yanıtın üst düzeyindeki sone.status.
  • Vercel AI SDK: finish parçasının messageMetadata.status alanı.

Sohbet ekipteyken ziyaretçi yazmaya devam edebilir; mesajları her zamanki uç noktalarla gönderin. Her biri human durumuyla döner ve ekibe ulaşır.

Sohbet ekipteyken ona yalnız sohbetin kimliğiyle ulaşılır, bu yüzden her istekte kimliği gönderin. Kimliği göndermeyen, yalnız role ve content saklayan ya da yalnız useChat'in id değerine dayanan bir istemcinin sonraki mesajı, geçmişi ne taşırsa taşısın ayrı bir sohbet açar ve o sohbette asistan yanıt verir: Sohbet sürekliliği. Sohbet asistana döndükten sonra da kimliği göndermeye devam edin: kimlik olmadan sohbet yalnız son yanıtı asistan kendisi yazdıysa, geçmişiniz o yanıtı kelimesi kelimesine taşıyorsa ve sohbetin ilk mesajından başlıyorsa sürer; hazır bir cümle, karşılama mesajı ya da ekibinizin bir mesajı buna yetmez. Ekibin yanıtlarını okumak için de kimliğe ihtiyacınız var. Her mesaja bir Idempotency-Key de ekleyin: ekip yazdıktan sonra adı olmayan bir mesajın yeniden denemesi ikinci kez kaydedilebilir.

Ekibin yanıtlarını okumak

Ekibin yazdıkları akışa gelmez, çünkü ne zaman yazacakları belli değildir. Onları sohbetin mesajlarından okursunuz:

GET https://pb.sonecentral.com/api/v1/conversations/{id}/messages?after={son_mesaj_kimliği}
curl "https://pb.sonecentral.com/api/v1/conversations/conv_x8k2m9p4q7w1r3t/messages?after=msg_p4q7w1x8k2m9a5z" \
  -H "Authorization: Bearer $SONE_API_KEY"
{
  "object": "list",
  "data": [
    {
      "id": "msg_v2k8r4t1m9q6w3e",
      "role": "user",
      "type": "text",
      "text": "Perşembe 15.00 olur mu?",
      "created_at": 1760867520
    },
    {
      "id": "msg_s7t4f2k9w1q3n8d",
      "role": "staff",
      "type": "text",
      "text": "Merhaba, ben Elif. Perşembe 15.00 için sizi not aldım, onaylayınca buradan yazacağım.",
      "created_at": 1760867538
    }
  ],
  "has_more": false,
  "mode": "team",
  "first_id": "msg_v2k8r4t1m9q6w3e",
  "last_id": "msg_s7t4f2k9w1q3n8d"
}

Alan

Tür

AlanAnlamı
dataMesajlar, eskiden yeniye. SoneMessage biçimindedir; ekip de ürün, hizmet, belge ve düğme gönderebilir.
has_moretrue ise sayfaya sığmayan mesajlar var. last_id ile hemen yeniden isteyin.
mode"team": sohbet ekipte. "assistant": asistan yanıt veriyor.
first_id, last_idSayfadaki ilk ve son mesajın kimliği. data boşsa bu alanlar gelmez.
  • Liste sohbetin bütün satırlarını taşır: ziyaretçinin kendi mesajları role: "user", asistanınkiler role: "assistant", ekibinizinkiler role: "staff" ile gelir. Ziyaretçinin mesajlarını zaten gösterdiğiniz için çizmeyin.
  • Bir sonraki istekte after olarak yanıttaki last_id'yi gönderin. Böylece ziyaretçinin satırları da imleci ilerletir ve aynı mesajı iki kez okumazsınız.
  • after bu sohbete ait bir mesajın kimliği değilse istek 400 invalid_cursor alır. İmleci bırakıp sohbeti baştan okuyun.
  • Sohbet bu çalışma alanında bir API sohbeti değilse ya da anahtarınızın çağıramadığı bir asistanın sohbetiyse 404 conversation_not_found döner. Aynı çalışma alanının, aynı asistanı çağırabilen her anahtarı bu sohbeti okuyabilir.
  • Her okuma anahtarın sınırlarına sayılır. Tarayıcı anahtarında okumaların kendi saatlik sınırı vardır; mesaj göndermek için ayrılan sınırdan düşmez.

Ekibin paylaştıkları

Ekibiniz Mesajlar ekranından yalnız yazmaz. WhatsApp'ta olduğu gibi envanterden bir ürünü, bir hizmeti ya da Bilgi Bankası'ndan bir belgeyi de sohbete paylaşabilir. Paylaşım size asistanın paylaşımlarıyla aynı yapıda gelir, yalnız role değeri "staff" olur:

PaylaşılantypeNerede
Ürün ya da hizmetcatalogKayıt catalog.items içindedir. kind ürün için inventory, hizmet için offering olur. Ekibin her paylaşımı tek kayıt taşır.
Belgefilefile.url belgenin imzalı indirme bağlantısıdır.
{
  "id": "msg_k8r3t6w1q9m2x5c",
  "role": "staff",
  "type": "catalog",
  "text": "Saç kesimi\nYıkama ve fön dahil.\n750₺\n45 dakika\nhttps://www.ornek.com/hizmet/sac-kesimi",
  "created_at": 1760867590,
  "catalog": {
    "format": "card",
    "items": [
      {
        "id": "o5n2k8w4q1t7m3x",
        "kind": "offering",
        "title": "Saç kesimi",
        "description": "Yıkama ve fön dahil.",
        "price": "750₺",
        "detail": "45 dakika",
        "url": "https://www.ornek.com/hizmet/sac-kesimi",
        "button_title": "Hizmeti incele"
      }
    ]
  }
}
  • Paylaşım, ekibin yazdığı bir mesaj gibi sohbeti ekibe geçirir ve akışa gelmez; onu bu mesaj listesinden okursunuz. Asistanın yanıt verdiği bir sohbete paylaşım yapıldıysa ziyaretçinin sonraki mesajı human durumuyla döner; son turda aldığınız son mesajdan sonrasını okuduğunuzda paylaşım da gelir.
  • Ekip, ziyaretçinin bir mesajını bir paylaşımla yanıtladıysa ve siz o mesajı aynı Idempotency-Key ile yeniden gönderirseniz, paylaşım o turun mesajları arasında da gelir.
  • Her paylaşımın text alanı doludur. Kart ya da dosya çizmeyen bir istemci yalnız onu gösterebilir; çok uzun bir kaydın text'i kısalır, kaydın tamamı catalog.items'tadır.
  • Kartları çizmek ve belgeyi indirmek asistanın paylaşımlarıyla aynıdır: Zengin mesajlar.

Sohbetin durumu

Yalnız sohbetin kimde olduğunu öğrenmek için mesajları okumanız gerekmez:

GET https://pb.sonecentral.com/api/v1/conversations/{id}
{
  "id": "conv_x8k2m9p4q7w1r3t",
  "object": "conversation",
  "created_at": 1760866000,
  "model": "k3m9x2p7q1w8e4r",
  "mode": "team"
}

model sohbetin bağlı olduğu asistanın kimliğidir. mode "assistant" ise asistan yanıt veriyor, "team" ise sohbet ekipte.

Sayfa yeniden açıldığında ekip yanıtlarını okumaya devam edip etmeyeceğinize buna bakarak karar verebilirsiniz.

Ne sıklıkla sormalı

Pencere açıkken ve sohbet ekipteyken beş saniyede bir sormak yeterli. Sekme arka plandayken ya da pencere kapalıyken sormayın; ziyaretçi geri döndüğünde bir kez sorup eksikleri tamamlayın.

Tarayıcı anahtarında okumaların kendi sınırı vardır: anahtar başına saatte 20.000. Beş saniyede bir soran her ziyaretçi saatte 720 okuma yapar; bu, bir anahtarla aynı anda yaklaşık 27 ziyaretçiye yeter. Sitenizde aynı anda daha çok ziyaretçi ekip yanıtı bekleyebiliyorsa aralığı uzatın:

  • Devirden ya da ekibin son mesajından sonraki ilk iki dakika beş saniyede bir, sonra 30 ile 60 saniyede bir sorun.
  • Yarım saat yeni mesaj gelmezse ziyaretçi yazana ya da pencereyi yeniden açana kadar sormayı bırakın.
  • 429 gelirse Retry-After kadar bekleyip öyle sorun.

Ayrıntılar: Limitler.

// İmleçten sonraki ekip ve asistan mesajlarını, sayfa sayfa sonuna kadar okur.
async function readTeamReplies(conversationId, cursor) {
  const messages = [];
  let mode = 'team';
  for (;;) {
    const url = new URL(`https://pb.sonecentral.com/api/v1/conversations/${encodeURIComponent(conversationId)}/messages`);
    if (cursor) url.searchParams.set('after', cursor);
    const response = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } });
    if (!response.ok) break;
    const page = await response.json();
    messages.push(...page.data.filter((message) => message.role !== 'user'));
    cursor = page.last_id ?? cursor;
    mode = page.mode;
    if (!page.has_more) break;
  }
  return { messages, cursor, mode }; // mode 'assistant' ise sormayı bırakın
}

İlk imleç, son turda aldığınız son mesajın kimliğidir. Sonraki her okumada dönen cursor'ı saklayıp onu gönderin.

Tam bir örnek: Site içine sohbet penceresi.

Asistan yanıt vermediğinde

Açık bir sohbette ai_turn_ceiling, assistant_quota_exhausted, plan_required ya da assistant_paused gelirse ziyaretçinin mesajı kaydedilir ve ekibe ulaşır ama asistan yanıt vermez. Sohbet bu sırada ekibe geçmez, mode "assistant" kalır. Ekipten biri yazdığında ya da bir kayıt paylaştığında sohbet ekibe geçer ve yanıt mesaj listesinde görünür.

Bu yüzden bu hatalardan sonra, mode "assistant" olsa da mesajları okumayı sürdürün. Ekipten bir yanıt geldiğinde ya da ziyaretçinin sonraki mesajı yanıtlandığında bırakın.

Aynı hatalar yeni bir sohbetin ilk mesajında gelirse hiçbir şey kaydedilmez ve sohbet açılmaz. Ziyaretçiye biraz sonra yeniden denemesini söyleyin.

Sohbet asistana geri döner

Ekibiniz işi bitince sohbeti Mesajlar ekranından asistana geri verebilir. O andan sonra mesaj listesindeki mode "assistant" olur; ekip yanıtlarını okumayı bırakın. Ziyaretçinin sonraki mesajı yine asistan tarafından yanıtlanır ve status answered olur.

Bu sayfada