refactor: move customer identification to agent node

- Add identify_customer_node to Agent class for customer lookup
- Webhook now only passes customer_phone to agent state
- Customer identification happens in agent flow: START -> identify_customer -> llm_call -> tool_node -> END
This commit is contained in:
2026-03-11 00:08:58 -05:00
parent 5de87b7c8f
commit 6eb381080a
2 changed files with 43 additions and 27 deletions

View File

@@ -6,6 +6,7 @@ from langchain_core.runnables import RunnableConfig
from typing import Literal, Callable, Any, Union from typing import Literal, Callable, Any, Union
from .schemas import MessagesState from .schemas import MessagesState
from dataclasses import dataclass from dataclasses import dataclass
import json
@dataclass @dataclass
@@ -134,10 +135,12 @@ class Agent:
"""Construye el grafo de estado completo.""" """Construye el grafo de estado completo."""
agent_builder = StateGraph(MessagesState) agent_builder = StateGraph(MessagesState)
agent_builder.add_node("identify_customer", self._identify_customer_node)
agent_builder.add_node("llm_call", self._llm_call_node) agent_builder.add_node("llm_call", self._llm_call_node)
agent_builder.add_node("tool_node", self._tool_node) agent_builder.add_node("tool_node", self._tool_node)
agent_builder.add_edge(START, "llm_call") agent_builder.add_edge(START, "identify_customer")
agent_builder.add_edge("identify_customer", "llm_call")
agent_builder.add_conditional_edges( agent_builder.add_conditional_edges(
"llm_call", self._should_continue, {"tool_node": "tool_node", END: END} "llm_call", self._should_continue, {"tool_node": "tool_node", END: END}
) )
@@ -145,6 +148,45 @@ class Agent:
return agent_builder return agent_builder
def _identify_customer_node(self, state: MessagesState) -> dict:
"""
Nodo de identificación del cliente.
Busca el cliente por teléfono y actualiza el estado.
"""
from ..logger import logger
customer_phone = state.get("customer_phone", "")
if not customer_phone:
return {"customer_name": "Cliente Anónimo", "customer_id": 0}
find_customer_tool = self._tools_by_name.get("find_customer_by_identifier")
if not find_customer_tool:
logger.warning("Tool find_customer_by_identifier not found")
return {"customer_name": "Cliente Final", "customer_id": 0}
try:
result = find_customer_tool.invoke({"identifier": customer_phone})
customer_data = json.loads(result) if result else {}
if isinstance(customer_data, list):
customer_data = customer_data[0] if customer_data else {}
if customer_data and customer_data.get("id"):
customer_name = customer_data.get("party", {}).get(
"name", "Cliente Final"
)
customer_id = customer_data.get("id", 0)
logger.info(
f"Cliente identificado: {customer_name} (ID: {customer_id})"
)
return {"customer_name": customer_name, "customer_id": customer_id}
else:
logger.info(f"Cliente no encontrado para teléfono: {customer_phone}")
return {"customer_name": "Cliente Nuevo", "customer_id": 0}
except Exception as e:
logger.error(f"Error identificando cliente: {e}")
return {"customer_name": "Cliente Final", "customer_id": 0}
def _llm_call_node(self, state: MessagesState) -> dict: def _llm_call_node(self, state: MessagesState) -> dict:
""" """
Nodo LLM: Decide siguiente acción o responde. Nodo LLM: Decide siguiente acción o responde.

View File

@@ -3,15 +3,12 @@ from langchain.messages import HumanMessage
from ...dependencies import get_agent from ...dependencies import get_agent
from ....logger import logger from ....logger import logger
from ....schemes.chat_schemes import SendMessageScheme from ....schemes.chat_schemes import SendMessageScheme
from naliiabot.bot.tools.naliia_tools import NaliiaTools
import httpx import httpx
import json import json
router = APIRouter() router = APIRouter()
_TOOLS = NaliiaTools()
async def send_whatsapp_message(request_data: SendMessageScheme): async def send_whatsapp_message(request_data: SendMessageScheme):
""" """
@@ -78,29 +75,6 @@ async def webhook_chat(request: Request, agent=Depends(get_agent)):
existing_state = await agent.checkpointer.aget(config) existing_state = await agent.checkpointer.aget(config)
if existing_state is None: if existing_state is None:
initial_state["customer_phone"] = customer_phone initial_state["customer_phone"] = customer_phone
find_customer_tool = _TOOLS.find_customer_by_identifier
customer_result = await find_customer_tool.ainvoke(
{"identifier": customer_phone}
)
try:
customer_data = json.loads(customer_result)
if isinstance(customer_data, list):
customer_data = customer_data[0] if customer_data else {}
if customer_data and customer_data.get("id"):
initial_state["customer_name"] = customer_data.get("party.", {}).get("name", "Cliente Final")
initial_state["customer_id"] = customer_data.get("id", 0)
logger.info(
f"Cliente registrado encontrado: {initial_state["customer_name"]} (ID: {initial_state["customer_id"]})"
)
else:
logger.info(
f"Cliente no encontrado para el teléfono: {customer_phone}"
)
except (json.JSONDecodeError, AttributeError) as e:
logger.error(f"Error al parsear datos del cliente: {e}")
logger.info(f"Nueva conversación iniciada con cliente: {customer_phone}") logger.info(f"Nueva conversación iniciada con cliente: {customer_phone}")
agent_response = await agent.ainvoke(initial_state, config=config) agent_response = await agent.ainvoke(initial_state, config=config)