Case study 03 / 06
BioStreamer
My own peer-reviewed anaerobic digestion research, turned into a hybrid data platform.
BioStreamer models daily bioreactor telemetry (pH, volatile fatty acids, alkalinity and biogas yield) for 100 simulated reactor lines, grounded in the co-digestion study I co-authored. Structured data and research literature flow through separate pipelines and meet in one API.

- Type
- Hybrid data platform
- Grounded in
- My 2024 co-authored paper
- Scale
- 100 reactors × 37 days
- Code
- Public on GitHub
- Maximum deviation from the paper's published yields
- 0.042 ml/day
- Pipeline throughput on ~3,700 rows
- ~27,000 rows/s
- Simulated bioreactor lines
- 100
01
The problem
A 36-digester anaerobic digestion study, observed daily for 37 days, needs more than 1,300 manual water-displacement readings. Each one is read off a calibrated cylinder and typed into a spreadsheet by hand.
That logging burden, not the number of digesters, is what limits how large a study a small lab can run. The study BioStreamer is built on is one I co-authored on co-digesting bean and plantain peels, published in GSC Biological and Pharmaceutical Sciences in 2024.
BioStreamer asks what that research looks like when collecting and interpreting the data is automated for a fleet an order of magnitude larger, while staying accountable to the published results.
02
What I built
BioStreamer simulates, stores and reasons over daily telemetry for 100 bioreactor lines: pH, volatile fatty acids, alkalinity and biogas yield. A retrieval layer over the research literature sits beside it, so numbers and explanations both come from verified sources, never from a language model's memory.
01
Mechanistic simulator
A coupled acidogenesis and methanogenesis model generates 37 days of telemetry for 100 reactors, across a continuous range of bean to plantain ratios at 5 to 25% total solids.
02
Two ingestion tracks
Telemetry lands as Parquet in a MinIO lake and loads into PostgreSQL. Research text is chunked, embedded and indexed in Qdrant.
03
Validation gates
Airflow refuses to load a dataset unless the 36 reference reactors reproduce the paper's published yields, and won't mark indexing complete unless retrieval probes return the right source type.
04
Hybrid answers
A FastAPI /chat endpoint combines exact SQL aggregates with ranked literature passages, then asks the language model to compose an answer from both.
05
Traceable claims
Every answer ships with its retrieval trace. Each passage is tagged as a published finding from the paper or as general domain context, so a claim can't be mistaken for a result the study didn't report.
06
Streamlit dashboard
A fleet overview, a reactor explorer and a research chat that shows its retrieval trace.
03
How it works
Architecture
Hover a component to trace its connections.
- 1
Simulate
A seeded simulator produces 100 reactors × 37 days of telemetry. A mechanistic pass sets the shape of each reactor's daily output, then a second pass calibrates the 36 reference reactors to the paper's Table 2 means.
- 2
Validate, then load
Airflow compares the reference cohort with all twelve cells of Table 2. Only if it passes does the data land in MinIO as Parquet and load into PostgreSQL.
- 3
Index the literature
A second DAG splits the paper and domain notes into chunks of 512 with an overlap of 64, embeds them as 384-dimension vectors and indexes them in Qdrant.
- 4
Answer
Every /chat question runs both retrievals: SQL for exact numbers, vector search for mechanism. The model composes the answer from that labelled context instead of doing the arithmetic itself.
- 5
Explore
Streamlit shows the fleet, any single reactor and the research chat. With no language model key set, /chat still returns the retrieved passages and aggregates.
04
Tech stack and why
PostgreSQL 15
Reactor config and daily telemetry, with CHECK constraints that encode the scientific envelope.
Qdrant
The literature's vector store: cosine distance over 384-dimension embeddings.
MinIO
An S3-compatible landing zone for raw telemetry Parquet, the bronze layer.
Apache Airflow 2.8
Two DAGs, each with a validation task in its critical path.
all-MiniLM-L6-v2
An embedding model that runs on a CPU, at roughly 16 ms per query once loaded.
FastAPI
Structured endpoints plus the hybrid /chat route.
Anthropic Claude
Synthesis only. It never sees a question without grounding, and the platform works without it.
Streamlit and Plotly
The fleet overview, reactor explorer and research chat.
NumPy and pandas
The kinetic model, with a modified Gompertz envelope.
05
Challenges and what I learned
Hybrid, not vector-only
A vector store can find a passage that discusses yields, but it can't compute a mean over 3,700 telemetry rows. SQL returns exact numbers with no explanation. Running both and labelling the context lets the model explain without doing the arithmetic.
Failing closed
A pipeline that quietly drifts from the ground truth is worse than one that stops. Both DAGs fail closed: no load unless the reference cohort matches the paper, and no finished index unless retrieval returns the right provenance.
Being precise about what matches
The 36 reference reactors match the paper's yields largely by construction, because they are calibrated to it. The leftover gap, at most 0.042 ml/day across twelve cells, measures calibration precision, not predictive accuracy. What the model decides on its own is which reactors sour and when: 361 soured reactor-days above roughly 20% total solids, and none inside the published range.
Degrading gracefully
In testing, an exhausted API key made the language model call fail. /chat reported the billing error and still returned the full retrieval context, instead of failing the request.
What I learned
- Ground truth belongs inside the pipeline. A validation gate in the DAG catches drift that a one-off check would miss.
- Be precise about what a number proves. Calibration precision and predictive accuracy are different claims, and the page should say which one it's making.
06
Screens
These screenshots come from a different simulation run, so a few figures differ slightly from the ones quoted above.
Fleet overview: the reference cohort checked cell by cell against the published Table 2.
Reactor explorer: daily and cumulative yield for a single reactor line.
Research chat: with the API key out of credit, it reports the error and still returns the retrieval trace.
07
Links
See BioStreamer for yourself.
Have something like this in mind?
Have a project, role or research opportunity in mind? Let's talk.