{
  "schemaVersion": "flowdrome.workflow.v1",
  "id": "",
  "name": "AI · 07 — RAG 2: retrieve & answer",
  "version": "0",
  "status": "draft",
  "nodes": [
    {
      "id": "seed",
      "type": "input.inject",
      "label": "Question",
      "category": "trigger",
      "config": {
        "contentType": "application/json",
        "payload": {
          "question": "How long do I have to return an order?"
        }
      },
      "position": {
        "x": 0,
        "y": 23
      }
    },
    {
      "id": "recall",
      "type": "ai.vector-store",
      "label": "Retrieve",
      "category": "ai",
      "config": {
        "operation": "query",
        "store": "ai-demo-kb",
        "provider": "ollama",
        "baseUrl": "",
        "apiKey": "",
        "model": "nomic-embed-text",
        "textField": "question",
        "metadataField": "",
        "topK": 3,
        "keywordWeight": "0",
        "backend": "memory",
        "backendUrl": "",
        "backendKey": "",
        "timeoutMs": 120000
      },
      "position": {
        "x": 530,
        "y": 23
      },
      "attachments": {
        "model": "emb"
      }
    },
    {
      "id": "emb",
      "type": "ai.model",
      "label": "Embeddings (local)",
      "category": "ai",
      "config": {
        "provider": "ollama",
        "baseUrl": "",
        "apiKey": "",
        "model": "nomic-embed-text",
        "models": [],
        "temperature": 0.2,
        "maxTokens": 512
      },
      "position": {
        "x": 530,
        "y": 245
      }
    },
    {
      "id": "join",
      "type": "logic.merge",
      "label": "Question + matches",
      "category": "flow",
      "config": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "numberInputs": 2
      },
      "position": {
        "x": 1060,
        "y": 0
      }
    },
    {
      "id": "compose",
      "type": "utility.javascript",
      "label": "Compose prompt",
      "category": "script",
      "config": {
        "code": "var src = Array.isArray(input) ? input[0] : input;   // Merge emits one combined row\nvar ctx = (src.matches || []).map(function (m, i) { return (i + 1) + '. ' + m.text; }).join('\\n');\nreturn { prompt: 'Answer using ONLY this context:\\n' + ctx + '\\n\\nQuestion: ' + (src.question || '') };",
        "outputs": [
          "output"
        ]
      },
      "position": {
        "x": 1590,
        "y": 0
      }
    },
    {
      "id": "answer",
      "type": "ai.prompt",
      "label": "Answer",
      "category": "ai",
      "config": {
        "provider": "ollama",
        "baseUrl": "",
        "apiKey": "",
        "model": "qwen2.5:1.5b",
        "systemPrompt": "Answer briefly from the given context. Say when the context does not contain the answer.",
        "promptMode": "field",
        "promptField": "prompt",
        "temperature": "0.2",
        "maxTokens": 300,
        "jsonMode": false,
        "timeoutMs": 120000
      },
      "position": {
        "x": 2120,
        "y": 0
      },
      "attachments": {
        "model": "llm"
      }
    },
    {
      "id": "llm",
      "type": "ai.model",
      "label": "Ollama (local)",
      "category": "ai",
      "config": {
        "provider": "ollama",
        "baseUrl": "",
        "apiKey": "",
        "model": "qwen2.5:1.5b",
        "models": [],
        "temperature": 0.2,
        "maxTokens": 512
      },
      "position": {
        "x": 2120,
        "y": 222
      }
    },
    {
      "id": "out",
      "type": "utility.console",
      "label": "Answer",
      "category": "utility",
      "config": {
        "message": "{{ $json.reply }}"
      },
      "position": {
        "x": 2650,
        "y": 0
      }
    }
  ],
  "edges": [
    {
      "id": "e0",
      "sourceNodeId": "seed",
      "sourcePort": "output",
      "targetNodeId": "recall",
      "targetPort": "input"
    },
    {
      "id": "e1",
      "sourceNodeId": "seed",
      "sourcePort": "output",
      "targetNodeId": "join",
      "targetPort": "input1"
    },
    {
      "id": "e2",
      "sourceNodeId": "recall",
      "sourcePort": "output",
      "targetNodeId": "join",
      "targetPort": "input2"
    },
    {
      "id": "e3",
      "sourceNodeId": "join",
      "sourcePort": "output",
      "targetNodeId": "compose",
      "targetPort": "input"
    },
    {
      "id": "e4",
      "sourceNodeId": "compose",
      "sourcePort": "output",
      "targetNodeId": "answer",
      "targetPort": "input"
    },
    {
      "id": "e5",
      "sourceNodeId": "answer",
      "sourcePort": "output",
      "targetNodeId": "out",
      "targetPort": "input"
    }
  ],
  "triggers": [
    {
      "id": "t0",
      "nodeId": "seed"
    }
  ],
  "meta": {
    "snippets": [],
    "source": "template-gallery",
    "description": "The ask half of RAG (run demo 06 first): the question pulls the top chunks from 'ai-demo-kb', a Merge joins the question back with the matches, and the ◈-wired Chat answers ONLY from those chunks. All local."
  }
}