scormon-predata-ai commited on
Commit
0bd36cc
·
verified ·
1 Parent(s): b75c907

Update researchsimulation/InteractiveInterviewChatbot.py

Browse files
researchsimulation/InteractiveInterviewChatbot.py CHANGED
@@ -43,7 +43,6 @@ def parse_question_with_llm(question, respondent_names, processor_llm):
43
  - **Correct (British):** organised, prioritise, minimise, realise, behaviour, centre, defence, travelling, practise (verb), licence (noun), programme, aeroplane.
44
  - **Incorrect (American):** organized, prioritize, minimize, realize, behavior, center, defense, traveling, practice (verb and noun), license (noun), program, airplane.
45
  6. Ensure that you follow the **Formatting Rules** exactly. THIS IS EXTREMELY IMPORTANT.
46
-
47
  ### Examples:
48
  - "Sourav, do you agree with this topic?" → "Do you agree with this topic?"
49
  - "What do you think about this topic, Divya?" → "What do you think about this topic?"
@@ -55,7 +54,6 @@ def parse_question_with_llm(question, respondent_names, processor_llm):
55
  - "Divya, what did you learn from this program?" → "What did you learn from this programme?"
56
  - "How do you stay organized, Rahul?" → "How do you stay organised?"
57
  - "Meena, how do you balance work and traveling?" → "How do you balance work and travelling?"
58
-
59
  ### **Formatting Rules**:
60
  For each question identified, respond using **only** the following format:
61
  - Respondent: <Respondent Name>
@@ -206,100 +204,85 @@ def validate_question_topics(parsed_questions, processor_llm):
206
 
207
 
208
 
209
- def generate_generic_answer(agent_name, agent_question, respondent_agent):
210
- """
211
- Generate a raw, generic answer in first person, British English, without applying any specific style or tone.
212
- """
213
- task_prompt = f"""
214
- You are {agent_name}. Respond to the question below **in first person**, using British English.
215
- Focus only on answering naturally and authentically. Do not apply any specific tone or style.
216
-
217
-
218
- ### Question:
219
- "{agent_question}"
220
- """
221
- raw_response_task = Task(description=task_prompt, expected_output="", agent=respondent_agent)
222
- crew = Crew(agents=[respondent_agent], tasks=[raw_response_task], process=Process.sequential)
223
- crew.kickoff()
224
- return raw_response_task.output.raw.strip()
225
-
226
-
227
-
228
-
229
- def stylise_answer(raw_response, communication_style, agent_name, processor_llm):
230
  """
231
- Rephrase the answer to match the respondent's style and tone, using British English.
 
 
232
  """
233
- style_prompt = f"""
234
- Rephrase the following response into a {communication_style} tone using British English.
235
- Keep it in first person and do not change the meaning.
236
-
237
-
238
- ### Original Response:
239
- "{raw_response}"
240
- """
241
- return processor_llm.invoke(style_prompt).content.strip()
242
-
243
-
244
-
245
-
246
- def validate_final_answer(answer, agent_name):
247
- """
248
- Validate the final answer. Returns a formatted string or an error message if invalid.
249
- """
250
- if not answer:
251
- return f"**{agent_name}**: I wasn't able to answer right now – can you try again?"
252
- return f"**{agent_name}**: {answer}"
253
-
254
-
255
 
 
 
256
 
257
- def run_interview_pipeline(respondent_agents_dict, last_active_agent, question, processor_llm):
258
- logging.info(f"Received question: {question}")
259
  agent_names = list(respondent_agents_dict.keys())
 
 
260
 
261
-
 
262
  # Step 1: Parse question
 
263
  parsed_questions = parse_question_with_llm(question, str(agent_names), processor_llm)
 
 
264
  if not parsed_questions:
 
265
  return ["**PreData Moderator**: No valid respondents were detected for this question."]
266
 
267
-
268
- # Step 2: Validate parsed questions
269
  validated_questions = validate_question_topics(parsed_questions, processor_llm)
270
- for resp, q in validated_questions.items():
271
- if q == "INVALID":
 
 
 
272
  return ["**PreData Moderator**: The question is invalid. Please ask another question."]
273
- parsed_questions = validated_questions
274
-
275
 
276
- # Handle multiple respondents
 
 
 
277
  if len(parsed_questions) > 1:
278
- return ["**PreData Moderator**: Please ask each respondent one question at a time."]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
 
281
- # Handle "General" or "All"
282
- # [Insert logic here – reuse your existing handling of General/All]
283
 
284
 
285
  last_active_agent = list(parsed_questions.keys())
286
- responses = []
287
 
 
 
288
 
289
- # === MAIN LOOP ===
290
  for agent_name, agent_question in parsed_questions.items():
291
- agent_entry = respondent_agents_dict.get(agent_name)
292
- if not agent_entry:
293
  responses.append(f"**PreData Moderator**: {agent_name} is not a valid respondent.")
294
  continue
295
 
296
-
297
- respondent_agent = agent_entry.get_agent()
298
- user_profile = agent_entry.get_user_profile()
299
- # communication_style = user_profile.get_field("Communication", "Style")
300
  communication_style = ""
301
 
302
-
303
  question_task_description = f"""
304
  You are {agent_name}. You are responding to a market research interview question. Your response must strictly follow the *style and tone* and *Hard Rules – You Must Follow These Without Exception* outlined below.
305
  ---
@@ -324,7 +307,7 @@ You are {agent_name}. You are responding to a market research interview question
324
  -Example:
325
  Q: Where are you from?
326
  A: I’m from [city], [country](DO NOT ADD ANY EXTRA COMMENTS).
327
- -For reflective or opinion-based questions (e.g., feelings, preferences, motivations), provide thoughtful but still clear and focused answers.
328
  -Never repeat the question or add unrelated background information.
329
  ---
330
  ### **How to Answer:**
@@ -333,8 +316,8 @@ You are {agent_name}. You are responding to a market research interview question
333
  - Adapt your **sentence structure, phrasing, and word choices** to match the intended communication style.
334
  - If applicable, incorporate **culturally relevant expressions, regional nuances, or industry-specific terminology** that fit the given tone.
335
  - **Adjust response length** based on the tone—**concise and direct** for casual styles, **structured and detailed** for professional styles.
336
- - **Always answer in first person (\"I\", \"my\", \"me\", \"mine\", etc.) as if you are personally responding to the question. You are an individual representing yourself, not speaking in third person.**
337
- -Always answer as if you are the individual being directly spoken to. Use first-person language such as “I,” “me,” “my,” and “mine” in every response. Imagine you are having a real conversation — your tone should feel natural, personal, and authentic. Do not refer to yourself in the third person (e.g., “She is from Trichy” or “Meena likes…”). Avoid describing yourself as if someone else is talking about you.
338
  -Everything you say should come from your own perspective, just like you would in everyday speech. The goal is to sound human, relatable, and direct — like you're truly present in the conversation.
339
  ---
340
  ### **Guidelines for Ensuring Authenticity & Alignment:**
@@ -354,7 +337,7 @@ You are {agent_name}. You are responding to a market research interview question
354
  - If the tone is formal, use a structured and professional format.
355
  - **Do not include emojis or hashtags in the response.**
356
  - Maintain **narrative and thematic consistency** across all answers to simulate a coherent personality.
357
- -**Personality Profile Alignment:
358
  -Consider your assigned personality traits across these dimensions:
359
  -Big Five Traits:
360
  -Openness: Reflect your level of curiosity, creativity, and openness to new experiences
@@ -387,24 +370,84 @@ Your final answer should be **a well-structured response that directly answers t
387
  **"{agent_question}"**
388
  """
389
 
390
-
391
  question_task_expected_output = f"""
392
  A culturally authentic and conversational response to the question: '{agent_question}'.
393
  - The response must reflect the respondent's **local cultural background and geographic influences**, ensuring it aligns with their **speech patterns, preferences, and linguistic style**.
394
- - The language must follow **strict British English spelling conventions**, ensuring it is **natural, personal, and free-flowing**, while strictly avoiding American spelling, phrasing, or grammar under any circumstances, regardless of the spelling, grammar, or vocabulary used in the input question.
395
  - The response **must not introduce the respondent**, nor include placeholders like "[Your Name]" or "[Brand Name]".
396
- - The response **must always be written in first person (\"I\", \"my\", \"me\", etc.) as if the respondent is personally answering the question directly. Third-person narration is never allowed.**
397
  - The final output should be a **single, well-structured paragraph that directly answers the question** while staying fully aligned with the specified communication style.
398
  """
399
-
400
-
401
- # Step 1: Generate generic answer
402
- raw_response = generate_generic_answer(agent_name, agent_question, respondent_agent)
403
- # Step 2: Stylise answer
404
- styled_response = stylise_answer(raw_response, communication_style, agent_name, processor_llm)
405
- # Step 3: Validate answer
406
- formatted_response = validate_final_answer(styled_response, agent_name)
407
- responses.append(formatted_response)
408
-
409
-
410
- return responses
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  - **Correct (British):** organised, prioritise, minimise, realise, behaviour, centre, defence, travelling, practise (verb), licence (noun), programme, aeroplane.
44
  - **Incorrect (American):** organized, prioritize, minimize, realize, behavior, center, defense, traveling, practice (verb and noun), license (noun), program, airplane.
45
  6. Ensure that you follow the **Formatting Rules** exactly. THIS IS EXTREMELY IMPORTANT.
 
46
  ### Examples:
47
  - "Sourav, do you agree with this topic?" → "Do you agree with this topic?"
48
  - "What do you think about this topic, Divya?" → "What do you think about this topic?"
 
54
  - "Divya, what did you learn from this program?" → "What did you learn from this programme?"
55
  - "How do you stay organized, Rahul?" → "How do you stay organised?"
56
  - "Meena, how do you balance work and traveling?" → "How do you balance work and travelling?"
 
57
  ### **Formatting Rules**:
58
  For each question identified, respond using **only** the following format:
59
  - Respondent: <Respondent Name>
 
204
 
205
 
206
 
207
+ def ask_interview_question(respondent_agents_dict, last_active_agent, question, processor_llm):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  """
209
+ Handles both individual and group interview questions while tracking conversation flow.
210
+ Uses OpenAI's LLM to extract the intended respondent(s) and their specific question(s).
211
+ Uses Groq's LLM for response generation.
212
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
+ logging.info(f"START: Processing new interview question: {question}")
215
+ responses = []
216
 
 
 
217
  agent_names = list(respondent_agents_dict.keys())
218
+ logging.info(f"Available respondents: {agent_names}")
219
+ print(f"Available respondents: {agent_names}")
220
 
221
+ # Use OpenAI LLM to parse questions into individual respondent-specific sub-questions and validate them
222
+
223
  # Step 1: Parse question
224
+ logging.info("STEP 1: Parsing question with LLM...")
225
  parsed_questions = parse_question_with_llm(question, str(agent_names), processor_llm)
226
+ logging.info(f"Parsed Questions Output: {parsed_questions}")
227
+
228
  if not parsed_questions:
229
+ logging.warning("No questions were parsed from input.")
230
  return ["**PreData Moderator**: No valid respondents were detected for this question."]
231
 
232
+ # Step 2: Validate question content (scope + spelling)
233
+ logging.info("STEP 2: Validating questions for topic relevance and British English...")
234
  validated_questions = validate_question_topics(parsed_questions, processor_llm)
235
+ logging.info(f"Validated Questions: {validated_questions}")
236
+
237
+ for resp_name, extracted_question in validated_questions.items():
238
+ if extracted_question == "INVALID":
239
+ logging.warning(f"Invalid question detected for {resp_name}: {extracted_question}")
240
  return ["**PreData Moderator**: The question is invalid. Please ask another question."]
 
 
241
 
242
+ # Use validated questions from this point on
243
+ parsed_questions = validated_questions
244
+ logging.info(f"Validated questions: {parsed_questions}")
245
+
246
  if len(parsed_questions) > 1:
247
+ logging.warning("More than one respondent specified. Exiting function.")
248
+ return "**PreData Moderator**: Please ask each respondent one question at a time."
249
+ else:
250
+ print(f"Parsed questions are: {parsed_questions}")
251
+
252
+ if "General" in parsed_questions:
253
+ if "General" in parsed_questions:
254
+ if isinstance(last_active_agent, list) and all(name in agent_names for name in last_active_agent):
255
+ logging.info(f"General case detected. Continuing with last active agent: {last_active_agent}")
256
+ parsed_questions = {name: parsed_questions["General"] for name in last_active_agent}
257
+ else:
258
+ logging.info("General case detected without a valid previous active agent. Assigning question to all respondents.")
259
+ parsed_questions = {name: parsed_questions["General"] for name in agent_names}
260
+ elif "All" in parsed_questions:
261
+ logging.info("All case detected. Assigning question to all respondents.")
262
+ validated_question = parsed_questions["All"]
263
+ parsed_questions = {name: validated_question for name in agent_names}
264
 
265
 
 
 
266
 
267
 
268
  last_active_agent = list(parsed_questions.keys())
269
+ logging.info(f"Final parsed questions: {parsed_questions}")
270
 
271
+ # Construct one crew and task for each agent and question
272
+ responses = []
273
 
 
274
  for agent_name, agent_question in parsed_questions.items():
275
+ if agent_name not in respondent_agents_dict:
276
+ logging.warning(f"No valid respondent found for {agent_name}. Skipping.")
277
  responses.append(f"**PreData Moderator**: {agent_name} is not a valid respondent.")
278
  continue
279
 
280
+ respondent_agent = respondent_agents_dict[agent_name].get_agent()
281
+ user_profile = respondent_agents_dict[agent_name].get_user_profile()
282
+
283
+ # communication_style = user_profile.get_field("Communication", "Style")
284
  communication_style = ""
285
 
 
286
  question_task_description = f"""
287
  You are {agent_name}. You are responding to a market research interview question. Your response must strictly follow the *style and tone* and *Hard Rules – You Must Follow These Without Exception* outlined below.
288
  ---
 
307
  -Example:
308
  Q: Where are you from?
309
  A: I’m from [city], [country](DO NOT ADD ANY EXTRA COMMENTS).
310
+ -For reflective or opinion-based questions (e.g., feelings, preferences, motivations), provide thoughtful but still clear and focused answers.
311
  -Never repeat the question or add unrelated background information.
312
  ---
313
  ### **How to Answer:**
 
316
  - Adapt your **sentence structure, phrasing, and word choices** to match the intended communication style.
317
  - If applicable, incorporate **culturally relevant expressions, regional nuances, or industry-specific terminology** that fit the given tone.
318
  - **Adjust response length** based on the tone—**concise and direct** for casual styles, **structured and detailed** for professional styles.
319
+ - **Always answer in first person ("I", "my", "me", "mine", etc.) as if you are personally responding to the question. You are an individual representing yourself, not speaking in third person.**
320
+ -Always answer as if you are the individual being directly spoken to. Use first-person language such as “I,” “me,” “my,” and “mine” in every response. Imagine you are having a real conversation — your tone should feel natural, personal, and authentic. Do not refer to yourself in the third person (e.g., “She is from Trichy” or “Meena likes…”). Avoid describing yourself as if someone else is talking about you.
321
  -Everything you say should come from your own perspective, just like you would in everyday speech. The goal is to sound human, relatable, and direct — like you're truly present in the conversation.
322
  ---
323
  ### **Guidelines for Ensuring Authenticity & Alignment:**
 
337
  - If the tone is formal, use a structured and professional format.
338
  - **Do not include emojis or hashtags in the response.**
339
  - Maintain **narrative and thematic consistency** across all answers to simulate a coherent personality.
340
+ -**Personality Profile Alignment:**
341
  -Consider your assigned personality traits across these dimensions:
342
  -Big Five Traits:
343
  -Openness: Reflect your level of curiosity, creativity, and openness to new experiences
 
370
  **"{agent_question}"**
371
  """
372
 
 
373
  question_task_expected_output = f"""
374
  A culturally authentic and conversational response to the question: '{agent_question}'.
375
  - The response must reflect the respondent's **local cultural background and geographic influences**, ensuring it aligns with their **speech patterns, preferences, and linguistic style**.
376
+ - The language must follow **strict British English spelling conventions**, ensuring it is **natural, personal, and free-flowing**, while strictly avoiding American spelling, phrasing, or grammar under any circumstances, regardless of the spelling, grammar, or vocabulary used in the input question.
377
  - The response **must not introduce the respondent**, nor include placeholders like "[Your Name]" or "[Brand Name]".
378
+ - The response **must always be written in first person ("I", "my", "me", etc.) as if the respondent is personally answering the question directly. Third-person narration is never allowed.**
379
  - The final output should be a **single, well-structured paragraph that directly answers the question** while staying fully aligned with the specified communication style.
380
  """
381
+
382
+ question_task = Task(
383
+ description=question_task_description,
384
+ expected_output=question_task_expected_output,
385
+ agent=respondent_agent
386
+ )
387
+
388
+ logging.debug(f"Created task for agent '{agent_name}' with description: {question_task_description}")
389
+
390
+ # Log before starting task execution
391
+ logging.info(f"Executing task for agent '{agent_name}'")
392
+
393
+ # Create a new crew for each agent-question pair
394
+ crew = Crew(
395
+ agents=[respondent_agent],
396
+ tasks=[question_task],
397
+ process=Process.sequential
398
+ )
399
+ logging.debug(f"Crew initialized for agent '{agent_name}' with 1 task and sequential process")
400
+
401
+ max_attempts = 3
402
+ attempt = 0
403
+ validated = False
404
+ validated_answer = None
405
+ while attempt < max_attempts and not validated:
406
+ try:
407
+ logging.info(f"Starting Response validation attempt {attempt+1} for agent '{agent_name}'")
408
+ crew_output = crew.kickoff()
409
+ logging.info(f"Task execution completed for agent '{agent_name}' (attempt {attempt+1})")
410
+ task_output = question_task.output
411
+ logging.debug(f"Raw output from agent '{agent_name}': {getattr(task_output, 'raw', str(task_output))}")
412
+ answer = task_output.raw if hasattr(task_output, 'raw') else str(task_output)
413
+ logging.info(f"Validating response for agent '{agent_name}' (attempt {attempt+1}): {answer}")
414
+ # Validate the response using validate_response from validation_utils
415
+ is_valid = validate_response(
416
+ question=agent_question,
417
+ answer=answer,
418
+ user_profile_str=str(user_profile),
419
+ fast_facts_str="",
420
+ interview_transcript_text="",
421
+ respondent_type=agent_name,
422
+ ai_evaluator_agent=None,
423
+ processor_llm=processor_llm
424
+ )
425
+ logging.info(f"Response Validation result for agent '{agent_name}' (attempt {attempt+1}): {is_valid}")
426
+ if is_valid:
427
+ validated = True
428
+ validated_answer = answer
429
+ logging.info(f"Response for agent '{agent_name}' passed validation on attempt {attempt+1}")
430
+ break
431
+ else:
432
+ attempt += 1
433
+ logging.warning(f"Response failed response validation for agent '{agent_name}' (attempt {attempt}). Retrying...")
434
+ except Exception as e:
435
+ logging.error(f"Error during task execution for agent '{agent_name}' (attempt {attempt+1}): {str(e)}", exc_info=True)
436
+ attempt += 1
437
+ # --- End validation and retry loop ---
438
+
439
+ if validated_answer:
440
+ formatted_response = f"**{agent_name}**: {validated_answer}"
441
+ responses.append(formatted_response)
442
+ logging.info(f"Validated response from agent '{agent_name}' added to responses")
443
+ else:
444
+ fallback_response = f"**PreData Moderator**: Unable to pass validation after {max_attempts} attempts for {agent_name}."
445
+ responses.append(fallback_response)
446
+ logging.warning(f"No validated output from agent '{agent_name}' after {max_attempts} attempts. Added fallback response.")
447
+ logging.info(f"All responses generated: {responses}")
448
+
449
+ if len(set(parsed_questions.values())) == 1:
450
+ combined_output = "\n\n".join(responses)
451
+ return [combined_output]
452
+ else:
453
+ return responses