TagRAG Index
The boring node that powers the whole TagRAG pack
- index_ref
- summary
- status
Every TagRAG workflow starts here, and that's not a compliment you'll hear often in a pack full of LLM calls. TagRAG Index reads your tag CSV, normalizes it, optionally builds a semantic index, and hands every other node a JSON index_ref string it can reload from. Nothing flashy happens in the UI - no images, no chat - but without this node the other three are just boxes that return ERROR: csv_path is required forever. It's the foundation, and it's the most solid piece in the pack.
The whole point of the pack is to build prompts from your tag dataset rather than whatever a generic LLM happens to know. You keep a CSV of tags you actually use - character sheets, your style keywords, the tags your favorite checkpoint genuinely responds to - and Index turns that CSV into something queryable. Think of it as a personal tag library with retrieval bolted on.
How it works
The node loads the CSV with an encoding fallback chain (utf-8-sig, utf-8, latin-1) and normalizes each row into a TagRecord with tag, aliases, category, description, source, and weight. Lexical retrieval then scores your query across all of those fields - so blue hair finds the row whose description says "azure colored hair" even if the tag column says something else. Underscores are treated as spaces, so blue_hair and blue hair are the same thing.
For semantic and hybrid modes it gets fancier: it embeds every row with an embedding-capable GGUF (native llama-cpp-python) or a llama-server /v1/embeddings endpoint, persists a FAISS index in a .tag_rag_indexes/<index_name>/ directory sitting right next to your CSV, and records where everything is. That's the design trick to understand: the index_ref isn't a live connection, it's a serialized recipe. It carries csv_path, the column names, retrieval_mode, and the embedding settings. Downstream nodes parse that JSON and reload the CSV themselves, so you wire the string once and never re-point anything.
Inputs that matter
- csv_path - full path to your CSV. The only thing that's actually required.
- tag_column - which column holds the tag text (default
tag). - retrieval_mode -
lexical(default, needs no model) vssemantic/hybrid, which need an embedding model plus FAISS. - rebuild - set this true when creating or refreshing a semantic index.
- test_query and test_top_k - smoke-test retrieval right in the Index node before you wire anything else up.
The CSV format is forgiving: a single tag column is the minimum, but the recommended shape adds aliases, category, description, source, weight, with aliases separated by | or ;.
Outputs
Three strings: index_ref (the JSON you feed into TagRAG Prompt, Chat, or Image), summary (tag count plus your test query results), and status (OK or an ERROR: message explaining what went wrong).
Install
ComfyUI Manager - search the pack title comfyui-tag-rag - or do it manually:
cd ComfyUI/custom_nodes
git clone https://github.com/FrostySDXL/comfyui-tag-rag
cd comfyui-tag-rag
pip install -r requirements.txt
Then restart ComfyUI. That requirements.txt pulls numpy, requests, faiss-cpu, onnxruntime, Pillow, and llama-cpp-python - and note the pack downloads no models itself, so semantic retrieval needs you to supply an embedding GGUF. The author's whole stance is local-first: no cloud, no API key, nothing fetched behind your back.
Where people get burned
ERROR: FAISS unavailableon semantic/hybrid -faiss-cpuisn't installed, or your index_ref came from a lexical-only build (themissing semantic_index_dirvariant). Installrequirements-rag.txtand rebuild withrebuild=true.ERROR: csv_path is required- the path is blank. It's the one field you can't skip.- Your CSV lives somewhere weird. The loader is deliberately sandboxed: 100 MB max, 200k rows, 4096-char cells, and the file must sit under your working directory or a root you add to the
COMFYUI_TAG_RAG_CSV_ROOTSenvironment variable. That env var is the answer if a legitimate path gets refused.
One honest caveat: this is a pre-alpha pack (version 0.1.0a0, a single-commit repo from mid-2026). Index is the calm, well-tested part, but treat the whole pack as something to poke at rather than infrastructure to bet a production pipeline on.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| csv_pathopt | STRING | — | |
| tag_columnopt | STRING | tag | — |
| index_nameopt | STRING | default | — |
| rebuildopt | BOOLEAN | false | — |
| retrieval_modeopt | COMBO | lexical | 3 options: lexical, semantic, hybrid |
| embedding_backendopt | COMBO | llama_cpp_python | 2 options: llama_cpp_python, llama_server |
| embedding_model_pathopt | STRING | — | |
| embedding_n_ctxopt | INT | 2048256–262144 | — |
| embedding_n_gpu_layersopt | INT | -1-1–200 | — |
| embedding_base_urlopt | STRING | http://127.0.0.1:8080 | — |
| embedding_modelopt | STRING | — | |
| embedding_endpoint_styleopt | COMBO | openai_compatible | 2 options: openai_compatible, llama_cpp_legacy |
| embedding_timeout_secondsopt | FLOAT | 30.01–300 | — |
| allow_remote_embedding_base_urlopt | BOOLEAN | false | — |
| aliases_columnopt | STRING | aliases | — |
| category_columnopt | STRING | category | — |
| description_columnopt | STRING | description | — |
| source_columnopt | STRING | source | — |
| weight_columnopt | STRING | weight | — |
| test_queryopt | STRING | — | |
| test_top_kopt | INT | 51–100 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| index_ref | STRING | — |
| summary | STRING | — |
| status | STRING | — |