Segizu commited on
Commit
8ea5933
1 Parent(s): 078a8aa
Files changed (1) hide show
  1. app.py +141 -67
app.py CHANGED
@@ -1,72 +1,146 @@
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from langgraph.graph import StateGraph, END
3
- from langchain_core.runnables import RunnableLambda
4
- from transformers import AutoTokenizer, AutoModelForCausalLM
5
- import torch
6
-
7
- # 1. Preparar el modelo Qwen/Qwen1.5-32B-Chat
8
- model_id = "Qwen/Qwen1.5-32B-Chat"
9
-
10
- tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
11
- model = AutoModelForCausalLM.from_pretrained(
12
- model_id,
13
- device_map="auto",
14
- torch_dtype=torch.float16,
15
- trust_remote_code=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  )
17
- model.eval()
18
-
19
- # 2. Crear un wrapper manual (sin langchain)
20
- class QwenWrapper:
21
- def invoke(self, prompt: str) -> str:
22
- messages = [{"role": "user", "content": prompt}]
23
- inputs = tokenizer.apply_chat_template(messages, return_tensors="pt").to(model.device)
24
- output = model.generate(inputs, max_new_tokens=256, do_sample=True, temperature=0.7)
25
- decoded = tokenizer.decode(output[0], skip_special_tokens=True)
26
- return decoded.split("assistant")[-1].strip()
27
-
28
- qwen = QwenWrapper()
29
-
30
- # 3. Definir el estado del agente
31
- class AgentState(dict):
32
- pass
33
-
34
- # 4. Paso del agente con rol de agente de viajes
35
- def agent_step(state: AgentState) -> AgentState:
36
- user_input = state.get("input")
37
- if not user_input:
38
- return {"input": "", "output": "No se recibi贸 entrada."}
39
- travel_prompt = (
40
- "Eres un agente de viajes profesional y experimentado. "
41
- "Asesora, recomienda y planifica itinerarios, destinos y actividades de viaje seg煤n las preferencias del usuario. "
42
- f"Usuario: {user_input}"
43
- )
44
- response = qwen.invoke(travel_prompt)
45
- return {"input": user_input, "output": response}
46
-
47
-
48
- # 5. Crear nodo LangGraph
49
- agent_node = RunnableLambda(agent_step)
50
-
51
- graph_builder = StateGraph(AgentState)
52
- graph_builder.add_node("agent", agent_node)
53
- graph_builder.set_entry_point("agent")
54
- graph_builder.add_edge("agent", END)
55
- graph = graph_builder.compile()
56
-
57
- # 6. Funci贸n para Gradio
58
- def chat_with_agent(message):
59
- result = graph.invoke({"input": message})
60
- print(result)
61
- return result["output"]
62
-
63
- # 7. Interfaz Gradio adaptada para un agente de viajes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  iface = gr.Interface(
65
- fn=chat_with_agent,
66
- inputs=gr.Textbox(lines=2, placeholder="Haz una consulta sobre viajes..."),
67
- outputs="text",
68
- title="Agente de Viajes con LangGraph y Qwen",
69
- description="Agente de viajes que utiliza LangGraph y Qwen/Qwen1.5-32B-Chat para recomendar destinos, itinerarios y consejos de viaje."
70
  )
71
 
72
- iface.launch()
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from langgraph.graph import StateGraph, START, END
4
+ from langgraph.graph.message import add_messages
5
+ from typing import TypedDict, Annotated, Literal
6
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
7
+ from langchain_openai import ChatOpenAI
8
+ from pydantic import BaseModel, Field
9
  import gradio as gr
10
+
11
+ load_dotenv()
12
+
13
+ # Configuraci贸n
14
+ max_tokens = 2000
15
+ num_iterations = 2
16
+ quality_threshold = 8
17
+
18
+ # Base de datos simulada de destinos tur铆sticos
19
+ travel_database = {
20
+ "paris": {"destination": "Paris", "price": 1500, "features": ["romantic", "cultural", "historic"]},
21
+ "bali": {"destination": "Bali", "price": 1200, "features": ["beach", "relaxing", "adventurous"]},
22
+ "new_york": {"destination": "New York", "price": 2000, "features": ["urban", "shopping", "nightlife"]},
23
+ "tokyo": {"destination": "Tokyo", "price": 1800, "features": ["modern", "cultural", "tech-savvy"]},
24
+ }
25
+
26
+ # Modelos estructurados para la salida de cada nodo
27
+ class GenerateRecommendation(BaseModel):
28
+ destination: str = Field(description="El destino tur铆stico recomendado")
29
+ explanation: str = Field(description="Explicaci贸n breve de la recomendaci贸n")
30
+
31
+ class RecommendationQualityScore(BaseModel):
32
+ score: int = Field(description="Puntuaci贸n de la recomendaci贸n entre 1-10")
33
+ comment: str = Field(description="Comentario sobre la calidad de la recomendaci贸n")
34
+
35
+ # Estado del grafo
36
+ class GraphState(TypedDict):
37
+ messages: Annotated[list, add_messages]
38
+ quality: Annotated[int, 0] # Valor inicial 0
39
+ iterations: Annotated[int, 0] # Valor inicial 0
40
+
41
+ # Inicializaci贸n del grafo
42
+ builder = StateGraph(GraphState)
43
+
44
+ llm = ChatOpenAI(
45
+ model="gpt-4o-mini",
46
+ temperature=0,
47
+ max_tokens=max_tokens,
48
+ api_key=os.getenv("OPENAI_API_KEY")
49
  )
50
+ developer_structure_llm = llm.with_structured_output(GenerateRecommendation, method="json_mode")
51
+ reviewer_structure_llm = llm.with_structured_output(RecommendationQualityScore, method="json_mode")
52
+
53
+ def travel_recommender(state):
54
+ # Se asume que el 煤ltimo mensaje contiene las preferencias del usuario
55
+ user_requirements = state["messages"][-1].content
56
+ system_prompt = f"""
57
+ Eres un experto en recomendaciones de viajes.
58
+ Con base en las siguientes preferencias del usuario: {user_requirements},
59
+ selecciona el mejor destino de la siguiente base de datos: {travel_database}.
60
+ Responde en JSON con la clave `destination` para el destino recomendado y `explanation` con una breve raz贸n de la recomendaci贸n.
61
+ """
62
+ human_messages = [msg for msg in state["messages"] if isinstance(msg, HumanMessage)]
63
+ ai_messages = [msg for msg in state["messages"] if isinstance(msg, AIMessage)]
64
+ system_messages = [SystemMessage(content=system_prompt)]
65
+
66
+ messages = system_messages + human_messages + ai_messages
67
+ message = developer_structure_llm.invoke(messages)
68
+
69
+ recommendation_output = f"Destination: {message.destination}\nExplanation: {message.explanation}"
70
+ # Se agrega la recomendaci贸n a los mensajes para los siguientes nodos
71
+ state["messages"].append(AIMessage(content=recommendation_output))
72
+ state["iterations"] += 1
73
+ return state
74
+
75
+ def recommendation_review(state):
76
+ system_prompt = """
77
+ Eres un revisor de recomendaciones con altos est谩ndares.
78
+ Revisa la recomendaci贸n proporcionada y asigna una puntuaci贸n de calidad entre 1-10.
79
+ Eval煤a la relevancia, precisi贸n y alineaci贸n con las necesidades del cliente.
80
+ Responde en JSON con las claves `score` y `comment`.
81
+ """
82
+ human_messages = [msg for msg in state["messages"] if isinstance(msg, HumanMessage)]
83
+ ai_messages = [msg for msg in state["messages"] if isinstance(msg, AIMessage)]
84
+ system_messages = [SystemMessage(content=system_prompt)]
85
+
86
+ messages = system_messages + human_messages + ai_messages
87
+ message = reviewer_structure_llm.invoke(messages)
88
+
89
+ review_comment = f"Review Score: {message.score}\nComment: {message.comment}"
90
+ state["messages"].append(AIMessage(content=review_comment))
91
+ state["quality"] = message.score
92
+ return state
93
+
94
+ def final_recommendation(state):
95
+ system_prompt = "Revisa la recomendaci贸n final y proporciona una respuesta final para el usuario."
96
+ human_messages = [msg for msg in state["messages"] if isinstance(msg, HumanMessage)]
97
+ ai_messages = [msg for msg in state["messages"] if isinstance(msg, AIMessage)]
98
+ system_messages = [SystemMessage(content=system_prompt)]
99
+
100
+ messages = system_messages + human_messages + ai_messages
101
+ final_message = llm.invoke(messages)
102
+
103
+ # Se guarda la recomendaci贸n final en el estado para mostrarla
104
+ state["final_recommendation"] = final_message.content
105
+ state["messages"].append(AIMessage(content=f"Final Recommendation: {final_message.content}"))
106
+ return state
107
+
108
+ # Funci贸n para definir la condici贸n de la bifurcaci贸n en el grafo
109
+ def quality_gate_condition(state) -> Literal["travel_recommender", "final_recommendation"]:
110
+ if state["iterations"] >= num_iterations:
111
+ return "final_recommendation"
112
+ if state["quality"] < quality_threshold:
113
+ return "travel_recommender"
114
+ else:
115
+ return "final_recommendation"
116
+
117
+ # Agregar nodos al grafo
118
+ builder.add_node("travel_recommender", travel_recommender)
119
+ builder.add_node("recommendation_review", recommendation_review)
120
+ builder.add_node("final_recommendation", final_recommendation)
121
+
122
+ # Conectar nodos
123
+ builder.add_edge(START, "travel_recommender")
124
+ builder.add_edge("travel_recommender", "recommendation_review")
125
+ builder.add_edge("final_recommendation", END)
126
+
127
+ builder.add_conditional_edges("recommendation_review", quality_gate_condition)
128
+
129
+ graph = builder.compile()
130
+
131
+ # Funci贸n que ejecuta el grafo con la entrada del usuario
132
+ def run_graph(user_input: str) -> str:
133
+ initial_state = {"messages": [HumanMessage(content=user_input)], "quality": 0, "iterations": 0}
134
+ final_state = graph.invoke(initial_state)
135
+ return final_state.get("final_recommendation", "No se gener贸 una recomendaci贸n final.")
136
+
137
+ # Interfaz de Gradio
138
  iface = gr.Interface(
139
+ fn=run_graph,
140
+ inputs=gr.Textbox(label="Ingrese sus preferencias de viaje"),
141
+ outputs=gr.Textbox(label="Recomendaci贸n Final"),
142
+ title="Sistema de Recomendaci贸n de Viajes"
 
143
  )
144
 
145
+ if __name__ == "__main__":
146
+ iface.launch()