zenaight commited on
Commit
41e7566
·
1 Parent(s): bcd0eb8

more humanlike

Browse files
Files changed (2) hide show
  1. ai_chat.py +54 -18
  2. persona_manager.py +44 -15
ai_chat.py CHANGED
@@ -36,17 +36,41 @@ def chat_with_session_memory(state):
36
 
37
  if is_valid:
38
  # Update persona in database (handled by async wrapper)
39
- return {
40
- "response": response_message,
41
- "user_message": user_message,
42
- "ai_response": response_message,
43
- "session_id": session_id,
44
- "wa_id": wa_id,
45
- "wamid": wamid,
46
- "update_persona": True,
47
- "persona_field": current_field,
48
- "persona_value": parsed_value
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  else:
51
  # Invalid response, ask for clarification or re-ask
52
  return {
@@ -78,14 +102,24 @@ def chat_with_session_memory(state):
78
  }
79
 
80
  # Add system message with user context and persona
81
- system_message = "You are a helpful industrial property agent assistant."
 
 
 
 
 
 
 
 
 
 
82
  if user_info.get("name") and user_info["name"] != "Unknown":
83
- system_message += f" The user's name is {user_info['name']}."
84
 
85
  # Add persona context if available
86
  persona_summary = get_persona_summary(persona)
87
  if persona_summary != "New user - profile incomplete":
88
- system_message += f" User profile: {persona_summary}. Use this information to provide relevant property advice."
89
 
90
  # Build messages array with history
91
  messages = [{"role": "system", "content": system_message}]
@@ -155,10 +189,6 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
155
  if session_id:
156
  session_messages = await get_session_messages(session_id, limit=30)
157
 
158
- # Check if we should ask persona questions
159
- conversation_context = " ".join([msg["content"] for msg in session_messages[-5:]]) + " " + user_message
160
- should_ask, field_to_ask = await should_ask_persona_question(persona, conversation_context)
161
-
162
  # Check if this is a response to a persona question
163
  is_persona_question = False
164
  current_field = None
@@ -175,6 +205,12 @@ async def process_message(user_message: str, user_info: dict = None, session_id:
175
  parsed_response = await parse_user_response(user_message, field)
176
  break
177
 
 
 
 
 
 
 
178
  # Process with AI
179
  result = await chat_graph.ainvoke({
180
  "user_message": user_message,
 
36
 
37
  if is_valid:
38
  # Update persona in database (handled by async wrapper)
39
+ # Check if there are more persona questions to ask
40
+ updated_persona = persona.copy()
41
+ updated_persona[current_field] = parsed_value
42
+ missing_fields = [f for f in PERSONA_FIELDS.keys() if not updated_persona.get(f)]
43
+
44
+ if missing_fields:
45
+ # Ask next persona question
46
+ next_field = missing_fields[0]
47
+ next_question = PERSONA_FIELDS[next_field]["prompt"]
48
+ return {
49
+ "response": f"{response_message}\n\n{next_question}",
50
+ "user_message": user_message,
51
+ "ai_response": f"{response_message}\n\n{next_question}",
52
+ "session_id": session_id,
53
+ "wa_id": wa_id,
54
+ "wamid": wamid,
55
+ "update_persona": True,
56
+ "persona_field": current_field,
57
+ "persona_value": parsed_value,
58
+ "ask_persona_question": True,
59
+ "current_field": next_field
60
+ }
61
+ else:
62
+ # Persona complete - continue with natural conversation
63
+ return {
64
+ "response": f"{response_message}\n\nGreat! Now I have a good understanding of what you're looking for. How can I help you find the right industrial property?",
65
+ "user_message": user_message,
66
+ "ai_response": f"{response_message}\n\nGreat! Now I have a good understanding of what you're looking for. How can I help you find the right industrial property?",
67
+ "session_id": session_id,
68
+ "wa_id": wa_id,
69
+ "wamid": wamid,
70
+ "update_persona": True,
71
+ "persona_field": current_field,
72
+ "persona_value": parsed_value
73
+ }
74
  else:
75
  # Invalid response, ask for clarification or re-ask
76
  return {
 
102
  }
103
 
104
  # Add system message with user context and persona
105
+ system_message = """You are a friendly, professional industrial property agent assistant. Be conversational and human-like in your responses.
106
+
107
+ Key guidelines:
108
+ - Always respond naturally to greetings (hi, hello, etc.) with a warm greeting back
109
+ - Use the user's name if available to make it personal
110
+ - Be helpful and informative about industrial properties
111
+ - Don't immediately jump to asking questions - have a natural conversation first
112
+ - If someone asks for clarification about property terms, explain clearly and helpfully
113
+ - Only ask about their property needs when they show interest in searching or viewing properties
114
+ - Keep responses conversational and not robotic"""
115
+
116
  if user_info.get("name") and user_info["name"] != "Unknown":
117
+ system_message += f"\nThe user's name is {user_info['name']} - use their name to make it personal."
118
 
119
  # Add persona context if available
120
  persona_summary = get_persona_summary(persona)
121
  if persona_summary != "New user - profile incomplete":
122
+ system_message += f"\nUser profile: {persona_summary}. Use this information to provide relevant property advice."
123
 
124
  # Build messages array with history
125
  messages = [{"role": "system", "content": system_message}]
 
189
  if session_id:
190
  session_messages = await get_session_messages(session_id, limit=30)
191
 
 
 
 
 
192
  # Check if this is a response to a persona question
193
  is_persona_question = False
194
  current_field = None
 
205
  parsed_response = await parse_user_response(user_message, field)
206
  break
207
 
208
+ # Only check for new persona questions if this isn't a response to one
209
+ should_ask, field_to_ask = (False, None)
210
+ if not is_persona_question:
211
+ conversation_context = " ".join([msg["content"] for msg in session_messages[-5:]]) + " " + user_message
212
+ should_ask, field_to_ask = await should_ask_persona_question(persona, conversation_context)
213
+
214
  # Process with AI
215
  result = await chat_graph.ainvoke({
216
  "user_message": user_message,
persona_manager.py CHANGED
@@ -5,28 +5,28 @@ from config import supabase, llm, OPENAI_API_KEY
5
  # Persona field definitions and their collection prompts
6
  PERSONA_FIELDS = {
7
  "intent": {
8
- "prompt": "Are you looking to buy a place, or maybe lease for now?",
9
- "clarification": "Great question. Buying gives you long-term control, but leasing gives flexibility. Happy to guide you either way.",
10
  "skip_response": "No problem at all. We can always revisit that later."
11
  },
12
  "location_preference": {
13
  "prompt": "Any specific areas you'd prefer to be based in?",
14
- "clarification": "I can help you find properties in any area. Just let me know what locations work best for you.",
15
  "skip_response": "No problem at all. We can always revisit that later."
16
  },
17
  "size_preference_sqm": {
18
  "prompt": "Do you have a rough size in mind? For example, 1500 or 3000 sqm?",
19
- "clarification": "Size can vary a lot - from small workshops to large warehouses. What kind of space do you need?",
20
  "skip_response": "No problem at all. We can always revisit that later."
21
  },
22
  "budget": {
23
  "prompt": "Is there a budget you'd like me to work within?",
24
- "clarification": "Budget helps me find the right properties for you. We can discuss options at any price point.",
25
  "skip_response": "No problem at all. We can always revisit that later."
26
  },
27
  "must_have": {
28
  "prompt": "Anything important that's a must-have? Like truck access or yard space?",
29
- "clarification": "Must-haves are features you really need, like loading docks, high ceilings, or specific zoning.",
30
  "skip_response": "No problem at all. We can always revisit that later."
31
  }
32
  }
@@ -159,13 +159,34 @@ async def fallback_parse_response(user_message: str, field: str) -> Tuple[bool,
159
  """Fallback parsing when LLM parsing fails"""
160
  user_message_lower = user_message.lower()
161
 
162
- # Check for clarification requests
163
- clarification_words = ["what", "how", "explain", "difference", "mean", "clarify"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  if any(word in user_message_lower for word in clarification_words):
165
  return False, None, PERSONA_FIELDS[field]["clarification"]
166
 
 
 
 
 
 
 
167
  # Check for skip requests
168
- skip_words = ["not sure", "skip", "later", "don't know", "maybe later"]
169
  if any(word in user_message_lower for word in skip_words):
170
  return True, None, PERSONA_FIELDS[field]["skip_response"]
171
 
@@ -243,18 +264,26 @@ async def should_ask_persona_question(persona: Dict, conversation_context: str =
243
  # Check if user is asking about properties (indicates they want to search)
244
  search_indicators = [
245
  "property", "warehouse", "industrial", "space", "building",
246
- "available", "looking for", "need", "find", "search"
247
  ]
248
 
249
  conversation_lower = conversation_context.lower()
250
  is_searching = any(indicator in conversation_lower for indicator in search_indicators)
251
 
252
- # If user is actively searching, ask persona questions
253
- if is_searching:
254
- return True, missing_fields[0]
 
 
 
 
255
 
256
- # If this is a new user (no fields filled), ask the first question
257
- if len(missing_fields) == len(PERSONA_FIELDS):
 
 
 
 
258
  return True, missing_fields[0]
259
 
260
  return False, None
 
5
  # Persona field definitions and their collection prompts
6
  PERSONA_FIELDS = {
7
  "intent": {
8
+ "prompt": "Great! Are you looking to buy a place, or maybe lease for now?",
9
+ "clarification": "Great question! Buying gives you long-term control and equity, but requires a larger upfront investment. Leasing gives you flexibility and lower initial costs, but you don't build equity. Both have their benefits - what feels right for your situation?",
10
  "skip_response": "No problem at all. We can always revisit that later."
11
  },
12
  "location_preference": {
13
  "prompt": "Any specific areas you'd prefer to be based in?",
14
+ "clarification": "I can help you find properties in any area! Some popular industrial areas include downtown, suburbs, industrial districts, and warehouse zones. Each has different benefits - downtown for accessibility, suburbs for space, industrial areas for zoning. What kind of location works best for your business?",
15
  "skip_response": "No problem at all. We can always revisit that later."
16
  },
17
  "size_preference_sqm": {
18
  "prompt": "Do you have a rough size in mind? For example, 1500 or 3000 sqm?",
19
+ "clarification": "Property sizes can vary a lot! Small workshops might be 500-1000 sqm, medium warehouses 1500-3000 sqm, and large facilities 5000+ sqm. Think about your current space needs and future growth. What kind of operations will you be running?",
20
  "skip_response": "No problem at all. We can always revisit that later."
21
  },
22
  "budget": {
23
  "prompt": "Is there a budget you'd like me to work within?",
24
+ "clarification": "Budget helps me find the right properties for you! Industrial properties can range from $200k for small units to millions for large facilities. We can discuss options at any price point - what's comfortable for your business?",
25
  "skip_response": "No problem at all. We can always revisit that later."
26
  },
27
  "must_have": {
28
  "prompt": "Anything important that's a must-have? Like truck access or yard space?",
29
+ "clarification": "Must-haves are features you really need for your business! Common ones include loading docks, high ceilings, truck access, yard space, office areas, parking, or specific zoning. What features are essential for your operations?",
30
  "skip_response": "No problem at all. We can always revisit that later."
31
  }
32
  }
 
159
  """Fallback parsing when LLM parsing fails"""
160
  user_message_lower = user_message.lower()
161
 
162
+ # Check for clarification requests - more comprehensive
163
+ clarification_words = [
164
+ "what", "how", "explain", "difference", "mean", "clarify", "tell me",
165
+ "what do you mean", "what does", "how does", "can you explain",
166
+ "i don't understand", "not sure what", "what's the difference"
167
+ ]
168
+
169
+ # Check for questions about the field specifically
170
+ field_specific_clarifications = {
171
+ "intent": ["buy", "lease", "purchase", "rent", "owning", "owning vs leasing"],
172
+ "size_preference_sqm": ["size", "sqm", "square meters", "how big", "dimensions"],
173
+ "budget": ["budget", "cost", "price", "how much", "expensive"],
174
+ "location_preference": ["location", "area", "where", "place"],
175
+ "must_have": ["must have", "features", "requirements", "need", "essential"]
176
+ }
177
+
178
+ # Check for general clarification requests
179
  if any(word in user_message_lower for word in clarification_words):
180
  return False, None, PERSONA_FIELDS[field]["clarification"]
181
 
182
+ # Check for field-specific clarification requests
183
+ if field in field_specific_clarifications:
184
+ field_words = field_specific_clarifications[field]
185
+ if any(word in user_message_lower for word in field_words):
186
+ return False, None, PERSONA_FIELDS[field]["clarification"]
187
+
188
  # Check for skip requests
189
+ skip_words = ["not sure", "skip", "later", "don't know", "maybe later", "pass"]
190
  if any(word in user_message_lower for word in skip_words):
191
  return True, None, PERSONA_FIELDS[field]["skip_response"]
192
 
 
264
  # Check if user is asking about properties (indicates they want to search)
265
  search_indicators = [
266
  "property", "warehouse", "industrial", "space", "building",
267
+ "available", "looking for", "need", "find", "search", "show me", "what do you have"
268
  ]
269
 
270
  conversation_lower = conversation_context.lower()
271
  is_searching = any(indicator in conversation_lower for indicator in search_indicators)
272
 
273
+ # Check for greetings or casual conversation - don't ask persona questions
274
+ casual_indicators = [
275
+ "hi", "hello", "hey", "good morning", "good afternoon", "good evening",
276
+ "how are you", "what's up", "thanks", "thank you", "bye", "goodbye"
277
+ ]
278
+
279
+ is_casual = any(indicator in conversation_lower for indicator in casual_indicators)
280
 
281
+ # Don't ask persona questions during casual conversation
282
+ if is_casual:
283
+ return False, None
284
+
285
+ # Only ask if user is actively searching for properties
286
+ if is_searching:
287
  return True, missing_fields[0]
288
 
289
  return False, None