AutomationFlowsAI & RAG › Split Test AI Agent Prompts with Supabase

Split Test AI Agent Prompts with Supabase

Original n8n title: Split Test Different Agent Prompts with Supabase and Openai

ByChris Carr @chriscarr on n8n.io

Oftentimes, it's useful to test different settings for a large language model in production against various metrics. Split testing is a good method for doing this.

Chat trigger trigger★★★★☆ complexityAI-powered16 nodesChat TriggerAgentSupabaseOpenAI ChatMemory Postgres Chat
AI & RAG Trigger: Chat trigger Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #2992 — we link there as the canonical source.

This workflow follows the Agent → Chat Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "TEA7K9MSVQGCWKe6",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "A/B Split Testing",
  "tags": [],
  "nodes": [
    {
      "id": "e8404493-4297-4169-a72f-89e668ae5fbc",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        -1460,
        -140
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "582e1c1b-12ff-42ff-8130-48f94eebd706",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        220,
        -160
      ],
      "parameters": {
        "text": "={{ $('When chat message received').item.json.chatInput }}",
        "options": {
          "systemMessage": "={{ $json.prompt }}"
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "39ca5c70-11d4-4f86-bde5-0f9827297be9",
      "name": "Check If Session Exists",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -960,
        -140
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "session_id",
              "keyValue": "={{ $('When chat message received').item.json.sessionId }}"
            }
          ]
        },
        "tableId": "split_test_sessions",
        "operation": "get"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "35f2c270-9571-41ba-ab7c-47a6742d7d90",
      "name": "If Session Does Exist",
      "type": "n8n-nodes-base.if",
      "position": [
        -720,
        -140
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4270c464-6874-45d2-aa3b-606f45544c3d",
              "operator": {
                "type": "number",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ec00ad92-96e9-4936-a547-2a2715ff5c32",
      "name": "Assign Path To Session",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -400,
        -20
      ],
      "parameters": {
        "tableId": "split_test_sessions",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "show_alternative",
              "fieldValue": "={{ Math.random() < 0.5 }}"
            },
            {
              "fieldId": "session_id",
              "fieldValue": "={{ $('When chat message received').item.json.sessionId }}"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "92ee7145-30ae-41e9-bc04-eef03b84485e",
      "name": "Define Path Values",
      "type": "n8n-nodes-base.set",
      "position": [
        -1200,
        -140
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9581a184-a120-4b1f-8408-cfe97520d107",
              "name": "baseline_value",
              "type": "string",
              "value": "The dog's name is Ben"
            },
            {
              "id": "1752f2c4-4ce4-4893-b8db-1c59131c298a",
              "name": "alternative_value",
              "type": "string",
              "value": "The dog's name is Tom"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "23c1b4e2-2ba2-4237-bb4b-b92da127d201",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        300,
        60
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "be3f14b9-68c7-457d-b5bf-a6abbadf5b67",
      "name": "Postgres Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "position": [
        480,
        60
      ],
      "parameters": {
        "tableName": "n8n_split_test_chat_histories",
        "sessionKey": "={{ $('When chat message received').item.json.sessionId }}",
        "sessionIdType": "customKey"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "1c20d274-2482-4551-a4ea-64860eb35276",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1520,
        -260
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 300,
        "content": "## 1. Receive Message\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ee22d3b1-d447-4e35-8ac4-d093edf6deee",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1280,
        -340
      ],
      "parameters": {
        "color": 7,
        "width": 1340,
        "height": 500,
        "content": "## 2. Determine Prompt for LLM\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7d90ec00-5fca-4b0d-bc1f-e8b8c179b960",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 440,
        "content": "## 3. AI Agent"
      },
      "typeVersion": 1
    },
    {
      "id": "b9b9e0e8-53c1-4d6a-bbdc-c2a13d740dfb",
      "name": "Get Correct Prompt",
      "type": "n8n-nodes-base.set",
      "position": [
        -80,
        -160
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "08de68ec-0f12-43ee-98ab-59d8a414f114",
              "name": "prompt",
              "type": "string",
              "value": "={{ $json.show_alternative ? $('Define Path Values').item.json.alternative_value : $('Define Path Values').item.json.baseline_value }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2b78ce9b-e6b4-4744-8ddf-00f8ae990fc8",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1260,
        -240
      ],
      "parameters": {
        "color": 5,
        "width": 220,
        "height": 260,
        "content": "### Modification\nSet the values of the  baseline and alternative prompts"
      },
      "typeVersion": 1
    },
    {
      "id": "0646f176-407a-41ee-b602-34bd681fc421",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        100,
        40
      ],
      "parameters": {
        "color": 5,
        "width": 340,
        "height": 140,
        "content": "### Modification\nReplace this sub-node \nto use a different language\n model"
      },
      "typeVersion": 1
    },
    {
      "id": "a391018c-5d28-4384-89e1-0435758a6945",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1480,
        -600
      ],
      "parameters": {
        "width": 520,
        "height": 240,
        "content": "### Setup\n1. Create a table in Supabase called **split_test_sessions**. It needs to have the following columns: **session_id** (`text`) and **show_alternative** (`bool`)\n2. Add your **Supabase**, **OpenAI**, and **PostgreSQL** credentials\n3. Modify the **Define Path Values** node to set the baseline and alternative prompt values.\n4. Activate the workflow and test by sending messages through n8n's inbuilt chat\n5. Experiment with different chat sessions to test see both prompts in action"
      },
      "typeVersion": 1
    },
    {
      "id": "2382d146-f0c1-4de6-9e90-b17c304df692",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2120,
        -360
      ],
      "parameters": {
        "width": 560,
        "height": 480,
        "content": "\n## Split Test Different Agent Prompts with Supabase and OpenAI\n### Use Case\nOftentimes, it's useful to test different settings for a large language model in production against various metrics. Split testing is a good method for doing this.\n### What it Does\nThis workflow randomly assigns chat sessions to one of two prompts, the baseline and the alternative. The agent will use the same prompt for all interactions in that chat session.\n### How it Works\n1. When messages arrive, a table containing information regarding session ID and which prompt to use is checked to see if the chat already exists\n2. If it does not, the session ID is added to the table and a prompt is randomly assigned\n3. These values are then used to generate a response\n### Next Steps\n- Modify the workflow to test different LLM settings such as temperature\n- Add a method to measure the efficacy of the two alternative prompts"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "339c6f2f-e4d1-4922-9442-2c1a78e96067",
  "connections": {
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Define Path Values": {
      "main": [
        [
          {
            "node": "Check If Session Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Correct Prompt": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "If Session Does Exist": {
      "main": [
        [
          {
            "node": "Get Correct Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Assign Path To Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assign Path To Session": {
      "main": [
        [
          {
            "node": "Get Correct Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Session Exists": {
      "main": [
        [
          {
            "node": "If Session Does Exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "Define Path Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Oftentimes, it's useful to test different settings for a large language model in production against various metrics. Split testing is a good method for doing this.

Source: https://n8n.io/workflows/2992/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

A/B Split Testing. Uses chatTrigger, agent, supabase, lmChatOpenAi. Chat trigger; 16 nodes.

Chat Trigger, Agent, Supabase +2
AI & RAG

✨📊Multi-AI Agent Chatbot for Postgres/Supabase DB and QuickCharts + Tool Router. Uses chatTrigger, postgresTool, executeWorkflowTrigger, toolWorkflow. Chat trigger; 40 nodes.

Chat Trigger, Postgres Tool, Execute Workflow Trigger +6
AI & RAG

This workflow is ideal for data analysts, developers, and business intelligence teams who need an AI-powered chatbot to query Postgres/Supabase databases and generate dynamic charts for data visualiza

Chat Trigger, Postgres Tool, Execute Workflow Trigger +6
AI & RAG

Extract Insights & Analyse Youtube Comments Via Ai Agent Chat. Uses stickyNote, lmChatOpenAi, toolWorkflow, memoryPostgresChat. Chat trigger; 29 nodes.

OpenAI Chat, Tool Workflow, Memory Postgres Chat +5
AI & RAG

I prepared a detailed guide to help you set up your workflow effectively, enabling you to extract insights from YouTube for content generation using an AI agent.

OpenAI Chat, Tool Workflow, Memory Postgres Chat +5