Mine sisu juurde
Sursa Developers

Küsimuste ja vastuste boti loomine

See juhend loob boti põhiosa, mis vastab küsimustele teie teadmusbaasi põhjal: vestlusrakenduses, teie abikeskuses või teie tootes. Assistent leiab vajalikud teadmised ja vastab, viidates kasutatud kaartidele; teie kood saadab küsimuse ja näitab tulemust.

Vajate võtit ulatustega search ja ask. Vastus kasutab ainult kaarte, mida võtmel on lubatud lugeda, seega määravad võtme roll, andmeprofiil ja kogud, millest bot saab rääkida. Andke klientidele mõeldud botile võti, mis näeb ainult avalikke kogusid.

Kolm viisi vastuse saamiseks

Lõpp-punktTagastabKasutage
POST /v1/searchSobivad tekstilõigud, parim esimesena. Kirjutatud vastust ei ole.Otsingukasti, „seotud artiklite“ ja oma järjestuse jaoks.
POST /v1/askTäieliku vastuse koos allikatega ühes vastuses.Bottide jaoks, mis postitavad iga vastuse kohta ühe sõnumi; taustatöödeks.
POST /v1/chatVastuse sündmuste voona selle koostamise ajal.Vestlusliideste jaoks, kus kasutaja peaks nägema vastuse tekkimist.
curl -X POST https://api.sursa.com/v1/search \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "how do I return a damaged item", "collection": "support"}'

Iga tabamus on kaardi tekstilõik: kaardi doc_id ja doc_name, jaotise heading, tekstilõigu text, leheküljed või tabeliread, kust see leiti, ning kaardi usaldus ja kontroll.

collection valib kogu, millest otsida; ilma selleta kasutatakse kogu, mille nimi on default. tags ja card_ids kitsendavad otsingu nende siltidega kaartidele või konkreetsetele kaartidele.

Küsimine ja täieliku vastuse saamine

curl -X POST https://api.sursa.com/v1/ask \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "How do I return a damaged item?", "collection": "support"}'

Väljal answer olev vastuse tekst on Markdown. Selle viited, näiteks [1] või [1, 3], vastavad loendi sources kirjete väärtusele ref; linkige iga viide kaardiga (doc_id), et lugeja saaks vastust kontrollida.

Tekstis olev arv, mida ei õnnestunud dokumentide põhjal kinnitada, võib ilmuda tähisena, näiteks ⟦n:a1⟧. verification.items kirjeldab iga tähist: selle marker, põhjus reason ja algne tekst original. Kui teie kood tähiseid ei käsitle, asendage iga tähis selle väärtusega original.

Kui vastust ei ole

Vastus on alati 200. Kui assistent ei saa vastata, ütleb empty, miks, ja answer sisaldab lühikest teadet, mida saate näidata:

emptyTähendus
no_hitsTeadmusbaasis ei ole midagi, mis küsimusele vastaks.
restrictedTeema on kaartides, mida võtmel ei ole lubatud lugeda.
guardrailKüsimus jääb väljapoole seda, millele organisatsioon lubab assistendil vastata.
no_data_for_periodKüsimus puudutab perioodi, mille kohta dokumente ei ole.

no_hits on kasulik signaal: see näitab, mida teie kasutajad küsivad, kuid mida teadmusbaas veel ei kata.

Režiimid „Fast“ ja „Precise“

Edastage "quality": "precise" (režiim „Precise“, täpne), et saada keerulisele küsimusele põhjalikum vastus, mis võtab rohkem aega. Vaikimisi kasutatav "fast" (režiim „Fast“, kiire) sobib enamikule küsimustele.

Vastuse voogedastus

Näidake vestlusliideses vastust selle koostamise ajal. POST /v1/chat väärtusega "stream": true vastab vormingus text/event-stream: üks sündmus sõnumi kohta, igaühel rida id: (selle number voos, alates 1-st) ja rida data: JSON-iga. JSON-i väli type ütleb, millise sündmusega on tegu.

id: 1
data: {"type":"meta","sources":3,"timings":{…},"answer_id":"…"}

id: 2
data: {"type":"sources","sources":[{"ref":1,"doc_id":"8c1d…","doc_name":"Returns","text":"…"}]}

id: 3
data: {"type":"token","text":"Send a photo of the item "}

id: 4
data: {"type":"token","text":"within 7 days [1]."}

id: 5
data: {"type":"done","answer":"Send a photo of the item within 7 days [1].","question_id":9812,"sources":[…]}
typeMida teha
metaVastus on alanud. Kui logite vastuseid, jätke answer_id alles.
sourcesKaardid, millel vastus põhineb. Võib tulla mitu korda; kehtib viimane.
tokenTükk vastuse tekstist. Lisage see näidatava teksti lõppu.
doneVastus on valmis. Sellel on samad väljad mis päringu /v1/ask vastusel.
errorVastus peatus. Voog lõpeb ilma sündmuseta done. Vt allpool.
followupsPärast sündmust done: soovitatud järgmised küsimused, mida saate pakkuda nuppudena.
kõik muuJätke vahele. Uued sündmusetüübid võivad ilmuda igal ajal.

Rida, mis algab märgiga : (näiteks : ping, mida saadetakse vastuse ettevalmistamise ajal iga 15 sekundi järel), on kommentaar. Jätke see vahele.

messages sisaldab senist vestlust, vanim esimesena, ja lõpeb kasutaja küsimusega. Saatke paar viimast vooru, et jätkuküsimused, näiteks „aga välismaalt tehtud tellimuste puhul?“, oleksid arusaadavad.

// Streams an answer and calls onText with each piece of text. Node.js 20+ or a browser.
export async function streamAnswer({ messages, collection, requestId, lastEventId, onText }) {
  const response = await fetch("https://api.sursa.com/v1/chat", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SURSA_API_KEY}`,
      "Content-Type": "application/json",
      Accept: "text/event-stream",
      ...(lastEventId ? { "Last-Event-ID": String(lastEventId) } : {}),
    },
    body: JSON.stringify({ messages, collection, stream: true, request_id: requestId }),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);

  const decoder = new TextDecoder();
  let buffer = "";
  let event = { id: 0, data: "" };
  let done = null;
  for await (const piece of response.body) {
    buffer += decoder.decode(piece, { stream: true });
    let newline;
    while ((newline = buffer.indexOf("\n")) >= 0) {
      const line = buffer.slice(0, newline).replace(/\r$/, "");
      buffer = buffer.slice(newline + 1);
      if (line.startsWith("id:")) event.id = Number(line.slice(3).trim());
      else if (line.startsWith("data:")) event.data += line.slice(5).trim();
      else if (line === "" && event.data) {
        const message = JSON.parse(event.data);
        if (message.type === "token") onText(message.text, event.id);
        else if (message.type === "done") done = message;
        else if (message.type === "error") throw Object.assign(new Error(message.message), { event: message, id: event.id });
        event = { id: 0, data: "" };
      }
    }
  }
  if (!done) throw new Error("the stream ended before the answer was complete"); // resume it
  return done;
}

Ilma väärtuseta "stream": true tagastab POST /v1/chat täieliku vastuse JSON-ina nagu /v1/ask.

Katkenud voo jätkamine

Ühendused katkevad: mobiilsidevõrk vahetub, puhverserveril saab aeg täis, teie server taaskäivitub. Andke igale küsimusele request_id ja vastus elab katkestuse üle:

  • Väärtusega request_id valmistatakse vastus ette ühendusest sõltumatult. Ühenduse katkemine seda ei peata: vastus lõpetatakse ja salvestatakse.
  • Jätkamiseks saatke sama päring uuesti sama väärtusega request_id ja päises Last-Event-ID viimase saadud sündmuse numbriga.
  • Kasutage ühe küsimuse kohta üht väärtust request_id (näiteks UUID) ja sama väärtust selle küsimuse igas korduskatses. Uus küsimus saab uue väärtuse request_id.

See, mida korduv päring saab, sõltub vastuse seisust:

OlukordKorduv päring saab
Vastust alles kirjutatakse või see just lõppesSündmused pärast väärtust Last-Event-ID, samade numbritega: voog lihtsalt jätkub.
Vastus lõppes mõni minut tagasiSalvestatud vastuse: token kogu tekstiga ja done, nummerdatud alates 1-st. Küsimust uuesti ei esitata.
Voog ei saa sellest numbrist jätkudaVastuse algusest, nummerdatud alates 1-st.

Teie kood käsitleb kõiki kolme juhtumit ühe reegliga: kui esimese saadud sündmuse number ei ole suurem kui Last-Event-ID, algab vastus otsast; tühjendage näidatu ja koostage see uuesti. Vastasel juhul lisage lõppu.

import { randomUUID } from "node:crypto";

async function answerWithResume(messages, collection, show) {
  const requestId = randomUUID();
  let text = "";
  let lastId = 0;
  for (let attempt = 1; ; attempt++) {
    let first = true;
    try {
      return await streamAnswer({
        messages, collection, requestId, lastEventId: lastId,
        onText(piece, id) {
          if (first && lastId && id <= lastId) text = ""; // the answer starts over
          first = false;
          text += piece;
          lastId = id;
          show(text);
        },
      });
    } catch (error) {
      const retry = error.event ? error.event.retry === true : true; // a network error, or a server restart
      if (!retry || attempt === 5) throw error;
      await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
    }
  }
}

Sursa API võib töötada mitmes serveris. Voog jätkub päise Last-Event-ID põhjal ainult serveris, mis vastust kirjutab; kui teie korduv päring jõuab teise serverisse, saab see salvestatud vastuse, kui see on valmis, või kirjutatakse vastus algusest uuesti. Ülaltoodud reegel katab mõlemad juhtumid ja küsimust ei loeta kunagi kaks korda.

Ilma väärtuseta request_id tühistab ühenduse sulgemine vastuse.

Vead voos

Enne voo algust tekkinud vead on tavalised HTTP-vead: 401, 403, 422 või 429 päisega Retry-After, kui organisatsioon on ära kasutanud oma küsimuste limiidi või kui tal on korraga liiga palju koostamisel olevaid vastuseid.

Kui voog on alanud, on selle HTTP-olek alati 200 ja probleem saabub sündmusena error:

{"type": "error", "code": "server_restart", "retry": true, "message": "…", "answer_id": "…"}
  • retry: true tähendab, et vastus peatus serveri tõttu, näiteks uuenduse ajal, ja võite saata sama päringu muutmata kujul uuesti.
  • Muud koodid, näiteks timeout, on loetletud API teatmikus. Käsitlege tundmatut koodi nagu iga muud voo viga: teavitage kasutajat ja laske tal uuesti küsida.

Vastuste hinnangud kasutajatelt

Igal vastusel on question_id. Saatke kasutaja hinnang lõpp-punktile POST /v1/questions/{question_id}/feedback: 1 tähendab abiks, -1 mitte abiks, valikuliselt koos väljadega reason ja note. Hinnangud aitavad organisatsioonil näha, millised kaardid vajavad tööd.

curl -X POST https://api.sursa.com/v1/questions/9812/feedback \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": -1, "reason": "outdated", "note": "The return window is now 14 days."}'

Tootmisboti kontroll-loend

  • Võti, millel on ainult search ja ask ning mis on piiratud kogudega, mida boti sihtrühm näha tohib.
  • Iga küsimuse jaoks request_id ja ülaltoodud jätkamise reegel.
  • Voo lugemise ajalõpp, mis on pikem kui 15 sekundit, et kommentaarid : ping hoiaksid voo elus.
  • Vastused kuvatakse Markdownina ja viited on lingitud oma allikatega.
  • Selge teade, kui empty on määratud, ja võimalus jõuda inimeseni.
  • Vastust 429 käsitletakse nii, et päises Retry-After antud aeg oodatakse ära.
  • Hinnangud saadetakse tagasi, et teadmusbaas aja jooksul paremaks muutuks.