| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- import { config } from "../config/index.js";
- import { chatCompletion, chatCompletionStream } from "../services/ollamaClient.js";
- import { getRecentMessages } from "../services/conversationsService.js";
- import { searchDocs } from "../services/searchService.js";
- const SYSTEM_PROMPT = [
- "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) {
- const lines = [];
- hits.forEach((h, i) => {
- 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("");
- });
- 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, userId) {
- if (!conversationId) return [];
- try {
- return await getRecentMessages(conversationId, userId, config.rag.historyLimit);
- } catch {
- return [];
- }
- }
- 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, userId, options }) {
- const searchQuery = config.rag.queryRewrite ? await rewriteQuery(message) : message;
- const [hits, history] = await Promise.all([
- searchDocs({ query: searchQuery, topK: config.rag.topK }),
- loadHistory(conversationId, userId)
- ]);
- const context = buildContextBlock(hits);
- const messages = buildMessages(message, context, history);
- const completion = await chatCompletion({ messages, options: buildDefaultOptions(options) });
- return {
- answer: completion.content,
- sources: hitsToSources(hits)
- };
- }
- export async function answerWithContextStream({ message, conversationId, userId, options, onChunk, signal }) {
- const searchQuery = config.rag.queryRewrite ? await rewriteQuery(message) : message;
- const [hits, history] = await Promise.all([
- searchDocs({ query: searchQuery, topK: config.rag.topK }),
- loadHistory(conversationId, userId)
- ]);
- const context = buildContextBlock(hits);
- const messages = buildMessages(message, context, history);
- const sources = hitsToSources(hits);
- const completion = await chatCompletionStream({ messages, onChunk, signal, options: buildDefaultOptions(options) });
- return {
- answer: completion.content,
- sources
- };
- }
|