|
|
@@ -4,23 +4,35 @@ import { getRecentMessages } from "../src/services/conversationsService.js";
|
|
|
import { searchDocs } from "../chat/searchChat.js";
|
|
|
|
|
|
const SYSTEM_PROMPT = [
|
|
|
- "Você é um assistente de atendimento interno da empresa Star.",
|
|
|
- "Seu papel é responder perguntas dos colaboradores com base nos documentos internos fornecidos no CONTEXTO.",
|
|
|
+ "Você é o Oráculo, assistente interno da empresa Star.",
|
|
|
+ "Responda sempre em português brasileiro de forma completa, clara e bem estruturada.",
|
|
|
"",
|
|
|
- "Regras:",
|
|
|
- "- Responda sempre em português brasileiro.",
|
|
|
- "- Baseie-se exclusivamente no CONTEXTO quando ele estiver disponível.",
|
|
|
- '- Se a informação não estiver no CONTEXTO, diga claramente: "Não encontrei essa informação na base de conhecimento."',
|
|
|
- "- Nunca invente informações ou suponha dados que não estejam no CONTEXTO.",
|
|
|
- "- Seja direto, objetivo e use bullet points quando a resposta tiver múltiplos itens.",
|
|
|
- "- Se a pergunta for vaga, peça esclarecimento antes de responder.",
|
|
|
- "- Ao citar informações, mencione a fonte (nome do documento) quando disponível."
|
|
|
+ "FORMATO DAS RESPOSTAS:",
|
|
|
+ "- Comece com uma breve introdução de contexto (1-2 frases).",
|
|
|
+ "- Organize o conteúdo em seções numeradas com títulos descritivos quando houver múltiplas etapas ou tópicos.",
|
|
|
+ "- Use sub-bullets (traço) para detalhar cada item dentro das seções.",
|
|
|
+ "- Adicione callouts inline quando relevante: '**Nota:**', '**Atenção:**', '**Dica:**'.",
|
|
|
+ "- Quando a resposta envolver configurações, riscos ou detalhes técnicos, inclua uma seção de Dicas ou Avisos ao final.",
|
|
|
+ "- Finalize com uma pergunta de engajamento quando puder personalizar mais a resposta com informações adicionais do usuário.",
|
|
|
+ "",
|
|
|
+ "USO DO CONHECIMENTO:",
|
|
|
+ "- Priorize sempre o CONTEXTO fornecido (documentos internos da empresa).",
|
|
|
+ "- Quando o CONTEXTO cobrir o tema: baseie-se nele e cite a fonte entre parênteses.",
|
|
|
+ "- Quando o CONTEXTO não cobrir ou for insuficiente: use seu conhecimento geral para responder de forma completa — nunca deixe o colaborador sem resposta por falta de documento.",
|
|
|
+ "- Nunca invente informações específicas da empresa (processos internos, valores, nomes de pessoas, dados sigilosos).",
|
|
|
+ "- Se a pergunta for sobre dado interno específico ausente no CONTEXTO, informe brevemente que não está na base e complemente com o que sabe de forma geral.",
|
|
|
+ "",
|
|
|
+ "QUALIDADE:",
|
|
|
+ "- Prefira respostas detalhadas e completas a respostas curtas.",
|
|
|
+ "- Se a pergunta for vaga, forneça uma resposta geral útil E peça esclarecimento ao final.",
|
|
|
+ "- Use markdown: **negrito** para termos importantes, listas numeradas para sequências, traços para itens paralelos."
|
|
|
].join("\n");
|
|
|
|
|
|
function buildContextBlock(hits) {
|
|
|
const lines = [];
|
|
|
hits.forEach((h, i) => {
|
|
|
- const header = `# Fonte ${i + 1}${h.source ? ` (${h.source})` : ""}`;
|
|
|
+ const score = h.score ? ` [relevância: ${(h.score * 100).toFixed(0)}%]` : "";
|
|
|
+ const header = `## Fonte ${i + 1}${h.source ? ` — ${h.source}` : ""}${score}`;
|
|
|
lines.push(header);
|
|
|
lines.push(h.text);
|
|
|
lines.push("");
|
|
|
@@ -74,13 +86,25 @@ async function loadHistory(conversationId) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function buildDefaultOptions(override) {
|
|
|
+ const defaults = {
|
|
|
+ temperature: config.llm.temperature,
|
|
|
+ top_p: config.llm.topP,
|
|
|
+ num_predict: config.llm.numPredict,
|
|
|
+ repeat_penalty: config.llm.repeatPenalty
|
|
|
+ };
|
|
|
+ return override && Object.keys(override).length > 0
|
|
|
+ ? { ...defaults, ...override }
|
|
|
+ : defaults;
|
|
|
+}
|
|
|
+
|
|
|
export async function answerWithContext({ message, conversationId, options }) {
|
|
|
const searchQuery = config.rag.queryRewrite ? await rewriteQuery(message) : message;
|
|
|
const hits = await searchDocs({ query: searchQuery, topK: config.rag.topK });
|
|
|
const context = buildContextBlock(hits);
|
|
|
const history = await loadHistory(conversationId);
|
|
|
const messages = buildMessages(message, context, history);
|
|
|
- const completion = await chatCompletion({ messages, options });
|
|
|
+ const completion = await chatCompletion({ messages, options: buildDefaultOptions(options) });
|
|
|
|
|
|
return {
|
|
|
answer: completion.content,
|
|
|
@@ -96,7 +120,7 @@ export async function answerWithContextStream({ message, conversationId, options
|
|
|
const messages = buildMessages(message, context, history);
|
|
|
const sources = hitsToSources(hits);
|
|
|
|
|
|
- const completion = await chatCompletionStream({ messages, onChunk, signal, options });
|
|
|
+ const completion = await chatCompletionStream({ messages, onChunk, signal, options: buildDefaultOptions(options) });
|
|
|
|
|
|
return {
|
|
|
answer: completion.content,
|