Files
agent/src/va_agent/dynamic_instruction.py
2026-03-05 06:06:11 +00:00

130 lines
4.1 KiB
Python

"""Dynamic instruction provider for VAia agent."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from google.adk.agents.readonly_context import ReadonlyContext
from va_agent.notifications import NotificationService
logger = logging.getLogger(__name__)
async def provide_dynamic_instruction(
notification_service: NotificationService,
ctx: ReadonlyContext | None = None,
) -> str:
"""Provide dynamic instructions based on pending notifications.
This function is called by the ADK agent on each message. It:
1. Checks if this is the first message in the session (< 2 events)
2. Queries Firestore for pending notifications
3. Marks them as notified
4. Returns a dynamic instruction for the agent to mention them
Args:
notification_service: Service for fetching/marking notifications
ctx: Agent context containing session information
Returns:
Dynamic instruction string (empty if no notifications or not first message)
"""
# Only check notifications on the first message
if not ctx:
logger.debug("No context available for dynamic instruction")
return ""
session = ctx.session
if not session:
logger.debug("No session available for dynamic instruction")
return ""
# FOR TESTING: Always check for notifications
# (comment out to enable first-message-only)
# Only check on first message (when events list is empty
# or has only 1-2 events)
# Events include both user and agent messages, so < 2 means first interaction
# event_count = len(session.events) if session.events else 0
#
# if event_count >= 2:
# logger.debug(
# "Skipping notification check: not first message (event_count=%d)",
# event_count,
# )
# return ""
# Extract phone number from user_id (they are the same in this implementation)
phone_number = session.user_id
logger.info(
"First message detected for user %s, checking for pending notifications",
phone_number,
)
try:
# Fetch pending notifications
pending_notifications = await notification_service.get_pending_notifications(
phone_number
)
if not pending_notifications:
logger.info("No pending notifications for user %s", phone_number)
return ""
# Build dynamic instruction with notification details
notification_ids = [
nid
for n in pending_notifications
if (nid := n.get("id_notificacion")) is not None
]
count = len(pending_notifications)
# Format notification details for the agent
notification_details = []
for notif in pending_notifications:
evento = notif.get("nombre_evento_dialogflow", "notificacion")
texto = notif.get("texto", "Sin texto")
notification_details.append(f" - Evento: {evento} | Texto: {texto}")
details_text = "\n".join(notification_details)
instruction = f"""
IMPORTANTE - NOTIFICACIONES PENDIENTES:
El usuario tiene {count} notificación(es) sin leer:
{details_text}
INSTRUCCIONES:
- Menciona estas notificaciones de forma natural en tu respuesta inicial
- No necesitas leerlas todas literalmente, solo hazle saber que las tiene
- Sé breve y directo según tu personalidad (directo y cálido)
- Si el usuario pregunta algo específico, prioriza responder eso primero\
y luego menciona las notificaciones
Ejemplo: "¡Hola! 👋 Tienes {count} notificación(es)\
pendiente(s). ¿Te gustaría revisarlas?"
"""
# Mark notifications as notified in Firestore
await notification_service.mark_as_notified(phone_number, notification_ids)
logger.info(
"Returning dynamic instruction with %d notification(s) for user %s",
count,
phone_number,
)
except Exception:
logger.exception(
"Error building dynamic instruction for user %s",
phone_number,
)
return ""
else:
return instruction