Lesson 6 Chat Memory RAG Sheet Vector Index MultiSource AI

📘 Lesson 6 — Multi-Folder Workspaces (Switch Between Knowledge Bases)

How to let your Apps Script + Gemini assistant dynamically switch between multiple folders (each with its own RAG index).


🔥 What Lesson 6 Adds

Until now, your assistant worked with exactly one folder, and one RAG index sheet.

Lesson 6 adds the ability to manage multiple “workspaces”, each with:

– A Drive Folder

– Its own RAG Index (a tab in the same spreadsheet OR multiple spreadsheets)

– A name (e.g. “Policies”, “Project X”, “Onboarding Docs”)

– A selectable workspace dropdown in the web UI

This means your AI assistant can answer questions like:

“Switch to the Onboarding Docs workspace.”
“Now answer using the Policies workspace.”
“Use the Research Notes workspace.”

Each workspace becomes a separate knowledge brain.


🎯 Learning Objectives

By the end of Lesson 6, you will be able to:

✔ Allow your AI to switch between multiple Drive folders

✔ Maintain a RAG index per workspace

✔ Add a workspace selector UI

✔ Load the correct embeddings for each workspace

✔ Keep chat history separate per workspace

✔ Enable “Reset per workspace”


🧱 Lesson 6 Architecture Overview

+—————————+

|    Workspace Registry     |  <– A JSON list saved in Script Properties

+—————————+

        |          |

        |          |

        v          v

 Workspace A    Workspace B     Workspace C

 ————   ————    ————

 – Folder ID    – Folder ID     – Folder ID

 – Index Sheet  – Index Sheet   – Index Sheet

 – Chat History – Chat History  – Chat History

Each workspace has:

{

  name: “Policies”,

  folderId: “1xxxx”,

  sheetId: “1xxxx (Spreadsheet for RAG index)”

}

Your assistant receives:

– Selected workspace

– Retrieves RAG index for that workspace

– Uses its Drive folder, embeddings, and chat history

– Answers


🧩 Part 1 — Store Workspaces in Script Properties

Add this new property:

WORKSPACES

Containing JSON:

[

  {

    “name”: “Policies”,

    “folderId”: “xxxxx”,

    “sheetId”: “xxxxx”

  },

  {

    “name”: “Onboarding”,

    “folderId”: “yyyyy”,

    “sheetId”: “yyyyy”

  }

]

Add helper functions into a new file named:

Lesson6_Workspaces.gs


✅ 1. Workspace Registry Helpers

const WORKSPACE_KEY = ‘WORKSPACES’;

/**

 * Returns array of workspaces:

 * [{name, folderId, sheetId}, …]

 */

function getWorkspaces_() {

  const s = PropertiesService.getScriptProperties().getProperty(WORKSPACE_KEY);

  if (!s) return [];

  try { return JSON.parse(s); }

  catch(e) { return []; }

}

/**

 * Saves workspace list.

 */

function saveWorkspaces_(arr) {

  PropertiesService.getScriptProperties()

    .setProperty(WORKSPACE_KEY, JSON.stringify(arr));

}

/**

 * Add a new workspace.

 */

function addWorkspace(name, folderId, sheetId) {

  const ws = getWorkspaces_();

  ws.push({ name, folderId, sheetId });

  saveWorkspaces_(ws);

  return ws;

}


🧩 Part 2 — Selecting a Workspace

We store the user’s active workspace in the User Cache:

const ACTIVE_WORKSPACE_KEY = ‘ACTIVE_WORKSPACE’;

function setActiveWorkspace(name) {

  const ws = getWorkspaces_();

  const found = ws.find(w => w.name === name);

  if (!found) throw new Error(“Workspace not found: ” + name);

  CacheService.getUserCache().put(

    ACTIVE_WORKSPACE_KEY,

    JSON.stringify(found),

    3600

  );

  return found;

}

function getActiveWorkspace_() {

  const cache = CacheService.getUserCache();

  const raw = cache.get(ACTIVE_WORKSPACE_KEY);

  if (!raw) return null;

  try { return JSON.parse(raw); }

  catch(e) { return null; }

}


🧩 Part 3 — Updating Lesson 4C to Use Workspaces

Modify answerWithChatRag to load:

– workspace RAG index

– workspace chat history

function answerWithChatRagMulti(question) {

  const q = question.trim();

  if (!q) throw new Error(“Question empty”);

  const ws = getActiveWorkspace_();

  if (!ws) throw new Error(“No active workspace selected.”);

  // load index for THIS workspace

  const index = loadRagIndexForSheet_(ws.sheetId);

  // history is unique per workspace

  const historyKey = CHAT_HISTORY_KEY + “_” + ws.name;

  const history = getChatHistoryForKey_(historyKey);

  // everything else is the same as Lesson 4C

  // … similarity search, Gemini call …

  // save history under workspace-specific key

  saveChatHistoryForKey_(historyKey, updatedHistory);

  return {

    answer,

    sources,

    workspace: ws.name

  };

}

Add helper functions:

function getChatHistoryForKey_(key) {

  const cache = CacheService.getUserCache();

  const json = cache.get(key);

  if (!json) return [];

  try { return JSON.parse(json); }

  catch(e) { return []; }

}

function saveChatHistoryForKey_(key, history) {

  CacheService.getUserCache().put(key, JSON.stringify(history), 21600);

}


🧩 Part 4 — Updating the Web App (UI)

Add a workspace dropdown:

<select id=”workspaceSelect” onchange=”onWorkspaceChange()”>

</select>

Populate it when the page loads:

function init() {

  google.script.run

    .withSuccessHandler(function (ws) {

      const sel = document.getElementById(“workspaceSelect”);

      sel.innerHTML = “”;

      ws.forEach(w => {

        const opt = document.createElement(“option”);

        opt.value = w.name;

        opt.textContent = w.name;

        sel.appendChild(opt);

      });

    })

    .getWorkspacesPublic(); // wrapper for getWorkspaces_()

}

Implement workspace switching:

function onWorkspaceChange() {

  const name = document.getElementById(“workspaceSelect”).value;

  google.script.run

    .withSuccessHandler(function () {

      addSystemMessage(“Workspace switched to: ” + name);

      document.getElementById(“messages”).innerHTML = “”;

    })

    .setActiveWorkspacePublic(name);

}

Add these two public functions in Apps Script:

function getWorkspacesPublic() {

  return getWorkspaces_();

}

function setActiveWorkspacePublic(name) {

  return setActiveWorkspace(name);

}


🧩 Part 5 — RAG in the New Workspace

Your original buildRagIndex() now becomes:

function buildRagIndexForWorkspace(name) {

  const ws = getWorkspaces_().find(w => w.name === name);

  if (!ws) throw new Error(“Workspace not found: ” + name);

  return buildRagIndex_(ws.folderId, ws.sheetId);

}

This allows:

buildRagIndexForWorkspace(“Policies”);

buildRagIndexForWorkspace(“Onboarding”);

buildRagIndexForWorkspace(“Project A”);


🧪 What Users Can Do Now

Try:

✔ “Switch workspace to Project A.”

✔ “Where is the onboarding checklist stored?”

✔ “Reset workspace chat.”

✔ “Build RAG index for Policies.”

✔ “Now answer using Policies.”

You now have a multi-brain AI system, each workspace isolated with:

– Its own documents

– Its own embeddings

– Its own chat memory

This is extremely close to a lightweight Notion AI, ChatGPT Files, or Mini RAG Assistant.