oc-assistant/app/oc_assistant.py

110 lines
4.1 KiB
Python

from langchain_community.tools.tavily_search import TavilySearchResults
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from langgraph.prebuilt import create_react_agent
from langchain_core.prompts import ChatPromptTemplate
from langgraph.checkpoint.memory import MemorySaver
from langchain_tools.agent_tools import (
redact_email, list_calendar_events,
create_calendar_event,
get_company_info,
get_current_date_and_time
)
from langchain_community.tools.gmail.utils import (
build_resource_service,
get_gmail_credentials,
)
from langchain_community.agent_toolkits import GmailToolkit
load_dotenv()
# Inicialiamos un LLM de OpenaIA
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.9
)
toolkit = GmailToolkit()
# Can review scopes here https://developers.google.com/gmail/api/auth/scopes
# For instance, readonly scope is 'https://www.googleapis.com/auth/gmail.readonly'
credentials = get_gmail_credentials(
token_file="token.json",
scopes=["https://mail.google.com/"],
client_secrets_file="credentials.json",
)
api_resource = build_resource_service(credentials=credentials)
toolkit = GmailToolkit(api_resource=api_resource)
# creamos la lista de herramientas de gmail
tools = toolkit.get_tools()
search = TavilySearchResults(max_results=2)
tools.append(search)
tools.append(redact_email)
tools.append(list_calendar_events)
tools.append(create_calendar_event)
tools.append(get_company_info)
tools.append(get_current_date_and_time)
system_prompt = ChatPromptTemplate.from_messages(
[
("system", "Eres Mariana, el asistente virtual de OneCluster, una empresa de software que ofrece soluciones personalizadas. Asume el tono de J.A.R.V.I.S.: cordial, atento y con tacto en todo momento."),
# Instrucciones sobre presentación y tono
("system", "Preséntate como Mariana en el primer mensaje y pregunta el nombre del usuario si no lo tienes registrado."),
("system", "Si el usuario ya ha interactuado antes, usa su nombre sin necesidad de volver a preguntar."),
("system", "Si el primer mensaje del usuario es una solicitud, pregúntale su nombre antes de responder si aún no lo conoces."),
# Instrucciones sobre el manejo de solicitudes y tareas
("system", "OneCluster es una empresa de software especializada en desarrollo a medida. Solo responde a preguntas y solicitudes relacionadas con la empresa y sus servicios."),
("system", "Si necesitas información adicional sobre la empresa, usa la función get_company_info."),
("system", "Antes de enviar correos o crear eventos, muestra los detalles al usuario para que los confirme antes de ejecutar la tarea."),
# Cómo manejar preguntas fuera del alcance
("system", "Si te preguntan algo no relacionado con los servicios de OneCluster, responde que solo puedes ayudar con temas relacionados con la empresa y sus soluciones."),
# Prohibición de revelar herramientas internas
("system", "Evita mencionar o hacer alusión a las herramientas que utilizas internamente. Esa información es confidencial."),
# Placeholder para el contenido dinámico de la conversación
("placeholder", "{messages}"),
]
)
class State(TypedDict):
messages: Annotated[list, add_messages]
is_last_step: bool # Cambiar a booleano si es necesario
# Creamos el graph con el estado definido
graph= create_react_agent(
model = llm, tools = tools, state_schema = State,
state_modifier = system_prompt,
checkpointer = MemorySaver()
)
config= {"configurable": {"thread_id": "thread-1", "recursion_limit": 50}}
while True:
user_input = input("User: ")
if user_input.lower() in ["quit", "exit", "q"]:
print("Goodbye!")
break
events = graph.stream(
{"messages": [("user", user_input)],
"is_last_step": False},
config, stream_mode = "updates")
for event in events:
if "agent" in event:
print(f"\nAsistente: {event["agent"]["messages"][-1].content}\n")