TenderML: AI Document Classification for German Construction Tenders
A German construction firm was manually sifting through dense tender PDFs — with over half of each document being irrelevant noise. I built a semi-supervised ML pipeline from scratch that automatically classifies text and extracts structured fields with 95% production accuracy, eliminating the manual review bottleneck entirely.
Manual review of construction tenders was slow, inconsistent, and error-prone
A large German construction company receives a high volume of tender documents — dense PDFs containing specification tables, pricing, SI units, and legal boilerplate all mixed together. More than half the text in every document was irrelevant filler. Staff had to read each document end-to-end just to locate the critical fields: section headings, unit measurements, and line-item prices.
The real technical difficulty was not just classifying text as relevant or irrelevant. The documents followed no consistent format. The same bullet-point hierarchy could be numbered as 1 / 1.1, 1. / 1.10, or 1.20 depending on the source. SI units and prices could appear on the same line as a heading, or several bullet levels below it. No regex or rule-based system could handle that variability reliably.
Sole developer — end-to-end ownership
I owned this project entirely: problem framing, ML approach selection, data preparation, model training, the tree and DFS extraction logic, FastAPI backend development, and production deployment on AWS EC2 with Ray-based autoscaling. No other engineers were involved.
How structural inconsistency was solved with semi-supervised learning and tree traversal
Challenge 1 — Limited labeled data
The training corpus was 1,000+ German PDF documents, but only a subset had been manually annotated — individual lines highlighted as important or not important by domain experts. Training a fully supervised classifier on this partial data would generalize poorly to the unlabeled majority.
I used a semi-supervised approach: train an initial SVM classifier on the labeled subset, then use its confidence scores to propagate labels to unlabeled documents iteratively — accepting only predictions above a confidence threshold. This allowed the model to learn from the full corpus without requiring exhaustive manual annotation.
Challenge 2 — Structural inconsistency across documents
Even after classifying text as relevant, extracting the specific fields — headings, SI units, prices — required understanding each document's structure. The bullet-point hierarchy was the semantic signal, but the numbering formats varied too much for regex patterns to handle reliably.
My solution was to model each document as a tree structure that mirrors the bullet-point hierarchy, regardless of the numbering format used. A custom parser normalizes the numbering schemes and builds the tree. Then a depth-first search (DFS) algorithm traverses it to correctly associate parent headings with their child body text, SI units, and prices — even when those fields appear in non-standard positions.
Challenge 3 — Production reliability
The system had to integrate cleanly into the client's existing software via API. I wrapped the entire inference pipeline in a FastAPI backend, deployed it on AWS EC2 with Ray clusters for autoscaling under variable document load. The model artifacts — SVM, SGD, CRF, and TF-IDF vectorizers — are serialized and loaded at startup, keeping inference latency low.
Pipeline overview: offline training → online inference
The system has two distinct phases. Offline training — run once or periodically to retrain — produces serialized model artifacts. The online inference pipeline loads those artifacts at startup and processes new PDFs in real time via the FastAPI layer.
Key engineering decisions
Why SVM over a neural approach
SVMs with TF-IDF features are highly interpretable and generalize well on small, domain-specific corpora — critical when labeled data is scarce. A fine-tuned transformer would have required far more labeled examples to avoid overfitting on this niche German construction vocabulary. The SVM achieved 95% accuracy in production, validating the choice.
CRF for sequence labeling
A Conditional Random Field (CRF) was used alongside the SVM to handle sequence-level decisions — specifically, labeling runs of lines within a bullet section. CRFs capture dependencies between adjacent labels, which matters when a single section spans multiple lines with mixed relevant and irrelevant content.
Tree + DFS instead of regex
Rather than writing brittle regex patterns for each numbering variant, I built a generalized parser that constructs a tree from any bullet hierarchy. DFS traversal then operates on the tree semantics — parent/child relationships — rather than raw text positions. This made the extraction logic robust to format changes without requiring code updates for each new document variant.
Semi-supervised confidence propagation
Labels were propagated from the annotated seed set by running the trained classifier on unlabeled documents and accepting predictions only above a confidence threshold, tuned via held-out validation. This self-training loop iterated until convergence, maximizing use of the available unlabeled data without introducing noisy pseudo-labels.
Delivered and integrated into production
The system achieved 95% classification accuracy in production — measured against ground truth on held-out documents the model had never seen during training. After integration into the client's existing software stack via the FastAPI API layer, the time required to extract structured information from each tender document dropped significantly compared to the manual review process.
The client left a 5-star review on Clutch.co, specifically calling out the quality of the engineering and the reliability of the delivered system. The project ran for 4+ months from initial scoping through production deployment — handled entirely by a single engineer.
The system successfully automated the extraction of critical tender information, significantly reducing manual review time. The accuracy and reliability of the pipeline exceeded our expectations.
Technologies used
- Python
- SVM (scikit-learn)
- CRF (sklearn-crfsuite)
- SGD Classifier
- TF-IDF Vectorizer
- FastAPI
- AWS EC2
- Ray Clusters
- PDF Processing
- Semi-supervised Learning
- DFS Tree Traversal