Why RAG on Historian Data Fails (and What to Do Instead)
Historians compress your data on purpose, and standard RAG has no idea. Here is why exception and compression exist, why vector search cannot answer historian questions, and the query-first architecture that works instead.
Say you have done everything right. You built the semantic layer so the model knows what FCV100.PV means, you route numeric questions to a query engine instead of a vector store, and you feed summarised windows rather than raw samples. That is the architecture I laid out in industrial RAG, and it is the right architecture. And the answers are still wrong, in ways nobody can quite explain.
At that point the problem has stopped being RAG. The problem is that a process historian does not store what you think it stores, and that standard retrieval was never designed to answer the kind of question people actually ask of plant data. Before criticising either, it is worth understanding why historians work the way they do, because the design decision that causes the trouble is a genuinely good one.
Why exception and compression exist in the first place
A single plant can generate an overwhelming volume of data. Thousands of tags, sampled every second, every second of every shift, for years. Storing every raw sample of every tag forever is possible in principle and ruinous in practice, and most of what you would be storing is not information. It is instrument noise: the last digit of a temperature reading wobbling around a steady value.
Historians solve this with two sequential filtering stages, and it helps to understand that they are two different things happening in two different places.
Exception runs first, at the interface node, close to the data source. Its job is to discard minor fluctuations that carry no information. If a value moves less than a configured deadband from the last reported value, it is simply not sent onward. This is noise rejection at the edge, before the data ever reaches the archive.
Compression runs second, on the archive server, and this is where the swinging-door algorithm does its work. Rather than storing every value that survived exception, it stores only the points that define the shape of the trend. If a straight line between two stored points reconstructs everything in between within a configured tolerance, the intermediate points are not archived, because they can be recreated when needed.
Together these two stages routinely cut stored volume dramatically, with figures as high as 94 percent commonly quoted, while preserving the trend closely enough that an engineer looking at a chart sees the same picture they would have seen from raw data. For decades of trending, reporting, and process review, this is exactly the right trade. It is why a historian can hold years of history for thousands of tags at all.
And that is precisely what breaks RAG
Here is the consequence nobody warns you about. After exception and compression, the historian is storing a shape, not a series of measurements. Ask what the temperature was at 14:32 and you may well get a value that was reconstructed by interpolation, not one that was ever recorded.
For trending, that is fine. For root cause analysis, it is not, because the thing you are usually hunting is a short, sharp excursion, and a short, sharp excursion that stayed inside the compression tolerance is exactly what got discarded. The peak that caused the trip may simply not exist in your archive.
Now put a language model on top of that. It reports the interpolated number as a fact, in a confident, fluent sentence, with nothing to indicate that the value was calculated rather than measured. The compression that made the historian viable has quietly become a source of authoritative-sounding fiction.
What to do instead: for any diagnostic or RCA query, retrieve raw recorded values rather than interpolated ones. Most historian interfaces default to interpolated retrieval and you have to explicitly ask for recorded points. Know the exception and compression settings per tag, and treat any tag with a wide deadband as unsuitable for fine-grained causal analysis. If the pipeline had to interpolate, the answer should say so.
Why standard RAG fails on historian data
Compression is only the first problem. The deeper mismatch is architectural.
Standard RAG retrieves semantically similar text chunks. That is what it is for, and it does it well. But the questions people ask of historian data almost never reduce to similarity. They require:
- Filtering to a specific time window
- Aggregation such as average, minimum, maximum, and standard deviation
- Alignment of several tags onto a common time basis
- Trend and anomaly detection
- Correlation between process variables
- An understanding of which operating regime the equipment was in
- Preservation of timestamp and unit semantics
Can vector databases handle historian data? Not for these operations. A vector database is built for similarity search, and similarity search is not numerical aggregation, not temporal alignment, and not correlation. Asking it to compute a shift average is asking the wrong component. The same distinction shows up in published work on using SQL-based retrieval for aggregation-heavy workloads, and it is the right instinct: match the operation to the engine that can actually perform it.
Why embeddings are unsuitable for raw sensor data
Consider a single historian record:
2026-08-10 14:05:00, Pump_03_Pressure = 8.42 barEmbed that and you have converted a precise measurement into a vector whose position in semantic space carries almost nothing useful. In isolation the record barely means anything even to a human. The information an engineer actually needs is elsewhere: how the pressure trended over the preceding thirty minutes, how it moved relative to flow and temperature, and whether the pump was in a startup, steady, or recirculation state at the time.
None of that survives embedding a single row. Worse, the two things that make the record meaningful, the timestamp and the unit, are exactly the parts an embedding is least able to preserve. Numeric precision and temporal ordering are not what semantic vectors encode.
This is the root of the failure. Not that the model is weak, but that the representation threw away the meaning before the model ever saw it.
The other historian traps
Compression is the headline problem, but several others cause quiet, confident errors.
Quality codes are dropped. Every serious historian stores a quality flag alongside each value: good, bad, uncertain, substituted. Most pipelines discard that column at ingest because it is not the number. Then a thermocouple fails, the system holds its last value, and the historian records a steady 4.0 degrees flagged bad for six hours. Your system reports that the product held perfectly at 4.0 during the exact window under investigation. It is not hallucinating. It is faithfully reporting data that was already wrong, which is worse, because nothing in the output looks suspicious. Carry quality through as a first-class field, and refuse precision answers on windows where quality is not good.
Aggregation ignores operating state. "What was the average line speed last week" folds three days of idle zeros into the mean if the line only ran four. Arithmetically correct, operationally meaningless, and very hard to spot once a model has written a fluent sentence around it. Join to the machine state and aggregate only over periods matching the question's intent.
Tags do not share a sampling rate. A fast pressure loop at one second and a lab result once a batch cannot be lined up by simply pairing returned rows. Naive correlation across mismatched rates produces artefacts of the sampling, not the process. Resample explicitly to a common basis and record which method was used, because it changes the answer.
Timestamps are not one thing. A record can carry the time the value occurred at the device, the time the historian received it, and the time it was written. Add timezone handling, daylight saving, and real clock drift between PLC, SCADA, and historian. Root cause analysis is an ordering problem, so if two sources disagree by seconds, the system will narrate a confident and entirely wrong causal chain. Normalise to UTC at ingest, keep source timestamps, and never let the model imply an ordering tighter than your timing accuracy supports.
Tags have a lifecycle. Tags get renamed during upgrades, instruments get replaced with different ranges, and old names get reused years later. A five-year query can span several meanings of the same tag. Version the semantic tag dictionary with valid-from and valid-to dates rather than treating it as a static lookup.
What to use instead of pure RAG
The stronger architecture stops treating everything as one retrieval problem and routes each data type to the engine built for it.
| Data type | Recommended method |
|---|---|
| Raw tag values | Historian or time-series database query |
| Averages and statistics | SQL, Flux, or native historian summaries |
| Trends and anomalies | Statistical analysis or time-series ML |
| Equipment hierarchy | Asset framework or knowledge graph |
| Operator notes and maintenance records | Hybrid or vector RAG |
| Alarms and events | Event database or structured retrieval |
| Natural-language interaction | LLM as query planner and explanation layer |
Notice where classic RAG genuinely belongs: operator notes and maintenance records, which are real text written by people. That is the one part of the plant where similarity search is the right tool, and it is the subject of the next piece in this series. Everything numeric belongs to a query engine.
The better pattern: query first, generate second
The workflow that actually works inverts the usual RAG order. Instead of retrieving text and hoping the model can reason over it, you resolve the question into a query, execute it, and only then bring in the language model to explain the result.

- User question
- Intent and entity extraction
- Tag and asset resolution, using the semantic dictionary
- Historian or time-series database query
- Aggregation, alignment, and analysis
- Optional retrieval of maintenance documents and alarm history
- LLM explanation, with citations and the actual values
Take a real question: "why did the compressor discharge temperature increase last Tuesday?" A query-first system resolves "last Tuesday" to a concrete window and "compressor discharge temperature" to a specific tag, pulls that tag alongside pressure, load, flow, and the alarm sequence for the same period, computes how they moved relative to each other, then searches maintenance records for related work. The model's job at the end is to explain calculated evidence, not to guess from embedded fragments.
The difference matters. In the standard pattern, the model is the analyst. In the query-first pattern, the model is the interpreter, and the analysis was done by systems that can actually do arithmetic. That is also what makes every number in the answer traceable back to a tag and a timestamp.
When time-series RAG can work
It would be overclaiming to say retrieval never applies to time-series data. It does, when it retrieves patterns rather than individual points.
Research frameworks in this area, such as TimeRAG, take a different approach from naive text embedding: sliding windows over the series, clustering to build a library of representative sequences, and Dynamic Time Warping to find genuinely similar shapes rather than semantically similar strings. Retrieving "times this pump behaved like it is behaving now" is a real and useful retrieval problem. TimeRAG's published experiments report an average forecasting improvement of around 2.97 percent over the baseline model on the M4 dataset, which is a modest but real gain, and more importantly a demonstration that the retrieval unit should be a sequence, not a row.
The distinction is simple. Retrieving a shape can work. Retrieving a reading does not.
How to tell if you have this problem
Three checks, none of which take long.
Pick an incident you already understand and ask the system about it. If the account differs subtly from what you know happened, you are usually looking at a compression or timestamp issue rather than a model problem.
Ask for a value during a window when an instrument was known to be faulty. If you get a confident number instead of a warning, your quality codes are being dropped.
Ask the same question twice, once against raw retrieval and once against interpolated. If the answers differ meaningfully, you have just measured how much your compression settings matter for that tag, and you should know that number before trusting the system for root cause analysis.
The bottom line
None of these are failures of the language model, which is why they persist. The model is doing its job faithfully on data that was compressed by design, held during a fault, misaligned across sampling rates, or timestamped from a drifting clock. Retrieval-augmented generation grounds an answer in your data, and that is only worth something if you know what your data actually is.
Exception and compression are not mistakes. They are the reason your historian exists at all. The mistake is building an AI layer that pretends they never happened. Treat the historian as what it is, an engineered compromise between fidelity and storage, then query first and generate second: raw values for diagnosis, quality codes carried through, state-aware aggregation, explicit resampling, disciplined time, and a versioned tag dictionary. Do that and the architecture from industrial RAG starts producing answers an engineer will stake a decision on. Which storage layer you read from shapes this too, and I worked through that choice in historian vs time-series database vs data lake.
Written by Usman Nasir — control systems engineer, Stockholm.