Add chat history and viewer memory to an AI bot
Store recent Twitch chat, preserve per-viewer conversations, and maintain compact long-term profiles for AI replies.
AlphaResult
The basic AI bot can answer one !bot command at a time, but every request starts without knowledge of the surrounding chat or the viewer's previous conversations. In this guide, you will add both short-term and long-term context with Streaming Hub's Database and Context Storage actions.
The finished setup uses two automations:
- Chat Logging to Context records every Twitch chat message and keeps a rolling recent-chat window.
- AI Bot call reads that shared chat, preserves a separate dialogue for each viewer, and periodically condenses older dialogue into a compact viewer profile.
Three files carry the memory:
| File | Purpose | Retention |
|---|---|---|
Chat/RecentChat.jsonl | Recent messages from the entire Twitch chat | Last 40 messages |
Chat/{event.user.id}.jsonl | Dialogue between one viewer and the AI bot | Summarized at 15 entries, then trimmed to 5 |
Chat/{event.user.id}_profile.jsonl | Compact long-term viewer profile | Rewritten after each summary |
Before you start
Complete Build an AI chat bot for Twitch first. Its AI Bot call automation, Twitch connection, AI profile, Call and Store, and Send Message actions are the starting point for this guide.
Step 1. Create a Context Root
Open the Database tab and select Manage Roots. Add a new Root with these settings:
| Field | Value |
|---|---|
| Display Name | Context |
| Root Id | context |
| Absolute Path | A private local folder of your choice |
| Enabled | On |
Select Validate if you want to confirm that the folder is writable, then select OK.
The Root defines the safe base folder. Every relative path used later is created underneath it.
Step 2. Capture every Twitch chat message
Return to Automation and create a new automation named Chat Logging to Context. Set its signal to Twitch Chat Message and choose Any for Text Match Mode.
This signal runs for each incoming Twitch chat message, regardless of its contents.
Step 3. Add the chat storage actions
Delete the default Log action, then add two Data actions in this order:
- Append JSONL
- Trim JSONL
For both actions, select the Context Root and use this Path:
Chat/RecentChat.jsonl
You do not need to create the Chat folder or file manually. Append JSONL creates both when the automation receives its first message.
Step 4. Store a structured chat record
In Append JSONL, enter this value in JSON Object:
Recent chat JSON object
{
"time": "{event.timestampUtc}",
"userId": "{event.user.id}",
"userName": "{event.user.name}",
"text": "{event.text}"
}Each run appends one compact JSON object as a new line. JSONL is useful here because the automation can add and trim individual messages without rewriting the entire history.
Step 5. Limit the shared chat history
Create a Number variable named chat_context_length and set it to 40.
In Trim JSONL, set Keep Last to:
{vars.chat_context_length}
The automation now appends the newest message and immediately removes entries beyond the selected limit.
Step 6. Save each viewer's conversation
Return to AI Bot call. After Send Message, add Append JSONL with these settings:
| Field | Value |
|---|---|
| Root | Context |
| Path | Chat/{event.user.id}.jsonl |
Use this JSON Object:
Per-viewer dialogue JSON object
{
"time": "{event.timestampUtc}",
"viewer": "{event.text}",
"llm": "{runtime.ai.last.text}"
}The viewer ID in the Path gives every viewer a separate dialogue file. Each entry stores the triggering message together with the AI reply produced during the same run.
Step 7. Attach the stored context to AI replies
Open the first Call and Store action, expand Advanced Settings, and add two Context Files:
| Source | Root | Path |
|---|---|---|
| Context Storage File | Context | Chat/RecentChat.jsonl |
| Context Storage File | Context | Chat/{event.user.id}.jsonl |
Streaming Hub attaches these files to the model request as contextual sections of the System Prompt. The model can now see both the wider conversation and this viewer's previous exchanges.
Step 8. Try a simple per-viewer limit
A viewer's dialogue file will otherwise grow indefinitely. The simple solution is to add Trim JSONL after the per-viewer Append JSONL:
| Field | Value |
|---|---|
| Root | Context |
| Path | Chat/{event.user.id}.jsonl |
| Keep Last | {vars.dialog_context_length} |
Create a Number variable named dialog_context_length and initially set it to 10.
For easier navigation, group chat_context_length and dialog_context_length under a variable group named Chat_Context_Length.
This approach bounds the file, but it permanently discards older dialogue. The remaining steps replace it with profile summarization.
Step 9. Count dialogue before summarizing it
Disable the simple per-viewer Trim JSONL action from Step 8. Add Count JSON/JSONL after the per-viewer Append JSONL:
| Field | Value |
|---|---|
| Root | Context |
| Path | Chat/{event.user.id}.jsonl |
| Format | Auto |
| Count Target Path | runtime.context.last.count |
This action stores the number of dialogue entries in Runtime so the automation can decide when summarization is necessary.
Step 10. Run profile maintenance at a threshold
Change dialog_context_length to 15. Add an If action after Count JSON/JSONL with this condition:
| Left | Operator | Right |
|---|---|---|
runtime.context.last.count | GreaterOrEqual | {vars.dialog_context_length} |
The profile-maintenance branch runs only when the current viewer reaches 15 stored exchanges.
Step 11. Build the profile-maintenance branch
Inside the If body, add these actions in order:
- Call and Store
- Trim JSONL
- Rewrite JSON
Streaming Hub closes the branch with End If. The model first creates an updated profile, the raw dialogue is then reduced, and the profile file is finally replaced with the new JSON.
Step 12. Generate and store the viewer profile
Configure the new Call and Store action with the same AI profile used by the reply action.
Use this Prompt:
Viewer profile update prompt
Review the attached existing viewer profile and recent dialogue.
Update the viewer profile using only reliable information found in those sources. Preserve important existing information, add useful confirmed details, remove duplication, and discard minor or temporary details.
Return the complete updated profile as the required JSON object.Use this System Prompt:
Viewer profile system prompt
You maintain a compact long-term profile of a livestream viewer.
The attached context contains:
1. The viewer's existing profile, which may be empty or missing.
2. Recent dialogue between the viewer and the assistant.
Create an updated profile by merging reliable new information from the dialogue into the existing profile.
Rules:
* Preserve important information from the existing profile unless the recent dialogue clearly corrects or contradicts it.
* Prefer stable, reusable information that may improve future conversations.
* Store confirmed facts, recurring interests, preferences, ongoing projects, communication preferences, and useful recurring context.
* Do not store greetings, jokes, isolated reactions, temporary moods, one-time questions, or details that are unlikely to matter later.
* Do not treat the assistant's statements as facts about the viewer.
* Do not invent, assume, diagnose, or infer sensitive personal information.
* Do not record passwords, contact details, addresses, financial information, or other secrets.
* When information is uncertain or mentioned only once without confirmation, omit it.
* Merge duplicate or closely related facts.
* Replace outdated information when a newer statement clearly corrects it.
* Keep descriptions neutral and concise.
* Do not preserve exact quotes unless their wording is essential.
* Keep the entire profile compact. Prefer fewer useful facts over many minor details.
* Maximum total profile length: 2000 characters.
Return only one valid JSON object. Do not use Markdown, comments, explanations, or text outside the JSON.
Use exactly this structure:
{
"summary": "A concise overview of the viewer in no more than 400 characters.",
"facts": [
"Confirmed stable fact"
],
"preferences": [
"Preference useful for future replies"
],
"interests": [
"Recurring interest, hobby, subject, or project"
],
"interactionNotes": [
"Useful communication or interaction preference"
]
}
Use empty arrays when no reliable information is available. Always return every field.Also configure:
| Field | Value |
|---|---|
| Require JSON | Enabled |
| Target Path | runtime.ai.last.text_profile |
Add these Context Files:
| Source | Root | Path |
|---|---|---|
| Context Storage File | Context | Chat/{event.user.id}.jsonl |
| Context Storage File | Context | Chat/{event.user.id}_profile.jsonl |
Create one more Number variable named dialog_context_trim and set it to 5.
Configure the Trim JSONL inside the If body:
| Field | Value |
|---|---|
| Root | Context |
| Path | Chat/{event.user.id}.jsonl |
| Keep Last | {vars.dialog_context_trim} |
Configure Rewrite JSON:
| Field | Value |
|---|---|
| Root | Context |
| Path | Chat/{event.user.id}_profile.jsonl |
| JSON | {runtime.ai.last.text_profile} |
| Format | Auto |
The profile response is written as one JSON object. The dialogue file keeps only the five newest exchanges, while durable information survives in the compact profile.
Step 13. Use the profile in normal replies
Return to the first Call and Store action that answers the viewer. Add one more Context File:
| Source | Root | Path |
|---|---|---|
| Context Storage File | Context | Chat/{event.user.id}_profile.jsonl |
The reply action now receives three layers of context: recent public chat, recent personal dialogue, and the viewer's compact long-term profile.
Step 14. Group the automations
Create a folder named Chatting with AI and move both automations into it:
- AI Bot call
- Chat Logging to Context
The folder keeps the related workflow together without changing how either signal runs. The exported download at the end of this guide contains this complete folder.
Verify the finished system
Test with a separate Twitch viewer account:
- Send ordinary chat messages and confirm that
Chat/RecentChat.jsonlappears beneath the Context Root. - Send several
!botcommands and confirm that a file named with the viewer ID is created. - Open the files from the Database tab and verify that the newest messages and AI replies are appended.
- Reach the profile threshold and confirm that the profile file is created, the dialogue is reduced to five entries, and the generated JSON contains every required field.
- Send another
!botcommand and confirm that the model can use information retained in the profile.
If it does not work
- No files appear: validate the Context Root, confirm it is enabled, and inspect the first failing storage action in Logs.
- Paths contain literal braces: verify the exact contract and variable syntax, including
{event.user.id}and thevars.prefix for project variables. - Recent chat grows indefinitely: confirm that Trim JSONL runs after Append JSONL and reads
{vars.chat_context_length}. - The If branch never runs: inspect
runtime.context.last.countand verify thatdialog_context_lengthis a Number. - The profile response is not valid JSON: enable Require JSON, keep the strict System Prompt, and inspect the model's raw response in Runtime and Logs.
- The reply has no long-term memory: add the profile file to the first reply-producing Call and Store, not only to the profile-maintenance action.
- A profile file is missing on the first run: keep the Context File's missing-data behavior set to continue; the first successful summary will create it.
Adapt it
- Use different retention variables for busy and quiet channels.
- Add a scheduled maintenance automation for files belonging to viewers who have not returned.
- Store separate profiles per platform by adding a service identifier to the Path.
- Add moderation or manual approval before using stored information in a public reply.
Related
Download the ready-made script
Import it into Streaming Hub, then follow this guide to add your own variables and connect the required services.
Download the contextual AI bot folder