{
  "name": "Transform your portfolio into an AI assistant with RAG and CV delivery",
  "nodes": [
    {
      "id": "2cbadb28-b31e-45f9-9eef-719c1f4f4ac9",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9200,
        9680
      ],
      "parameters": {
        "width": 688,
        "height": 668,
        "content": "## AI Portfolio Assistant with RAG & CV Delivery\n\nA two-pipeline workflow that turns your Google Drive documents into a 24/7 recruiter-ready AI assistant. Recruiters chat via webhook; the agent retrieves evidence from your portfolio, answers questions, and automatically emails your CV as a PDF when requested.\n\n### How it works\n\n**Ingestion pipeline**\n1. Google Drive triggers detect new or updated portfolio documents\n2. Files are downloaded, enriched with metadata, and chunked (600 chars / 100 overlap)\n3. Chunks are embedded via OpenAI and upserted into Pinecone\n\n**Chat agent pipeline**\n1. A webhook receives `{ chatInput, sessionId, email }` POST requests\n2. Claude Sonnet 4.5 queries Pinecone (with Cohere reranking) for relevant evidence\n3. Structured output parsing extracts the answer and a `cvRequested` flag\n4. If `cvRequested` is true, the CV PDF is downloaded from Drive and emailed via Gmail\n\n**Error handling:** The Error Trigger catches any node failure and formats the error for alerting.\n\n### Setup steps\n\n1. Create a Pinecone index: dimension 1536, metric cosine, name it `portfolio-docs`\n2. Configure all 6 credentials (Google Drive, OpenAI, Pinecone, Anthropic, Cohere, Gmail)\n3. Set your Google Drive **folder ID** in both trigger nodes\n4. Set your **CV file ID** in the Download CV PDF node\n5. Personalize the AI Agent **system prompt** with your name and specialization\n6. Upload a document to Drive to trigger ingestion, then test the chat webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "f2616c59-b30d-4c73-b3a9-3a9d0772ac09",
      "name": "Section 1 Background",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9952,
        9680
      ],
      "parameters": {
        "color": 7,
        "width": 1450,
        "height": 684,
        "content": "## Ingestion pipeline\nMonitors Google Drive for new and updated documents, chunks and embeds them with OpenAI, then upserts into Pinecone for retrieval."
      },
      "typeVersion": 1
    },
    {
      "id": "b226e700-fb11-417b-8dc8-0844ab0cb4bb",
      "name": "File Created Trigger",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "position": [
        10176,
        9808
      ],
      "parameters": {
        "event": "fileCreated",
        "options": {
          "fileType": "all"
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 1
    },
    {
      "id": "76549fc9-02dd-483b-a0af-cd089348e252",
      "name": "File Updated Trigger",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "position": [
        10176,
        10000
      ],
      "parameters": {
        "event": "fileUpdated",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e7d12492-fd3f-497b-858a-87fc16c66397",
      "name": "Download File",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        10432,
        9888
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {
          "fileName": "={{ $json.name }}"
        },
        "operation": "download"
      },
      "typeVersion": 3
    },
    {
      "id": "91ebe6c1-6722-405f-8362-cb15212bde01",
      "name": "Enrich Metadata",
      "type": "n8n-nodes-base.set",
      "position": [
        10704,
        9888
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "meta-date",
              "name": "upload_date",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "meta-source",
              "name": "source_filename",
              "type": "string",
              "value": "={{ $json.name }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "e723c6a0-80ff-4905-932b-09a526dfcfd3",
      "name": "Pinecone Insert",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        11008,
        9888
      ],
      "parameters": {
        "mode": "insert",
        "options": {},
        "pineconeIndex": {
          "__rl": true,
          "mode": "id",
          "value": "portfolio-docs"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "e6ac53d6-994a-45d9-a261-a50085a5a2ac",
      "name": "OpenAI Embeddings",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        10880,
        10096
      ],
      "parameters": {
        "model": "text-embedding-3-small",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "60b0db34-d656-4825-862d-0c0055321bdb",
      "name": "Document Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        11136,
        10096
      ],
      "parameters": {
        "options": {},
        "dataType": "binary",
        "binaryMode": "specificField",
        "binaryDataKey": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "3c098fa5-cce1-4f65-9641-5c7a98c8f75a",
      "name": "Text Splitter",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        11136,
        10240
      ],
      "parameters": {
        "options": {},
        "chunkSize": 600,
        "chunkOverlap": 100
      },
      "typeVersion": 1
    },
    {
      "id": "0cc33506-b37a-41a2-80dd-1fe4a87fc382",
      "name": "Section 2 Background",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9952,
        10480
      ],
      "parameters": {
        "color": 7,
        "width": 2204,
        "height": 1008,
        "content": "## AI agent\nWebhook-triggered Claude Sonnet 4.5 agent with Pinecone RAG, Cohere reranking, structured output CV detection, and Gmail delivery."
      },
      "typeVersion": 1
    },
    {
      "id": "c4d8a16f-3a77-47ea-80c1-5637b557857e",
      "name": "Chat Webhook",
      "type": "n8n-nodes-base.webhook",
      "onError": "continueRegularOutput",
      "position": [
        10080,
        10688
      ],
      "parameters": {
        "path": "portfolio-query",
        "options": {
          "allowedOrigins": "*"
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "c1dac88d-5a0d-434e-a0bb-46e2b7a04a1e",
      "name": "Portfolio AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        10480,
        10688
      ],
      "parameters": {
        "text": "={{ $json.body.chatInput }}",
        "options": {
          "systemMessage": "You are a professional AI assistant representing your portfolio.\n\nYour role:\n- Answer questions about skills, projects, experience, and technical capabilities\n- Use the portfolio_knowledge tool to retrieve relevant technical evidence before answering\n- Be concise, professional, and technically precise\n- Ground every answer in retrieved evidence \u2014 never fabricate projects or skills\n\nBehavior:\n- If the user is a recruiter or asks to see/send/share a CV/resume, set cvRequested to true\n- If asked about something not in the portfolio data, say so honestly\n- When discussing projects, reference specific technical details from the evidence\n- Offer a call-to-action: suggest booking a meeting or requesting the CV when appropriate\n\nYou MUST respond using the structured output format provided."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "6ecbcbbc-56c5-4351-8cab-4b3577ac22b2",
      "name": "Claude Sonnet 4.5",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        10144,
        10992
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "claude-sonnet-4-5-20250929"
        },
        "options": {
          "temperature": 0.3
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "96fa14b6-8e19-4077-9c70-9170fc8af42b",
      "name": "Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        10352,
        10992
      ],
      "parameters": {
        "sessionKey": "={{ $json.body.sessionId }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "typeVersion": 1.3
    },
    {
      "id": "cca3c727-66bb-456f-a5ab-a1c9688aa8be",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        10544,
        11104
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n\t\"answer\": \"Your detailed response about the portfolio\",\n\t\"cvRequested\": false\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "45d186bf-3318-4053-9d5c-0f05e2379d85",
      "name": "Portfolio Vector Tool",
      "type": "@n8n/n8n-nodes-langchain.toolVectorStore",
      "position": [
        10880,
        10928
      ],
      "parameters": {
        "name": "portfolio_knowledge",
        "topK": 5,
        "description": "Retrieve information about the portfolio owner's skills, projects, work experience, and technical capabilities."
      },
      "typeVersion": 1
    },
    {
      "id": "aa493916-54fd-4edb-a185-c03070c80b8a",
      "name": "Claude Retrieval Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        11104,
        11168
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "claude-sonnet-4-5-20250929"
        },
        "options": {
          "temperature": 0.2
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "a0a8bd94-075c-4987-be51-433b1d2bc1ba",
      "name": "Pinecone Retrieval",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        10800,
        11168
      ],
      "parameters": {
        "options": {},
        "useReranker": true,
        "pineconeIndex": {
          "__rl": true,
          "mode": "id",
          "value": "portfolio-docs"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "cohere-reranker-node",
      "name": "Cohere Reranker",
      "type": "@n8n/n8n-nodes-langchain.rerankerCohere",
      "position": [
        11104,
        11344
      ],
      "parameters": {
        "topN": 3,
        "modelName": "rerank-v3.5"
      },
      "typeVersion": 1
    },
    {
      "id": "ee30e696-9b2f-4bcf-ac2f-e889072e4766",
      "name": "OpenAI Embeddings Retrieval",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        10800,
        11344
      ],
      "parameters": {
        "model": "text-embedding-3-small",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "5e101523-b700-4a08-a84f-6f6bc1a1090d",
      "name": "Check CV Request",
      "type": "n8n-nodes-base.if",
      "position": [
        11152,
        10688
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cv-check",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.cvRequested }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "37cdfcd4-65f4-4e1c-94d4-224fd03806cd",
      "name": "Download CV PDF",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        11456,
        10496
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "options": {
          "fileName": "Resume.pdf"
        },
        "operation": "download"
      },
      "typeVersion": 3
    },
    {
      "id": "9522bde8-1c8d-4cbc-b273-efc88d6d1ecf",
      "name": "Send CV Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        11712,
        10496
      ],
      "parameters": {
        "sendTo": "={{ $('Chat Webhook').item.json.body.email }}",
        "message": "Hi,\n\nThank you for your interest! Please find the resume attached.\n\nBest regards,\nPortfolio Assistant",
        "options": {
          "attachmentsBinary": true
        },
        "subject": "Resume \u2014 Portfolio"
      },
      "typeVersion": 2.2
    },
    {
      "id": "82046fee-e114-4b9d-88ee-eac01006a54b",
      "name": "Respond CV Sent",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        11952,
        10496
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ answer: $('Portfolio AI Agent').item.json.output.answer, cvSent: true }) }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "c5a9028b-2610-497c-9fc5-812b15d0eb36",
      "name": "Respond with Answer",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        11456,
        10896
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ answer: $json.output.answer, cvSent: false }) }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "bce718ea-5dee-4590-8c75-8c0e956109dc",
      "name": "Error Handling Guide",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        9952,
        11584
      ],
      "parameters": {
        "color": 7,
        "width": 700,
        "height": 320,
        "content": "## Error handling\nCatches failures from any node. Connect an alerting service (Slack, email, webhook) after the Format Error Data node."
      },
      "typeVersion": 1
    },
    {
      "id": "44ee3831-bd99-4a57-b7df-6ce5de6839e4",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        10144,
        11760
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "43fc8114-1fa1-47f3-8c62-f2f6f3a46f99",
      "name": "Format Error Data",
      "type": "n8n-nodes-base.set",
      "position": [
        10416,
        11760
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "err-msg",
              "name": "error_message",
              "type": "string",
              "value": "={{ $json.execution.error.message }}"
            },
            {
              "id": "err-node",
              "name": "failed_node",
              "type": "string",
              "value": "={{ $json.execution.error.node.name }}"
            },
            {
              "id": "err-workflow",
              "name": "workflow_name",
              "type": "string",
              "value": "={{ $json.workflow.name }}"
            },
            {
              "id": "err-url",
              "name": "execution_url",
              "type": "string",
              "value": "={{ $json.execution.url }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "Portfolio AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Chat Webhook": {
      "main": [
        [
          {
            "node": "Portfolio AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File": {
      "main": [
        [
          {
            "node": "Enrich Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Format Error Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send CV Email": {
      "main": [
        [
          {
            "node": "Respond CV Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Document Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "Cohere Reranker": {
      "ai_reranker": [
        [
          {
            "node": "Pinecone Retrieval",
            "type": "ai_reranker",
            "index": 0
          }
        ]
      ]
    },
    "Document Loader": {
      "ai_document": [
        [
          {
            "node": "Pinecone Insert",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Download CV PDF": {
      "main": [
        [
          {
            "node": "Send CV Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enrich Metadata": {
      "main": [
        [
          {
            "node": "Pinecone Insert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check CV Request": {
      "main": [
        [
          {
            "node": "Download CV PDF",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond with Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Sonnet 4.5": {
      "ai_languageModel": [
        [
          {
            "node": "Portfolio AI Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Embeddings": {
      "ai_embedding": [
        [
          {
            "node": "Pinecone Insert",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Pinecone Retrieval": {
      "ai_vectorStore": [
        [
          {
            "node": "Portfolio Vector Tool",
            "type": "ai_vectorStore",
            "index": 0
          }
        ]
      ]
    },
    "Portfolio AI Agent": {
      "main": [
        [
          {
            "node": "Check CV Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "File Created Trigger": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "File Updated Trigger": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Portfolio Vector Tool": {
      "ai_tool": [
        [
          {
            "node": "Portfolio AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Claude Retrieval Model": {
      "ai_languageModel": [
        [
          {
            "node": "Portfolio Vector Tool",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Portfolio AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Embeddings Retrieval": {
      "ai_embedding": [
        [
          {
            "node": "Pinecone Retrieval",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    }
  }
}