{
  "name": "RAG - Preguntar a los Documentos",
  "nodes": [
    {
      "parameters": {
        "public": false,
        "options": {
          "responseMode": "lastNode"
        }
      },
      "id": "8b2d4e10-0000-4000-8000-000000000001",
      "name": "Preguntar",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.4,
      "position": [
        240,
        300
      ],
      "notes": "public: false on purpose. An open chat with no authentication in front of a paid model is an uncapped invoice. How this gets exposed is still an open decision."
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "Eres un asistente que responde UNICAMENTE con base en los documentos indexados.\n\nReglas:\n1. Antes de responder, SIEMPRE usa la herramienta \"Buscar en Documentos\".\n2. Responde solo con lo que aparezca en los fragmentos recuperados. No uses tu conocimiento propio.\n3. Cita siempre de que documento salio la respuesta.\n4. Si la respuesta no esta en los fragmentos, responde exactamente: \"No encontre eso en los documentos cargados.\" No la inventes.\n5. Responde en espanol, corto y directo.",
          "maxIterations": 5,
          "returnIntermediateSteps": false
        }
      },
      "id": "8b2d4e10-0000-4000-8000-000000000002",
      "name": "Responder Con Fuente",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        560,
        300
      ],
      "notes": "The exact refusal sentence in rule 4 is what makes the system measurable: four of the twenty evaluation questions have no answer in the corpus, and the correct behaviour is that literal string. See workflows/system-prompt.md for why each rule exists."
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini"
        },
        "options": {}
      },
      "id": "8b2d4e10-0000-4000-8000-000000000003",
      "name": "Modelo Redactor",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        520,
        520
      ],
      "notes": ">>> REPLACE: attach an OpenAI credential, and set a spending cap on that account first. Note what is NOT here: no temperature (GPT-5 reasoning models reject it) and no maxTokens (they spend the budget reasoning and return an empty answer)."
    },
    {
      "parameters": {
        "mode": "retrieve-as-tool",
        "toolDescription": "Busca fragmentos relevantes en los documentos cargados. Usala SIEMPRE antes de responder cualquier pregunta sobre normativa o contenido de los documentos.",
        "tableName": "documentos_v2",
        "topK": 20,
        "options": {}
      },
      "id": "8b2d4e10-0000-4000-8000-000000000004",
      "name": "Buscar en Documentos",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePGVector",
      "typeVersion": 1.3,
      "position": [
        800,
        460
      ],
      "notes": ">>> REPLACE: attach the same Postgres credential used for loading. topK is 20 and that is a MEASURED trade-off, not a default: going from 5 to 20 fixed four answers and broke one. Production guidance says not to pass more than 10 without a reranker, because the model ignores what sits in the middle. Reranking is the open fix."
    },
    {
      "parameters": {
        "model": "bge-m3:latest"
      },
      "id": "8b2d4e10-0000-4000-8000-000000000005",
      "name": "Embeddings Locales",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
      "typeVersion": 1,
      "position": [
        940,
        680
      ],
      "notes": ">>> REPLACE: attach the Ollama credential. This MUST be the same model used when loading. The question becomes a vector with the same model that turned the documents into vectors; change one side and they stop understanding each other."
    },
    {
      "parameters": {
        "content": "## Ask the documents\n\nThe question is turned into a vector with the SAME model used at load time (bge-m3). The 20 nearest fragments are retrieved and only those reach the writer.\n\nThe prompt forbids answering from memory and forces the exact sentence \"No encontre eso en los documentos cargados\" when the answer is not in the fragments. That literal string is what makes the system measurable.\n\n**Corpus: documentos_v2** \u2014 HIPAA pulled from the eCFR API, one section per file. Every fragment carries its \u00a7 and its official citation.",
        "height": 260,
        "width": 620,
        "color": 3
      },
      "id": "8b2d4e10-0000-4000-8000-000000000006",
      "name": "Nota",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        200,
        20
      ]
    }
  ],
  "connections": {
    "Preguntar": {
      "main": [
        [
          {
            "node": "Responder Con Fuente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Modelo Redactor": {
      "ai_languageModel": [
        [
          {
            "node": "Responder Con Fuente",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Buscar en Documentos": {
      "ai_tool": [
        [
          {
            "node": "Responder Con Fuente",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings Locales": {
      "ai_embedding": [
        [
          {
            "node": "Buscar en Documentos",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}