| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- import { config } from "../src/config/index.js";
- import { chatCompletion, chatCompletionStream } from "../src/services/ollamaClient.js";
- 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.",
- "",
- "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."
- ].join("\n");
- function buildContextBlock(hits) {
- const lines = [];
- hits.forEach((h, i) => {
- const header = `# Fonte ${i + 1}${h.source ? ` (${h.source})` : ""}`;
- lines.push(header);
- lines.push(h.text);
- lines.push("");
- });
- return lines.join("\n").trim();
- }
- function buildMessages(message, context, history = []) {
- return [
- { role: "system", content: SYSTEM_PROMPT },
- ...(context ? [{ role: "system", content: `CONTEXTO:\n${context}` }] : []),
- ...history.map((m) => ({ role: m.Role.toLowerCase(), content: m.Content })),
- { role: "user", content: message }
- ];
- }
- function hitsToSources(hits) {
- return hits.map((h) => ({
- id: h.id,
- source: h.source,
- score: h.score,
- chunkIndex: h.chunkIndex,
- metadata: h.metadata
- }));
- }
- async function rewriteQuery(query) {
- try {
- const { content } = await chatCompletion({
- messages: [
- {
- role: "system",
- content:
- "Você é um motor de busca interno. Reescreva a pergunta abaixo de forma mais específica e completa para busca em documentos internos de empresa. Responda APENAS com a query reescrita, sem explicações ou formatação extra."
- },
- { role: "user", content: query }
- ]
- });
- return content?.trim() || query;
- } catch {
- return query;
- }
- }
- async function loadHistory(conversationId) {
- if (!conversationId) return [];
- try {
- return await getRecentMessages(conversationId, 12);
- } catch {
- return [];
- }
- }
- 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 });
- return {
- answer: completion.content,
- sources: hitsToSources(hits)
- };
- }
- export async function answerWithContextStream({ message, conversationId, options, onChunk, signal }) {
- 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 sources = hitsToSources(hits);
- const completion = await chatCompletionStream({ messages, onChunk, signal, options });
- return {
- answer: completion.content,
- sources
- };
- }
|