diff --git a/src/naliiabot/bot/agent/agent.py b/src/naliiabot/bot/agent/agent.py index 68844c4..2c9c412 100644 --- a/src/naliiabot/bot/agent/agent.py +++ b/src/naliiabot/bot/agent/agent.py @@ -17,8 +17,8 @@ class AgentConfig: "helpful assistant that can call tools when needed." " Always respond with a message." ) - max_iterations: int = 200 - timeout_seconds: float = 30.0 + max_iterations: int = 100 + timeout_seconds: float = 15.0 class Agent: @@ -135,12 +135,12 @@ class Agent: """Construye el grafo de estado completo.""" agent_builder = StateGraph(MessagesState) - agent_builder.add_node("identify_customer", self._identify_customer_node) + # agent_builder.add_node("identify_customer", self._identify_customer_node) agent_builder.add_node("llm_call", self._llm_call_node) agent_builder.add_node("tool_node", self._tool_node) - agent_builder.add_edge(START, "identify_customer") - agent_builder.add_edge("identify_customer", "llm_call") + agent_builder.add_edge(START, "llm_call") + # agent_builder.add_edge("identify_customer", "llm_call") agent_builder.add_conditional_edges( "llm_call", self._should_continue, {"tool_node": "tool_node", END: END} ) @@ -226,6 +226,7 @@ class Agent: return {"messages": state["messages"]} tool_results: list[BaseMessage] = [] + updates = {} for tool_call in last_message.tool_calls: result = self._execute_tool( @@ -233,10 +234,29 @@ class Agent: ) tool_results.append(result) + if tool_call.get("name") == "find_customer_by_identifier": + try: + customer_data = json.loads(result.content) if result.content else {} + if isinstance(customer_data, list): + customer_data = customer_data[0] if customer_data else {} + if customer_data and customer_data.get("party.", {}).get("id", 0): + updates["customer_name"] = customer_data.get("party.", {}).get( + "name", "Cliente" + ) + updates["customer_id"] = customer_data.get("party.", {}).get( + "id", 0 + ) + except (json.JSONDecodeError, KeyError): + pass + if self._post_tool_hook: self._post_tool_hook(state, result) - return {"messages": state["messages"] + tool_results} + result_dict = {"messages": state["messages"] + tool_results} + if updates: + result_dict.update(updates) + + return result_dict def _execute_tool( self, tool_call: dict, customer_id=None, customer_name=None diff --git a/src/naliiabot/bot/prompts/prompts/prompts_template.yaml b/src/naliiabot/bot/prompts/prompts/prompts_template.yaml index 17da636..351aac7 100644 --- a/src/naliiabot/bot/prompts/prompts/prompts_template.yaml +++ b/src/naliiabot/bot/prompts/prompts/prompts_template.yaml @@ -21,6 +21,13 @@ prompts: que puedas obtener a travez de las tools. - Destaca opciones importantes con `código inline`. + IMPORTANTE - Identificación del Cliente: + - Cuando el usuario proporcione un número de teléfono, documento de identidad, + DEBES inmediatamente llamar a la herramienta find_customer_by_identifier con ese valor. + - No asumas que ya conoces al cliente, verifica su identidad usando la herramienta. + - Ejemplo: Si el usuario dice "mi teléfono es 3016859278" o "3016859278", llama inmediatamente + a find_customer_by_identifier con ese identificador. + @@ -53,7 +60,7 @@ prompts: - schedule_appointment: Agendar y confirmar cita. - find_service_centers: Consultar los centros de atencion o servicio disponibles. - find_products_and_services: Consultar los productos y servicios disponibles con precios. - - find_customer_by_identifier: Buscar cliente por telefono, email o numero de documento. + - find_customer_by_identifier: Buscar cliente por telefono, email o numero de documento. SIEMPRE llama esta herramienta cuando el usuario proporcione un identificador. """ diff --git a/src/naliiabot/bot/tools/naliia_tools.py b/src/naliiabot/bot/tools/naliia_tools.py index 229f2af..3f3be2f 100644 --- a/src/naliiabot/bot/tools/naliia_tools.py +++ b/src/naliiabot/bot/tools/naliia_tools.py @@ -24,12 +24,32 @@ class NaliiaTools: return [ self.get_current_datetime, self.get_tomorrow_date, + self.register_customer, self.schedule_appointment, self.find_service_centers, self.find_products_and_services, self.find_customer_by_identifier, + self.check_mcp_connection, ] + @tool + def check_mcp_connection(self) -> str: + """ + Verifica la conexión con el servidor MCP. + + Returns: + str: "connected" si el servidor está disponible, "disconnected" si no hay conexión. + """ + + async def call_tool(): + async with _MCP_CLIENT: + result = await _MCP_CLIENT.ping() + logger.info(f"MCP connection status: {result}") + return True + + is_connected = asyncio.run(call_tool()) + return "connected" if is_connected else "disconnected" + @tool def get_tomorrow_date() -> str: """ @@ -43,7 +63,6 @@ class NaliiaTools: - No se aceptan fechas en el pasado con respecto a esta fecha. - Utiliza la zona horaria de Colombia (America/Bogota, UTC-5). """ - td = timedelta(days=1) tomorrow = datetime.now().date() + td tomorrow_date_formated = tomorrow.isoformat() @@ -73,6 +92,41 @@ class NaliiaTools: return today_date_formated + @tool + def register_customer(name: str, cellphone: str, email: str = None) -> bool: + """ + Registra un nuevo cliente + + Args: + name: Nombre Completo del Cliente. + cellphone: Numero de Celular o Contacto. + + Returns: + list: En caso del cliente fuese creado exitosamente se obtendra una lista con el id del cliente en la base de datos. + Ejemplo: [1] + """ + + identifiers = { + "type": "mobile", + "code": cellphone + } + + async def call_tool(): + async with _MCP_CLIENT: + result = await _MCP_CLIENT.call_tool( + 'create_customer', { + "name": name, + "identifiers": identifiers + }) + + logger.info(result.content[0].text) + + return result + + result = asyncio.run(call_tool()) + + return result.content[0].text + @tool def find_customer_by_identifier(identifier: str) -> str: """ diff --git a/src/naliiabotapi/api/v1/webhooks/chat_hook.py b/src/naliiabotapi/api/v1/webhooks/chat_hook.py index 82120cf..23bfeb3 100644 --- a/src/naliiabotapi/api/v1/webhooks/chat_hook.py +++ b/src/naliiabotapi/api/v1/webhooks/chat_hook.py @@ -15,7 +15,7 @@ async def send_whatsapp_message(request_data: SendMessageScheme): Envía un mensaje de texto a través de la API de WhatsApp. """ # instance_url = request_data["instance_url"] - instance_url = 'http://192.168.58.109:8080' + instance_url = "http://192.168.58.109:8080" instance_name = request_data["instance_name"] apikey = request_data["apikey"] jid = request_data["jid"] @@ -65,18 +65,27 @@ async def webhook_chat(request: Request, agent=Depends(get_agent)): config = {"configurable": {"thread_id": thread_id}} - initial_state = { - "messages": [HumanMessage(content=user_message)], - "llm_calls": 0, - "customer_phone": "", - "customer_name": "", - "customer_id": 0, - } - existing_state = await agent.checkpointer.aget(config) + if existing_state is None: - initial_state["customer_phone"] = customer_phone + initial_state = { + "messages": [HumanMessage(content=user_message)], + "llm_calls": 0, + "customer_phone": customer_phone, + "customer_name": "", + "customer_id": 0, + } logger.info(f"Nueva conversación iniciada con cliente: {customer_phone}") + else: + initial_state = { + "messages": existing_state["messages"] + + [HumanMessage(content=user_message)], + "llm_calls": existing_state.get("llm_calls", 0), + "customer_phone": customer_phone, + "customer_name": existing_state.get("customer_name", ""), + "customer_id": existing_state.get("customer_id", 0), + } + logger.info(f"Conversación reanudada con cliente: {customer_phone}") agent_response = await agent.ainvoke(initial_state, config=config) agent_response_content = agent_response["messages"][-1].content diff --git a/src/ui/src/ui/agent_client.py b/src/ui/src/ui/agent_client.py index 8129ae0..db27f3f 100644 --- a/src/ui/src/ui/agent_client.py +++ b/src/ui/src/ui/agent_client.py @@ -10,7 +10,7 @@ class AgentClient: logger.info(f"Sending message to agent: {message}") response = await self._client.post( - "http://localhost:8010/chat", + "http://192.168.58.109:8010/chat", json={"messages": message}, timeout=60 )