Romanchello-bit commited on
Commit
5e404a3
·
1 Parent(s): 84f1fc6

Refine intent analysis and lead data export

Browse files

Updated intent analysis logic in app.py to enforce 'never give up' objection handling and clarified rules for MOVE, STAY, and EXIT. Improved lead export in leads_manager.py by adding an 'AI Insights' column and using escaped newlines in transcripts. Removed ambiguous transitions from 'hook_rejection' in sales_script.json for clearer flow.

Files changed (3) hide show
  1. app.py +20 -18
  2. leads_manager.py +4 -3
  3. sales_script.json +0 -10
app.py CHANGED
@@ -110,8 +110,10 @@ def get_predicted_path(graph, start_id, target_id, id_to_node, node_to_id):
110
  if dist[target_id] == float('inf'): return []
111
  path = [target_id]
112
  curr = target_id
 
113
  while curr != start_id and attempts < 200:
114
  found = False
 
115
  for u in range(graph.num_vertices):
116
  for v, w in graph.adj_list[u]:
117
  if v == curr and dist[v] == dist[u] + w:
@@ -122,32 +124,34 @@ def get_predicted_path(graph, start_id, target_id, id_to_node, node_to_id):
122
 
123
  def analyze_full_context(model, user_input, current_node, chat_history):
124
  """
125
- Аналізує Інтент, Емоції та ПСИХОТИП клієнта.
126
  """
127
- history_text = "\n".join([f"{m['role']}: {m['content']}" for m in chat_history[-4:]]) # Беремо останні 4 фрази для контексту
128
 
129
  prompt = f"""
130
- ROLE: Behavioral Psychologist & Sales Expert.
131
 
132
  CONTEXT:
133
  Current Step: "{current_node}"
134
- Recent Chat History:
135
- {history_text}
136
- User just said: "{user_input}"
137
 
138
- TASK 1: Detect User Archetype (Pattern). Choose ONE:
139
- - DRIVER (Direct, impatient, results-oriented)
140
- - ANALYST (Detail-oriented, asks 'how', skeptical)
141
- - EXPRESSIVE (Emotional, enthusiastic, visionary)
142
- - CONSERVATIVE (Risk-averse, slow, likes stability)
143
 
144
- TASK 2: Analyze Intent (MOVE, STAY, EXIT).
 
 
 
 
 
 
 
 
145
 
146
  OUTPUT JSON format:
147
  {{
148
  "archetype": "DRIVER" | "ANALYST" | "EXPRESSIVE" | "CONSERVATIVE",
149
  "intent": "MOVE" | "STAY" | "EXIT",
150
- "reasoning": "Why you chose this archetype (1 short sentence)"
151
  }}
152
  """
153
  try:
@@ -155,7 +159,8 @@ def analyze_full_context(model, user_input, current_node, chat_history):
155
  clean_text = response.text.replace("```json", "").replace("```", "").strip()
156
  return json.loads(clean_text)
157
  except:
158
- return {"archetype": "UNKNOWN", "intent": "STAY", "reasoning": "Error"}
 
159
 
160
  def generate_response(model, instruction_text, user_input, intent, lead_info, archetype):
161
  """
@@ -603,11 +608,8 @@ elif st.session_state.page == "chat":
603
  # --- ГЕНЕРАЦІЯ ПЕРШОГО ПОВІДОМЛЕННЯ ---
604
  if not st.session_state.messages:
605
  with st.spinner("AI готується до дзвінка..."):
606
- start_instruction = nodes["start"]
607
  # Викликаємо AI для генерації живого привітання
608
- # lead_info keys might differ if coming from very old session, but setup ensures keys exist.
609
- # Just in case, defaults from setup form are used.
610
- greeting = generate_greeting(model, start_instruction, st.session_state.lead_info)
611
 
612
  st.session_state.messages.append({"role": "assistant", "content": greeting})
613
  st.rerun()
 
110
  if dist[target_id] == float('inf'): return []
111
  path = [target_id]
112
  curr = target_id
113
+ attempts = 0
114
  while curr != start_id and attempts < 200:
115
  found = False
116
+ attempts += 1
117
  for u in range(graph.num_vertices):
118
  for v, w in graph.adj_list[u]:
119
  if v == curr and dist[v] == dist[u] + w:
 
124
 
125
  def analyze_full_context(model, user_input, current_node, chat_history):
126
  """
127
+ Аналізує Інтент з налаштуванням "NEVER GIVE UP".
128
  """
129
+ history_text = "\n".join([f"{m['role']}: {m['content']}" for m in chat_history[-4:]])
130
 
131
  prompt = f"""
132
+ ROLE: World-Class Sales Psychologist.
133
 
134
  CONTEXT:
135
  Current Step: "{current_node}"
136
+ User said: "{user_input}"
 
 
137
 
138
+ TASK: Determine Intent (MOVE, STAY, EXIT).
 
 
 
 
139
 
140
+ CRITICAL RULES FOR INTENT:
141
+ 1. **EXIT** triggers ONLY if user is HOSTILE or EXPLICITLY ends the call.
142
+ - Examples: "Stop calling me", "Fuck off", "Put me on blacklist", "Bye", "Hang up".
143
+
144
+ 2. **STAY** (Objection Handling) triggers for ANY resistance.
145
+ - Examples: "Not interested", "No time", "We have a vendor", "Too expensive", "Send info to mail".
146
+ - Even if they say "No" to the first question -> It is NOT an exit. It is an objection to handle!
147
+
148
+ 3. **MOVE** triggers only if user agrees or answers a question positively.
149
 
150
  OUTPUT JSON format:
151
  {{
152
  "archetype": "DRIVER" | "ANALYST" | "EXPRESSIVE" | "CONSERVATIVE",
153
  "intent": "MOVE" | "STAY" | "EXIT",
154
+ "reasoning": "Why?"
155
  }}
156
  """
157
  try:
 
159
  clean_text = response.text.replace("```json", "").replace("```", "").strip()
160
  return json.loads(clean_text)
161
  except:
162
+ # За замовчуванням STAY! Краще зайвий раз перепитати, ніж кинути слухавку.
163
+ return {"archetype": "UNKNOWN", "intent": "STAY", "reasoning": "Fallback safety"}
164
 
165
  def generate_response(model, instruction_text, user_input, intent, lead_info, archetype):
166
  """
 
608
  # --- ГЕНЕРАЦІЯ ПЕРШОГО ПОВІДОМЛЕННЯ ---
609
  if not st.session_state.messages:
610
  with st.spinner("AI готується до дзвінка..."):
 
611
  # Викликаємо AI для генерації живого привітання
612
+ greeting = generate_greeting(model, nodes["start"], st.session_state.lead_info)
 
 
613
 
614
  st.session_state.messages.append({"role": "assistant", "content": greeting})
615
  st.rerun()
leads_manager.py CHANGED
@@ -43,14 +43,14 @@ def save_lead_to_db(lead_info, chat_history, outcome):
43
  if not sheet.get_all_values():
44
  sheet.append_row([
45
  "Date", "Name", "Company", "Type", "Context",
46
- "Pain Point", "Budget", "Outcome", "Transcript"
47
  ])
48
  except:
49
  pass # Таблиця може бути новою
50
 
51
  # Формуємо рядок даних
52
  # Збираємо весь текст діалогу для навчання
53
- transcript = "\n".join([f"{msg['role']}: {msg['content']}" for msg in chat_history])
54
 
55
  row = [
56
  datetime.now().strftime("%Y-%m-%d %H:%M"),
@@ -61,7 +61,8 @@ def save_lead_to_db(lead_info, chat_history, outcome):
61
  "AI Pending", # Тут можна додати AI аналіз
62
  "Unknown",
63
  outcome,
64
- transcript
 
65
  ]
66
 
67
  # Додаємо рядок
 
43
  if not sheet.get_all_values():
44
  sheet.append_row([
45
  "Date", "Name", "Company", "Type", "Context",
46
+ "Pain Point", "Budget", "Outcome", "Transcript", "AI Insights"
47
  ])
48
  except:
49
  pass # Таблиця може бути новою
50
 
51
  # Формуємо рядок даних
52
  # Збираємо весь текст діалогу для навчання
53
+ transcript = "\\n".join([f"{msg['role']}: {msg['content']}" for msg in chat_history])
54
 
55
  row = [
56
  datetime.now().strftime("%Y-%m-%d %H:%M"),
 
61
  "AI Pending", # Тут можна додати AI аналіз
62
  "Unknown",
63
  outcome,
64
+ transcript,
65
+ "" # AI Insights placeholder
66
  ]
67
 
68
  # Додаємо рядок
sales_script.json CHANGED
@@ -35,16 +35,6 @@
35
  "to": "exit_fail",
36
  "weight": 1000
37
  },
38
- {
39
- "from": "hook_rejection",
40
- "to": "qualification_needs",
41
- "weight": 1
42
- },
43
- {
44
- "from": "hook_rejection",
45
- "to": "exit_fail",
46
- "weight": 50
47
- },
48
  {
49
  "from": "qualification_needs",
50
  "to": "pain_followup",
 
35
  "to": "exit_fail",
36
  "weight": 1000
37
  },
 
 
 
 
 
 
 
 
 
 
38
  {
39
  "from": "qualification_needs",
40
  "to": "pain_followup",