Add comprehensive chatbot improvements: better LLM prompts, caching, confidence labels, smarter follow-ups
This commit is contained in:
@@ -89,6 +89,34 @@ let lastVisitorStatsWrite = { ok: true, at: null, error: null }
|
||||
let lastHitStatsWrite = { ok: true, at: null, error: null }
|
||||
let lastBackupStatus = { ok: true, at: null, error: null, file: null }
|
||||
const adminSessions = new Map()
|
||||
const llmResponseCache = new Map()
|
||||
const MAX_CACHE_SIZE = 500
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000
|
||||
|
||||
function getCacheKey(question, draftAnswer) {
|
||||
return sha256(`${question}||${draftAnswer}`).slice(0, 16)
|
||||
}
|
||||
|
||||
function getCachedResponse(cacheKey) {
|
||||
const cached = llmResponseCache.get(cacheKey)
|
||||
if (cached && Date.now() - cached.at < CACHE_TTL_MS) {
|
||||
return cached.text
|
||||
}
|
||||
if (cached) {
|
||||
llmResponseCache.delete(cacheKey)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function setCachedResponse(cacheKey, text) {
|
||||
if (llmResponseCache.size >= MAX_CACHE_SIZE) {
|
||||
const firstKey = llmResponseCache.keys().next().value
|
||||
if (firstKey) llmResponseCache.delete(firstKey)
|
||||
}
|
||||
llmResponseCache.set(cacheKey, { text, at: Date.now() })
|
||||
}
|
||||
|
||||
function sha256(value)
|
||||
|
||||
function sha256(value) {
|
||||
return createHash('sha256').update(value).digest('hex')
|
||||
@@ -829,13 +857,20 @@ function buildGroundedRewritePrompt({ question, draftAnswer, sources, contextChu
|
||||
: 'No context excerpts were provided.'
|
||||
|
||||
return [
|
||||
'You are rewriting a Bible-study chatbot answer.',
|
||||
'Hard rules:',
|
||||
'1) Use ONLY facts in DRAFT ANSWER and CONTEXT EXCERPTS.',
|
||||
'2) If DRAFT ANSWER is uncertain or says content is missing, keep that uncertainty and do not fill gaps.',
|
||||
'3) Do not add verses, names, history, or claims not present in the provided text.',
|
||||
'4) Keep a warm pastoral tone but concise.',
|
||||
'5) Return plain text only. No markdown headers, no bullets unless needed for clarity.',
|
||||
'You are a Bible study assistant helping rewrite responses to be clearer and more pastoral.',
|
||||
'',
|
||||
'HARD RULES:',
|
||||
'1) Use ONLY facts from DRAFT ANSWER and CONTEXT EXCERPTS. Never add new information.',
|
||||
'2) If the draft is uncertain or incomplete, preserve that. Do not fill gaps or speculate.',
|
||||
'3) Do not invent verses, names, historical details, or theological claims.',
|
||||
'4) Maintain a warm, encouraging pastoral tone—like Nate teaching directly.',
|
||||
'5) Keep sentences clear and direct. Avoid jargon unless biblical.',
|
||||
'6) Return plain text only. Use paragraph breaks but no markdown formatting.',
|
||||
'',
|
||||
'GOALS:',
|
||||
'- Help the reader understand Scripture better',
|
||||
'- Stay faithful to Nate\'s teaching and tone',
|
||||
'- Be encouraging but honest about limitations',
|
||||
'',
|
||||
`QUESTION:\n${question}`,
|
||||
'',
|
||||
@@ -850,6 +885,12 @@ function buildGroundedRewritePrompt({ question, draftAnswer, sources, contextChu
|
||||
async function rewriteWithChatbotLlm({ question, draftAnswer, sources, contextChunks }) {
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), CHATBOT_LLM_TIMEOUT_MS)
|
||||
const cacheKey = getCacheKey(question, draftAnswer)
|
||||
const cached = getCachedResponse(cacheKey)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const prompt = buildGroundedRewritePrompt({
|
||||
@@ -897,7 +938,9 @@ async function rewriteWithChatbotLlm({ question, draftAnswer, sources, contextCh
|
||||
throw new Error('LLM returned an empty response')
|
||||
}
|
||||
|
||||
return rewritten.slice(0, 3500)
|
||||
const result = rewritten.slice(0, 3500)
|
||||
setCachedResponse(cacheKey, result)
|
||||
return result
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user