Spaces:
Sleeping
Sleeping
Upload 8 files
Browse files- CHANGELOG.md +73 -0
- README.md +189 -171
- USER_GUIDE.md +1339 -0
- app.py +398 -1
- conversation_flow.py +197 -0
- conversation_moderator.py +243 -0
- conversation_session.py +226 -0
- export_utils.py +105 -0
CHANGELOG.md
CHANGED
|
@@ -2,6 +2,79 @@
|
|
| 2 |
|
| 3 |
All notable changes to ConversAI will be documented in this file.
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
## [1.2.0] - 2025-11-XX
|
| 6 |
|
| 7 |
### Changed - MAJOR UPDATE
|
|
|
|
| 2 |
|
| 3 |
All notable changes to ConversAI will be documented in this file.
|
| 4 |
|
| 5 |
+
## [2.0.0] - 2025-10-26
|
| 6 |
+
|
| 7 |
+
### Added - MAJOR FEATURE: Conversational Research
|
| 8 |
+
- **💬 New Conversational Research Module**: AI-moderated interviews with dynamic adaptation
|
| 9 |
+
- **Design custom conversation flows** with scripted questions
|
| 10 |
+
- **AI moderator** conducts real-time interviews with intelligent probing
|
| 11 |
+
- **Dynamic follow-up questions** generated based on respondent answers
|
| 12 |
+
- **Intelligent probing logic**: Asks follow-ups when detecting interesting keywords or every N responses
|
| 13 |
+
- **Automatic summarization** of conversation insights
|
| 14 |
+
- **Export capabilities**: Transcripts, JSON, and CSV formats
|
| 15 |
+
|
| 16 |
+
### New Files
|
| 17 |
+
- **conversation_flow.py** - Conversation flow design and management
|
| 18 |
+
- `ConversationNode` class for individual conversation steps
|
| 19 |
+
- `ConversationFlow` class for managing complete flows
|
| 20 |
+
- Flow validation and persistence (save/load JSON)
|
| 21 |
+
- Example flow generator for quick start
|
| 22 |
+
|
| 23 |
+
- **conversation_session.py** - Live conversation session tracking
|
| 24 |
+
- `ConversationTurn` class for tracking individual messages
|
| 25 |
+
- `ConversationSession` class for managing live interviews
|
| 26 |
+
- `SessionManager` for handling multiple concurrent sessions
|
| 27 |
+
- Conversation transcript generation
|
| 28 |
+
- Session statistics and analytics
|
| 29 |
+
|
| 30 |
+
- **conversation_moderator.py** - AI-powered interview moderator
|
| 31 |
+
- Conducts interviews following conversation flows
|
| 32 |
+
- Decides when to ask scripted questions vs. dynamic follow-ups
|
| 33 |
+
- Generates contextual follow-up questions using LLM
|
| 34 |
+
- Probes on interesting keywords (emotional/reasoning indicators)
|
| 35 |
+
- Configurable follow-up threshold (default: every 3rd response)
|
| 36 |
+
- Conversation summarization capability
|
| 37 |
+
|
| 38 |
+
### UI Enhancements
|
| 39 |
+
- **New Tab: "💬 Conversational Research"** with two sub-interfaces:
|
| 40 |
+
- **🎨 Design Flow**: Create and manage conversation flows
|
| 41 |
+
- Create new flows or load example templates
|
| 42 |
+
- Add conversation nodes (questions, branches, endings)
|
| 43 |
+
- Live flow preview
|
| 44 |
+
- Save flows to JSON files
|
| 45 |
+
|
| 46 |
+
- **🎙️ Conduct Interview**: Chat-based interview interface
|
| 47 |
+
- Start conversation sessions from designed flows
|
| 48 |
+
- Real-time chat with AI moderator
|
| 49 |
+
- Session status tracking
|
| 50 |
+
- Export conversations in multiple formats
|
| 51 |
+
|
| 52 |
+
### Export Utilities Enhanced
|
| 53 |
+
- Added `conversation_to_transcript()` - Export as readable text
|
| 54 |
+
- Added `conversation_to_json()` - Export session data as JSON
|
| 55 |
+
- Added `conversation_to_csv()` - Export conversation turns as CSV
|
| 56 |
+
- Added `flow_to_markdown()` - Document conversation flows
|
| 57 |
+
|
| 58 |
+
### Technical Details
|
| 59 |
+
- Seamless integration with existing Phi-2 LLM backend
|
| 60 |
+
- Session state management with unique IDs
|
| 61 |
+
- Timestamp tracking for all conversation turns
|
| 62 |
+
- Node-based conversation flow with linked questions
|
| 63 |
+
- Probing logic triggers on:
|
| 64 |
+
- Response length (>5 words)
|
| 65 |
+
- Turn count (every 3rd user response)
|
| 66 |
+
- Interesting keywords (emotional/reasoning words)
|
| 67 |
+
|
| 68 |
+
### Use Cases
|
| 69 |
+
- Qualitative research interviews
|
| 70 |
+
- Customer feedback sessions
|
| 71 |
+
- User experience research
|
| 72 |
+
- Market research interviews
|
| 73 |
+
- Product discovery conversations
|
| 74 |
+
- Exploratory research with adaptive questioning
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
## [1.2.0] - 2025-11-XX
|
| 79 |
|
| 80 |
### Changed - MAJOR UPDATE
|
README.md
CHANGED
|
@@ -1,171 +1,189 @@
|
|
| 1 |
-
---
|
| 2 |
-
title:
|
| 3 |
-
emoji: 🔬
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: purple
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 5.
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
license: mit
|
| 11 |
-
---
|
| 12 |
-
|
| 13 |
-
# ConversAI - AI-Powered Qualitative Research Assistant
|
| 14 |
-
|
| 15 |
-
Battle the blank page, reach global audiences, and uncover insights with AI assistance.
|
| 16 |
-
|
| 17 |
-
---
|
| 18 |
-
|
| 19 |
-
> **✨ UPDATED (Nov 2025):** Now uses **local transformers** with **Microsoft Phi-2** - Fast, contextual, and **completely FREE**! No API dependencies, runs directly on HuggingFace Spaces. Generates actual topic-specific questions (not generic templates).
|
| 20 |
-
|
| 21 |
-
---
|
| 22 |
-
|
| 23 |
-
## 🌟 Features
|
| 24 |
-
|
| 25 |
-
### 📝 Survey Generation
|
| 26 |
-
- Generate professional surveys from simple outlines
|
| 27 |
-
- Follow industry best practices automatically
|
| 28 |
-
- Choose from qualitative, quantitative, or mixed methods
|
| 29 |
-
- Customize number of questions and target audience
|
| 30 |
-
|
| 31 |
-
### 🌍 Survey Translation
|
| 32 |
-
- Translate surveys to 18+ languages
|
| 33 |
-
- Maintain cultural appropriateness and meaning
|
| 34 |
-
- Reach global audiences effortlessly
|
| 35 |
-
- Batch translation support
|
| 36 |
-
|
| 37 |
-
### 📊 Data Analysis
|
| 38 |
-
- AI-assisted thematic analysis
|
| 39 |
-
- Sentiment analysis and emotional insights
|
| 40 |
-
- Automatic pattern and trend detection
|
| 41 |
-
- Generate actionable insights and recommendations
|
| 42 |
-
- Export detailed analysis reports
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
**
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
**
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
**
|
| 98 |
-
-
|
| 99 |
-
-
|
| 100 |
-
|
| 101 |
-
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
-
|
| 161 |
-
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: ConversAI - Qualitative Research Assistant
|
| 3 |
+
emoji: 🔬
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.45.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# ConversAI - AI-Powered Qualitative Research Assistant
|
| 14 |
+
|
| 15 |
+
Battle the blank page, reach global audiences, and uncover insights with AI assistance.
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
> **✨ UPDATED (Nov 2025):** Now uses **local transformers** with **Microsoft Phi-2** - Fast, contextual, and **completely FREE**! No API dependencies, runs directly on HuggingFace Spaces. Generates actual topic-specific questions (not generic templates).
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 🌟 Features
|
| 24 |
+
|
| 25 |
+
### 📝 Survey Generation
|
| 26 |
+
- Generate professional surveys from simple outlines
|
| 27 |
+
- Follow industry best practices automatically
|
| 28 |
+
- Choose from qualitative, quantitative, or mixed methods
|
| 29 |
+
- Customize number of questions and target audience
|
| 30 |
+
|
| 31 |
+
### 🌍 Survey Translation
|
| 32 |
+
- Translate surveys to 18+ languages
|
| 33 |
+
- Maintain cultural appropriateness and meaning
|
| 34 |
+
- Reach global audiences effortlessly
|
| 35 |
+
- Batch translation support
|
| 36 |
+
|
| 37 |
+
### 📊 Data Analysis
|
| 38 |
+
- AI-assisted thematic analysis
|
| 39 |
+
- Sentiment analysis and emotional insights
|
| 40 |
+
- Automatic pattern and trend detection
|
| 41 |
+
- Generate actionable insights and recommendations
|
| 42 |
+
- Export detailed analysis reports
|
| 43 |
+
|
| 44 |
+
### 💬 Conversational Research
|
| 45 |
+
- Design custom conversation flows with scripted questions
|
| 46 |
+
- AI-moderated interviews with dynamic follow-up questions
|
| 47 |
+
- Real-time adaptation based on respondent answers
|
| 48 |
+
- Intelligent probing for deeper insights
|
| 49 |
+
- Automatic conversation summarization and export
|
| 50 |
+
- Export conversations as transcripts, JSON, or CSV
|
| 51 |
+
|
| 52 |
+
## 🚀 Quick Start
|
| 53 |
+
|
| 54 |
+
**On HuggingFace Spaces:** Works immediately with zero configuration! Uses the free HF Inference API.
|
| 55 |
+
|
| 56 |
+
**Workflow:**
|
| 57 |
+
|
| 58 |
+
**Static Surveys:**
|
| 59 |
+
1. **Generate a Survey**: Start with an outline or topic description
|
| 60 |
+
2. **Translate**: Select target languages to reach global audiences
|
| 61 |
+
3. **Collect Responses**: Use the generated survey with your participants
|
| 62 |
+
4. **Analyze**: Upload responses to uncover key findings and trends
|
| 63 |
+
|
| 64 |
+
**Conversational Research:**
|
| 65 |
+
1. **Design Flow**: Create a conversation flow with scripted questions
|
| 66 |
+
2. **Conduct Interview**: AI moderator engages with respondents in real-time
|
| 67 |
+
3. **Export & Analyze**: Export transcripts and analyze conversation insights
|
| 68 |
+
|
| 69 |
+
## 🔧 Configuration
|
| 70 |
+
|
| 71 |
+
### Default: Local Transformers (Completely FREE!)
|
| 72 |
+
|
| 73 |
+
**✨ Zero configuration needed!** ConversAI works out-of-the-box on HuggingFace Spaces using local model loading.
|
| 74 |
+
|
| 75 |
+
**Default Model:** microsoft/phi-2
|
| 76 |
+
- ✅ **100% Free** - No API keys, no costs, ever
|
| 77 |
+
- ✅ **Excellent quality** - 2.7GB causal language model, great at creative text generation
|
| 78 |
+
- ✅ **Good speed** - Typically 5-10 seconds per request after initial load
|
| 79 |
+
- ✅ **No API dependencies** - Runs entirely on your Space's compute
|
| 80 |
+
- ✅ **Private** - All processing happens locally, nothing sent to external APIs
|
| 81 |
+
- ✅ **Contextual** - Generates relevant, topic-specific questions (not generic)
|
| 82 |
+
|
| 83 |
+
**Setup for HuggingFace Spaces:**
|
| 84 |
+
- Just deploy - models download automatically on first run
|
| 85 |
+
- **No API keys or tokens required!**
|
| 86 |
+
- Models are cached after first download for faster subsequent loads
|
| 87 |
+
|
| 88 |
+
### Alternative Free Models
|
| 89 |
+
|
| 90 |
+
You can try different free models by setting the `LLM_MODEL` environment variable:
|
| 91 |
+
|
| 92 |
+
**Recommended Free Models (Local Transformers):**
|
| 93 |
+
|
| 94 |
+
| Model | Best For | Speed | Quality | Model Size |
|
| 95 |
+
|-------|----------|-------|---------|------------|
|
| 96 |
+
| **TinyLlama/TinyLlama-1.1B-Chat-v1.0** | Quick testing | ⚡⚡⚡ Very Fast | ⭐⭐ Fair | 1.1GB |
|
| 97 |
+
| **google/gemma-2b-it** | Faster alternative | ⚡⚡ Fast | ⭐⭐⭐ Good | 2GB |
|
| 98 |
+
| **microsoft/phi-2** (default) | **Recommended** - best balance | ⚡ Good | ⭐⭐⭐⭐ Excellent | 2.7GB |
|
| 99 |
+
| **mistralai/Mistral-7B-Instruct-v0.2** | Maximum quality | ⚡ Slower | ⭐⭐⭐⭐⭐ Best | 7GB |
|
| 100 |
+
|
| 101 |
+
**Note:** These are causal language models (decoder-only) designed for text generation. **Do NOT use Flan-T5 models** - they copy examples instead of generating contextual questions.
|
| 102 |
+
|
| 103 |
+
**To change model:**
|
| 104 |
+
```bash
|
| 105 |
+
# In Space Settings → Variables
|
| 106 |
+
LLM_MODEL=google/gemma-2b-it # Faster alternative
|
| 107 |
+
|
| 108 |
+
# Or for maximum quality (requires more memory)
|
| 109 |
+
LLM_MODEL=mistralai/Mistral-7B-Instruct-v0.2
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
**Why Local Transformers?**
|
| 113 |
+
- ✅ **No API dependencies** - runs entirely on your Space
|
| 114 |
+
- ✅ **No 404 errors** - no network issues
|
| 115 |
+
- ✅ **Fast after loading** - models cached in memory
|
| 116 |
+
- ✅ **Instruction-tuned** - designed for following prompts
|
| 117 |
+
- ✅ **Privacy** - all processing happens locally
|
| 118 |
+
|
| 119 |
+
### Tips for Best Performance with Local Models
|
| 120 |
+
|
| 121 |
+
1. **Use Phi-2 (default)** - Best balance of quality and resource usage
|
| 122 |
+
2. **First load takes time** - Model downloads and loads (~2-3 minutes for Phi-2)
|
| 123 |
+
3. **Subsequent requests are fast** - Model stays in memory (5-10 seconds)
|
| 124 |
+
4. **For maximum quality** - Use Mistral-7B-Instruct (requires 8GB+ RAM)
|
| 125 |
+
5. **For faster loading** - Use Gemma-2B-IT or TinyLlama (good quality, smaller)
|
| 126 |
+
6. **Avoid Flan-T5 models** - They copy examples instead of generating contextual questions
|
| 127 |
+
7. **Be specific in outlines** - More detail helps model generate better questions
|
| 128 |
+
|
| 129 |
+
## 📦 Installation
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
# Install dependencies
|
| 133 |
+
pip install -r requirements.txt
|
| 134 |
+
|
| 135 |
+
# Check environment setup (optional but recommended)
|
| 136 |
+
python check_env.py
|
| 137 |
+
|
| 138 |
+
# Run the app
|
| 139 |
+
python app.py
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
## 🏗️ Architecture
|
| 143 |
+
|
| 144 |
+
ConversAI is built with a modular architecture:
|
| 145 |
+
|
| 146 |
+
- **llm_backend.py** - Unified LLM interface supporting multiple providers
|
| 147 |
+
- **survey_generator.py** - AI-powered survey generation
|
| 148 |
+
- **survey_translator.py** - Multi-language translation engine
|
| 149 |
+
- **data_analyzer.py** - Qualitative data analysis and insights
|
| 150 |
+
- **conversation_flow.py** - Conversation flow design and management
|
| 151 |
+
- **conversation_session.py** - Live conversation session tracking
|
| 152 |
+
- **conversation_moderator.py** - AI-powered interview moderator
|
| 153 |
+
- **app.py** - Gradio-based web interface
|
| 154 |
+
- **export_utils.py** - Export to JSON, CSV, Markdown
|
| 155 |
+
|
| 156 |
+
## 📄 Data Privacy
|
| 157 |
+
|
| 158 |
+
- All processing is done through your configured LLM provider
|
| 159 |
+
- No data is stored permanently by this application
|
| 160 |
+
- Survey data and responses remain in your control
|
| 161 |
+
- Suitable for sensitive research projects
|
| 162 |
+
|
| 163 |
+
## 🤝 Contributing
|
| 164 |
+
|
| 165 |
+
Contributions are welcome! This is a production-grade application designed for real-world qualitative research.
|
| 166 |
+
|
| 167 |
+
## 📝 License
|
| 168 |
+
|
| 169 |
+
MIT License - Feel free to use for research and commercial purposes.
|
| 170 |
+
|
| 171 |
+
---
|
| 172 |
+
|
| 173 |
+
## 📚 Documentation
|
| 174 |
+
|
| 175 |
+
**New to ConversAI?** Start with **[USER_GUIDE.md](USER_GUIDE.md)** for a complete walkthrough.
|
| 176 |
+
|
| 177 |
+
**Quick Links:**
|
| 178 |
+
- 📖 [Complete User Guide](USER_GUIDE.md) - How to use ConversAI (START HERE)
|
| 179 |
+
- ⚡ [Quick Start for HF Spaces](QUICK_START_HF_SPACES.md) - 5-minute deployment
|
| 180 |
+
- 🔧 [Troubleshooting](TROUBLESHOOTING.md) - Common issues and solutions
|
| 181 |
+
- 🆓 [Free Models Guide](FREE_MODELS.md) - Best free models to use
|
| 182 |
+
|
| 183 |
+
**Diagnostic Tools:**
|
| 184 |
+
- Run `python check_env.py` - Check your environment setup
|
| 185 |
+
- Run `python test_hf_backend.py` - Test HuggingFace connection
|
| 186 |
+
|
| 187 |
+
---
|
| 188 |
+
|
| 189 |
+
Built with ❤️ using Gradio and state-of-the-art open-source LLMs
|
USER_GUIDE.md
ADDED
|
@@ -0,0 +1,1339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ConversAI User Guide
|
| 2 |
+
|
| 3 |
+
## Your AI-Powered Qualitative Research Assistant
|
| 4 |
+
|
| 5 |
+
ConversAI is a professional-grade research platform that transforms how you create surveys, reach global audiences, and analyze qualitative data. Powered by advanced AI, it automates hours of manual work while maintaining the quality and rigor expected in professional research.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 🎯 What ConversAI Does
|
| 10 |
+
|
| 11 |
+
ConversAI provides three powerful capabilities that work together to streamline your entire research workflow:
|
| 12 |
+
|
| 13 |
+
### 1. 📝 Generate Professional Surveys in Minutes
|
| 14 |
+
Turn a simple outline into a complete, research-ready survey with industry best practices automatically applied.
|
| 15 |
+
|
| 16 |
+
**What you get:**
|
| 17 |
+
- Well-structured questions that avoid common biases
|
| 18 |
+
- Professional introduction and closing messages
|
| 19 |
+
- Appropriate question types for your research goals
|
| 20 |
+
- Ready-to-deploy surveys that save hours of manual work
|
| 21 |
+
|
| 22 |
+
**Perfect for:**
|
| 23 |
+
- Market researchers launching new studies
|
| 24 |
+
- UX researchers gathering user feedback
|
| 25 |
+
- Academic researchers designing questionnaires
|
| 26 |
+
- Product teams validating ideas
|
| 27 |
+
- Healthcare professionals conducting patient surveys
|
| 28 |
+
|
| 29 |
+
### 2. 🌍 Translate Surveys to Reach Global Audiences
|
| 30 |
+
Instantly translate your surveys into 18+ languages while maintaining cultural appropriateness and meaning.
|
| 31 |
+
|
| 32 |
+
**What you get:**
|
| 33 |
+
- Professionally translated surveys in minutes
|
| 34 |
+
- Cultural adaptation, not just word-for-word translation
|
| 35 |
+
- Support for major world languages
|
| 36 |
+
- Batch translation to multiple languages at once
|
| 37 |
+
|
| 38 |
+
**Perfect for:**
|
| 39 |
+
- International market research
|
| 40 |
+
- Multi-country product launches
|
| 41 |
+
- Global user studies
|
| 42 |
+
- Diverse demographic research
|
| 43 |
+
- Multilingual community surveys
|
| 44 |
+
|
| 45 |
+
### 3. 📊 Uncover Insights from Qualitative Data
|
| 46 |
+
Transform open-ended responses into actionable insights with AI-assisted analysis.
|
| 47 |
+
|
| 48 |
+
**What you get:**
|
| 49 |
+
- Thematic analysis identifying key patterns
|
| 50 |
+
- Sentiment analysis and emotional insights
|
| 51 |
+
- Executive summaries highlighting findings
|
| 52 |
+
- Trend detection across responses
|
| 53 |
+
- Exportable reports ready for presentations
|
| 54 |
+
|
| 55 |
+
**Perfect for:**
|
| 56 |
+
- Analyzing customer feedback
|
| 57 |
+
- Understanding user pain points
|
| 58 |
+
- Identifying product opportunities
|
| 59 |
+
- Reporting research findings
|
| 60 |
+
- Making data-driven decisions
|
| 61 |
+
|
| 62 |
+
---
|
| 63 |
+
|
| 64 |
+
## 💼 Why ConversAI is Production-Grade
|
| 65 |
+
|
| 66 |
+
### Enterprise-Quality Features
|
| 67 |
+
|
| 68 |
+
**1. Flexible LLM Backend**
|
| 69 |
+
- Support for multiple AI providers (OpenAI, Anthropic, HuggingFace)
|
| 70 |
+
- Automatic failover and provider selection
|
| 71 |
+
- No vendor lock-in - switch providers anytime
|
| 72 |
+
- Works with both free and premium AI services
|
| 73 |
+
|
| 74 |
+
**2. Robust Error Handling**
|
| 75 |
+
- Graceful degradation when services are unavailable
|
| 76 |
+
- Clear, actionable error messages
|
| 77 |
+
- Automatic retry logic for transient failures
|
| 78 |
+
- Validation at every step to prevent bad data
|
| 79 |
+
|
| 80 |
+
**3. Data Privacy & Security**
|
| 81 |
+
- No permanent data storage by default
|
| 82 |
+
- All processing through your chosen AI provider
|
| 83 |
+
- Complete control over your research data
|
| 84 |
+
- Suitable for sensitive research projects
|
| 85 |
+
- Environment-based credential management
|
| 86 |
+
|
| 87 |
+
**4. Professional Export Options**
|
| 88 |
+
- JSON format for programmatic access
|
| 89 |
+
- Markdown reports for documentation
|
| 90 |
+
- CSV export for spreadsheet analysis
|
| 91 |
+
- Ready for integration with other tools
|
| 92 |
+
|
| 93 |
+
**5. Scalability**
|
| 94 |
+
- Handle small pilot studies or large-scale research
|
| 95 |
+
- Batch operations for efficiency
|
| 96 |
+
- Optimized for performance
|
| 97 |
+
- Rate limiting and cost controls
|
| 98 |
+
|
| 99 |
+
**6. Production-Ready Architecture**
|
| 100 |
+
- Modular, maintainable codebase
|
| 101 |
+
- Clean separation of concerns
|
| 102 |
+
- Comprehensive error handling
|
| 103 |
+
- Extensive documentation
|
| 104 |
+
- Easy deployment options
|
| 105 |
+
|
| 106 |
+
### Quality Assurance
|
| 107 |
+
|
| 108 |
+
**Research Best Practices:**
|
| 109 |
+
- Questions designed to minimize bias
|
| 110 |
+
- Appropriate question types for different data needs
|
| 111 |
+
- Logical survey flow from general to specific
|
| 112 |
+
- Culturally sensitive translations
|
| 113 |
+
- Rigorous analytical methods
|
| 114 |
+
|
| 115 |
+
**Technical Excellence:**
|
| 116 |
+
- Comprehensive input validation
|
| 117 |
+
- Type checking and error prevention
|
| 118 |
+
- Graceful handling of edge cases
|
| 119 |
+
- Performance optimization
|
| 120 |
+
- Security-first design
|
| 121 |
+
|
| 122 |
+
**User Experience:**
|
| 123 |
+
- Intuitive interface requiring no technical knowledge
|
| 124 |
+
- Clear status messages and progress indicators
|
| 125 |
+
- Helpful examples and templates
|
| 126 |
+
- Responsive design for any device
|
| 127 |
+
- Accessibility considerations
|
| 128 |
+
|
| 129 |
+
---
|
| 130 |
+
|
| 131 |
+
## 🚀 How to Use ConversAI
|
| 132 |
+
|
| 133 |
+
### Getting Started
|
| 134 |
+
|
| 135 |
+
**Step 1: Access ConversAI**
|
| 136 |
+
- On HuggingFace Spaces: Open the Space URL (works immediately)
|
| 137 |
+
- Self-hosted: Launch with `python app.py`
|
| 138 |
+
|
| 139 |
+
**Step 2: Verify Setup**
|
| 140 |
+
- Look for the green status banner at the top
|
| 141 |
+
- Should show: "✅ Active LLM Provider: [Provider Name]"
|
| 142 |
+
- If you see a warning, check the About tab for setup instructions
|
| 143 |
+
|
| 144 |
+
**Step 3: Choose Your Task**
|
| 145 |
+
- Navigate between tabs based on what you want to do
|
| 146 |
+
- Start with survey generation, then translate, then analyze
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
## 📝 Feature Guide: Survey Generation
|
| 151 |
+
|
| 152 |
+
### Creating Your First Survey
|
| 153 |
+
|
| 154 |
+
**1. Navigate to the "Generate Survey" Tab**
|
| 155 |
+
|
| 156 |
+
**2. Describe Your Research**
|
| 157 |
+
|
| 158 |
+
Enter your outline in the text box. Be specific about:
|
| 159 |
+
- **Topic**: What are you researching?
|
| 160 |
+
- **Goals**: What do you want to learn?
|
| 161 |
+
- **Focus Areas**: What specific aspects matter?
|
| 162 |
+
|
| 163 |
+
**Example Outlines:**
|
| 164 |
+
|
| 165 |
+
```
|
| 166 |
+
Good: "I want to understand patient experiences with a new
|
| 167 |
+
diabetes medication, focusing on effectiveness in managing
|
| 168 |
+
blood sugar, side effects experienced, and impact on daily
|
| 169 |
+
quality of life."
|
| 170 |
+
|
| 171 |
+
Better: "We're studying user satisfaction with our mobile
|
| 172 |
+
banking app. Key areas: ease of use for common transactions,
|
| 173 |
+
trust in security features, pain points in the account setup
|
| 174 |
+
process, and feature requests for future versions."
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
**3. Configure Survey Settings**
|
| 178 |
+
|
| 179 |
+
- **Survey Type**:
|
| 180 |
+
- *Qualitative*: Open-ended questions for deep insights
|
| 181 |
+
- *Quantitative*: Structured questions with measurable responses
|
| 182 |
+
- *Mixed*: Combination of both
|
| 183 |
+
|
| 184 |
+
- **Number of Questions**:
|
| 185 |
+
- Start with 10-15 for most studies
|
| 186 |
+
- 5-8 for quick feedback surveys
|
| 187 |
+
- 15-25 for comprehensive research
|
| 188 |
+
|
| 189 |
+
- **Target Audience**:
|
| 190 |
+
- Be specific: "Adults 25-45 who use fitness apps daily"
|
| 191 |
+
- Not just: "General public"
|
| 192 |
+
|
| 193 |
+
**4. Generate and Review**
|
| 194 |
+
|
| 195 |
+
Click "🚀 Generate Survey" and wait 10-30 seconds.
|
| 196 |
+
|
| 197 |
+
Review the generated survey:
|
| 198 |
+
- ✅ Questions are clear and unbiased
|
| 199 |
+
- ✅ Appropriate question types are used
|
| 200 |
+
- ✅ Logical flow from general to specific
|
| 201 |
+
- ✅ Professional introduction and closing
|
| 202 |
+
|
| 203 |
+
**5. Download and Deploy**
|
| 204 |
+
|
| 205 |
+
- Download the JSON file for your survey platform
|
| 206 |
+
- Copy questions to your preferred survey tool
|
| 207 |
+
- Customize further if needed
|
| 208 |
+
|
| 209 |
+
### Tips for Better Surveys
|
| 210 |
+
|
| 211 |
+
**Do:**
|
| 212 |
+
- ✅ Be specific about your research goals
|
| 213 |
+
- ✅ Mention your target audience characteristics
|
| 214 |
+
- ✅ Specify key topics or themes to explore
|
| 215 |
+
- ✅ Include context about why you're researching
|
| 216 |
+
|
| 217 |
+
**Don't:**
|
| 218 |
+
- ❌ Use vague descriptions like "customer feedback"
|
| 219 |
+
- ❌ Request too many questions (causes fatigue)
|
| 220 |
+
- ❌ Skip the target audience field
|
| 221 |
+
- ❌ Forget to review before deploying
|
| 222 |
+
|
| 223 |
+
**Example Use Cases:**
|
| 224 |
+
|
| 225 |
+
1. **Product Feedback**
|
| 226 |
+
```
|
| 227 |
+
Outline: "Gather feedback from beta users of our project
|
| 228 |
+
management software. Focus on: workflow improvements over
|
| 229 |
+
previous tools, collaboration features effectiveness,
|
| 230 |
+
learning curve challenges, and missing features that would
|
| 231 |
+
increase productivity."
|
| 232 |
+
```
|
| 233 |
+
|
| 234 |
+
2. **Customer Experience**
|
| 235 |
+
```
|
| 236 |
+
Outline: "Understand customer experience at our retail
|
| 237 |
+
stores. Key areas: staff helpfulness, product selection
|
| 238 |
+
satisfaction, checkout process efficiency, store
|
| 239 |
+
cleanliness, and likelihood to recommend."
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
3. **Academic Research**
|
| 243 |
+
```
|
| 244 |
+
Outline: "Study remote work impact on work-life balance
|
| 245 |
+
among knowledge workers. Topics: boundary management,
|
| 246 |
+
productivity changes, social isolation, communication
|
| 247 |
+
challenges, and preferences for future work arrangements."
|
| 248 |
+
```
|
| 249 |
+
|
| 250 |
+
---
|
| 251 |
+
|
| 252 |
+
## 🌍 Feature Guide: Survey Translation
|
| 253 |
+
|
| 254 |
+
### Translating Surveys to Multiple Languages
|
| 255 |
+
|
| 256 |
+
**1. Generate or Upload a Survey**
|
| 257 |
+
- Create a survey using the generation feature, OR
|
| 258 |
+
- Have your existing survey in the correct JSON format
|
| 259 |
+
|
| 260 |
+
**2. Navigate to "Translate Survey" Tab**
|
| 261 |
+
|
| 262 |
+
**3. Select Target Languages**
|
| 263 |
+
|
| 264 |
+
Choose from 18+ supported languages:
|
| 265 |
+
- **European**: Spanish, French, German, Italian, Portuguese, Dutch, Swedish, Polish
|
| 266 |
+
- **Asian**: Chinese, Japanese, Korean, Vietnamese, Thai, Indonesian, Hindi
|
| 267 |
+
- **Middle Eastern**: Arabic, Turkish
|
| 268 |
+
- **Eastern European**: Russian
|
| 269 |
+
|
| 270 |
+
**Pro Tip**: Select multiple languages at once for batch translation
|
| 271 |
+
|
| 272 |
+
**4. Generate Translations**
|
| 273 |
+
|
| 274 |
+
Click "🌐 Translate Survey" and wait.
|
| 275 |
+
|
| 276 |
+
Processing time:
|
| 277 |
+
- 1-2 languages: 20-40 seconds
|
| 278 |
+
- 3-5 languages: 1-2 minutes
|
| 279 |
+
- 6+ languages: 2-3 minutes
|
| 280 |
+
|
| 281 |
+
**5. Review and Download**
|
| 282 |
+
|
| 283 |
+
- Each translation appears in a separate section
|
| 284 |
+
- Check for cultural appropriateness
|
| 285 |
+
- Download JSON file containing all translations
|
| 286 |
+
|
| 287 |
+
### Translation Best Practices
|
| 288 |
+
|
| 289 |
+
**Quality Assurance:**
|
| 290 |
+
|
| 291 |
+
1. **Back-Translation Testing**
|
| 292 |
+
- For critical surveys, have a native speaker back-translate
|
| 293 |
+
- Compare with original to ensure meaning preserved
|
| 294 |
+
|
| 295 |
+
2. **Cultural Adaptation**
|
| 296 |
+
- Review idioms and expressions
|
| 297 |
+
- Check that examples make sense in target culture
|
| 298 |
+
- Verify formality level is appropriate
|
| 299 |
+
|
| 300 |
+
3. **Pilot Testing**
|
| 301 |
+
- Test with small group of native speakers
|
| 302 |
+
- Gather feedback on clarity and appropriateness
|
| 303 |
+
- Refine before full deployment
|
| 304 |
+
|
| 305 |
+
**When to Use Each Language:**
|
| 306 |
+
|
| 307 |
+
| Language | When to Use | Notes |
|
| 308 |
+
|----------|------------|-------|
|
| 309 |
+
| Spanish | Latin America, Spain | Specify region for dialect |
|
| 310 |
+
| French | France, Canada, Africa | Consider regional variations |
|
| 311 |
+
| German | DACH region | Formal vs informal matters |
|
| 312 |
+
| Chinese | China, Taiwan, Singapore | Simplified vs Traditional |
|
| 313 |
+
| Arabic | MENA region | Right-to-left formatting needed |
|
| 314 |
+
| Portuguese | Brazil, Portugal | Brazilian vs European Portuguese |
|
| 315 |
+
|
| 316 |
+
### Use Cases
|
| 317 |
+
|
| 318 |
+
1. **Global Product Launch**
|
| 319 |
+
```
|
| 320 |
+
Scenario: Launching mobile app in 5 countries
|
| 321 |
+
Languages: English, Spanish, French, German, Japanese
|
| 322 |
+
Questions: 12 (mix of usability and satisfaction)
|
| 323 |
+
Time saved: ~8 hours of professional translation
|
| 324 |
+
```
|
| 325 |
+
|
| 326 |
+
2. **Multinational Employee Survey**
|
| 327 |
+
```
|
| 328 |
+
Scenario: Annual engagement survey across offices
|
| 329 |
+
Languages: English, Chinese, Hindi, Spanish, Portuguese
|
| 330 |
+
Questions: 15 (engagement, culture, development)
|
| 331 |
+
Time saved: ~10 hours + faster deployment
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
3. **Academic International Study**
|
| 335 |
+
```
|
| 336 |
+
Scenario: Cross-cultural research project
|
| 337 |
+
Languages: English, French, German, Italian, Spanish
|
| 338 |
+
Questions: 20 (detailed qualitative questions)
|
| 339 |
+
Time saved: Professional translation would cost $500+
|
| 340 |
+
```
|
| 341 |
+
|
| 342 |
+
---
|
| 343 |
+
|
| 344 |
+
## 📊 Feature Guide: Data Analysis
|
| 345 |
+
|
| 346 |
+
### Analyzing Survey Responses
|
| 347 |
+
|
| 348 |
+
**1. Prepare Your Data**
|
| 349 |
+
|
| 350 |
+
Format responses as JSON array:
|
| 351 |
+
|
| 352 |
+
```json
|
| 353 |
+
[
|
| 354 |
+
{
|
| 355 |
+
"q1": "First respondent's answer to question 1",
|
| 356 |
+
"q2": "First respondent's answer to question 2",
|
| 357 |
+
"q3": "First respondent's answer to question 3"
|
| 358 |
+
},
|
| 359 |
+
{
|
| 360 |
+
"q1": "Second respondent's answer to question 1",
|
| 361 |
+
"q2": "Second respondent's answer to question 2",
|
| 362 |
+
"q3": "Second respondent's answer to question 3"
|
| 363 |
+
}
|
| 364 |
+
]
|
| 365 |
+
```
|
| 366 |
+
|
| 367 |
+
**2. Navigate to "Analyze Data" Tab**
|
| 368 |
+
|
| 369 |
+
**3. Input Your Data**
|
| 370 |
+
|
| 371 |
+
- Paste responses JSON in the "Survey Responses" field
|
| 372 |
+
- Optionally add questions JSON for better context
|
| 373 |
+
- Use "Load Example" button to see format
|
| 374 |
+
|
| 375 |
+
**4. Run Analysis**
|
| 376 |
+
|
| 377 |
+
Click "🔍 Analyze Data" and wait 30-60 seconds.
|
| 378 |
+
|
| 379 |
+
**5. Review Results**
|
| 380 |
+
|
| 381 |
+
The analysis includes:
|
| 382 |
+
|
| 383 |
+
**Executive Summary**
|
| 384 |
+
- High-level overview of findings
|
| 385 |
+
- Key patterns observed
|
| 386 |
+
- Notable discoveries
|
| 387 |
+
- Response quality assessment
|
| 388 |
+
|
| 389 |
+
**Thematic Analysis**
|
| 390 |
+
- 5-7 main themes identified
|
| 391 |
+
- Description of each theme
|
| 392 |
+
- Prevalence percentages
|
| 393 |
+
- Representative quotes
|
| 394 |
+
|
| 395 |
+
**Sentiment Analysis**
|
| 396 |
+
- Overall sentiment (positive/negative/neutral/mixed)
|
| 397 |
+
- Sentiment distribution breakdown
|
| 398 |
+
- Key emotions detected
|
| 399 |
+
- Intensity assessment
|
| 400 |
+
|
| 401 |
+
**Key Insights**
|
| 402 |
+
- 5-7 actionable insights
|
| 403 |
+
- Specific, evidence-based findings
|
| 404 |
+
- Strategic recommendations
|
| 405 |
+
- Trend observations
|
| 406 |
+
|
| 407 |
+
**Statistics**
|
| 408 |
+
- Total responses analyzed
|
| 409 |
+
- Average response length
|
| 410 |
+
- Completion rates
|
| 411 |
+
- Data quality metrics
|
| 412 |
+
|
| 413 |
+
**6. Download Reports**
|
| 414 |
+
|
| 415 |
+
- JSON file: Full analysis data for further processing
|
| 416 |
+
- Markdown file: Formatted report for presentations
|
| 417 |
+
|
| 418 |
+
### Analysis Best Practices
|
| 419 |
+
|
| 420 |
+
**Data Preparation:**
|
| 421 |
+
|
| 422 |
+
1. **Minimum Response Count**
|
| 423 |
+
- Absolute minimum: 10 responses
|
| 424 |
+
- Good results: 20-50 responses
|
| 425 |
+
- Best results: 50+ responses
|
| 426 |
+
|
| 427 |
+
2. **Response Quality**
|
| 428 |
+
- Encourage detailed, thoughtful responses
|
| 429 |
+
- Filter out spam or very short responses
|
| 430 |
+
- Include diverse perspectives
|
| 431 |
+
|
| 432 |
+
3. **Data Cleaning**
|
| 433 |
+
- Remove duplicates
|
| 434 |
+
- Handle incomplete responses
|
| 435 |
+
- Fix formatting issues
|
| 436 |
+
|
| 437 |
+
**Interpretation Guidelines:**
|
| 438 |
+
|
| 439 |
+
1. **Themes**
|
| 440 |
+
- Look for recurring patterns
|
| 441 |
+
- Consider theme prevalence percentages
|
| 442 |
+
- Read example quotes for context
|
| 443 |
+
- Cross-reference with your research questions
|
| 444 |
+
|
| 445 |
+
2. **Sentiment**
|
| 446 |
+
- Don't over-interpret mixed sentiment
|
| 447 |
+
- Look for sentiment patterns by theme
|
| 448 |
+
- Consider intensity levels
|
| 449 |
+
- Watch for contradictions
|
| 450 |
+
|
| 451 |
+
3. **Insights**
|
| 452 |
+
- Prioritize insights supported by multiple responses
|
| 453 |
+
- Look for unexpected findings
|
| 454 |
+
- Consider business/research implications
|
| 455 |
+
- Validate with quantitative data when available
|
| 456 |
+
|
| 457 |
+
**Common Pitfalls to Avoid:**
|
| 458 |
+
|
| 459 |
+
❌ **Cherry-picking**: Don't just highlight what confirms your hypothesis
|
| 460 |
+
✅ **Balanced reporting**: Include contradictory findings
|
| 461 |
+
|
| 462 |
+
❌ **Small sample bias**: Don't generalize from <20 responses
|
| 463 |
+
✅ **Appropriate scope**: Acknowledge sample size limitations
|
| 464 |
+
|
| 465 |
+
❌ **Over-reliance on AI**: AI assists but doesn't replace human judgment
|
| 466 |
+
✅ **Critical review**: Validate AI findings with domain expertise
|
| 467 |
+
|
| 468 |
+
❌ **Ignoring context**: Raw numbers without situational understanding
|
| 469 |
+
✅ **Contextual analysis**: Consider external factors and timing
|
| 470 |
+
|
| 471 |
+
### Use Cases
|
| 472 |
+
|
| 473 |
+
**1. Customer Feedback Analysis**
|
| 474 |
+
```
|
| 475 |
+
Input: 50 responses to "What could we improve?"
|
| 476 |
+
Output:
|
| 477 |
+
- 5 themes (pricing concerns, feature requests, UX issues, etc.)
|
| 478 |
+
- Overall negative sentiment (68%) but constructive tone
|
| 479 |
+
- 7 actionable insights for product roadmap
|
| 480 |
+
Time saved: 4-6 hours of manual coding
|
| 481 |
+
```
|
| 482 |
+
|
| 483 |
+
**2. Employee Engagement Study**
|
| 484 |
+
```
|
| 485 |
+
Input: 120 responses across 10 open-ended questions
|
| 486 |
+
Output:
|
| 487 |
+
- Themes: work-life balance, career development, management
|
| 488 |
+
- Mixed sentiment with strong positives and negatives
|
| 489 |
+
- Insights on retention risks and opportunities
|
| 490 |
+
Time saved: 8-12 hours of analysis
|
| 491 |
+
```
|
| 492 |
+
|
| 493 |
+
**3. User Research Interviews**
|
| 494 |
+
```
|
| 495 |
+
Input: 25 interview transcripts (formatted as responses)
|
| 496 |
+
Output:
|
| 497 |
+
- Themes: user goals, pain points, feature priorities
|
| 498 |
+
- Positive sentiment on core functionality
|
| 499 |
+
- Insights for next sprint planning
|
| 500 |
+
Time saved: 6-8 hours of manual synthesis
|
| 501 |
+
```
|
| 502 |
+
|
| 503 |
+
---
|
| 504 |
+
|
| 505 |
+
## 💬 Feature Guide: Conversational Research
|
| 506 |
+
|
| 507 |
+
### Conducting AI-Moderated Interviews
|
| 508 |
+
|
| 509 |
+
**What is Conversational Research?**
|
| 510 |
+
|
| 511 |
+
Unlike static surveys, conversational research creates a dynamic dialogue between an AI moderator and respondents. The AI follows a scripted conversation flow but adapts in real-time by asking follow-up questions based on responses, creating a more natural and engaging interview experience.
|
| 512 |
+
|
| 513 |
+
**When to Use Conversational Research:**
|
| 514 |
+
- 🎤 Exploratory research where you want to probe deeper
|
| 515 |
+
- 💡 User research requiring contextual follow-ups
|
| 516 |
+
- 🔍 Customer discovery with adaptive questioning
|
| 517 |
+
- 📝 Qualitative interviews at scale
|
| 518 |
+
- 🤝 Situations requiring empathetic, human-like interaction
|
| 519 |
+
|
| 520 |
+
### Designing a Conversation Flow
|
| 521 |
+
|
| 522 |
+
**1. Navigate to "💬 Conversational Research" Tab**
|
| 523 |
+
|
| 524 |
+
Click on the "🎨 Design Flow" sub-tab.
|
| 525 |
+
|
| 526 |
+
**2. Create a New Flow**
|
| 527 |
+
|
| 528 |
+
Enter flow details:
|
| 529 |
+
- **Flow Name**: Descriptive title (e.g., "Product Feedback Interview")
|
| 530 |
+
- **Flow Description**: Purpose and context of the conversation
|
| 531 |
+
|
| 532 |
+
Click "✨ Create New Flow" to start.
|
| 533 |
+
|
| 534 |
+
**3. Add Conversation Steps**
|
| 535 |
+
|
| 536 |
+
For each question/step:
|
| 537 |
+
- **Question/Message**: The scripted question the AI will ask
|
| 538 |
+
- **Step Type**: Choose "Question" or "End"
|
| 539 |
+
- *Question*: Regular conversation step
|
| 540 |
+
- *End*: Final closing message
|
| 541 |
+
|
| 542 |
+
Click "➕ Add Step" to add each node to the flow.
|
| 543 |
+
|
| 544 |
+
**Example Flow Structure:**
|
| 545 |
+
```
|
| 546 |
+
Step 1 (Question): "Hello! Thank you for taking the time to speak with me.
|
| 547 |
+
What initially attracted you to our product?"
|
| 548 |
+
|
| 549 |
+
Step 2 (Question): "How would you describe your overall experience using
|
| 550 |
+
the product so far?"
|
| 551 |
+
|
| 552 |
+
Step 3 (Question): "What specific features do you find most valuable?"
|
| 553 |
+
|
| 554 |
+
Step 4 (Question): "Have you encountered any challenges or frustrations?"
|
| 555 |
+
|
| 556 |
+
Step 5 (Question): "What improvements would you most like to see?"
|
| 557 |
+
|
| 558 |
+
Step 6 (End): "Thank you for sharing your thoughts! Your feedback is
|
| 559 |
+
incredibly valuable."
|
| 560 |
+
```
|
| 561 |
+
|
| 562 |
+
**4. Preview Your Flow**
|
| 563 |
+
|
| 564 |
+
The flow preview shows:
|
| 565 |
+
- All conversation steps in order
|
| 566 |
+
- Step types and IDs
|
| 567 |
+
- How the conversation will progress
|
| 568 |
+
|
| 569 |
+
**5. Save Your Flow**
|
| 570 |
+
|
| 571 |
+
Click "💾 Save Flow" to save to a JSON file.
|
| 572 |
+
|
| 573 |
+
**Pro Tip**: Start by clicking "📋 Load Example" to see a complete customer feedback interview template.
|
| 574 |
+
|
| 575 |
+
### Conducting an Interview
|
| 576 |
+
|
| 577 |
+
**1. Navigate to "🎙️ Conduct Interview" Sub-Tab**
|
| 578 |
+
|
| 579 |
+
**2. Start a Conversation Session**
|
| 580 |
+
|
| 581 |
+
Click "🚀 Start Conversation" to begin.
|
| 582 |
+
|
| 583 |
+
The AI moderator will:
|
| 584 |
+
- Greet the respondent
|
| 585 |
+
- Ask the first question from your flow
|
| 586 |
+
- Wait for a response
|
| 587 |
+
|
| 588 |
+
**3. Respond as the Interviewee**
|
| 589 |
+
|
| 590 |
+
Type your response in the text box and click "Send" (or press Enter).
|
| 591 |
+
|
| 592 |
+
**4. Experience Dynamic Adaptation**
|
| 593 |
+
|
| 594 |
+
The AI moderator intelligently decides whether to:
|
| 595 |
+
|
| 596 |
+
**Ask Scripted Question** (Default):
|
| 597 |
+
- Continues with the next question in your flow
|
| 598 |
+
- Maintains structure and coverage
|
| 599 |
+
|
| 600 |
+
**Ask Dynamic Follow-Up** (Adaptive):
|
| 601 |
+
- Probes deeper into interesting responses
|
| 602 |
+
- Generated based on what you just said
|
| 603 |
+
- Examples: "Tell me more about...", "Can you elaborate on...", "Why do you think..."
|
| 604 |
+
|
| 605 |
+
**Triggers for Follow-Up Questions:**
|
| 606 |
+
- Every 3rd user response (configurable)
|
| 607 |
+
- Responses longer than 5 words
|
| 608 |
+
- Interesting keywords detected:
|
| 609 |
+
- Emotional: "frustrated", "excited", "worried", "confused"
|
| 610 |
+
- Reasoning: "because", "however", "although", "surprisingly"
|
| 611 |
+
|
| 612 |
+
**5. Session Progress**
|
| 613 |
+
|
| 614 |
+
Monitor session status:
|
| 615 |
+
- Active conversation indicator
|
| 616 |
+
- Turn count
|
| 617 |
+
- Current flow position
|
| 618 |
+
|
| 619 |
+
**6. Export the Conversation**
|
| 620 |
+
|
| 621 |
+
When finished, click "📥 Export Conversation" to save:
|
| 622 |
+
- **Transcript**: Readable text format (.txt)
|
| 623 |
+
- **JSON**: Full session data with timestamps
|
| 624 |
+
- **CSV**: Turn-by-turn analysis format
|
| 625 |
+
|
| 626 |
+
### Best Practices for Conversation Flows
|
| 627 |
+
|
| 628 |
+
**Flow Design:**
|
| 629 |
+
|
| 630 |
+
1. **Start Broad, Get Specific**
|
| 631 |
+
```
|
| 632 |
+
Good flow:
|
| 633 |
+
1. General experience → 2. Specific features → 3. Pain points → 4. Improvements
|
| 634 |
+
|
| 635 |
+
Poor flow:
|
| 636 |
+
1. Very specific detail → 2. General opinion (order reversed)
|
| 637 |
+
```
|
| 638 |
+
|
| 639 |
+
2. **Optimal Flow Length**
|
| 640 |
+
- Short interviews: 4-6 questions
|
| 641 |
+
- Standard interviews: 6-10 questions
|
| 642 |
+
- In-depth interviews: 10-15 questions
|
| 643 |
+
- Note: AI follow-ups extend the conversation naturally
|
| 644 |
+
|
| 645 |
+
3. **Question Types**
|
| 646 |
+
- Open-ended: "Tell me about...", "Describe your experience..."
|
| 647 |
+
- Focused: "What specific features...", "When did you first..."
|
| 648 |
+
- Reflective: "How did that make you feel?", "What did you learn?"
|
| 649 |
+
|
| 650 |
+
4. **Professional Tone**
|
| 651 |
+
- Empathetic and non-judgmental
|
| 652 |
+
- Clear and conversational
|
| 653 |
+
- Respectful of respondent's time
|
| 654 |
+
- Genuine curiosity
|
| 655 |
+
|
| 656 |
+
**Conducting Interviews:**
|
| 657 |
+
|
| 658 |
+
1. **Set Expectations**
|
| 659 |
+
- Tell respondents this is an AI-moderated interview
|
| 660 |
+
- Mention it will ask follow-up questions
|
| 661 |
+
- Encourage detailed responses (5+ words)
|
| 662 |
+
|
| 663 |
+
2. **Response Quality**
|
| 664 |
+
- Encourage thoughtful, detailed answers
|
| 665 |
+
- Very short responses (<5 words) won't trigger follow-ups
|
| 666 |
+
- Rich responses get more adaptive probing
|
| 667 |
+
|
| 668 |
+
3. **Managing Length**
|
| 669 |
+
- AI limits follow-ups to avoid fatigue
|
| 670 |
+
- Flow continues even with dynamic questions
|
| 671 |
+
- Respondents can keep answers brief to move faster
|
| 672 |
+
|
| 673 |
+
4. **Technical Tips**
|
| 674 |
+
- One respondent per session
|
| 675 |
+
- Sessions auto-save conversation history
|
| 676 |
+
- Can't resume abandoned sessions (by design)
|
| 677 |
+
- Export immediately after completion
|
| 678 |
+
|
| 679 |
+
### Use Cases
|
| 680 |
+
|
| 681 |
+
**1. Customer Discovery Interviews**
|
| 682 |
+
```
|
| 683 |
+
Scenario: Understanding why users chose your product
|
| 684 |
+
Flow: 5 scripted questions about decision process
|
| 685 |
+
AI Adaptation: Probes on "competitor comparison" mentions
|
| 686 |
+
Result: Rich insights on differentiation factors
|
| 687 |
+
Time: 15-20 minutes per interview
|
| 688 |
+
```
|
| 689 |
+
|
| 690 |
+
**2. UX Research Sessions**
|
| 691 |
+
```
|
| 692 |
+
Scenario: Exploring pain points in onboarding flow
|
| 693 |
+
Flow: 8 questions walking through user journey
|
| 694 |
+
AI Adaptation: Asks follow-ups on confusion/frustration
|
| 695 |
+
Result: Detailed understanding of UX issues
|
| 696 |
+
Time: 20-25 minutes per session
|
| 697 |
+
```
|
| 698 |
+
|
| 699 |
+
**3. Product Feedback at Scale**
|
| 700 |
+
```
|
| 701 |
+
Scenario: Collecting beta feedback from 50 users
|
| 702 |
+
Flow: 6 standard questions + AI follow-ups
|
| 703 |
+
AI Adaptation: Probes interesting feature requests
|
| 704 |
+
Result: Prioritized roadmap from user insights
|
| 705 |
+
Time: 10-15 minutes × 50 = 8-12 hours total (automated)
|
| 706 |
+
```
|
| 707 |
+
|
| 708 |
+
**4. Market Research Interviews**
|
| 709 |
+
```
|
| 710 |
+
Scenario: Understanding buyer preferences
|
| 711 |
+
Flow: 10 questions on needs, alternatives, priorities
|
| 712 |
+
AI Adaptation: Explores "price sensitivity" mentions
|
| 713 |
+
Result: Market positioning insights
|
| 714 |
+
Time: 25-30 minutes per interview
|
| 715 |
+
```
|
| 716 |
+
|
| 717 |
+
### Analysis Tips
|
| 718 |
+
|
| 719 |
+
**Reviewing Transcripts:**
|
| 720 |
+
1. Export all sessions after completion
|
| 721 |
+
2. Look for recurring themes across conversations
|
| 722 |
+
3. Note where AI follow-ups uncovered insights
|
| 723 |
+
4. Compare scripted vs. dynamic question value
|
| 724 |
+
|
| 725 |
+
**Processing Conversations:**
|
| 726 |
+
1. **Manual Analysis**: Review transcripts for themes
|
| 727 |
+
2. **Automated Analysis**: Use the "Analyze Data" tab
|
| 728 |
+
- Export conversation turns to CSV
|
| 729 |
+
- Format responses for analysis
|
| 730 |
+
- Run thematic analysis
|
| 731 |
+
|
| 732 |
+
**Key Metrics:**
|
| 733 |
+
- Average conversation length (turns)
|
| 734 |
+
- Follow-up question frequency
|
| 735 |
+
- Response depth (words per turn)
|
| 736 |
+
- Topic coverage across sessions
|
| 737 |
+
|
| 738 |
+
### Advanced Features
|
| 739 |
+
|
| 740 |
+
**Conversation Summarization** (Coming Soon):
|
| 741 |
+
- AI-generated summary of each conversation
|
| 742 |
+
- Key points extraction
|
| 743 |
+
- Sentiment analysis per session
|
| 744 |
+
|
| 745 |
+
**Flow Branching** (Planned):
|
| 746 |
+
- Conditional logic based on responses
|
| 747 |
+
- Different paths for different respondent types
|
| 748 |
+
- Skip logic for efficiency
|
| 749 |
+
|
| 750 |
+
**Multi-Moderator Styles** (Planned):
|
| 751 |
+
- Empathetic interviewer
|
| 752 |
+
- Business analyst
|
| 753 |
+
- Technical researcher
|
| 754 |
+
- Cultural variations
|
| 755 |
+
|
| 756 |
+
### Limitations
|
| 757 |
+
|
| 758 |
+
**Current Limitations:**
|
| 759 |
+
- ❌ Linear flows only (no branching yet)
|
| 760 |
+
- ❌ Cannot resume abandoned sessions
|
| 761 |
+
- ❌ One respondent per session
|
| 762 |
+
- ❌ English language optimized (other languages work but less refined)
|
| 763 |
+
|
| 764 |
+
**Best Suited For:**
|
| 765 |
+
- ✅ Qualitative research interviews
|
| 766 |
+
- ✅ Exploratory customer discovery
|
| 767 |
+
- ✅ User research at scale
|
| 768 |
+
- ✅ Standardized but adaptive interviews
|
| 769 |
+
|
| 770 |
+
**Not Ideal For:**
|
| 771 |
+
- ❌ Highly structured surveys (use static surveys instead)
|
| 772 |
+
- ❌ Quantitative data collection
|
| 773 |
+
- ❌ Complex branching logic requirements
|
| 774 |
+
- ❌ Multi-party conversations
|
| 775 |
+
|
| 776 |
+
---
|
| 777 |
+
|
| 778 |
+
## 🎓 Complete Workflow Examples
|
| 779 |
+
|
| 780 |
+
### Example 1: New Product Feature Research
|
| 781 |
+
|
| 782 |
+
**Objective**: Understand if users want a new AI assistant feature
|
| 783 |
+
|
| 784 |
+
**Step 1: Generate Survey** (5 minutes)
|
| 785 |
+
```
|
| 786 |
+
Outline: "Explore interest in an AI assistant feature for our
|
| 787 |
+
productivity app. Focus on: use cases users envision, concerns
|
| 788 |
+
about AI, willingness to pay, and preferred interaction methods."
|
| 789 |
+
|
| 790 |
+
Settings:
|
| 791 |
+
- Type: Mixed (qualitative + quantitative)
|
| 792 |
+
- Questions: 12
|
| 793 |
+
- Audience: Current users of productivity apps, tech-savvy
|
| 794 |
+
```
|
| 795 |
+
|
| 796 |
+
**Step 2: Deploy Survey** (You handle this)
|
| 797 |
+
- Export to your survey platform
|
| 798 |
+
- Send to 100 beta users
|
| 799 |
+
- Collect responses over 1 week
|
| 800 |
+
|
| 801 |
+
**Step 3: Translate for Global Test** (10 minutes)
|
| 802 |
+
```
|
| 803 |
+
Selected languages: Spanish, French, German, Japanese
|
| 804 |
+
Purpose: Test with international user base
|
| 805 |
+
Result: 4 localized versions ready to deploy
|
| 806 |
+
```
|
| 807 |
+
|
| 808 |
+
**Step 4: Analyze Results** (15 minutes)
|
| 809 |
+
```
|
| 810 |
+
Input: 78 responses in JSON format
|
| 811 |
+
Analysis reveals:
|
| 812 |
+
- 3 main use cases (writing assistance, data analysis, scheduling)
|
| 813 |
+
- Positive sentiment (72%) but privacy concerns (45% mention)
|
| 814 |
+
- Insights: Users willing to pay $5-10/month, prefer opt-in
|
| 815 |
+
```
|
| 816 |
+
|
| 817 |
+
**Total Time**: ~30 minutes of work
|
| 818 |
+
**Traditional Time**: 8-12 hours
|
| 819 |
+
**Savings**: ~10 hours
|
| 820 |
+
|
| 821 |
+
---
|
| 822 |
+
|
| 823 |
+
### Example 2: Multi-Country Market Research
|
| 824 |
+
|
| 825 |
+
**Objective**: Launch product in 5 new markets, need local insights
|
| 826 |
+
|
| 827 |
+
**Step 1: Generate Core Survey** (5 minutes)
|
| 828 |
+
```
|
| 829 |
+
Outline: "Market research for launching a sustainable fashion
|
| 830 |
+
brand. Topics: sustainability priorities, price sensitivity,
|
| 831 |
+
preferred materials, shopping habits, brand perception factors."
|
| 832 |
+
|
| 833 |
+
Settings:
|
| 834 |
+
- Type: Qualitative
|
| 835 |
+
- Questions: 15
|
| 836 |
+
- Audience: Environmentally conscious consumers, 25-45
|
| 837 |
+
```
|
| 838 |
+
|
| 839 |
+
**Step 2: Translate to Target Markets** (15 minutes)
|
| 840 |
+
```
|
| 841 |
+
Languages: Spanish (Mexico), Portuguese (Brazil), French (France),
|
| 842 |
+
German (Germany), Japanese (Japan)
|
| 843 |
+
|
| 844 |
+
Result: 5 culturally adapted versions
|
| 845 |
+
Quality check: Review by native speakers on team
|
| 846 |
+
```
|
| 847 |
+
|
| 848 |
+
**Step 3: Deploy and Collect** (You handle this)
|
| 849 |
+
- 50 responses per country
|
| 850 |
+
- 250 total responses
|
| 851 |
+
- 2-week collection period
|
| 852 |
+
|
| 853 |
+
**Step 4: Analyze by Market** (30 minutes)
|
| 854 |
+
```
|
| 855 |
+
Run analysis separately for each market:
|
| 856 |
+
- Identify market-specific themes
|
| 857 |
+
- Compare sentiment across markets
|
| 858 |
+
- Note cultural differences in priorities
|
| 859 |
+
|
| 860 |
+
Key findings example:
|
| 861 |
+
- Japan: Quality and durability top priority
|
| 862 |
+
- Germany: Certifications and transparency crucial
|
| 863 |
+
- Brazil: Price sensitivity higher, but willing to pay for story
|
| 864 |
+
```
|
| 865 |
+
|
| 866 |
+
**Total Time**: ~50 minutes
|
| 867 |
+
**Traditional Time**: 20-30 hours (translation + analysis)
|
| 868 |
+
**Cost Savings**: $2000+ in professional services
|
| 869 |
+
|
| 870 |
+
---
|
| 871 |
+
|
| 872 |
+
### Example 3: Academic Research Project
|
| 873 |
+
|
| 874 |
+
**Objective**: Study impact of remote work on work-life balance
|
| 875 |
+
|
| 876 |
+
**Step 1: Design Survey** (10 minutes)
|
| 877 |
+
```
|
| 878 |
+
Outline: "Investigate how remote work affects work-life balance
|
| 879 |
+
among knowledge workers. Explore: boundary management strategies,
|
| 880 |
+
productivity changes, social isolation experiences, family
|
| 881 |
+
dynamics, preference for future work arrangements, and mental
|
| 882 |
+
health impacts."
|
| 883 |
+
|
| 884 |
+
Settings:
|
| 885 |
+
- Type: Qualitative (open-ended for rich data)
|
| 886 |
+
- Questions: 18
|
| 887 |
+
- Audience: Knowledge workers with 1+ year remote experience
|
| 888 |
+
```
|
| 889 |
+
|
| 890 |
+
**Step 2: Review & Refine** (20 minutes)
|
| 891 |
+
- Review generated questions
|
| 892 |
+
- Ensure alignment with research framework
|
| 893 |
+
- Verify no leading questions
|
| 894 |
+
|
| 895 |
+
**Step 3: Collect Data** (You handle this)
|
| 896 |
+
- Deploy via academic participant pool
|
| 897 |
+
- Collect 150 responses
|
| 898 |
+
- 3-week collection period
|
| 899 |
+
|
| 900 |
+
**Step 4: Comprehensive Analysis** (45 minutes)
|
| 901 |
+
```
|
| 902 |
+
Input: 150 detailed responses
|
| 903 |
+
|
| 904 |
+
Analysis output:
|
| 905 |
+
- 7 major themes with sub-themes
|
| 906 |
+
- Sentiment patterns by demographic
|
| 907 |
+
- 12 key insights for paper
|
| 908 |
+
|
| 909 |
+
Export: Markdown report for lit review section
|
| 910 |
+
JSON for coding in qualitative software
|
| 911 |
+
```
|
| 912 |
+
|
| 913 |
+
**Step 5: Follow-up Translation** (Optional)
|
| 914 |
+
```
|
| 915 |
+
If publishing internationally or presenting at conference:
|
| 916 |
+
Translate survey instrument to show methodology
|
| 917 |
+
Languages: Spanish, French (common in academia)
|
| 918 |
+
```
|
| 919 |
+
|
| 920 |
+
**Total Time**: ~2 hours
|
| 921 |
+
**Traditional Time**: 15-25 hours of manual thematic coding
|
| 922 |
+
**Quality**: Comparable to manual coding for exploratory research
|
| 923 |
+
|
| 924 |
+
---
|
| 925 |
+
|
| 926 |
+
## 💡 Tips for Power Users
|
| 927 |
+
|
| 928 |
+
### Optimizing for Quality
|
| 929 |
+
|
| 930 |
+
**1. Survey Generation**
|
| 931 |
+
- Iterate on outlines to get better questions
|
| 932 |
+
- Generate multiple versions and combine best questions
|
| 933 |
+
- Use specific examples in outlines for better context
|
| 934 |
+
- Mention your theoretical framework for academic research
|
| 935 |
+
|
| 936 |
+
**2. Translation**
|
| 937 |
+
- Start with common languages to test quality
|
| 938 |
+
- Use back-translation for critical surveys
|
| 939 |
+
- Keep original English version for reference
|
| 940 |
+
- Test with native speakers before full deployment
|
| 941 |
+
|
| 942 |
+
**3. Analysis**
|
| 943 |
+
- Provide questions JSON for better context
|
| 944 |
+
- Clean data before analysis (remove duplicates, spam)
|
| 945 |
+
- Run analysis multiple times for consistency
|
| 946 |
+
- Combine AI insights with manual review
|
| 947 |
+
|
| 948 |
+
### Optimizing for Cost
|
| 949 |
+
|
| 950 |
+
**Using Free HuggingFace:**
|
| 951 |
+
- Perfect for testing and development
|
| 952 |
+
- Good for small-scale research (<50 responses)
|
| 953 |
+
- Be patient with first request (cold start)
|
| 954 |
+
- Simplify requests for better performance
|
| 955 |
+
|
| 956 |
+
**Using Paid Providers:**
|
| 957 |
+
|
| 958 |
+
| Provider | Best For | Cost Range | Speed |
|
| 959 |
+
|----------|----------|------------|-------|
|
| 960 |
+
| **OpenAI GPT-4o-mini** | Best value | $0.01-0.05/survey | Fast |
|
| 961 |
+
| **OpenAI GPT-4** | Best quality | $0.05-0.15/survey | Fast |
|
| 962 |
+
| **Anthropic Claude** | Complex analysis | $0.02-0.08/survey | Fast |
|
| 963 |
+
|
| 964 |
+
**Cost Control Tips:**
|
| 965 |
+
- Use GPT-4o-mini for generation and translation
|
| 966 |
+
- Use GPT-4 only for complex analysis
|
| 967 |
+
- Batch operations when possible
|
| 968 |
+
- Set up usage alerts in provider dashboards
|
| 969 |
+
|
| 970 |
+
### Workflow Optimization
|
| 971 |
+
|
| 972 |
+
**Create Templates:**
|
| 973 |
+
Save outlines for common research types:
|
| 974 |
+
- Customer satisfaction surveys
|
| 975 |
+
- Product feedback forms
|
| 976 |
+
- Employee engagement surveys
|
| 977 |
+
- User experience studies
|
| 978 |
+
- Academic research instruments
|
| 979 |
+
|
| 980 |
+
**Batch Processing:**
|
| 981 |
+
- Generate multiple survey versions at once
|
| 982 |
+
- Translate to all needed languages in one go
|
| 983 |
+
- Analyze all demographics separately for comparison
|
| 984 |
+
|
| 985 |
+
**Quality Checkpoints:**
|
| 986 |
+
1. After generation: Review questions for bias
|
| 987 |
+
2. After translation: Spot-check with native speakers
|
| 988 |
+
3. After data collection: Clean data before analysis
|
| 989 |
+
4. After analysis: Validate insights with domain experts
|
| 990 |
+
|
| 991 |
+
---
|
| 992 |
+
|
| 993 |
+
## 🔒 Privacy & Data Security
|
| 994 |
+
|
| 995 |
+
### What Data is Stored?
|
| 996 |
+
|
| 997 |
+
**By ConversAI:**
|
| 998 |
+
- ❌ No survey data is permanently stored
|
| 999 |
+
- ❌ No responses are saved to disk
|
| 1000 |
+
- ❌ No user information is retained
|
| 1001 |
+
- ✅ Temporary files for downloads only (deleted after download)
|
| 1002 |
+
|
| 1003 |
+
**By LLM Providers:**
|
| 1004 |
+
- Varies by provider (check their policies)
|
| 1005 |
+
- OpenAI: Data not used for training by default
|
| 1006 |
+
- Anthropic: Enterprise plans have data guarantees
|
| 1007 |
+
- HuggingFace: Depends on model provider
|
| 1008 |
+
|
| 1009 |
+
### Best Practices for Sensitive Research
|
| 1010 |
+
|
| 1011 |
+
**1. Choose Provider Carefully**
|
| 1012 |
+
- For healthcare: Use HIPAA-compliant LLM service
|
| 1013 |
+
- For confidential: Use Anthropic or OpenAI enterprise
|
| 1014 |
+
- For maximum privacy: Self-host open-source models
|
| 1015 |
+
|
| 1016 |
+
**2. Anonymize Data**
|
| 1017 |
+
- Remove identifying information before analysis
|
| 1018 |
+
- Use participant IDs instead of names
|
| 1019 |
+
- Redact sensitive details from responses
|
| 1020 |
+
|
| 1021 |
+
**3. Access Control**
|
| 1022 |
+
- Use private HuggingFace Spaces if needed
|
| 1023 |
+
- Limit team access to credentials
|
| 1024 |
+
- Rotate API keys regularly
|
| 1025 |
+
|
| 1026 |
+
**4. Compliance**
|
| 1027 |
+
- GDPR: Ensure LLM provider is compliant
|
| 1028 |
+
- IRB requirements: Document AI use in protocols
|
| 1029 |
+
- Data retention: Follow your organization's policies
|
| 1030 |
+
|
| 1031 |
+
---
|
| 1032 |
+
|
| 1033 |
+
## 📈 Measuring Success
|
| 1034 |
+
|
| 1035 |
+
### Survey Generation Success Metrics
|
| 1036 |
+
|
| 1037 |
+
✅ **Quality Indicators:**
|
| 1038 |
+
- Questions are unbiased and clear
|
| 1039 |
+
- Logical flow through survey
|
| 1040 |
+
- Appropriate question types used
|
| 1041 |
+
- All research objectives covered
|
| 1042 |
+
|
| 1043 |
+
✅ **Efficiency Gains:**
|
| 1044 |
+
- Time to first draft: <5 minutes (vs. hours manually)
|
| 1045 |
+
- Iterations needed: 1-2 (vs. 4-5 manually)
|
| 1046 |
+
- Team review time reduced by 50%+
|
| 1047 |
+
|
| 1048 |
+
### Translation Success Metrics
|
| 1049 |
+
|
| 1050 |
+
✅ **Quality Indicators:**
|
| 1051 |
+
- Back-translation matches original meaning
|
| 1052 |
+
- Native speaker approval
|
| 1053 |
+
- Cultural appropriateness confirmed
|
| 1054 |
+
- Response rates comparable across languages
|
| 1055 |
+
|
| 1056 |
+
✅ **Efficiency Gains:**
|
| 1057 |
+
- Time to translate: Minutes (vs. days/weeks)
|
| 1058 |
+
- Cost: Near-zero (vs. $0.10-0.30 per word)
|
| 1059 |
+
- Speed to market: Immediate (vs. 1-2 weeks)
|
| 1060 |
+
|
| 1061 |
+
### Analysis Success Metrics
|
| 1062 |
+
|
| 1063 |
+
✅ **Quality Indicators:**
|
| 1064 |
+
- Themes align with manual coding
|
| 1065 |
+
- Insights lead to actionable decisions
|
| 1066 |
+
- Stakeholders find report valuable
|
| 1067 |
+
- Findings supported by quotes
|
| 1068 |
+
|
| 1069 |
+
✅ **Efficiency Gains:**
|
| 1070 |
+
- Time to insights: <1 hour (vs. 8-20 hours)
|
| 1071 |
+
- Cost: Minimal (vs. $500-2000 for analyst)
|
| 1072 |
+
- Consistency: High (vs. variable with manual coding)
|
| 1073 |
+
|
| 1074 |
+
---
|
| 1075 |
+
|
| 1076 |
+
## 🎯 Use Case Library
|
| 1077 |
+
|
| 1078 |
+
### Market Research
|
| 1079 |
+
- **New product concept testing**: Generate survey → deploy → analyze feedback
|
| 1080 |
+
- **Brand perception studies**: Multi-language surveys for global brands
|
| 1081 |
+
- **Customer satisfaction tracking**: Quarterly analysis of feedback trends
|
| 1082 |
+
- **Competitive analysis**: Survey design for feature comparison studies
|
| 1083 |
+
|
| 1084 |
+
### User Experience Research
|
| 1085 |
+
- **Usability study debriefs**: Analyze interview transcripts
|
| 1086 |
+
- **Feature prioritization**: Generate surveys for user voting
|
| 1087 |
+
- **Beta testing feedback**: Quick analysis of bug reports and suggestions
|
| 1088 |
+
- **Accessibility research**: Multi-language surveys for diverse users
|
| 1089 |
+
|
| 1090 |
+
### Academic Research
|
| 1091 |
+
- **Exploratory studies**: Generate initial survey instruments
|
| 1092 |
+
- **Cross-cultural research**: Translate surveys for international studies
|
| 1093 |
+
- **Qualitative analysis**: Thematic coding of open-ended responses
|
| 1094 |
+
- **Mixed methods**: Combine with quantitative data collection
|
| 1095 |
+
|
| 1096 |
+
### Human Resources
|
| 1097 |
+
- **Employee engagement**: Annual or pulse surveys
|
| 1098 |
+
- **Exit interviews**: Analysis of leaving employee feedback
|
| 1099 |
+
- **Training needs assessment**: Identify development opportunities
|
| 1100 |
+
- **Culture studies**: Understand organizational dynamics
|
| 1101 |
+
|
| 1102 |
+
### Product Management
|
| 1103 |
+
- **Feature requests**: Analyze user suggestions
|
| 1104 |
+
- **Beta feedback**: Quick turnaround on pre-release testing
|
| 1105 |
+
- **Roadmap validation**: Survey users on priorities
|
| 1106 |
+
- **Competitor research**: Generate comparison surveys
|
| 1107 |
+
|
| 1108 |
+
### Healthcare
|
| 1109 |
+
- **Patient satisfaction**: HIPAA-compliant survey generation
|
| 1110 |
+
- **Treatment experience**: Multi-language patient surveys
|
| 1111 |
+
- **Quality improvement**: Analyze patient feedback themes
|
| 1112 |
+
- **Clinical research**: Generate research questionnaires
|
| 1113 |
+
|
| 1114 |
+
---
|
| 1115 |
+
|
| 1116 |
+
## 🚧 Limitations & When NOT to Use
|
| 1117 |
+
|
| 1118 |
+
### Current Limitations
|
| 1119 |
+
|
| 1120 |
+
**Survey Generation:**
|
| 1121 |
+
- ❌ Cannot create complex branching logic
|
| 1122 |
+
- ❌ May need manual refinement for highly specialized topics
|
| 1123 |
+
- ❌ Not a replacement for expert survey design in all cases
|
| 1124 |
+
- ✅ Best for: Standard research surveys, exploratory studies
|
| 1125 |
+
|
| 1126 |
+
**Translation:**
|
| 1127 |
+
- ❌ Not certified/legal translation quality
|
| 1128 |
+
- ❌ May miss subtle cultural nuances in idioms
|
| 1129 |
+
- ❌ Requires native speaker review for publication
|
| 1130 |
+
- ✅ Best for: Research surveys, internal communications
|
| 1131 |
+
|
| 1132 |
+
**Analysis:**
|
| 1133 |
+
- ❌ Not a replacement for rigorous qualitative coding
|
| 1134 |
+
- ❌ May miss domain-specific insights
|
| 1135 |
+
- ❌ Cannot replace human interpretation completely
|
| 1136 |
+
- ✅ Best for: Initial exploration, large-scale feedback, trend identification
|
| 1137 |
+
|
| 1138 |
+
### When to Use Traditional Methods
|
| 1139 |
+
|
| 1140 |
+
**Use professional survey designers when:**
|
| 1141 |
+
- Regulatory compliance requires certified instruments
|
| 1142 |
+
- High-stakes research with legal implications
|
| 1143 |
+
- Complex adaptive survey logic needed
|
| 1144 |
+
- Validated scales are required
|
| 1145 |
+
|
| 1146 |
+
**Use professional translators when:**
|
| 1147 |
+
- Legal or medical translations needed
|
| 1148 |
+
- Publishing in academic journals
|
| 1149 |
+
- Official government communications
|
| 1150 |
+
- Marketing materials with brand sensitivity
|
| 1151 |
+
|
| 1152 |
+
**Use professional analysts when:**
|
| 1153 |
+
- Publishing peer-reviewed research
|
| 1154 |
+
- Complex coding schemes required
|
| 1155 |
+
- Deep domain expertise needed
|
| 1156 |
+
- Consensus coding is methodology requirement
|
| 1157 |
+
|
| 1158 |
+
### Best Approach: Hybrid
|
| 1159 |
+
|
| 1160 |
+
**Recommended workflow:**
|
| 1161 |
+
1. ✅ Use ConversAI for initial draft (fast, cheap)
|
| 1162 |
+
2. ✅ Expert review and refinement (quality assurance)
|
| 1163 |
+
3. ✅ Deploy and collect data
|
| 1164 |
+
4. ✅ ConversAI for preliminary analysis (quick insights)
|
| 1165 |
+
5. ✅ Deep manual analysis for key findings (rigor)
|
| 1166 |
+
|
| 1167 |
+
---
|
| 1168 |
+
|
| 1169 |
+
## 📞 Support & Resources
|
| 1170 |
+
|
| 1171 |
+
### Getting Help
|
| 1172 |
+
|
| 1173 |
+
**Documentation:**
|
| 1174 |
+
- `USER_GUIDE.md` (this document) - Complete user guide
|
| 1175 |
+
- `QUICK_START_HF_SPACES.md` - Fast deployment
|
| 1176 |
+
- `TROUBLESHOOTING.md` - Common issues and solutions
|
| 1177 |
+
- `README.md` - Technical overview
|
| 1178 |
+
|
| 1179 |
+
**Diagnostics:**
|
| 1180 |
+
- Run `python check_env.py` - Environment checker
|
| 1181 |
+
- Check logs for error messages
|
| 1182 |
+
- Use example data to test functionality
|
| 1183 |
+
|
| 1184 |
+
**Community:**
|
| 1185 |
+
- GitHub Issues - Report bugs and feature requests
|
| 1186 |
+
- HuggingFace Space discussions
|
| 1187 |
+
- Research methods forums
|
| 1188 |
+
|
| 1189 |
+
### Feedback & Feature Requests
|
| 1190 |
+
|
| 1191 |
+
We'd love to hear from you:
|
| 1192 |
+
- What features would make ConversAI more valuable?
|
| 1193 |
+
- What use cases are we missing?
|
| 1194 |
+
- What pain points can we solve?
|
| 1195 |
+
|
| 1196 |
+
---
|
| 1197 |
+
|
| 1198 |
+
## 🎓 Learning Resources
|
| 1199 |
+
|
| 1200 |
+
### Understanding Qualitative Research
|
| 1201 |
+
- Introduction to thematic analysis
|
| 1202 |
+
- Survey design best practices
|
| 1203 |
+
- Avoiding bias in questions
|
| 1204 |
+
- Cross-cultural research methods
|
| 1205 |
+
|
| 1206 |
+
### AI & Research Ethics
|
| 1207 |
+
- Using AI in research responsibly
|
| 1208 |
+
- Disclosing AI use in publications
|
| 1209 |
+
- Data privacy considerations
|
| 1210 |
+
- Bias in AI-generated content
|
| 1211 |
+
|
| 1212 |
+
### Maximizing ConversAI
|
| 1213 |
+
- Video tutorials (coming soon)
|
| 1214 |
+
- Webinar series on research workflows
|
| 1215 |
+
- Case studies from real users
|
| 1216 |
+
- Best practices blog
|
| 1217 |
+
|
| 1218 |
+
---
|
| 1219 |
+
|
| 1220 |
+
## 🌟 Success Stories
|
| 1221 |
+
|
| 1222 |
+
### Story 1: Startup Product Validation
|
| 1223 |
+
|
| 1224 |
+
**Challenge**: Early-stage startup needed to validate product concept across 3 markets in 2 weeks.
|
| 1225 |
+
|
| 1226 |
+
**Solution**:
|
| 1227 |
+
- Generated survey in English (10 questions)
|
| 1228 |
+
- Translated to Spanish and Portuguese
|
| 1229 |
+
- Analyzed 200+ responses in 24 hours
|
| 1230 |
+
|
| 1231 |
+
**Results**:
|
| 1232 |
+
- Launched in correct market first (Brazil, not Mexico as planned)
|
| 1233 |
+
- Saved $3,000 in research costs
|
| 1234 |
+
- Made launch decision 2 weeks faster
|
| 1235 |
+
|
| 1236 |
+
---
|
| 1237 |
+
|
| 1238 |
+
### Story 2: University Research Project
|
| 1239 |
+
|
| 1240 |
+
**Challenge**: PhD student analyzing 150 interview transcripts for dissertation.
|
| 1241 |
+
|
| 1242 |
+
**Solution**:
|
| 1243 |
+
- Formatted transcripts as survey responses
|
| 1244 |
+
- Ran thematic analysis
|
| 1245 |
+
- Used insights as starting point for manual coding
|
| 1246 |
+
|
| 1247 |
+
**Results**:
|
| 1248 |
+
- Identified 7 themes in 2 hours vs. estimated 40 hours
|
| 1249 |
+
- Used time savings for deeper literature review
|
| 1250 |
+
- Graduated on schedule
|
| 1251 |
+
|
| 1252 |
+
---
|
| 1253 |
+
|
| 1254 |
+
### Story 3: Enterprise Employee Engagement
|
| 1255 |
+
|
| 1256 |
+
**Challenge**: Multinational company with 5,000 employees in 12 countries.
|
| 1257 |
+
|
| 1258 |
+
**Solution**:
|
| 1259 |
+
- Generated engagement survey (20 questions)
|
| 1260 |
+
- Translated to 8 languages
|
| 1261 |
+
- Analyzed responses by region and department
|
| 1262 |
+
|
| 1263 |
+
**Results**:
|
| 1264 |
+
- 40% higher response rate (due to language options)
|
| 1265 |
+
- Identified region-specific retention risks
|
| 1266 |
+
- Informed $500K investment in benefits program
|
| 1267 |
+
|
| 1268 |
+
---
|
| 1269 |
+
|
| 1270 |
+
## 🚀 Next Steps
|
| 1271 |
+
|
| 1272 |
+
### New Users
|
| 1273 |
+
1. ✅ Start with the "Generate Survey" tab
|
| 1274 |
+
2. ✅ Use the example outline provided
|
| 1275 |
+
3. ✅ Review the generated questions
|
| 1276 |
+
4. ✅ Experiment with different settings
|
| 1277 |
+
5. ✅ Try the example data in Analysis tab
|
| 1278 |
+
|
| 1279 |
+
### Regular Users
|
| 1280 |
+
1. ✅ Create outline templates for common projects
|
| 1281 |
+
2. ✅ Establish quality review processes
|
| 1282 |
+
3. ✅ Integrate into research workflow
|
| 1283 |
+
4. ✅ Share best practices with team
|
| 1284 |
+
5. ✅ Provide feedback for improvements
|
| 1285 |
+
|
| 1286 |
+
### Advanced Users
|
| 1287 |
+
1. ✅ Explore API integration (coming soon)
|
| 1288 |
+
2. ✅ Customize LLM models for your domain
|
| 1289 |
+
3. ✅ Build automated research pipelines
|
| 1290 |
+
4. ✅ Contribute to open source development
|
| 1291 |
+
5. ✅ Share case studies with community
|
| 1292 |
+
|
| 1293 |
+
---
|
| 1294 |
+
|
| 1295 |
+
## 📋 Quick Reference
|
| 1296 |
+
|
| 1297 |
+
### Common Tasks
|
| 1298 |
+
|
| 1299 |
+
| Task | Location | Time | Tip |
|
| 1300 |
+
|------|----------|------|-----|
|
| 1301 |
+
| Generate survey | Generate tab | 30 sec | Be specific in outline |
|
| 1302 |
+
| Translate survey | Translate tab | 1-2 min | Do all languages at once |
|
| 1303 |
+
| Analyze responses | Analyze tab | 1 min | Min. 10 responses needed |
|
| 1304 |
+
| Download results | Each tab | Instant | JSON for data, MD for reports |
|
| 1305 |
+
|
| 1306 |
+
### Quality Checklist
|
| 1307 |
+
|
| 1308 |
+
**Before deploying survey:**
|
| 1309 |
+
- [ ] Questions are unbiased and clear
|
| 1310 |
+
- [ ] Appropriate question types used
|
| 1311 |
+
- [ ] Logical flow through survey
|
| 1312 |
+
- [ ] Introduction explains purpose
|
| 1313 |
+
- [ ] Pilot tested with 3-5 people
|
| 1314 |
+
|
| 1315 |
+
**Before deploying translation:**
|
| 1316 |
+
- [ ] Native speaker reviewed
|
| 1317 |
+
- [ ] Cultural appropriateness checked
|
| 1318 |
+
- [ ] Technical terms verified
|
| 1319 |
+
- [ ] Examples make sense in target culture
|
| 1320 |
+
|
| 1321 |
+
**Before presenting analysis:**
|
| 1322 |
+
- [ ] Sufficient responses (20+)
|
| 1323 |
+
- [ ] Themes make sense with data
|
| 1324 |
+
- [ ] Insights are actionable
|
| 1325 |
+
- [ ] Validated with domain knowledge
|
| 1326 |
+
- [ ] Limitations acknowledged
|
| 1327 |
+
|
| 1328 |
+
---
|
| 1329 |
+
|
| 1330 |
+
**ConversAI** - Transforming qualitative research with AI assistance.
|
| 1331 |
+
|
| 1332 |
+
*Battle the blank page. Reach global audiences. Uncover insights.* 🔬
|
| 1333 |
+
|
| 1334 |
+
---
|
| 1335 |
+
|
| 1336 |
+
**Version**: 1.0
|
| 1337 |
+
**Last Updated**: 2025
|
| 1338 |
+
**License**: MIT
|
| 1339 |
+
**Support**: See TROUBLESHOOTING.md
|
app.py
CHANGED
|
@@ -12,13 +12,24 @@ from llm_backend import LLMBackend, LLMProvider
|
|
| 12 |
from survey_generator import SurveyGenerator
|
| 13 |
from survey_translator import SurveyTranslator
|
| 14 |
from data_analyzer import DataAnalyzer
|
| 15 |
-
from export_utils import save_json_file, survey_to_csv, analysis_to_markdown_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
# Global state for current survey
|
| 19 |
current_survey = None
|
| 20 |
current_responses = []
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
def initialize_backend():
|
| 24 |
"""Initialize LLM backend based on environment"""
|
|
@@ -337,6 +348,196 @@ def load_example_responses():
|
|
| 337 |
return json.dumps(example, indent=2)
|
| 338 |
|
| 339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
# ===========================
|
| 341 |
# Gradio Interface
|
| 342 |
# ===========================
|
|
@@ -523,6 +724,202 @@ See the **About** tab for detailed instructions."""
|
|
| 523 |
outputs=[responses_input]
|
| 524 |
)
|
| 525 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
# ========== ABOUT TAB ==========
|
| 527 |
with gr.Tab("ℹ️ About"):
|
| 528 |
gr.Markdown("""
|
|
|
|
| 12 |
from survey_generator import SurveyGenerator
|
| 13 |
from survey_translator import SurveyTranslator
|
| 14 |
from data_analyzer import DataAnalyzer
|
| 15 |
+
from export_utils import (save_json_file, survey_to_csv, analysis_to_markdown_file,
|
| 16 |
+
conversation_to_transcript, conversation_to_json, conversation_to_csv,
|
| 17 |
+
flow_to_markdown)
|
| 18 |
+
from conversation_flow import ConversationFlow, ConversationNode, create_example_flow
|
| 19 |
+
from conversation_session import ConversationSession, SessionManager
|
| 20 |
+
from conversation_moderator import ConversationModerator
|
| 21 |
|
| 22 |
|
| 23 |
# Global state for current survey
|
| 24 |
current_survey = None
|
| 25 |
current_responses = []
|
| 26 |
|
| 27 |
+
# Global state for conversational research
|
| 28 |
+
current_flow = None
|
| 29 |
+
session_manager = SessionManager()
|
| 30 |
+
current_session = None
|
| 31 |
+
saved_flows = {}
|
| 32 |
+
|
| 33 |
|
| 34 |
def initialize_backend():
|
| 35 |
"""Initialize LLM backend based on environment"""
|
|
|
|
| 348 |
return json.dumps(example, indent=2)
|
| 349 |
|
| 350 |
|
| 351 |
+
# ===========================
|
| 352 |
+
# Conversational Research Handlers
|
| 353 |
+
# ===========================
|
| 354 |
+
|
| 355 |
+
def create_new_flow(flow_name: str, flow_description: str):
|
| 356 |
+
"""Create a new conversation flow"""
|
| 357 |
+
global current_flow, saved_flows
|
| 358 |
+
|
| 359 |
+
if not flow_name or not flow_name.strip():
|
| 360 |
+
return "❌ Please provide a flow name.", "", None
|
| 361 |
+
|
| 362 |
+
try:
|
| 363 |
+
flow = ConversationFlow(name=flow_name, description=flow_description)
|
| 364 |
+
current_flow = flow
|
| 365 |
+
saved_flows[flow.id] = flow
|
| 366 |
+
|
| 367 |
+
return (
|
| 368 |
+
f"✅ Flow '{flow_name}' created successfully!",
|
| 369 |
+
f"**Flow ID:** {flow.id}\n**Name:** {flow.name}\n**Description:** {flow.description}",
|
| 370 |
+
flow.id
|
| 371 |
+
)
|
| 372 |
+
except Exception as e:
|
| 373 |
+
return f"❌ Error creating flow: {str(e)}", "", None
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
def load_example_flow():
|
| 377 |
+
"""Load an example conversation flow"""
|
| 378 |
+
global current_flow, saved_flows
|
| 379 |
+
|
| 380 |
+
flow = create_example_flow()
|
| 381 |
+
current_flow = flow
|
| 382 |
+
saved_flows[flow.id] = flow
|
| 383 |
+
|
| 384 |
+
return (
|
| 385 |
+
f"✅ Example flow loaded: {flow.name}",
|
| 386 |
+
display_flow(flow),
|
| 387 |
+
flow.id
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def add_flow_node(flow_id: str, node_content: str, node_type: str):
|
| 392 |
+
"""Add a node to the current flow"""
|
| 393 |
+
global current_flow, saved_flows
|
| 394 |
+
|
| 395 |
+
if not flow_id:
|
| 396 |
+
return "❌ No flow selected.", ""
|
| 397 |
+
|
| 398 |
+
flow = saved_flows.get(flow_id)
|
| 399 |
+
if not flow:
|
| 400 |
+
return "❌ Flow not found.", ""
|
| 401 |
+
|
| 402 |
+
if not node_content or not node_content.strip():
|
| 403 |
+
return "❌ Please provide content for the node.", ""
|
| 404 |
+
|
| 405 |
+
try:
|
| 406 |
+
node = ConversationNode(content=node_content, node_type=node_type.lower())
|
| 407 |
+
|
| 408 |
+
# Link to previous node if exists
|
| 409 |
+
if flow.nodes:
|
| 410 |
+
last_node = flow.nodes[-1]
|
| 411 |
+
last_node.next = node.id
|
| 412 |
+
|
| 413 |
+
flow.add_node(node)
|
| 414 |
+
current_flow = flow
|
| 415 |
+
|
| 416 |
+
return (
|
| 417 |
+
f"✅ Node added successfully! Total nodes: {len(flow.nodes)}",
|
| 418 |
+
display_flow(flow)
|
| 419 |
+
)
|
| 420 |
+
except Exception as e:
|
| 421 |
+
return f"❌ Error adding node: {str(e)}", ""
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def display_flow(flow: ConversationFlow) -> str:
|
| 425 |
+
"""Display flow as markdown"""
|
| 426 |
+
if not flow or not flow.nodes:
|
| 427 |
+
return "No flow to display"
|
| 428 |
+
|
| 429 |
+
output = f"# {flow.name}\n\n"
|
| 430 |
+
output += f"**Description:** {flow.description}\n\n"
|
| 431 |
+
output += f"**Total Steps:** {len(flow.nodes)}\n\n"
|
| 432 |
+
output += "---\n\n"
|
| 433 |
+
|
| 434 |
+
for i, node in enumerate(flow.nodes, 1):
|
| 435 |
+
output += f"### Step {i}: {node.type.capitalize()}\n\n"
|
| 436 |
+
output += f"{node.content}\n\n"
|
| 437 |
+
|
| 438 |
+
return output
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
def save_current_flow(flow_id: str):
|
| 442 |
+
"""Save the current flow to file"""
|
| 443 |
+
if not flow_id:
|
| 444 |
+
return "❌ No flow selected.", None
|
| 445 |
+
|
| 446 |
+
flow = saved_flows.get(flow_id)
|
| 447 |
+
if not flow:
|
| 448 |
+
return "❌ Flow not found.", None
|
| 449 |
+
|
| 450 |
+
try:
|
| 451 |
+
filepath = save_json_file(flow.to_dict(), "conversation_flow")
|
| 452 |
+
return f"✅ Flow saved to {filepath}", filepath
|
| 453 |
+
except Exception as e:
|
| 454 |
+
return f"❌ Error saving flow: {str(e)}", None
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
def start_conversation_session(flow_id: str):
|
| 458 |
+
"""Start a new conversation session"""
|
| 459 |
+
global current_session, session_manager
|
| 460 |
+
|
| 461 |
+
if not flow_id:
|
| 462 |
+
return [], "❌ Please select a flow first."
|
| 463 |
+
|
| 464 |
+
flow = saved_flows.get(flow_id)
|
| 465 |
+
if not flow:
|
| 466 |
+
return [], "❌ Flow not found."
|
| 467 |
+
|
| 468 |
+
if not llm_backend:
|
| 469 |
+
return [], "❌ LLM backend not initialized."
|
| 470 |
+
|
| 471 |
+
try:
|
| 472 |
+
# Create session
|
| 473 |
+
session = session_manager.create_session(flow_id=flow.id, flow_name=flow.name)
|
| 474 |
+
current_session = session
|
| 475 |
+
|
| 476 |
+
# Create moderator
|
| 477 |
+
moderator = ConversationModerator(llm_backend, flow)
|
| 478 |
+
|
| 479 |
+
# Start conversation
|
| 480 |
+
opening_message = moderator.start_conversation(session)
|
| 481 |
+
|
| 482 |
+
# Return chat history in Gradio format
|
| 483 |
+
return [[None, opening_message]], f"✅ Conversation started! Session ID: {session.id}"
|
| 484 |
+
|
| 485 |
+
except Exception as e:
|
| 486 |
+
return [], f"❌ Error starting conversation: {str(e)}"
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def chat_with_moderator(user_message: str, history: List):
|
| 490 |
+
"""Handle chat messages with the AI moderator"""
|
| 491 |
+
global current_session
|
| 492 |
+
|
| 493 |
+
if not current_session:
|
| 494 |
+
return history, "❌ No active session. Please start a conversation first."
|
| 495 |
+
|
| 496 |
+
if not llm_backend:
|
| 497 |
+
return history, "❌ LLM backend not initialized."
|
| 498 |
+
|
| 499 |
+
if not user_message or not user_message.strip():
|
| 500 |
+
return history, "❌ Please enter a message."
|
| 501 |
+
|
| 502 |
+
try:
|
| 503 |
+
# Get the flow
|
| 504 |
+
flow = saved_flows.get(current_session.flow_id)
|
| 505 |
+
if not flow:
|
| 506 |
+
return history, "❌ Flow not found."
|
| 507 |
+
|
| 508 |
+
# Create moderator
|
| 509 |
+
moderator = ConversationModerator(llm_backend, flow)
|
| 510 |
+
|
| 511 |
+
# Process user response
|
| 512 |
+
ai_response = moderator.process_user_response(current_session, user_message)
|
| 513 |
+
|
| 514 |
+
# Update history
|
| 515 |
+
history.append([user_message, ai_response])
|
| 516 |
+
|
| 517 |
+
status = f"Session: {current_session.id} | Turns: {current_session.get_turn_count()}"
|
| 518 |
+
if current_session.status == "completed":
|
| 519 |
+
status += " | ✅ Conversation completed"
|
| 520 |
+
|
| 521 |
+
return history, status
|
| 522 |
+
|
| 523 |
+
except Exception as e:
|
| 524 |
+
return history, f"❌ Error: {str(e)}"
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
def export_conversation():
|
| 528 |
+
"""Export the current conversation"""
|
| 529 |
+
global current_session
|
| 530 |
+
|
| 531 |
+
if not current_session:
|
| 532 |
+
return "❌ No active session to export.", None
|
| 533 |
+
|
| 534 |
+
try:
|
| 535 |
+
filepath = conversation_to_transcript(current_session)
|
| 536 |
+
return f"✅ Conversation exported to {filepath}", filepath
|
| 537 |
+
except Exception as e:
|
| 538 |
+
return f"❌ Error exporting conversation: {str(e)}", None
|
| 539 |
+
|
| 540 |
+
|
| 541 |
# ===========================
|
| 542 |
# Gradio Interface
|
| 543 |
# ===========================
|
|
|
|
| 724 |
outputs=[responses_input]
|
| 725 |
)
|
| 726 |
|
| 727 |
+
# ========== CONVERSATIONAL RESEARCH TAB ==========
|
| 728 |
+
with gr.Tab("💬 Conversational Research"):
|
| 729 |
+
gr.Markdown("""
|
| 730 |
+
## AI-Moderated Conversations
|
| 731 |
+
Design conversation flows and conduct AI-powered qualitative interviews with respondents.
|
| 732 |
+
""")
|
| 733 |
+
|
| 734 |
+
with gr.Tabs():
|
| 735 |
+
# Design Flow Sub-Tab
|
| 736 |
+
with gr.Tab("🎨 Design Flow"):
|
| 737 |
+
gr.Markdown("""
|
| 738 |
+
### Create Conversation Flows
|
| 739 |
+
Design custom conversation paths for AI-moderated interviews.
|
| 740 |
+
""")
|
| 741 |
+
|
| 742 |
+
with gr.Row():
|
| 743 |
+
with gr.Column(scale=1):
|
| 744 |
+
gr.Markdown("#### Flow Setup")
|
| 745 |
+
|
| 746 |
+
flow_name_input = gr.Textbox(
|
| 747 |
+
label="Flow Name",
|
| 748 |
+
placeholder="e.g., HCP Interview for New Dermatology Product",
|
| 749 |
+
value=""
|
| 750 |
+
)
|
| 751 |
+
|
| 752 |
+
flow_desc_input = gr.Textbox(
|
| 753 |
+
label="Flow Description",
|
| 754 |
+
placeholder="Describe the purpose of this conversation flow...",
|
| 755 |
+
lines=3
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
with gr.Row():
|
| 759 |
+
create_flow_btn = gr.Button("✨ Create New Flow", variant="primary")
|
| 760 |
+
load_example_flow_btn = gr.Button("📋 Load Example", variant="secondary")
|
| 761 |
+
|
| 762 |
+
flow_id_state = gr.State(value="")
|
| 763 |
+
|
| 764 |
+
gr.Markdown("#### Add Steps to Flow")
|
| 765 |
+
|
| 766 |
+
node_content_input = gr.Textbox(
|
| 767 |
+
label="Question/Message",
|
| 768 |
+
placeholder="Enter the question or message for this step...",
|
| 769 |
+
lines=4
|
| 770 |
+
)
|
| 771 |
+
|
| 772 |
+
node_type_input = gr.Radio(
|
| 773 |
+
label="Step Type",
|
| 774 |
+
choices=["Question", "End"],
|
| 775 |
+
value="Question"
|
| 776 |
+
)
|
| 777 |
+
|
| 778 |
+
add_node_btn = gr.Button("➕ Add Step", variant="secondary")
|
| 779 |
+
|
| 780 |
+
save_flow_btn = gr.Button("💾 Save Flow", variant="primary")
|
| 781 |
+
|
| 782 |
+
with gr.Column(scale=1):
|
| 783 |
+
flow_status = gr.Textbox(label="Status", interactive=False)
|
| 784 |
+
flow_display = gr.Markdown(label="Flow Preview", value="No flow created yet")
|
| 785 |
+
|
| 786 |
+
flow_download = gr.File(label="Download Flow JSON", visible=False)
|
| 787 |
+
|
| 788 |
+
# Event handlers for flow design
|
| 789 |
+
create_flow_btn.click(
|
| 790 |
+
fn=create_new_flow,
|
| 791 |
+
inputs=[flow_name_input, flow_desc_input],
|
| 792 |
+
outputs=[flow_status, flow_display, flow_id_state]
|
| 793 |
+
)
|
| 794 |
+
|
| 795 |
+
load_example_flow_btn.click(
|
| 796 |
+
fn=load_example_flow,
|
| 797 |
+
outputs=[flow_status, flow_display, flow_id_state]
|
| 798 |
+
)
|
| 799 |
+
|
| 800 |
+
add_node_btn.click(
|
| 801 |
+
fn=add_flow_node,
|
| 802 |
+
inputs=[flow_id_state, node_content_input, node_type_input],
|
| 803 |
+
outputs=[flow_status, flow_display]
|
| 804 |
+
).then(
|
| 805 |
+
fn=lambda: "",
|
| 806 |
+
outputs=[node_content_input]
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
save_flow_btn.click(
|
| 810 |
+
fn=save_current_flow,
|
| 811 |
+
inputs=[flow_id_state],
|
| 812 |
+
outputs=[flow_status, flow_download]
|
| 813 |
+
).then(
|
| 814 |
+
fn=lambda x: gr.File(value=x, visible=True) if x else gr.File(visible=False),
|
| 815 |
+
inputs=[flow_download],
|
| 816 |
+
outputs=[flow_download]
|
| 817 |
+
)
|
| 818 |
+
|
| 819 |
+
# Conduct Interview Sub-Tab
|
| 820 |
+
with gr.Tab("🎙️ Conduct Interview"):
|
| 821 |
+
gr.Markdown("""
|
| 822 |
+
### AI-Moderated Interview
|
| 823 |
+
Start a conversation session with the AI moderator using your designed flow.
|
| 824 |
+
""")
|
| 825 |
+
|
| 826 |
+
with gr.Row():
|
| 827 |
+
with gr.Column(scale=1):
|
| 828 |
+
conversation_flow_selector = gr.State(value="")
|
| 829 |
+
|
| 830 |
+
gr.Markdown("""
|
| 831 |
+
**Instructions:**
|
| 832 |
+
1. Design a flow in the 'Design Flow' tab first (or load the example)
|
| 833 |
+
2. Click 'Start Conversation' to begin
|
| 834 |
+
3. The AI moderator will ask questions from your flow
|
| 835 |
+
4. The AI adapts with follow-up questions based on responses
|
| 836 |
+
5. Export the conversation when finished
|
| 837 |
+
""")
|
| 838 |
+
|
| 839 |
+
with gr.Row():
|
| 840 |
+
start_conversation_btn = gr.Button("🚀 Start Conversation", variant="primary")
|
| 841 |
+
export_conversation_btn = gr.Button("📥 Export Conversation", variant="secondary")
|
| 842 |
+
|
| 843 |
+
conversation_status = gr.Textbox(label="Session Status", interactive=False)
|
| 844 |
+
conversation_download = gr.File(label="Download Transcript", visible=False)
|
| 845 |
+
|
| 846 |
+
with gr.Column(scale=1):
|
| 847 |
+
chatbot = gr.Chatbot(
|
| 848 |
+
label="AI-Moderated Interview",
|
| 849 |
+
height=500
|
| 850 |
+
)
|
| 851 |
+
|
| 852 |
+
msg_input = gr.Textbox(
|
| 853 |
+
label="Your Response",
|
| 854 |
+
placeholder="Type your response here...",
|
| 855 |
+
lines=2
|
| 856 |
+
)
|
| 857 |
+
|
| 858 |
+
with gr.Row():
|
| 859 |
+
submit_btn = gr.Button("Send", variant="primary")
|
| 860 |
+
clear_btn = gr.Button("Clear")
|
| 861 |
+
|
| 862 |
+
# Chat event handlers
|
| 863 |
+
def user_submit(user_message, history):
|
| 864 |
+
"""Handle user message submission"""
|
| 865 |
+
if not user_message:
|
| 866 |
+
return history, history, ""
|
| 867 |
+
return history, history + [[user_message, None]], ""
|
| 868 |
+
|
| 869 |
+
def bot_respond(history):
|
| 870 |
+
"""Get bot response"""
|
| 871 |
+
if not history or history[-1][1] is not None:
|
| 872 |
+
return history, ""
|
| 873 |
+
|
| 874 |
+
user_msg = history[-1][0]
|
| 875 |
+
updated_history, status = chat_with_moderator(user_msg, history[:-1])
|
| 876 |
+
return updated_history, status
|
| 877 |
+
|
| 878 |
+
# Start conversation
|
| 879 |
+
start_conversation_btn.click(
|
| 880 |
+
fn=lambda: saved_flows[list(saved_flows.keys())[-1]].id if saved_flows else "",
|
| 881 |
+
outputs=[conversation_flow_selector]
|
| 882 |
+
).then(
|
| 883 |
+
fn=start_conversation_session,
|
| 884 |
+
inputs=[conversation_flow_selector],
|
| 885 |
+
outputs=[chatbot, conversation_status]
|
| 886 |
+
)
|
| 887 |
+
|
| 888 |
+
# Message submission
|
| 889 |
+
msg_input.submit(
|
| 890 |
+
fn=user_submit,
|
| 891 |
+
inputs=[msg_input, chatbot],
|
| 892 |
+
outputs=[chatbot, chatbot, msg_input],
|
| 893 |
+
queue=False
|
| 894 |
+
).then(
|
| 895 |
+
fn=bot_respond,
|
| 896 |
+
inputs=[chatbot],
|
| 897 |
+
outputs=[chatbot, conversation_status]
|
| 898 |
+
)
|
| 899 |
+
|
| 900 |
+
submit_btn.click(
|
| 901 |
+
fn=user_submit,
|
| 902 |
+
inputs=[msg_input, chatbot],
|
| 903 |
+
outputs=[chatbot, chatbot, msg_input],
|
| 904 |
+
queue=False
|
| 905 |
+
).then(
|
| 906 |
+
fn=bot_respond,
|
| 907 |
+
inputs=[chatbot],
|
| 908 |
+
outputs=[chatbot, conversation_status]
|
| 909 |
+
)
|
| 910 |
+
|
| 911 |
+
clear_btn.click(lambda: None, None, chatbot, queue=False)
|
| 912 |
+
|
| 913 |
+
# Export conversation
|
| 914 |
+
export_conversation_btn.click(
|
| 915 |
+
fn=export_conversation,
|
| 916 |
+
outputs=[conversation_status, conversation_download]
|
| 917 |
+
).then(
|
| 918 |
+
fn=lambda x: gr.File(value=x, visible=True) if x else gr.File(visible=False),
|
| 919 |
+
inputs=[conversation_download],
|
| 920 |
+
outputs=[conversation_download]
|
| 921 |
+
)
|
| 922 |
+
|
| 923 |
# ========== ABOUT TAB ==========
|
| 924 |
with gr.Tab("ℹ️ About"):
|
| 925 |
gr.Markdown("""
|
conversation_flow.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Conversation Flow Management - Design and store conversation paths
|
| 3 |
+
"""
|
| 4 |
+
import json
|
| 5 |
+
import uuid
|
| 6 |
+
from typing import Dict, List, Optional
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class ConversationNode:
|
| 11 |
+
"""Represents a single node in a conversation flow"""
|
| 12 |
+
|
| 13 |
+
def __init__(self, node_id: str = None, node_type: str = "question",
|
| 14 |
+
content: str = "", next_node: str = None, branches: List[Dict] = None):
|
| 15 |
+
self.id = node_id or str(uuid.uuid4())
|
| 16 |
+
self.type = node_type # "question", "branch", "end"
|
| 17 |
+
self.content = content
|
| 18 |
+
self.next = next_node
|
| 19 |
+
self.branches = branches or [] # For conditional branching
|
| 20 |
+
|
| 21 |
+
def to_dict(self) -> Dict:
|
| 22 |
+
"""Convert node to dictionary"""
|
| 23 |
+
return {
|
| 24 |
+
"id": self.id,
|
| 25 |
+
"type": self.type,
|
| 26 |
+
"content": self.content,
|
| 27 |
+
"next": self.next,
|
| 28 |
+
"branches": self.branches
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def from_dict(cls, data: Dict) -> 'ConversationNode':
|
| 33 |
+
"""Create node from dictionary"""
|
| 34 |
+
return cls(
|
| 35 |
+
node_id=data.get("id"),
|
| 36 |
+
node_type=data.get("type", "question"),
|
| 37 |
+
content=data.get("content", ""),
|
| 38 |
+
next_node=data.get("next"),
|
| 39 |
+
branches=data.get("branches", [])
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class ConversationFlow:
|
| 44 |
+
"""Manages a complete conversation flow"""
|
| 45 |
+
|
| 46 |
+
def __init__(self, flow_id: str = None, name: str = "Untitled Flow",
|
| 47 |
+
description: str = "", nodes: List[ConversationNode] = None):
|
| 48 |
+
self.id = flow_id or str(uuid.uuid4())
|
| 49 |
+
self.name = name
|
| 50 |
+
self.description = description
|
| 51 |
+
self.nodes = nodes or []
|
| 52 |
+
self.created_at = datetime.now().isoformat()
|
| 53 |
+
self.updated_at = datetime.now().isoformat()
|
| 54 |
+
|
| 55 |
+
def add_node(self, node: ConversationNode, position: int = None):
|
| 56 |
+
"""Add a node to the flow"""
|
| 57 |
+
if position is None:
|
| 58 |
+
self.nodes.append(node)
|
| 59 |
+
else:
|
| 60 |
+
self.nodes.insert(position, node)
|
| 61 |
+
self.updated_at = datetime.now().isoformat()
|
| 62 |
+
|
| 63 |
+
def remove_node(self, node_id: str):
|
| 64 |
+
"""Remove a node from the flow"""
|
| 65 |
+
self.nodes = [n for n in self.nodes if n.id != node_id]
|
| 66 |
+
self.updated_at = datetime.now().isoformat()
|
| 67 |
+
|
| 68 |
+
def get_node(self, node_id: str) -> Optional[ConversationNode]:
|
| 69 |
+
"""Get a node by ID"""
|
| 70 |
+
for node in self.nodes:
|
| 71 |
+
if node.id == node_id:
|
| 72 |
+
return node
|
| 73 |
+
return None
|
| 74 |
+
|
| 75 |
+
def get_start_node(self) -> Optional[ConversationNode]:
|
| 76 |
+
"""Get the first node in the flow"""
|
| 77 |
+
return self.nodes[0] if self.nodes else None
|
| 78 |
+
|
| 79 |
+
def reorder_node(self, node_id: str, new_position: int):
|
| 80 |
+
"""Move a node to a different position"""
|
| 81 |
+
node = self.get_node(node_id)
|
| 82 |
+
if node:
|
| 83 |
+
self.nodes.remove(node)
|
| 84 |
+
self.nodes.insert(new_position, node)
|
| 85 |
+
self.updated_at = datetime.now().isoformat()
|
| 86 |
+
|
| 87 |
+
def to_dict(self) -> Dict:
|
| 88 |
+
"""Convert flow to dictionary"""
|
| 89 |
+
return {
|
| 90 |
+
"id": self.id,
|
| 91 |
+
"name": self.name,
|
| 92 |
+
"description": self.description,
|
| 93 |
+
"nodes": [node.to_dict() for node in self.nodes],
|
| 94 |
+
"created_at": self.created_at,
|
| 95 |
+
"updated_at": self.updated_at
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
@classmethod
|
| 99 |
+
def from_dict(cls, data: Dict) -> 'ConversationFlow':
|
| 100 |
+
"""Create flow from dictionary"""
|
| 101 |
+
flow = cls(
|
| 102 |
+
flow_id=data.get("id"),
|
| 103 |
+
name=data.get("name", "Untitled Flow"),
|
| 104 |
+
description=data.get("description", "")
|
| 105 |
+
)
|
| 106 |
+
flow.nodes = [ConversationNode.from_dict(n) for n in data.get("nodes", [])]
|
| 107 |
+
flow.created_at = data.get("created_at", datetime.now().isoformat())
|
| 108 |
+
flow.updated_at = data.get("updated_at", datetime.now().isoformat())
|
| 109 |
+
return flow
|
| 110 |
+
|
| 111 |
+
def save_to_file(self, filepath: str):
|
| 112 |
+
"""Save flow to JSON file"""
|
| 113 |
+
with open(filepath, 'w') as f:
|
| 114 |
+
json.dump(self.to_dict(), f, indent=2)
|
| 115 |
+
|
| 116 |
+
@classmethod
|
| 117 |
+
def load_from_file(cls, filepath: str) -> 'ConversationFlow':
|
| 118 |
+
"""Load flow from JSON file"""
|
| 119 |
+
with open(filepath, 'r') as f:
|
| 120 |
+
data = json.load(f)
|
| 121 |
+
return cls.from_dict(data)
|
| 122 |
+
|
| 123 |
+
def validate(self) -> tuple[bool, str]:
|
| 124 |
+
"""Validate the flow structure"""
|
| 125 |
+
if not self.nodes:
|
| 126 |
+
return False, "Flow must have at least one node"
|
| 127 |
+
|
| 128 |
+
if not self.name or not self.name.strip():
|
| 129 |
+
return False, "Flow must have a name"
|
| 130 |
+
|
| 131 |
+
# Check for orphaned nodes (nodes that can't be reached)
|
| 132 |
+
reachable = set()
|
| 133 |
+
if self.nodes:
|
| 134 |
+
current = self.nodes[0]
|
| 135 |
+
reachable.add(current.id)
|
| 136 |
+
|
| 137 |
+
# Simple validation: check if nodes are in sequence
|
| 138 |
+
for node in self.nodes:
|
| 139 |
+
if not node.content or not node.content.strip():
|
| 140 |
+
return False, f"Node {node.id} has no content"
|
| 141 |
+
|
| 142 |
+
return True, "Flow is valid"
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def create_example_flow() -> ConversationFlow:
|
| 146 |
+
"""Create an example conversation flow"""
|
| 147 |
+
flow = ConversationFlow(
|
| 148 |
+
name="Customer Feedback Interview",
|
| 149 |
+
description="Structured interview to gather customer feedback on product experience"
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
# Add nodes
|
| 153 |
+
node1 = ConversationNode(
|
| 154 |
+
content="Hello! Thank you for taking the time to speak with me today. I'd like to understand your experience with our product. First, can you tell me what initially attracted you to our product?",
|
| 155 |
+
node_type="question"
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
node2 = ConversationNode(
|
| 159 |
+
content="That's interesting. How would you describe your overall experience using the product so far?",
|
| 160 |
+
node_type="question"
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
node3 = ConversationNode(
|
| 164 |
+
content="What specific features do you find most valuable, and why?",
|
| 165 |
+
node_type="question"
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
node4 = ConversationNode(
|
| 169 |
+
content="Have you encountered any challenges or frustrations while using the product? If so, can you describe them?",
|
| 170 |
+
node_type="question"
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
node5 = ConversationNode(
|
| 174 |
+
content="Based on your experience, what improvements or new features would you most like to see?",
|
| 175 |
+
node_type="question"
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
node6 = ConversationNode(
|
| 179 |
+
content="Thank you so much for sharing your thoughts! Your feedback is incredibly valuable and will help us improve the product. Is there anything else you'd like to add?",
|
| 180 |
+
node_type="end"
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
# Link nodes
|
| 184 |
+
node1.next = node2.id
|
| 185 |
+
node2.next = node3.id
|
| 186 |
+
node3.next = node4.id
|
| 187 |
+
node4.next = node5.id
|
| 188 |
+
node5.next = node6.id
|
| 189 |
+
|
| 190 |
+
flow.add_node(node1)
|
| 191 |
+
flow.add_node(node2)
|
| 192 |
+
flow.add_node(node3)
|
| 193 |
+
flow.add_node(node4)
|
| 194 |
+
flow.add_node(node5)
|
| 195 |
+
flow.add_node(node6)
|
| 196 |
+
|
| 197 |
+
return flow
|
conversation_moderator.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Conversation Moderator - AI-powered interview moderator
|
| 3 |
+
"""
|
| 4 |
+
from typing import Dict, List, Optional, Tuple
|
| 5 |
+
from llm_backend import LLMBackend
|
| 6 |
+
from conversation_flow import ConversationFlow, ConversationNode
|
| 7 |
+
from conversation_session import ConversationSession
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class ConversationModerator:
|
| 11 |
+
"""
|
| 12 |
+
AI moderator that conducts conversations based on flows.
|
| 13 |
+
Handles scripted questions, dynamic follow-ups, and probing.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
def __init__(self, llm_backend: LLMBackend, flow: ConversationFlow):
|
| 17 |
+
self.llm = llm_backend
|
| 18 |
+
self.flow = flow
|
| 19 |
+
self.follow_up_threshold = 3 # Ask follow-up every N user responses
|
| 20 |
+
|
| 21 |
+
def start_conversation(self, session: ConversationSession) -> str:
|
| 22 |
+
"""
|
| 23 |
+
Start a conversation by asking the first question.
|
| 24 |
+
|
| 25 |
+
Returns:
|
| 26 |
+
The opening message from the AI
|
| 27 |
+
"""
|
| 28 |
+
first_node = self.flow.get_start_node()
|
| 29 |
+
if not first_node:
|
| 30 |
+
return "I apologize, but there seems to be an issue with the conversation flow."
|
| 31 |
+
|
| 32 |
+
session.current_node_id = first_node.id
|
| 33 |
+
session.add_turn("ai", first_node.content, node_id=first_node.id)
|
| 34 |
+
return first_node.content
|
| 35 |
+
|
| 36 |
+
def process_user_response(self, session: ConversationSession, user_message: str) -> str:
|
| 37 |
+
"""
|
| 38 |
+
Process a user response and generate the next AI message.
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
session: Current conversation session
|
| 42 |
+
user_message: The user's message
|
| 43 |
+
|
| 44 |
+
Returns:
|
| 45 |
+
The AI's response
|
| 46 |
+
"""
|
| 47 |
+
# Add user message to session
|
| 48 |
+
session.add_turn("user", user_message)
|
| 49 |
+
|
| 50 |
+
# Decide whether to ask scripted question or dynamic follow-up
|
| 51 |
+
if self._should_probe(session, user_message):
|
| 52 |
+
# Generate dynamic follow-up question
|
| 53 |
+
ai_response = self._generate_follow_up(session, user_message)
|
| 54 |
+
session.add_turn("ai", ai_response)
|
| 55 |
+
else:
|
| 56 |
+
# Move to next node in flow
|
| 57 |
+
ai_response = self._get_next_scripted_question(session)
|
| 58 |
+
if ai_response:
|
| 59 |
+
session.add_turn("ai", ai_response, node_id=session.current_node_id)
|
| 60 |
+
else:
|
| 61 |
+
# End of flow
|
| 62 |
+
ai_response = self._generate_closing(session)
|
| 63 |
+
session.add_turn("ai", ai_response)
|
| 64 |
+
session.end_session()
|
| 65 |
+
|
| 66 |
+
return ai_response
|
| 67 |
+
|
| 68 |
+
def _should_probe(self, session: ConversationSession, user_message: str) -> bool:
|
| 69 |
+
"""
|
| 70 |
+
Decide if we should probe deeper or continue with scripted questions.
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
True if should ask follow-up, False if should continue flow
|
| 74 |
+
"""
|
| 75 |
+
# Don't probe on very short responses
|
| 76 |
+
if len(user_message.split()) < 5:
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
# Probe every few responses (but not too often)
|
| 80 |
+
user_turns = [t for t in session.conversation_history if t.role == "user"]
|
| 81 |
+
turn_count = len(user_turns)
|
| 82 |
+
|
| 83 |
+
# Probe on turns 2, 5, 8, etc. (every 3 turns, starting after first question)
|
| 84 |
+
if turn_count > 1 and (turn_count - 1) % self.follow_up_threshold == 0:
|
| 85 |
+
return True
|
| 86 |
+
|
| 87 |
+
# Also probe if response contains interesting keywords
|
| 88 |
+
interesting_keywords = [
|
| 89 |
+
"because", "however", "although", "surprisingly", "unfortunately",
|
| 90 |
+
"frustrated", "confused", "excited", "worried", "concerned"
|
| 91 |
+
]
|
| 92 |
+
if any(keyword in user_message.lower() for keyword in interesting_keywords):
|
| 93 |
+
return True
|
| 94 |
+
|
| 95 |
+
return False
|
| 96 |
+
|
| 97 |
+
def _generate_follow_up(self, session: ConversationSession, user_message: str) -> str:
|
| 98 |
+
"""
|
| 99 |
+
Generate a dynamic follow-up question using the LLM.
|
| 100 |
+
|
| 101 |
+
Args:
|
| 102 |
+
session: Current conversation session
|
| 103 |
+
user_message: The user's latest message
|
| 104 |
+
|
| 105 |
+
Returns:
|
| 106 |
+
A follow-up question
|
| 107 |
+
"""
|
| 108 |
+
# Create prompt for generating follow-up
|
| 109 |
+
system_prompt = """You are a professional qualitative research interviewer. Your goal is to probe deeper into the respondent's answers to uncover insights.
|
| 110 |
+
|
| 111 |
+
Generate ONE follow-up question that:
|
| 112 |
+
- Explores an interesting point the respondent mentioned
|
| 113 |
+
- Asks for more detail or clarification
|
| 114 |
+
- Uses phrases like "Tell me more about...", "Can you elaborate on...", "What do you mean by...", "Why do you think..."
|
| 115 |
+
- Is empathetic and non-judgmental
|
| 116 |
+
- Is concise (one sentence)
|
| 117 |
+
|
| 118 |
+
Respond ONLY with the follow-up question, nothing else."""
|
| 119 |
+
|
| 120 |
+
user_prompt = f"""The respondent just said: "{user_message}"
|
| 121 |
+
|
| 122 |
+
Generate a single follow-up question to probe deeper into their response."""
|
| 123 |
+
|
| 124 |
+
messages = [
|
| 125 |
+
{"role": "system", "content": system_prompt},
|
| 126 |
+
{"role": "user", "content": user_prompt}
|
| 127 |
+
]
|
| 128 |
+
|
| 129 |
+
try:
|
| 130 |
+
follow_up = self.llm.generate(messages, max_tokens=100, temperature=0.7)
|
| 131 |
+
# Clean up the response
|
| 132 |
+
follow_up = follow_up.strip().strip('"').strip("'")
|
| 133 |
+
if not follow_up.endswith("?"):
|
| 134 |
+
follow_up += "?"
|
| 135 |
+
return follow_up
|
| 136 |
+
except Exception as e:
|
| 137 |
+
# Fallback to generic follow-up
|
| 138 |
+
return "Can you tell me more about that?"
|
| 139 |
+
|
| 140 |
+
def _get_next_scripted_question(self, session: ConversationSession) -> Optional[str]:
|
| 141 |
+
"""
|
| 142 |
+
Get the next scripted question from the flow.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
The next question, or None if end of flow
|
| 146 |
+
"""
|
| 147 |
+
if not session.current_node_id:
|
| 148 |
+
return None
|
| 149 |
+
|
| 150 |
+
current_node = self.flow.get_node(session.current_node_id)
|
| 151 |
+
if not current_node or not current_node.next:
|
| 152 |
+
return None
|
| 153 |
+
|
| 154 |
+
next_node = self.flow.get_node(current_node.next)
|
| 155 |
+
if not next_node:
|
| 156 |
+
return None
|
| 157 |
+
|
| 158 |
+
session.current_node_id = next_node.id
|
| 159 |
+
return next_node.content
|
| 160 |
+
|
| 161 |
+
def _generate_closing(self, session: ConversationSession) -> str:
|
| 162 |
+
"""
|
| 163 |
+
Generate a closing message for the conversation.
|
| 164 |
+
|
| 165 |
+
Returns:
|
| 166 |
+
Closing message
|
| 167 |
+
"""
|
| 168 |
+
return "Thank you so much for sharing your thoughts with me today. Your insights are incredibly valuable and will help us better understand this topic. Is there anything else you'd like to add before we finish?"
|
| 169 |
+
|
| 170 |
+
def generate_summary(self, session: ConversationSession) -> str:
|
| 171 |
+
"""
|
| 172 |
+
Generate a summary of the conversation using the LLM.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
session: The conversation session to summarize
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
A summary of the conversation
|
| 179 |
+
"""
|
| 180 |
+
# Get conversation transcript
|
| 181 |
+
transcript_parts = []
|
| 182 |
+
for turn in session.conversation_history:
|
| 183 |
+
speaker = "Moderator" if turn.role == "ai" else "Respondent"
|
| 184 |
+
transcript_parts.append(f"{speaker}: {turn.content}")
|
| 185 |
+
|
| 186 |
+
transcript = "\n".join(transcript_parts)
|
| 187 |
+
|
| 188 |
+
system_prompt = """You are analyzing a qualitative research interview. Generate a concise summary that captures:
|
| 189 |
+
1. The main topics discussed
|
| 190 |
+
2. Key insights or themes from the respondent
|
| 191 |
+
3. Notable quotes or moments
|
| 192 |
+
4. Overall sentiment
|
| 193 |
+
|
| 194 |
+
Keep the summary to 3-4 paragraphs."""
|
| 195 |
+
|
| 196 |
+
user_prompt = f"""Summarize this interview:
|
| 197 |
+
|
| 198 |
+
{transcript}
|
| 199 |
+
|
| 200 |
+
Provide a professional summary suitable for a research report."""
|
| 201 |
+
|
| 202 |
+
messages = [
|
| 203 |
+
{"role": "system", "content": system_prompt},
|
| 204 |
+
{"role": "user", "content": user_prompt}
|
| 205 |
+
]
|
| 206 |
+
|
| 207 |
+
try:
|
| 208 |
+
summary = self.llm.generate(messages, max_tokens=500, temperature=0.5)
|
| 209 |
+
return summary.strip()
|
| 210 |
+
except Exception as e:
|
| 211 |
+
return f"Summary generation failed: {str(e)}"
|
| 212 |
+
|
| 213 |
+
def reflect_understanding(self, session: ConversationSession) -> str:
|
| 214 |
+
"""
|
| 215 |
+
Periodically reflect back understanding to the respondent.
|
| 216 |
+
|
| 217 |
+
Returns:
|
| 218 |
+
A reflection statement
|
| 219 |
+
"""
|
| 220 |
+
recent_turns = [t for t in session.conversation_history if t.role == "user"][-3:]
|
| 221 |
+
if not recent_turns:
|
| 222 |
+
return "Let me make sure I understand you correctly..."
|
| 223 |
+
|
| 224 |
+
recent_content = " ".join([t.content for t in recent_turns])
|
| 225 |
+
|
| 226 |
+
system_prompt = """You are a research interviewer reflecting back what you've heard. Create a brief summary (1-2 sentences) of what the respondent has shared, then ask if you understood correctly.
|
| 227 |
+
|
| 228 |
+
Format: "So if I understand correctly, [summary]. Is that right?" """
|
| 229 |
+
|
| 230 |
+
user_prompt = f"""The respondent recently said: "{recent_content}"
|
| 231 |
+
|
| 232 |
+
Reflect back your understanding and ask for confirmation."""
|
| 233 |
+
|
| 234 |
+
messages = [
|
| 235 |
+
{"role": "system", "content": system_prompt},
|
| 236 |
+
{"role": "user", "content": user_prompt}
|
| 237 |
+
]
|
| 238 |
+
|
| 239 |
+
try:
|
| 240 |
+
reflection = self.llm.generate(messages, max_tokens=150, temperature=0.5)
|
| 241 |
+
return reflection.strip()
|
| 242 |
+
except Exception as e:
|
| 243 |
+
return "Let me make sure I understand you correctly - can you confirm that I've captured your main points accurately?"
|
conversation_session.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Conversation Session Management - Track live conversations
|
| 3 |
+
"""
|
| 4 |
+
import json
|
| 5 |
+
import uuid
|
| 6 |
+
from typing import Dict, List, Optional
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class ConversationTurn:
|
| 11 |
+
"""Represents a single turn in a conversation"""
|
| 12 |
+
|
| 13 |
+
def __init__(self, role: str, content: str, timestamp: str = None,
|
| 14 |
+
node_id: str = None, summary: str = None):
|
| 15 |
+
self.role = role # "ai" or "user"
|
| 16 |
+
self.content = content
|
| 17 |
+
self.timestamp = timestamp or datetime.now().isoformat()
|
| 18 |
+
self.node_id = node_id # Which node in the flow this relates to
|
| 19 |
+
self.summary = summary # AI's summary of this turn
|
| 20 |
+
|
| 21 |
+
def to_dict(self) -> Dict:
|
| 22 |
+
"""Convert turn to dictionary"""
|
| 23 |
+
return {
|
| 24 |
+
"role": self.role,
|
| 25 |
+
"content": self.content,
|
| 26 |
+
"timestamp": self.timestamp,
|
| 27 |
+
"node_id": self.node_id,
|
| 28 |
+
"summary": self.summary
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def from_dict(cls, data: Dict) -> 'ConversationTurn':
|
| 33 |
+
"""Create turn from dictionary"""
|
| 34 |
+
return cls(
|
| 35 |
+
role=data.get("role"),
|
| 36 |
+
content=data.get("content"),
|
| 37 |
+
timestamp=data.get("timestamp"),
|
| 38 |
+
node_id=data.get("node_id"),
|
| 39 |
+
summary=data.get("summary")
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class ConversationSession:
|
| 44 |
+
"""Manages a live conversation session"""
|
| 45 |
+
|
| 46 |
+
def __init__(self, session_id: str = None, flow_id: str = None,
|
| 47 |
+
respondent_id: str = None, flow_name: str = ""):
|
| 48 |
+
self.id = session_id or str(uuid.uuid4())
|
| 49 |
+
self.flow_id = flow_id
|
| 50 |
+
self.flow_name = flow_name
|
| 51 |
+
self.respondent_id = respondent_id or f"respondent_{uuid.uuid4().hex[:8]}"
|
| 52 |
+
self.conversation_history: List[ConversationTurn] = []
|
| 53 |
+
self.current_node_id: Optional[str] = None
|
| 54 |
+
self.started_at = datetime.now().isoformat()
|
| 55 |
+
self.ended_at: Optional[str] = None
|
| 56 |
+
self.status = "active" # "active", "completed", "abandoned"
|
| 57 |
+
self.metadata = {}
|
| 58 |
+
|
| 59 |
+
def add_turn(self, role: str, content: str, node_id: str = None, summary: str = None):
|
| 60 |
+
"""Add a turn to the conversation"""
|
| 61 |
+
turn = ConversationTurn(
|
| 62 |
+
role=role,
|
| 63 |
+
content=content,
|
| 64 |
+
node_id=node_id,
|
| 65 |
+
summary=summary
|
| 66 |
+
)
|
| 67 |
+
self.conversation_history.append(turn)
|
| 68 |
+
|
| 69 |
+
def get_conversation_for_llm(self) -> List[Dict[str, str]]:
|
| 70 |
+
"""Get conversation history in format suitable for LLM"""
|
| 71 |
+
messages = []
|
| 72 |
+
for turn in self.conversation_history:
|
| 73 |
+
messages.append({
|
| 74 |
+
"role": "assistant" if turn.role == "ai" else "user",
|
| 75 |
+
"content": turn.content
|
| 76 |
+
})
|
| 77 |
+
return messages
|
| 78 |
+
|
| 79 |
+
def get_last_user_message(self) -> Optional[str]:
|
| 80 |
+
"""Get the most recent user message"""
|
| 81 |
+
for turn in reversed(self.conversation_history):
|
| 82 |
+
if turn.role == "user":
|
| 83 |
+
return turn.content
|
| 84 |
+
return None
|
| 85 |
+
|
| 86 |
+
def get_turn_count(self) -> int:
|
| 87 |
+
"""Get total number of turns"""
|
| 88 |
+
return len(self.conversation_history)
|
| 89 |
+
|
| 90 |
+
def end_session(self):
|
| 91 |
+
"""Mark session as completed"""
|
| 92 |
+
self.status = "completed"
|
| 93 |
+
self.ended_at = datetime.now().isoformat()
|
| 94 |
+
|
| 95 |
+
def abandon_session(self):
|
| 96 |
+
"""Mark session as abandoned"""
|
| 97 |
+
self.status = "abandoned"
|
| 98 |
+
self.ended_at = datetime.now().isoformat()
|
| 99 |
+
|
| 100 |
+
def to_dict(self) -> Dict:
|
| 101 |
+
"""Convert session to dictionary"""
|
| 102 |
+
return {
|
| 103 |
+
"id": self.id,
|
| 104 |
+
"flow_id": self.flow_id,
|
| 105 |
+
"flow_name": self.flow_name,
|
| 106 |
+
"respondent_id": self.respondent_id,
|
| 107 |
+
"conversation_history": [turn.to_dict() for turn in self.conversation_history],
|
| 108 |
+
"current_node_id": self.current_node_id,
|
| 109 |
+
"started_at": self.started_at,
|
| 110 |
+
"ended_at": self.ended_at,
|
| 111 |
+
"status": self.status,
|
| 112 |
+
"metadata": self.metadata
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
@classmethod
|
| 116 |
+
def from_dict(cls, data: Dict) -> 'ConversationSession':
|
| 117 |
+
"""Create session from dictionary"""
|
| 118 |
+
session = cls(
|
| 119 |
+
session_id=data.get("id"),
|
| 120 |
+
flow_id=data.get("flow_id"),
|
| 121 |
+
respondent_id=data.get("respondent_id"),
|
| 122 |
+
flow_name=data.get("flow_name", "")
|
| 123 |
+
)
|
| 124 |
+
session.conversation_history = [
|
| 125 |
+
ConversationTurn.from_dict(t) for t in data.get("conversation_history", [])
|
| 126 |
+
]
|
| 127 |
+
session.current_node_id = data.get("current_node_id")
|
| 128 |
+
session.started_at = data.get("started_at", datetime.now().isoformat())
|
| 129 |
+
session.ended_at = data.get("ended_at")
|
| 130 |
+
session.status = data.get("status", "active")
|
| 131 |
+
session.metadata = data.get("metadata", {})
|
| 132 |
+
return session
|
| 133 |
+
|
| 134 |
+
def save_to_file(self, filepath: str):
|
| 135 |
+
"""Save session to JSON file"""
|
| 136 |
+
with open(filepath, 'w') as f:
|
| 137 |
+
json.dump(self.to_dict(), f, indent=2)
|
| 138 |
+
|
| 139 |
+
@classmethod
|
| 140 |
+
def load_from_file(cls, filepath: str) -> 'ConversationSession':
|
| 141 |
+
"""Load session from JSON file"""
|
| 142 |
+
with open(filepath, 'r') as f:
|
| 143 |
+
data = json.load(f)
|
| 144 |
+
return cls.from_dict(data)
|
| 145 |
+
|
| 146 |
+
def get_transcript(self) -> str:
|
| 147 |
+
"""Get conversation as readable transcript"""
|
| 148 |
+
lines = []
|
| 149 |
+
lines.append(f"Conversation Session: {self.id}")
|
| 150 |
+
lines.append(f"Flow: {self.flow_name}")
|
| 151 |
+
lines.append(f"Respondent: {self.respondent_id}")
|
| 152 |
+
lines.append(f"Started: {self.started_at}")
|
| 153 |
+
if self.ended_at:
|
| 154 |
+
lines.append(f"Ended: {self.ended_at}")
|
| 155 |
+
lines.append(f"Status: {self.status}")
|
| 156 |
+
lines.append("\n" + "="*60 + "\n")
|
| 157 |
+
|
| 158 |
+
for i, turn in enumerate(self.conversation_history, 1):
|
| 159 |
+
speaker = "AI Moderator" if turn.role == "ai" else "Respondent"
|
| 160 |
+
lines.append(f"[{i}] {speaker} ({turn.timestamp}):")
|
| 161 |
+
lines.append(f"{turn.content}\n")
|
| 162 |
+
|
| 163 |
+
if turn.summary:
|
| 164 |
+
lines.append(f" Summary: {turn.summary}\n")
|
| 165 |
+
|
| 166 |
+
return "\n".join(lines)
|
| 167 |
+
|
| 168 |
+
def get_summary_stats(self) -> Dict:
|
| 169 |
+
"""Get summary statistics about the session"""
|
| 170 |
+
user_turns = [t for t in self.conversation_history if t.role == "user"]
|
| 171 |
+
ai_turns = [t for t in self.conversation_history if t.role == "ai"]
|
| 172 |
+
|
| 173 |
+
return {
|
| 174 |
+
"total_turns": len(self.conversation_history),
|
| 175 |
+
"user_turns": len(user_turns),
|
| 176 |
+
"ai_turns": len(ai_turns),
|
| 177 |
+
"avg_user_response_length": sum(len(t.content) for t in user_turns) / max(len(user_turns), 1),
|
| 178 |
+
"duration_minutes": self._calculate_duration_minutes(),
|
| 179 |
+
"status": self.status
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
def _calculate_duration_minutes(self) -> float:
|
| 183 |
+
"""Calculate session duration in minutes"""
|
| 184 |
+
if not self.ended_at:
|
| 185 |
+
end_time = datetime.now()
|
| 186 |
+
else:
|
| 187 |
+
end_time = datetime.fromisoformat(self.ended_at)
|
| 188 |
+
|
| 189 |
+
start_time = datetime.fromisoformat(self.started_at)
|
| 190 |
+
duration = (end_time - start_time).total_seconds() / 60
|
| 191 |
+
return round(duration, 2)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
class SessionManager:
|
| 195 |
+
"""Manages multiple conversation sessions"""
|
| 196 |
+
|
| 197 |
+
def __init__(self):
|
| 198 |
+
self.sessions: Dict[str, ConversationSession] = {}
|
| 199 |
+
|
| 200 |
+
def create_session(self, flow_id: str, flow_name: str = "", respondent_id: str = None) -> ConversationSession:
|
| 201 |
+
"""Create a new session"""
|
| 202 |
+
session = ConversationSession(
|
| 203 |
+
flow_id=flow_id,
|
| 204 |
+
flow_name=flow_name,
|
| 205 |
+
respondent_id=respondent_id
|
| 206 |
+
)
|
| 207 |
+
self.sessions[session.id] = session
|
| 208 |
+
return session
|
| 209 |
+
|
| 210 |
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
| 211 |
+
"""Get a session by ID"""
|
| 212 |
+
return self.sessions.get(session_id)
|
| 213 |
+
|
| 214 |
+
def get_active_sessions(self) -> List[ConversationSession]:
|
| 215 |
+
"""Get all active sessions"""
|
| 216 |
+
return [s for s in self.sessions.values() if s.status == "active"]
|
| 217 |
+
|
| 218 |
+
def get_all_sessions(self) -> List[ConversationSession]:
|
| 219 |
+
"""Get all sessions"""
|
| 220 |
+
return list(self.sessions.values())
|
| 221 |
+
|
| 222 |
+
def end_session(self, session_id: str):
|
| 223 |
+
"""End a session"""
|
| 224 |
+
session = self.sessions.get(session_id)
|
| 225 |
+
if session:
|
| 226 |
+
session.end_session()
|
export_utils.py
CHANGED
|
@@ -136,3 +136,108 @@ def create_survey_package(survey_data: Dict) -> Dict[str, str]:
|
|
| 136 |
package['csv'] = survey_to_csv(survey_data)
|
| 137 |
|
| 138 |
return package
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
package['csv'] = survey_to_csv(survey_data)
|
| 137 |
|
| 138 |
return package
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def conversation_to_transcript(conversation_session) -> str:
|
| 142 |
+
"""
|
| 143 |
+
Export conversation session as readable text transcript.
|
| 144 |
+
|
| 145 |
+
Args:
|
| 146 |
+
conversation_session: ConversationSession object
|
| 147 |
+
|
| 148 |
+
Returns:
|
| 149 |
+
Path to transcript file
|
| 150 |
+
"""
|
| 151 |
+
transcript = conversation_session.get_transcript()
|
| 152 |
+
|
| 153 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 154 |
+
filename = f"conversation_transcript_{timestamp}.txt"
|
| 155 |
+
|
| 156 |
+
with open(filename, 'w', encoding='utf-8') as f:
|
| 157 |
+
f.write(transcript)
|
| 158 |
+
|
| 159 |
+
return filename
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def conversation_to_json(conversation_session) -> str:
|
| 163 |
+
"""
|
| 164 |
+
Export conversation session as JSON.
|
| 165 |
+
|
| 166 |
+
Args:
|
| 167 |
+
conversation_session: ConversationSession object
|
| 168 |
+
|
| 169 |
+
Returns:
|
| 170 |
+
Path to JSON file
|
| 171 |
+
"""
|
| 172 |
+
return save_json_file(conversation_session.to_dict(), "conversation_session")
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def conversation_to_csv(conversation_session) -> str:
|
| 176 |
+
"""
|
| 177 |
+
Export conversation turns as CSV.
|
| 178 |
+
|
| 179 |
+
Args:
|
| 180 |
+
conversation_session: ConversationSession object
|
| 181 |
+
|
| 182 |
+
Returns:
|
| 183 |
+
Path to CSV file
|
| 184 |
+
"""
|
| 185 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 186 |
+
filename = f"conversation_{timestamp}.csv"
|
| 187 |
+
|
| 188 |
+
with open(filename, 'w', newline='', encoding='utf-8') as f:
|
| 189 |
+
writer = csv.writer(f)
|
| 190 |
+
|
| 191 |
+
# Write header
|
| 192 |
+
writer.writerow(['Turn', 'Speaker', 'Timestamp', 'Content', 'Node ID', 'Summary'])
|
| 193 |
+
|
| 194 |
+
# Write turns
|
| 195 |
+
for i, turn in enumerate(conversation_session.conversation_history, 1):
|
| 196 |
+
speaker = "AI Moderator" if turn.role == "ai" else "Respondent"
|
| 197 |
+
writer.writerow([
|
| 198 |
+
i,
|
| 199 |
+
speaker,
|
| 200 |
+
turn.timestamp,
|
| 201 |
+
turn.content,
|
| 202 |
+
turn.node_id or '',
|
| 203 |
+
turn.summary or ''
|
| 204 |
+
])
|
| 205 |
+
|
| 206 |
+
return filename
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def flow_to_markdown(conversation_flow) -> str:
|
| 210 |
+
"""
|
| 211 |
+
Export conversation flow as markdown document.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
conversation_flow: ConversationFlow object
|
| 215 |
+
|
| 216 |
+
Returns:
|
| 217 |
+
Path to markdown file
|
| 218 |
+
"""
|
| 219 |
+
lines = []
|
| 220 |
+
lines.append(f"# {conversation_flow.name}\n")
|
| 221 |
+
lines.append(f"**Description:** {conversation_flow.description}\n")
|
| 222 |
+
lines.append(f"**Created:** {conversation_flow.created_at}")
|
| 223 |
+
lines.append(f"**Updated:** {conversation_flow.updated_at}\n")
|
| 224 |
+
lines.append("\n## Conversation Flow\n")
|
| 225 |
+
|
| 226 |
+
for i, node in enumerate(conversation_flow.nodes, 1):
|
| 227 |
+
lines.append(f"### Step {i}: {node.type.capitalize()}\n")
|
| 228 |
+
lines.append(f"**Content:** {node.content}\n")
|
| 229 |
+
if node.next:
|
| 230 |
+
lines.append(f"**Next Node:** {node.next}\n")
|
| 231 |
+
if node.branches:
|
| 232 |
+
lines.append(f"**Branches:** {len(node.branches)}\n")
|
| 233 |
+
lines.append("")
|
| 234 |
+
|
| 235 |
+
content = "\n".join(lines)
|
| 236 |
+
|
| 237 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 238 |
+
filename = f"conversation_flow_{timestamp}.md"
|
| 239 |
+
|
| 240 |
+
with open(filename, 'w', encoding='utf-8') as f:
|
| 241 |
+
f.write(content)
|
| 242 |
+
|
| 243 |
+
return filename
|