|
@@ -5,8 +5,27 @@ import { AtendimentoRagIndex } from "../models/AtendimentoRagIndex.model.js";
|
|
|
import { buildConversaTexto, calcularUltimaMensagemId } from "../utils/atendimentoFormat.js";
|
|
import { buildConversaTexto, calcularUltimaMensagemId } from "../utils/atendimentoFormat.js";
|
|
|
import { ingestDocuments } from "./ingestService.js";
|
|
import { ingestDocuments } from "./ingestService.js";
|
|
|
import { searchDocs } from "./searchService.js";
|
|
import { searchDocs } from "./searchService.js";
|
|
|
|
|
+import { generateHydeDocument } from "./hydeService.js";
|
|
|
import { NotFoundError } from "../shared/errors/index.js";
|
|
import { NotFoundError } from "../shared/errors/index.js";
|
|
|
|
|
|
|
|
|
|
+const ATENDIMENTO_HYDE_SYSTEM_PROMPT = [
|
|
|
|
|
+ "Você é um atendente experiente de suporte ao cliente. Dada a pergunta ou descrição",
|
|
|
|
|
+ "abaixo, escreva um parágrafo curto (3 a 6 frases) no estilo de um trecho real de",
|
|
|
|
|
+ "uma conversa de atendimento ao cliente (chat de suporte) que trate exatamente desse",
|
|
|
|
|
+ "assunto — mensagens típicas de cliente e de atendente, termos usados nesse tipo de",
|
|
|
|
|
+ "conversa (ex.: protocolo, boleto, fatura, cancelamento, setor, prazo), quando fizer",
|
|
|
|
|
+ "sentido. Não inclua a pergunta original, saudações genéricas ou ressalvas de",
|
|
|
|
|
+ "incerteza. Se não tiver certeza do conteúdo exato, escreva de forma plausível no",
|
|
|
|
|
+ "mesmo estilo, pois o texto será usado apenas para busca por similaridade, nunca",
|
|
|
|
|
+ "mostrado ao usuário. Responda em português brasileiro."
|
|
|
|
|
+].join("\n");
|
|
|
|
|
+
|
|
|
|
|
+const ATENDIMENTO_HYDE_MIN_QUERY_LENGTH = 15;
|
|
|
|
|
+
|
|
|
|
|
+function shouldSkipAtendimentoHyde(query) {
|
|
|
|
|
+ return query.trim().length < ATENDIMENTO_HYDE_MIN_QUERY_LENGTH;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function upsertRagIndex(atendimentoId, dados) {
|
|
async function upsertRagIndex(atendimentoId, dados) {
|
|
|
const now = new Date();
|
|
const now = new Date();
|
|
|
const row = { AtendimentoId: atendimentoId, ...dados, UpdatedAt: now };
|
|
const row = { AtendimentoId: atendimentoId, ...dados, UpdatedAt: now };
|
|
@@ -128,13 +147,26 @@ export async function ingestarPendentes({ limite = config.atendimentosRag.batchS
|
|
|
export async function buscarAtendimentosSemelhantes({
|
|
export async function buscarAtendimentosSemelhantes({
|
|
|
query,
|
|
query,
|
|
|
topK = config.atendimentosRag.topK,
|
|
topK = config.atendimentosRag.topK,
|
|
|
- minScore = config.atendimentosRag.minScore
|
|
|
|
|
|
|
+ minScore = config.atendimentosRag.minScore,
|
|
|
|
|
+ hyde = config.atendimentosRag.hyde
|
|
|
}) {
|
|
}) {
|
|
|
|
|
+ if (!hyde || shouldSkipAtendimentoHyde(query)) {
|
|
|
|
|
+ return searchDocs({
|
|
|
|
|
+ query,
|
|
|
|
|
+ topK,
|
|
|
|
|
+ minScore,
|
|
|
|
|
+ collectionName: config.atendimentosRag.collection,
|
|
|
|
|
+ embedRole: "query"
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const hydeText = await generateHydeDocument(query, ATENDIMENTO_HYDE_SYSTEM_PROMPT);
|
|
|
|
|
+
|
|
|
return searchDocs({
|
|
return searchDocs({
|
|
|
- query,
|
|
|
|
|
|
|
+ query: hydeText || query,
|
|
|
topK,
|
|
topK,
|
|
|
minScore,
|
|
minScore,
|
|
|
collectionName: config.atendimentosRag.collection,
|
|
collectionName: config.atendimentosRag.collection,
|
|
|
- embedRole: "query"
|
|
|
|
|
|
|
+ embedRole: hydeText ? "passage" : "query"
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|