/** * R42 — system prompts, greeting, and the propose_graph_delta tool spec. * All user-facing strings live here so they can be localised in one place. */ export const BOT_NAME = 'R42'; export const STRINGS = { greeting: (name) => `Hoi ${name}, ik ben R42 — vraag iets over je weekonderwerp, of laat me uitleggen wat je in de quiz tegenkwam.`, placeholder: `Vraag R42 iets…`, send: 'Stuur', status: 'online · antwoordt direct', errorGeneric: 'Sorry, ik kon dat niet beantwoorden. Probeer het nog eens.', errorNoKey: 'Er is geen API-sleutel ingesteld. Vraag een beheerder om hem toe te voegen in Admin → Settings.', suggestionTitle: 'Ik hoorde iets nieuws — voeg toe aan de kennisgraaf?', suggestionAccept: 'Ja, voeg toe', suggestionReject: 'Nee, sla over', suggestionAppliedAdmin: 'Toegevoegd aan de graaf.', suggestionQueuedUser: 'Genoteerd — ik geef het door aan een beheerder.', suggestionDismissed: 'Oké, niets gedaan.', closeAria: 'Sluit chatvenster', openAria: 'Open R42 chatbot', }; const STABLE_PREAMBLE = [ `Je bent R42, de chatbot-avatar van Respellion — een leerplatform voor microlearning, quizzen en kennisontwikkeling.`, `Antwoord altijd in het Nederlands, kort en zakelijk-vriendelijk. Spreek de gebruiker aan met hun voornaam wanneer dat natuurlijk voelt.`, ``, `JE TAKEN:`, `1. Leg onderwerpen uit die in de kennisbasis staan.`, `2. Help de gebruiker quizvragen begrijpen ná afloop (niet tijdens een actieve quiz).`, `3. Verwijs bij twijfel terug naar het bronmateriaal of zeg eerlijk dat je het niet weet.`, ``, `JE KENNIS:`, `Je kennis is beperkt tot de Respellion-kennisgraaf die hieronder volgt. Als een vraag duidelijk buiten dit bereik valt, zeg dat dan eerlijk en stel voor dat de gebruiker de bron toevoegt via Admin → Sources.`, ``, `KENNISGRAAF VERFIJNEN:`, `Wanneer de gebruiker iets noemt dat duidelijk een nieuw topic, nieuwe relatie, proces of rol is — en dat nog niet in de kennisgraaf staat — gebruik dan de tool "propose_graph_delta" om een voorstel te maken. Verzin niets: stel alleen iets voor als de gebruiker het concreet noemt. Stel maximaal 3 topics en 5 relaties per beurt voor.`, ``, `STIJL:`, `- Houd antwoorden onder de 4 zinnen tenzij de gebruiker om uitleg vraagt.`, `- Geen markdown-headers; gewone Nederlandse tekst.`, `- Bij onzekerheid: "Ik weet het niet zeker — controleer dit in het handboek."`, ].join('\n'); /** * Build the R42 system prompt as three cacheable blocks: * 1. stable preamble (role, tasks, style) — cached * 2. KB context (current topics + relations) — cached (hash-bust comes in Phase 5) * 3. per-turn tail (user name + admin status) — NOT cached * * Returning an array lets `callLLM` pass it through unchanged so the * Anthropic API caches each block with the 5-minute ephemeral TTL. */ export function buildSystemPrompt({ userName, isAdmin, kbContext }) { const tail = [ `De gebruiker heet ${userName}.`, isAdmin ? `De gebruiker is beheerder; voorstellen die de tool genereert worden direct toegepast.` : `De gebruiker is geen beheerder; voorstellen worden in een goedkeuringswachtrij gezet.`, ].join('\n'); return [ { type: 'text', text: STABLE_PREAMBLE, cache_control: { type: 'ephemeral' } }, { type: 'text', text: kbContext, cache_control: { type: 'ephemeral' } }, { type: 'text', text: tail }, ]; } export const PROPOSE_GRAPH_DELTA_TOOL = { name: 'propose_graph_delta', description: 'Stel toevoegingen aan de Respellion-kennisgraaf voor wanneer de gebruiker een topic, proces, rol of relatie noemt die nog niet bestaat. Gebruik dit alleen wanneer je iets nieuws en concreets hoort — niet voor algemene speculatie.', input_schema: { type: 'object', properties: { reason: { type: 'string', description: 'Korte uitleg (1 zin, NL) waarom dit voorstel relevant is.', }, topics: { type: 'array', description: 'Maximaal 3 nieuwe topics.', items: { type: 'object', properties: { id: { type: 'string', description: 'kebab-case identifier, bv. "onboarding-buddy".', }, label: { type: 'string', description: 'Mens-leesbare naam.', }, type: { type: 'string', enum: ['concept', 'role', 'process'], }, description: { type: 'string', description: '1–2 zinnen Nederlandstalige beschrijving.', }, }, required: ['id', 'label', 'type', 'description'], }, }, relations: { type: 'array', description: 'Maximaal 5 nieuwe relaties.', items: { type: 'object', properties: { source: { type: 'string', description: 'topic id' }, target: { type: 'string', description: 'topic id' }, type: { type: 'string', enum: ['related_to', 'depends_on', 'part_of', 'executed_by'], }, }, required: ['source', 'target', 'type'], }, }, }, required: ['reason'], }, };