UPDATE 2 10-sept
This commit is contained in:
@@ -103,11 +103,13 @@ public class MessageEntryFilter {
|
|||||||
|
|
||||||
String resultCategory = switch (geminiResponse != null ? geminiResponse.trim().toUpperCase() : "") {
|
String resultCategory = switch (geminiResponse != null ? geminiResponse.trim().toUpperCase() : "") {
|
||||||
case CATEGORY_CONVERSATION -> {
|
case CATEGORY_CONVERSATION -> {
|
||||||
logger.info("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION, queryInputText);
|
logger.info("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION);
|
||||||
|
logger.debug("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION, queryInputText);
|
||||||
yield CATEGORY_CONVERSATION;
|
yield CATEGORY_CONVERSATION;
|
||||||
}
|
}
|
||||||
case CATEGORY_NOTIFICATION -> {
|
case CATEGORY_NOTIFICATION -> {
|
||||||
logger.info("Classified as {}. Input: '{}'", CATEGORY_NOTIFICATION, queryInputText);
|
logger.info("Classified as {}. Input: '{}'", CATEGORY_NOTIFICATION);
|
||||||
|
logger.debug("Classified as {}. Input: '{}'", CATEGORY_NOTIFICATION, queryInputText);
|
||||||
yield CATEGORY_NOTIFICATION;
|
yield CATEGORY_NOTIFICATION;
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public class NotificationContextResolver {
|
|||||||
|
|
||||||
public String resolveContext(String queryInputText, String notificationsJson, String conversationJson,
|
public String resolveContext(String queryInputText, String notificationsJson, String conversationJson,
|
||||||
String metadata, String userId, String sessionId, String userPhoneNumber) {
|
String metadata, String userId, String sessionId, String userPhoneNumber) {
|
||||||
|
logger.debug("resolveContext -> queryInputText: {}, notificationsJson: {}, conversationJson: {}, metadata: {}", queryInputText, notificationsJson, conversationJson, metadata);
|
||||||
if (queryInputText == null || queryInputText.isBlank()) {
|
if (queryInputText == null || queryInputText.isBlank()) {
|
||||||
logger.warn("Query input text for context resolution is null or blank. Returning {}.", CATEGORY_DIALOGFLOW);
|
logger.warn("Query input text for context resolution is null or blank. Returning {}.", CATEGORY_DIALOGFLOW);
|
||||||
return CATEGORY_DIALOGFLOW;
|
return CATEGORY_DIALOGFLOW;
|
||||||
@@ -82,7 +83,7 @@ public class NotificationContextResolver {
|
|||||||
metadata,
|
metadata,
|
||||||
queryInputText);
|
queryInputText);
|
||||||
|
|
||||||
logger.info("Sending context resolution request to Gemini for input (first 100 chars): '{}'...",
|
logger.debug("Sending context resolution request to Gemini for input (first 100 chars): '{}'...",
|
||||||
queryInputText.substring(0, Math.min(queryInputText.length(), 100)));
|
queryInputText.substring(0, Math.min(queryInputText.length(), 100)));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ public class ConversationManagerService {
|
|||||||
String notificationText = notificationContextMapper.toText(notification);
|
String notificationText = notificationContextMapper.toText(notification);
|
||||||
|
|
||||||
Map<String, Object> filteredParams = notification.parametros().entrySet().stream()
|
Map<String, Object> filteredParams = notification.parametros().entrySet().stream()
|
||||||
.filter(entry -> entry.getKey().startsWith("po_"))
|
.filter(entry -> entry.getKey().startsWith("notification_po_"))
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
String resolvedContext = notificationContextResolver.resolveContext(userMessageText,
|
String resolvedContext = notificationContextResolver.resolveContext(userMessageText,
|
||||||
@@ -360,13 +360,15 @@ public class ConversationManagerService {
|
|||||||
|
|
||||||
private Mono<Void> persistNotificationTurn(String userId, String sessionId, ConversationEntryDTO entry,
|
private Mono<Void> persistNotificationTurn(String userId, String sessionId, ConversationEntryDTO entry,
|
||||||
String userPhoneNumber) {
|
String userPhoneNumber) {
|
||||||
logger.debug("Starting Write-Back persistence for notification session {}. Type: {}. Writing to Redis first.", sessionId,
|
logger.debug("Starting Write-Back persistence for notification session {}. Type: {}. Writing to Redis first.",
|
||||||
|
sessionId,
|
||||||
entry.type().name());
|
entry.type().name());
|
||||||
return memoryStoreNotificationService.saveEntry(userId, sessionId, entry, userPhoneNumber)
|
return memoryStoreNotificationService.saveEntry(userId, sessionId, entry, userPhoneNumber)
|
||||||
.doOnSuccess(v -> logger.info(
|
.doOnSuccess(v -> logger.info(
|
||||||
"Entry saved to Redis for notification session {}. Type: {}. Kicking off async Firestore write-back.",
|
"Entry saved to Redis for notification session {}. Type: {}. Kicking off async Firestore write-back.",
|
||||||
sessionId, entry.type().name()))
|
sessionId, entry.type().name()))
|
||||||
.doOnError(e -> logger.error("Error during primary Redis write for notification session {}. Type: {}: {}", sessionId,
|
.doOnError(e -> logger.error(
|
||||||
|
"Error during primary Redis write for notification session {}. Type: {}: {}", sessionId,
|
||||||
entry.type().name(), e.getMessage(), e));
|
entry.type().name(), e.getMessage(), e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,18 +49,18 @@ public class MemoryStoreConversationService {
|
|||||||
String sessionKey = SESSION_KEY_PREFIX + sessionId;
|
String sessionKey = SESSION_KEY_PREFIX + sessionId;
|
||||||
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + userPhoneNumber;
|
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + userPhoneNumber;
|
||||||
|
|
||||||
logger.info("Attempting to save entry to Redis for session {}. Entity: {}", sessionId, newEntry.entity().name());
|
logger.info("Attempting to save entry to Memorystore for session {}. Entity: {}", sessionId, newEntry.entity().name());
|
||||||
|
|
||||||
return redisTemplate.opsForValue().get(sessionKey)
|
return redisTemplate.opsForValue().get(sessionKey)
|
||||||
.doOnSuccess(session -> {
|
.doOnSuccess(session -> {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
logger.info("Found existing session in Redis: {}", session);
|
logger.info("Found existing session in Memorystore: {}", session);
|
||||||
} else {
|
} else {
|
||||||
logger.info("No session found in Redis for key: {}", sessionKey);
|
logger.info("No session found in Memorystore for key: {}", sessionKey);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.defer(() -> {
|
||||||
logger.info("Creating new session {} in Redis with TTL.", sessionId);
|
logger.info("Creating new session {} in Memorystore with TTL.", sessionId);
|
||||||
ConversationSessionDTO newSession = ConversationSessionDTO.create(sessionId, userId, userPhoneNumber);
|
ConversationSessionDTO newSession = ConversationSessionDTO.create(sessionId, userId, userPhoneNumber);
|
||||||
return redisTemplate.opsForValue().set(sessionKey, newSession, SESSION_TTL)
|
return redisTemplate.opsForValue().set(sessionKey, newSession, SESSION_TTL)
|
||||||
.then(stringRedisTemplate.opsForValue().set(phoneToSessionKey, sessionId, SESSION_TTL))
|
.then(stringRedisTemplate.opsForValue().set(phoneToSessionKey, sessionId, SESSION_TTL))
|
||||||
@@ -71,7 +71,7 @@ public class MemoryStoreConversationService {
|
|||||||
ConversationSessionDTO sessionWithPantallaContexto = (pantallaContexto != null) ? sessionWithUpdatedTelefono.withPantallaContexto(pantallaContexto) : sessionWithUpdatedTelefono;
|
ConversationSessionDTO sessionWithPantallaContexto = (pantallaContexto != null) ? sessionWithUpdatedTelefono.withPantallaContexto(pantallaContexto) : sessionWithUpdatedTelefono;
|
||||||
ConversationSessionDTO updatedSession = sessionWithPantallaContexto.withAddedEntry(newEntry);
|
ConversationSessionDTO updatedSession = sessionWithPantallaContexto.withAddedEntry(newEntry);
|
||||||
|
|
||||||
logger.info("Updated session to be saved in Redis: {}", updatedSession);
|
logger.debug("Updated session to be saved in Memorystore: {}", updatedSession);
|
||||||
logger.info("Attempting to set updated session {} with new entry entity {} in Redis.", sessionId, newEntry.entity().name());
|
logger.info("Attempting to set updated session {} with new entry entity {} in Redis.", sessionId, newEntry.entity().name());
|
||||||
|
|
||||||
return redisTemplate.opsForValue().set(sessionKey, updatedSession)
|
return redisTemplate.opsForValue().set(sessionKey, updatedSession)
|
||||||
@@ -81,14 +81,14 @@ public class MemoryStoreConversationService {
|
|||||||
.doOnSuccess(success -> {
|
.doOnSuccess(success -> {
|
||||||
logger.info("Successfully saved updated session and phone mapping to Redis for session {}. Entity Type: {}", sessionId, newEntry.entity().name());
|
logger.info("Successfully saved updated session and phone mapping to Redis for session {}. Entity Type: {}", sessionId, newEntry.entity().name());
|
||||||
})
|
})
|
||||||
.doOnError(e -> logger.error("Error appending entry to Redis for session {}: {}", sessionId, e.getMessage(), e));
|
.doOnError(e -> logger.error("Error appending entry to Memorystore for session {}: {}", sessionId, e.getMessage(), e));
|
||||||
}
|
}
|
||||||
public Mono<ConversationSessionDTO> getSessionByTelefono(String telefono) {
|
public Mono<ConversationSessionDTO> getSessionByTelefono(String telefono) {
|
||||||
if (telefono == null || telefono.isBlank()) {
|
if (telefono == null || telefono.isBlank()) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + telefono;
|
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + telefono;
|
||||||
logger.debug("Attempting to retrieve session ID for phone number {} from Redis.", telefono);
|
logger.debug("Attempting to retrieve session ID for phone number {} from Memorystore.", telefono);
|
||||||
return stringRedisTemplate.opsForValue().get(phoneToSessionKey)
|
return stringRedisTemplate.opsForValue().get(phoneToSessionKey)
|
||||||
.flatMap(sessionId -> {
|
.flatMap(sessionId -> {
|
||||||
logger.debug("Found session ID {} for phone number {}. Retrieving session data.", sessionId, telefono);
|
logger.debug("Found session ID {} for phone number {}. Retrieving session data.", sessionId, telefono);
|
||||||
@@ -106,7 +106,7 @@ public class MemoryStoreConversationService {
|
|||||||
|
|
||||||
public Mono<Void> updateSession(ConversationSessionDTO session) {
|
public Mono<Void> updateSession(ConversationSessionDTO session) {
|
||||||
String sessionKey = SESSION_KEY_PREFIX + session.sessionId();
|
String sessionKey = SESSION_KEY_PREFIX + session.sessionId();
|
||||||
logger.info("Attempting to update session {} in Redis.", session.sessionId());
|
logger.info("Attempting to update session {} in Memorystore.", session.sessionId());
|
||||||
return redisTemplate.opsForValue().set(sessionKey, session).then();
|
return redisTemplate.opsForValue().set(sessionKey, session).then();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,75 @@
|
|||||||
Eres un agente conversacional de soporte al usuario, amable, servicial y conciso.
|
Eres un agente conversacional de soporte al usuario, amable, servicial y conciso.
|
||||||
Tu principal objetivo es responder a la pregunta específica del usuario de manera clara y directa, utilizando la información proporcionada en la notificación, sus metadatos y el historial de conversación.
|
|
||||||
Instrucciones Clave:
|
|
||||||
Prioridad: La información de la Notificación y sus Metadatos tienen la máxima prioridad. Si la pregunta del usuario se refiere a un dato que está en los metadatos, debes usarlo para responder.
|
|
||||||
El Historial de Conversación sirve para entender el contexto previo y evitar repeticiones.
|
|
||||||
Manejo de JSON (Metadatos y Conversación):Los campos de los metadatos y el historial pueden variar o no estar presentes.
|
|
||||||
Deberás analizar e inferir la información relevante de estos objetos JSON, incluso si la estructura no es idéntica en cada interacción.Si un dato es crucial y no está presente,
|
|
||||||
indícalo educadamente.
|
|
||||||
Claridad y Concisión: Proporciona la respuesta más directa y útil posible. Evita la verbosidad.
|
|
||||||
Tono: Mantén un tono profesional, amable y servicial.
|
|
||||||
Idioma: Responde en el mismo idioma en el que el usuario hizo la pregunta.
|
|
||||||
Información Insuficiente: Si la pregunta del usuario no puede ser respondida con la información provista, devuelve la cadena DIALOGFLOW.
|
|
||||||
|
|
||||||
Ejemplo 1: Pregunta directa, sin historial
|
Recibirás cuatro piezas de información:
|
||||||
Notificación: Hola Usuario, ya puedes revisar tu documentación en el sitio web. Deberás presentar una identificación oficial (INE, pasaporte) para acceder.
|
1. HISTORIAL_CONVERSACION: El diálogo previo con el usuario. Úsalo para entender el contexto y evitar repetir información.
|
||||||
Metadatos de la notificación: {
|
2. NOTIFICACION: El texto del mensaje que el usuario acaba de recibir.
|
||||||
"tipo_documentacion": "verificacion de identidad",
|
3. METADATOS_NOTIFICACION: Un objeto JSON con datos estructurados relacionados con la notificación. Esta es tu fuente de verdad principal.
|
||||||
"documentos_permitidos_adicionales": ["licencia de conducir", "cedula profesional"],
|
4. PREGUNTA_USUARIO: La pregunta específica del usuario que debes responder.
|
||||||
"fecha_limite_revision": "2024-12-31"
|
|
||||||
|
Tu objetivo es sintetizar la información de estas fuentes para dar la respuesta más directa y útil posible.
|
||||||
|
|
||||||
|
**Reglas de Comportamiento:**
|
||||||
|
|
||||||
|
**Proceso Lógico:** Debes seguir este orden de prioridad para encontrar la respuesta:
|
||||||
|
1. Autoridad Principal: Busca la respuesta primero en el objeto METADATOS_NOTIFICACION. Los datos aquí tienen la máxima autoridad.
|
||||||
|
2. Fuente Alternativa: Si la respuesta no está en el objeto METADATOS_NOTIFICACION, busca como alternativa en el texto de HISTORIAL_CONVERSACION los datos que empiecen con el prefijo notification_po_.
|
||||||
|
3. Contexto: Utiliza el HISTORIAL_CONVERSACION únicamente para dar contexto y asegurarte de no repetir algo que ya se dijo
|
||||||
|
|
||||||
|
**Manejo de Datos Faltantes:** Si la respuesta a la PREGUNTA_USUARIO no se encuentra METADATOS_NOTIFICACION ni en el HISTORIAL_CONVERSACION (con el prefijo notification_po_) entonces debes responder exactamente con la palabra DIALOGFLOW.No intentes adivinar ni disculparte
|
||||||
|
**Concisión y Tono:** Tu respuesta debe ser directa, clara y resolver la pregunta. Mantén un tono profesional, amable y servicial.
|
||||||
|
**Idioma:** Responde siempre en el mismo idioma de la PREGUNTA_USUARIO.
|
||||||
|
|
||||||
|
Manejo de Datos Faltantes: Si la respuesta a la PREGUNTA_USUARIO no se encuentra ni en METADATOS_NOTIFICACION ni en el HISTORIAL_CONVERSACION (con el prefijo notification_po_),
|
||||||
|
entonces debes responder exactamente con la palabra DIALOGFLOW.
|
||||||
|
No intentes adivinar ni disculparte.
|
||||||
|
|
||||||
|
Estrategia de Respuesta:
|
||||||
|
Siempre sintetiza la información encontrada en una respuesta completa y conversacional. No devuelvas solo el dato. Utiliza el dato para construir una frase que sea útil y siga el tono. Por ejemplo, si encuentras el dato "30/09/2025", tu respuesta debe ser una frase como "La vigencia de tu solicitud es hasta el 30 de septiembre de 2025." o similar.
|
||||||
|
|
||||||
|
**Ejemplos (Few-Shot Learning):**
|
||||||
|
|
||||||
|
**Ejemplo 1: La respuesta está en los Metadatos**
|
||||||
|
HISTORIAL_CONVERSACION:
|
||||||
|
Usuario: Hola, necesito ayuda con una documentación.
|
||||||
|
Agente: Claro, ¿en qué puedo ayudarte?
|
||||||
|
NOTIFICACION: Hola :Pasó algo con la captura de tu INE y no se completó tu solicitud de tarjeta de crédito con folio ###.¡Reinténtalo cuando quieras! Solo toma en cuenta estos consejos:
|
||||||
|
Presenta tu INE original (no copias ni escaneos).📅Revisa que esté vigente y sin tachaduras.📷 Confirma que la fotografía sea clara.🏠 Asegúrate de que la dirección sea legible.
|
||||||
|
Estamos listos para recibirte.
|
||||||
|
METADATOS_NOTIFICACION: {
|
||||||
|
"parametrosOcultos": {
|
||||||
|
"vigencia": "30/09/2025"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
PREGUNTA_USUARIO: ¿Hasta cuando esta disponible esta solicitud?
|
||||||
|
Respuesta: Tienes hasta el 30 de septiembre de 2025 para revisarlos.
|
||||||
|
|
||||||
**Examples (Few-Shot Learning):**
|
**Ejemplo 2: Poca Información encontrada en texto de Notificacion *
|
||||||
Ejemplo 2: Información faltante
|
HISTORIAL_CONVERSACION:
|
||||||
|
Usuario: Hola.
|
||||||
|
Agente: ¡Qué onda! Soy Beto, tu asistente virtual de Sigma. ¿Como te puedo ayudar hoy? 🧐
|
||||||
|
NOTIFICACION: Hola :Pasó algo con la captura de tu INE y no se completó tu *solicitud de tarjeta de crédito con folio ###*.
|
||||||
|
¡Reinténtalo cuando quieras! Solo toma en cuenta estos consejos: Presenta tu INE original (no copias ni escaneos)...
|
||||||
|
Estamos listos para recibirte.
|
||||||
|
METADATOS_NOTIFICACION: {
|
||||||
|
"parametrosOcultos": {
|
||||||
|
"vigencia": "30/09/2025"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PREGUNTA_USUARIO: Mi INE tiene algunas tachaduras y en general esta en mal estado
|
||||||
|
Respuesta: DIALOGFLOW
|
||||||
|
|
||||||
Notificación: Tu cita para el servicio de mantenimiento ha sido confirmada. Por favor, llega 15 minutos antes.
|
**Ejemplo 3: Información no encontrada en ninguna fuente**
|
||||||
Metadatos_ Notificación: {
|
HISTORIAL_CONVERSACION:
|
||||||
|
Usuario: ¿Cómo van mis trámites?
|
||||||
|
Agente: Veo que tienes una cita de mantenimiento programada.
|
||||||
|
NOTIFICACION: Tu cita para el servicio de mantenimiento ha sido confirmada. Por favor, llega 15 minutos antes.
|
||||||
|
METADATOS_NOTIFICACION: {
|
||||||
"tipo_servicio": "mantenimiento rutinario",
|
"tipo_servicio": "mantenimiento rutinario",
|
||||||
"ubicacion": "Sucursal Centro"
|
"ubicacion": "Sucursal Centro",
|
||||||
|
"id_cita": "C-182736"
|
||||||
}
|
}
|
||||||
|
PREGUNTA_USUARIO: Perfecto, ¿cuál será el costo del mantenimiento?
|
||||||
|
Respuesta: DIALOGFLOW
|
||||||
|
|
||||||
Historial de Conversación:
|
Historial de Conversación:
|
||||||
%s
|
%s
|
||||||
|
|||||||
@@ -35,9 +35,10 @@ public class NotificationContextResolverLiveTest {
|
|||||||
"🏠 Asegúrate de que la dirección sea legible.\n" +
|
"🏠 Asegúrate de que la dirección sea legible.\n" +
|
||||||
"Estamos listos para recibirte.\n";
|
"Estamos listos para recibirte.\n";
|
||||||
|
|
||||||
conversationJson = "{}";
|
conversationJson = "System: Hola :Pasó algo con la captura de tu INE y no se completó tu *solicitud de tarjeta de crédito con folio *.¡Reinténtalo cuando quieras! Solo toma en cuenta estos consejos:🪪 Presenta tu INE original (no copias ni escaneos).📅Revisa que esté vigente y sin tachaduras.📷 Confirma que la fotografía sea clara.🏠 Asegúrate de que la dirección sea legible.Estamos listos para recibirte.notification_po_contexto=campañaprueba, notification_po_id_campaña=campaña01, notification_po_id_aplicacion=TestSigma, notification_po_id_notificacion=Prueba2";
|
||||||
queryInputText = "necesito saber el id de la campaña";
|
queryInputText = "cual es el id de la notificaion?";
|
||||||
metadataJson = "{\"contexto\":\"campañaprueba\",\"id_aplicacion\":\"TestSigma\",\"id_campaña\":\"campaña01\",\"id_notificacion\":\"Prueba2\",\"vigencia\":\"30/09/2025\"}";
|
metadataJson = "{\"contexto\":\"campañaprueba\",\"id_aplicacion\":\"TestSigma\",\"id_campaña\":\"campaña01\",\"id_notificacion\":\"Prueba2\",\"vigencia\":\"30/09/2025\"}";
|
||||||
|
//metadataJson = "{}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user