enhance: auto-identify customers and add new tools
- Agent now auto-calls find_customer_by_identifier when user provides identifier - Added register_customer and check_mcp_connection tools - Refactored chat_hook to resume existing conversations from state - Reduced default max_iterations (100) and timeout (15s)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
</Reglas de Conversación>
|
||||
|
||||
<Límites éticos y legales (INQUEBRANTABLES)>
|
||||
@@ -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.
|
||||
|
||||
</Tools Disponibles Para Brindar Atencion al Cliente>
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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}}
|
||||
|
||||
existing_state = await agent.checkpointer.aget(config)
|
||||
|
||||
if existing_state is None:
|
||||
initial_state = {
|
||||
"messages": [HumanMessage(content=user_message)],
|
||||
"llm_calls": 0,
|
||||
"customer_phone": "",
|
||||
"customer_phone": 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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user