Database Schema¶
This document describes the database schema for ChatbotLab’s backend.
The database is implemented in MySQL and stores information about
bots, conversations, utterances, and keystrokes. Each table is prefixed
with chatbot_ and linked by foreign keys for data integrity.
Bots Table¶
Table name: chatbot_bot
This table defines each chatbot available in the system and stores its configuration, prompt, and assigned model.
Column |
Type / Key |
Description |
|---|---|---|
|
PK |
Unique ID of the bot. |
|
VARCHAR, unique |
Descriptive name of the bot (must be unique). Use a |
|
TEXT |
Prompt describing how the bot should behave. |
|
FK → chatbot_model |
The AI model (provider + model ID) this bot uses. |
|
VARCHAR |
Legacy provider/model-ID fields, superseded by |
|
TEXT |
Optional initial message sent when a conversation starts. |
|
VARCHAR |
|
|
TEXT |
Prompt used to generate the bot’s avatar image; falls back to a default prompt when empty. |
|
BOOLEAN |
If true, split responses into human-like chunks; if false, send each response as one block. |
|
BOOLEAN |
If true, apply human-like typing/reading delays; if false, show messages instantly. |
|
FLOAT |
Parameters controlling the simulated delay before the bot “reads” an incoming message, in seconds (jitter/thinking bounds) or words/minute. |
|
FLOAT |
Parameters controlling the simulated delay before the bot “writes” its reply, in seconds (jitter/thinking bounds) or words/minute. |
|
FLOAT |
Delay range between chunked message parts, in seconds. Only applies
when |
|
FLOAT |
Minimum reading delay applied regardless of message length, in seconds. |
|
BOOLEAN |
If true, the bot sends follow-up messages when the participant goes idle. |
|
INT |
Minutes of inactivity before a participant is considered idle. |
|
TEXT |
Instructions used to generate follow-up messages while idle. |
|
BOOLEAN |
If true, keep sending follow-ups for the duration of an idle period; if false, send at most one per idle period. |
|
INT |
Max messages included in the chat history sent to the LLM. |
|
DECIMAL(3,2) |
Per-category moderation thresholds (0.0-1.0, lower = stricter). See Content Moderation for what each category means and how thresholds are applied. |
|
M2M → chatbot_persona |
Personas this bot can embody, via the |
Conversations Table¶
Table name: chatbot_conversation
Stores high-level metadata for each chat session, including study context, participant identifiers, and Qualtrics linkage.
Column |
Type / Key |
Description |
|---|---|---|
|
PK |
Auto-incremented unique ID for each conversation. |
|
VARCHAR, unique |
Response ID from Qualtrics (or equivalent) representing the unique session. |
|
VARCHAR |
Name of the bot the participant interacted with. Stored as a plain
string, not a foreign key to |
|
TEXT (JSON) |
Snapshot of the bot’s configuration (model, personas, delay settings, etc.) captured when the conversation started. This is how “which model was actually used” survives later edits to the bot. |
|
VARCHAR |
Participant ID (fetched from Qualtrics or Prolific). |
|
VARCHAR |
Optional name of the study. |
|
VARCHAR |
Qualtrics condition or randomized group assignment. |
|
DATETIME |
Timestamp when the conversation began. |
|
VARCHAR |
The initial message if the bot began the conversation. |
|
VARCHAR |
Qualtrics |
|
TEXT (JSON) |
Full Qualtrics metadata payload received at start. |
|
FK → chatbot_persona |
The persona randomly selected for this conversation, if the bot has
any personas configured; |
—
Utterances Table¶
Table name: chatbot_utterance
Each row corresponds to a single message (utterance) in a conversation.
Column |
Type / Key |
Description |
|---|---|---|
|
PK |
Unique ID for each utterance. |
|
VARCHAR |
Who sent the message: |
|
VARCHAR |
Name of the bot in this conversation. |
|
FK → chatbot_conversation |
Conversation foreign key. |
|
VARCHAR |
Qualtrics or Prolific participant ID. |
|
TEXT |
Content of the utterance. |
|
DATETIME |
When the utterance was created. |
|
VARCHAR (URL) |
Link to audio file stored in S3 (if applicable). |
|
BOOLEAN |
Indicates whether the utterance includes voice (1 = True). |
|
TEXT |
System prompt (bot prompt + persona) sent to the LLM. |
|
TEXT (JSON) |
Chat history that was actually passed to the LLM. |
|
VARCHAR |
Moderation category that blocked this exchange (e.g. |
|
JSON |
Full category → score map returned by the moderation API for a
blocked message, including categories that passed. Recorded on the
participant’s row only; |
Note
When a message is blocked, no LLM call is made. Two rows are written: the
participant’s message and a fixed warning reply. Both carry
moderation_category, so WHERE moderation_category IS NOT NULL
returns the complete exchange; to exclude canned warnings from a
transcript, drop the assistant rows where that column is set.
moderation_scores is set on the participant’s row only, since that is
the message the scores describe. Blocks that predate these columns are
labeled unknown — their real category and scores were never recorded.
—
Keystrokes Table¶
Table name: chatbot_keystroke
Captures timing and engagement metrics for each chat session, providing context about user attention and typing behavior.
Column |
Type / Key |
Description |
|---|---|---|
|
PK |
Unique ID for each keystroke record. |
|
VARCHAR |
Matches |
|
FLOAT |
Time (in seconds) the participant spent on the chat page. |
|
FLOAT |
Time (in seconds) the participant was away from the page. |
|
INT |
Number of keys pressed during the chat. |
|
DATETIME |
When the keystroke data was recorded (set by the client, not auto-generated on write). |
—
Schema Relationships¶
Each conversation references one bot via
bot_name— a plain string match, not an enforced foreign key.Each utterance belongs to one conversation, via the real foreign key
conversation_id.Each keystroke record is associated with a conversation by matching
conversation_idvalues, but this is not a foreign key relationship — see the note on the Keystrokes table above.Participants and studies can be linked across sessions by
participant_idorstudy_name.
Note
moderation_category and moderation_scores on chatbot_utterance
are documented here as they will exist once PR #192 merges; they are not
yet present on main.
The diagram below abbreviates chatbot_bot to a representative subset of
columns for readability — see the full Bots Table above for all fields.
erDiagram
chatbot_bot ||..o{ chatbot_conversation : "bot_name (string match, not FK)"
chatbot_conversation ||--o{ chatbot_utterance : "conversation_id (FK)"
chatbot_conversation ||..o{ chatbot_keystroke : "conversation_id (string match, not FK)"
chatbot_bot {
int id PK
varchar name UK
text prompt
int ai_model FK
varchar model_id
varchar model_type
text initial_utterance
varchar avatar_type
text avatar_prompt
boolean chunk_messages
boolean humanlike_delay
float reading_words_per_minute
float writing_words_per_minute
int max_transcript_length
decimal moderation_harassment
}
chatbot_conversation {
int id PK
varchar conversation_id UK
varchar bot_name
text bot_config
varchar participant_id
varchar study_name
varchar user_group
datetime started_time
varchar initial_utterance
varchar survey_id
text survey_meta_data
int selected_persona FK
}
chatbot_utterance {
int id PK
varchar speaker_id
varchar bot_name
int conversation_id FK
varchar participant_id
text text
datetime created_time
varchar audio_file
boolean is_voice
text instruction_prompt
json chat_history_used
varchar moderation_category
json moderation_scores
}
chatbot_keystroke {
int id PK
varchar conversation_id
float total_time_on_page
float total_time_away_from_page
int keystroke_count
datetime timestamp
}