nat232 commited on
Commit
b75c907
·
verified ·
1 Parent(s): fbc8125

Update researchsimulation/InteractiveInterviewChatbot.py

Browse files
researchsimulation/InteractiveInterviewChatbot.py CHANGED
@@ -206,154 +206,205 @@ def validate_question_topics(parsed_questions, processor_llm):
206
 
207
 
208
 
209
- def ask_interview_question(respondent_agents_dict, last_active_agent, question, processor_llm):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  logging.info(f"Received question: {question}")
211
  agent_names = list(respondent_agents_dict.keys())
212
 
213
- # Step 1: Parse and validate questions
 
214
  parsed_questions = parse_question_with_llm(question, str(agent_names), processor_llm)
215
  if not parsed_questions:
216
  return ["**PreData Moderator**: No valid respondents were detected for this question."]
217
 
 
 
218
  validated_questions = validate_question_topics(parsed_questions, processor_llm)
219
  for resp, q in validated_questions.items():
220
  if q == "INVALID":
221
  return ["**PreData Moderator**: The question is invalid. Please ask another question."]
222
  parsed_questions = validated_questions
223
 
 
 
224
  if len(parsed_questions) > 1:
225
  return ["**PreData Moderator**: Please ask each respondent one question at a time."]
226
 
 
 
 
 
 
227
  last_active_agent = list(parsed_questions.keys())
228
  responses = []
229
 
 
 
230
  for agent_name, agent_question in parsed_questions.items():
231
  agent_entry = respondent_agents_dict.get(agent_name)
232
  if not agent_entry:
233
  responses.append(f"**PreData Moderator**: {agent_name} is not a valid respondent.")
234
  continue
235
 
236
- # === Step 1: Generate raw answer ===
237
- raw_answer = generate_generic_answer(agent_name, agent_question, agent_entry.get_agent())
238
-
239
- # === Step 2: Stylise answer ===
240
- styled_answer = stylise_answer_to_profile(
241
- raw_answer,
242
- agent_name,
243
- agent_entry.get_user_profile(),
244
- processor_llm
245
- )
246
 
247
- # === Step 3: Final validation ===
248
- if not validate_final_answer(styled_answer):
249
- responses.append(f"**PreData Moderator**: The answer could not be validated.")
250
- continue
251
-
252
- responses.append(f"**{agent_name}**: {styled_answer}")
253
-
254
- return responses
255
-
256
-
257
- # === STEP 1: GENERATE RAW ANSWER ===
258
- def generate_generic_answer(agent_name, question, agent):
259
- prompt = f"""
260
- You are {agent_name}. Answer the following question naturally and authentically in first person.
261
- Use British English. Do not apply any tone or formatting rules.
262
-
263
- ### Question:
264
- "{question}"
265
- """
266
- task = Task(description=prompt, expected_output="", agent=agent)
267
- Crew(agents=[agent], tasks=[task], process=Process.sequential).kickoff()
268
- return task.output.raw.strip()
269
-
270
-
271
- # === STEP 2: STYLISE ANSWER TO PROFILE ===
272
- def stylise_answer_to_profile(raw_answer, agent_name, user_profile, processor_llm):
273
- communication_style = user_profile.get_field("Communication", "Style") or "conversational"
274
- prompt = f"""
275
- Rephrase the following response into a {communication_style} tone using British English.
276
- Keep it in first person. Do not change the meaning or add new content.
277
-
278
- ### Original:
279
- "{raw_answer}"
280
- """
281
- response = processor_llm.invoke(prompt)
282
- return response.content.strip()
283
-
284
-
285
- # === STEP 3: FINAL OUTPUT VALIDATION ===
286
- def validate_final_answer(answer):
287
- return bool(answer and len(answer.split()) > 2) # Example: check it's not empty or too short
288
-
289
-
290
- # === PARSE QUESTIONS WITH LLM (Your existing code or external import) ===
291
- def parse_question_with_llm(question, respondent_names, processor_llm):
292
- prompt = f"""
293
- You are an expert in market research interview analysis.
294
- Your task is to identify respondents mentioned in the question and extract the exact question posed to them.
295
-
296
- ### User Input:
297
- {question}
298
-
299
- ### Instructions:
300
- 1. Identify each respondent being addressed.
301
- 2. Extract the exact question posed to them.
302
- 3. Use "General" if no specific name is mentioned. Use "All" if it's for everyone.
303
- 4. If the question is out of scope, return "INVALID" as the question.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
 
305
- ### Format:
306
- - Respondent: <Respondent Name>
307
- Question: <Extracted Question>
308
- """
309
- response = processor_llm.invoke(prompt)
310
- chatgpt_output = response.content.strip()
311
 
312
- parsed_questions = {}
313
- lines = chatgpt_output.split("\n")
314
- respondent_name = "General"
315
- question_text = None
316
-
317
- for line in lines:
318
- if "- Respondent:" in line:
319
- respondent_name = line.split(":")[1].strip()
320
- elif "Question:" in line:
321
- question_text = line.split(":")[1].strip()
322
- if question_text:
323
- parsed_questions[respondent_name] = question_text
324
 
325
- return parsed_questions
326
 
 
 
 
 
 
 
 
327
 
328
- # === VALIDATE QUESTIONS FOR TOPIC SCOPE (Your existing logic) ===
329
- def validate_question_topics(parsed_questions, processor_llm):
330
- validated = {}
331
- for respondent, question in parsed_questions.items():
332
- prompt = f"""
333
- You are a research analyst. Validate whether the question is in the allowed topic scope and convert it to British English.
334
 
335
- ### Question:
336
- {question}
337
-
338
- ### If invalid:
339
- Return exactly "INVALID"
340
-
341
- ### Permitted Topics:
342
- - Demographics
343
- - Values & Beliefs
344
- - Career & Aspirations
345
- - Influences
346
- - Interests & Hobbies
347
- - Health & Lifestyle
348
- - Social Media & Tech
349
- - Personal Relationships
350
- - Future Outlook
351
- - Social & Societal Issues
352
- - Lifestyle Preferences
353
- - Personal Growth
354
-
355
- ### Output:
356
- """
357
- result = processor_llm.invoke(prompt)
358
- validated[respondent] = result.content.strip()
359
- return validated
 
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
+ ---
306
+ ### *Communication Profile Reference:*
307
+ - **Style:** {user_profile.get_field('Communication', 'Style')}
308
+ - **Tone:** {user_profile.get_field('Communication', 'Tone')}
309
+ - **Length:** {user_profile.get_field('Communication', 'Length')}
310
+ - **Topics:** {user_profile.get_field('Communication', 'Topics')}
311
+ ---
312
+ ---
313
+ ### 🔒 **Hard Rules – You Must Follow These Without Exception**
314
+ - You must answer **only the question(s)** that are **explicitly asked**.
315
+ - **Never provide extra information** beyond what was asked.
316
+ - Keep your response **as short as possible** while still sounding natural and complete.
317
+ - Do **not infer or assume** what the user *might* want — only respond to what they *actually* asked.
318
+ - If multiple questions are asked, respond to **each one briefly**, and **nothing else**.
319
+ - If the question is vague, respond minimally and only within that scope.
320
+ -Give concise answers, whether the question is asked to the group or individually.
321
+ -For factual or demographic questions (e.g., age, gender, location, housing), keep responses brief and to the point, without extra commentary.
322
+ -Do not add any explanations, opinions, or additional information.
323
+ -Use simple, clear sentences.
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:**
331
+ - Your response should be **natural, authentic, and fully aligned** with the specified style and tone.
332
+ - Ensure the answer is **clear, engaging, and directly relevant** to the 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:**
341
+ - **Consistency**: Maintain the same tone throughout the response.
342
+ - **Authenticity**: The response should feel natural and match the speaker’s persona.
343
+ - **Avoid Overgeneralisation**: Ensure responses are specific and not overly generic or robotic.
344
+ - **Cultural & Linguistic Relevance**: Adapt language and references to match the speaker’s background, industry, or region where appropriate.
345
+ - **Strict British Spelling & Grammar**:
346
+ - All responses must use correct British English spelling, grammar, and usage, **irrespective of how the question is phrased**.
347
+ - You must not mirror any American spelling, terminology, or phrasing found in the input question.
348
+ - Where there are regional variations (e.g. 'licence' vs 'license', 'programme' vs 'program', 'aeroplane' vs 'airplane'), always default to the standard British form.
349
+ - Examples:
350
+ - **Correct (British):** organised, prioritise, minimise, realise, behaviour, centre, defence, travelling, practise (verb), licence (noun), programme, aeroplane.
351
+ - **Incorrect (American):** organized, prioritize, minimize, realize, behavior, center, defense, traveling, practice (verb and noun), license (noun), program, airplane.
352
+ - **Formatting**:
353
+ - If the tone is informal, allow a conversational flow that mirrors natural speech.
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
361
+ -Conscientiousness: Show your degree of organization, responsibility, and planning
362
+ -Extraversion: Express your sociability and energy level in interactions
363
+ -Agreeableness: Demonstrate your warmth, cooperation, and consideration for others
364
+ -Neuroticism: Consider your emotional stability and stress response
365
+ -Values and Priorities:
366
+ -Achievement Orientation: Show your drive for success and goal-setting approach
367
+ -Risk Tolerance: Express your comfort with uncertainty and change
368
+ -Traditional Values: Reflect your adherence to conventional norms and practices
369
+ -Communication Style:
370
+ -Detail Orientation: Demonstrate your preference for specific vs. general information
371
+ -Complexity: Show your comfort with nuanced vs. straightforward explanations
372
+ -Directness: Express your communication as either straightforward or diplomatic
373
+ -Emotional Expressiveness: Reflect your tendency to share or withhold emotions
374
+ -Your responses must consistently align with these personality traits from your profile.
375
+ ---
376
+ ### **Example Responses (for Different Styles & Tones)**
377
+ #### **Casual & Conversational Tone**
378
+ **Question:** "How do you stay updated on the latest fashion and tech trends?"
379
+ **Correct Response:**
380
+ "I keep up with trends by following influencers on Instagram and watching product reviews on YouTube. Brands like Noise and Boat always drop stylish, affordable options, so I make sure to stay ahead of the curve."
381
+ #### **Formal & Professional Tone**
382
+ **Question:** "How do you stay updated on the latest fashion and tech trends?"
383
+ **Correct Response:**
384
+ "I actively follow industry trends by reading reports, attending webinars, and engaging with thought leaders on LinkedIn. I also keep up with global fashion and technology updates through leading publications such as *The Business of Fashion* and *TechCrunch*."
385
+ ---
386
+ Your final answer should be **a well-structured response that directly answers the question while maintaining the specified style and tone**:
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