Kaynağa Gözat

ajuste do prompt do sistema

leonardo 2 ay önce
ebeveyn
işleme
970ecfd487
3 değiştirilmiş dosya ile 30 ekleme ve 30 silme
  1. 12 0
      loaders/txtLoader.js
  2. 14 27
      src/chat/chatChain.js
  3. 4 3
      src/config/index.js

+ 12 - 0
loaders/txtLoader.js

@@ -0,0 +1,12 @@
+export async function loadTxtFromString(text, { source, metadata } = {}) {
+  const normalized = String(text ?? "").trim();
+  if (!normalized) return [];
+
+  return [
+    {
+      text: normalized,
+      source: source ?? "txt",
+      metadata: metadata ?? null
+    }
+  ];
+}

+ 14 - 27
src/chat/chatChain.js

@@ -4,28 +4,11 @@ import { getRecentMessages } from "../services/conversationsService.js";
 import { searchDocs } from "../services/searchService.js";
 
 const SYSTEM_PROMPT = [
-  "Você é o Oráculo, assistente interno da empresa Star.",
-  "Responda sempre em português brasileiro de forma completa, clara e bem estruturada.",
-  "",
-  "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."
+  "Você é o Oráculo, assistente interno da Star.",
+  "Responda em português brasileiro de forma natural, clara e completa.",
+  "Quando o CONTEXTO abaixo cobrir a pergunta, use-o e cite a fonte entre parênteses.",
+  "Para perguntas gerais sem relação com documentos internos, responda normalmente com seu conhecimento.",
+  "Nunca invente dados específicos da empresa (processos, valores, nomes) que não estejam no CONTEXTO."
 ].join("\n");
 
 function buildContextBlock(hits) {
@@ -80,7 +63,7 @@ async function rewriteQuery(query) {
 async function loadHistory(conversationId, userId) {
   if (!conversationId) return [];
   try {
-    return await getRecentMessages(conversationId, userId, 12);
+    return await getRecentMessages(conversationId, userId, config.rag.historyLimit);
   } catch {
     return [];
   }
@@ -100,9 +83,11 @@ function buildDefaultOptions(override) {
 
 export async function answerWithContext({ message, conversationId, userId, options }) {
   const searchQuery = config.rag.queryRewrite ? await rewriteQuery(message) : message;
-  const hits = await searchDocs({ query: searchQuery, topK: config.rag.topK });
+  const [hits, history] = await Promise.all([
+    searchDocs({ query: searchQuery, topK: config.rag.topK }),
+    loadHistory(conversationId, userId)
+  ]);
   const context = buildContextBlock(hits);
-  const history = await loadHistory(conversationId, userId);
   const messages = buildMessages(message, context, history);
   const completion = await chatCompletion({ messages, options: buildDefaultOptions(options) });
 
@@ -114,9 +99,11 @@ export async function answerWithContext({ message, conversationId, userId, optio
 
 export async function answerWithContextStream({ message, conversationId, userId, options, onChunk, signal }) {
   const searchQuery = config.rag.queryRewrite ? await rewriteQuery(message) : message;
-  const hits = await searchDocs({ query: searchQuery, topK: config.rag.topK });
+  const [hits, history] = await Promise.all([
+    searchDocs({ query: searchQuery, topK: config.rag.topK }),
+    loadHistory(conversationId, userId)
+  ]);
   const context = buildContextBlock(hits);
-  const history = await loadHistory(conversationId, userId);
   const messages = buildMessages(message, context, history);
   const sources = hitsToSources(hits);
 

+ 4 - 3
src/config/index.js

@@ -35,11 +35,12 @@ export const config = {
     visionModel: process.env.OLLAMA_VISION_MODEL ?? "llava:latest"
   },
   rag: {
-    topK: Number(process.env.RAG_TOP_K ?? 6),
-    minScore: Number(process.env.RAG_MIN_SCORE ?? 0.45),
+    topK: Number(process.env.RAG_TOP_K ?? 4),
+    minScore: Number(process.env.RAG_MIN_SCORE ?? 0.55),
     chunkSize: Number(process.env.RAG_CHUNK_SIZE ?? 900),
     chunkOverlap: Number(process.env.RAG_CHUNK_OVERLAP ?? 150),
-    queryRewrite: process.env.RAG_QUERY_REWRITE === "true"
+    queryRewrite: process.env.RAG_QUERY_REWRITE === "true",
+    historyLimit: Number(process.env.RAG_HISTORY_LIMIT ?? 6)
   },
   llm: {
     temperature: Number(process.env.LLM_TEMPERATURE ?? 0.7),