Template Text Generation Systems: Three Approaches

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1347
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    948
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1183
  • image_logo-advance_0.webp
    B2B Advance company logo design
    642
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    921

Problem: Why Deterministic Templates Aren't Enough

When building a text generation system for document workflows processing millions of letters or contracts annually, simple data substitution via {{name}} quickly hits a ceiling. Clients expect personalization: a letter from a bank that addresses them by name and remembers the last interaction is no longer a luxury but a baseline. In practice, every third contract requires manual rework due to non-standard terms — that's hundreds of person-hours monthly. Our team of 15 engineers has implemented over 50 text generation systems for banks, retail, and logistics. This accumulated experience allows us to precisely determine which approach works in your case. There is no universal recipe, but there are three working approaches. Templating as a concept has been around for a long time, but modern realities require hybrid methods.

Why Template Text Generation Goes Beyond Data Substitution

Deterministic templates are reliable: Jinja2, Word templates, LaTeX. No surprises, full auditability. Perfect for contracts, invoices, legal documents where the cost of error is a lawsuit. But as soon as you need to adapt the tone to a client, add a unique offer, or generate an introduction tailored to a specific situation — simple substitution falls short. That's where LLM comes in. However, relying solely on LLM is risky: hallucinations reach 2% of tokens, which is unacceptable for legally significant fields. Therefore, we use fine-tuning with LoRA to adapt the model to a specific domain — this reduces hallucination rates to 0.3% while preserving personalization.

How to Choose Between Deterministic, LLM, and Hybrid Approaches

Here's a comparison by key metrics. Hybrid generation is 3x faster than manual drafting and 10x more accurate than pure LLM for critical fields:

Parameter Deterministic (Jinja2) LLM-based (GPT-4, Claude) Hybrid (Jinja2 + LLM)
Accuracy of legally significant data 100% 95–99% 100% for critical fields
Text personalization No (only substitution) High Medium (LLM only for blocks)
Generation time (per document) <10 ms 1–5 s 0.5–3 s
Hallucinations None Risk (0.5–2% tokens) Only in LLM blocks, controlled
Audit and versioning Git, diff Prompt versions Git for structure + prompt

The hybrid approach delivers maximum accuracy for critical fields and flexibility for variable blocks. Savings on a single template can exceed $30,000 annually by reducing manual rework.

When a Hybrid Templating Engine Becomes a Necessity

If a single document contains rigid blocks (table with sums, signature) and variable blocks (introduction, offer) — the hybrid gives the best of both worlds. We implement it this way: at the architecture level we split the template into segments. Each segment is tagged with a meta tag [deterministic] or [llm]. Rendering runs in parallel: deterministic blocks execute locally (Jinja2), while LLM blocks are sent to the model asynchronously.

Hybrid Renderer Implementation Example
from jinja2 import Template
import asyncio
import openai

async def render_hybrid(template_text: str, data: dict) -> str:
    segments = parse_segments(template_text)
    tasks = []
    for seg in segments:
        if seg.type == 'deterministic':
            t = Template(seg.content)
            tasks.append(asyncio.to_thread(t.render, **data))
        else:
            prompt = seg.content.replace('{{context}}', json.dumps(data, ensure_ascii=False))
            tasks.append(llm_generate(prompt))

    results = await asyncio.gather(*tasks)
    return ''.join(results)

After generation, validation runs: checking matching of key values (sums, dates), regex format checks, and consistency check. For example, if the LLM generates a value that doesn't match the expected data — the template is marked as erroneous and sent for re-generation. To improve quality, we use RAG: we pull relevant contexts from a vector DB (Qdrant) with embeddings of dimension 1536. This reduces hallucinations by an additional 40%.

Implementation Process: From Audit to Deployment

Stage Duration Result
Template audit 1–2 days Structure analysis, variability identification
Architecture design 2–3 days Stack selection, RAG insertion points
Template implementation from 3 days to 2 weeks Working templates with validation
Testing 3–5 days 1000+ scenario run, A/B test
Deployment and monitoring 1–2 days Docker + Kubernetes, logs in W&B

Details of each stage:

  1. Template audit — we analyze your current documents: structure, mandatory fields, variability. Identify blocks that can be automated.
  2. Architecture design — choose a stack: Jinja2 + LLM (OpenAI, Claude, Llama 3) + vector DB (Qdrant) for external context. Determine where RAG insertion is needed. For LLM inference we use INT8 quantization via vLLM — this reduces p99 latency to 1.2 s.
  3. Template implementation — write deterministic templates, configure prompts for LLM blocks, add validators (regex, consistency check). Use few-shot: prepare 3–5 example templates for each document type. For rare cases use chain-of-thought prompts.
  4. Testing — run on 1000+ scenarios: check mandatory fields, no hallucinations, generation time p99 < 3 s. Use A/B test: compare conversion of old (manual) and new template. Monitor GPU utilization — optimize resource consumption.
  5. Deployment and monitoring — deploy via Docker + Kubernetes, log all generations to Weights & Biases. Set up alerts for quality degradation (shorter text length, increased LLM retry rate).

What's Included in the Work and Guarantees

  • Source code of templates (Python + Jinja2, prompts on Hugging Face)
  • Test scenarios (Pytest + mock LLM)
  • Documentation: architecture description, instructions for adding a new template
  • Team training (2 sessions of 2 hours)
  • 1 month post-release support (bugs, fine-tuning)

We have implemented generation for 12 major clients. With 8+ years of experience in document automation and over 50 successful projects, we guarantee: complete auditability of deterministic blocks, no hallucinations in critical fields (via validation), performance p99 latency < 3 s per document. We hold ISO 27001 certification (information security). Hybrid generation pays off in 3–6 months.

Our template text generation system for document workflow automation uses a hybrid templating engine. We'll evaluate your template in one day: contact us to discuss the task. Order a pilot implementation — get a working prototype of the text generation system in 7–14 days turnkey. Get a consultation on document workflow automation.

NLP Development: Text Classification, NER, Embeddings, and Information Extraction

We often receive a task: process 50,000 support tickets — currently all manual. Dataset — 3,000 labeled examples, 12 categories, imbalance: one category occupies 40% of the sample, three at 1-2% each. Baseline accuracy — 78%. Sounds decent until you look at recall for rare classes: 0.31, 0.44, 0.28. These classes — complaints and churn threats — are most important to the business.

This is a typical NLP development project. The problem is not the algorithm but that accuracy is the wrong metric. Our experience across 30+ projects shows: we start by analyzing business metrics and only then choose the model.

Why accuracy is not the right metric for rare classes?

Accuracy ignores imbalance. If the "churn" class appears in 2% of cases, the model can predict "all good" and get 98% accuracy — but the business loses clients. Solution: F1 macro (averaged over all classes) or weighted F1. For NER — strict entity F1 (exact matches only). We guarantee: after choosing the correct metric, model quality becomes measurable and predictable.

Text Classification: From BERT to Distillation

BERT-like models are the standard for classification. ruBERT-base or ruBERT-large from DeepPavlov for Russian. multilingual-e5-large — for multiple languages in one pipeline. XLM-RoBERTa-large — a strong multilingual backbone.

Fine-tuning for classification: add a classification head on top of the [CLS] token, train for 3-5 epochs with lr=2e-5, weight decay=0.01. For imbalance — weighted CrossEntropyLoss or focal loss with gamma=2.0. Contact us — we will show a code snippet.

Imbalance case study. Dataset — 3,000 examples, imbalance 1:20. Solution: class_weight via sklearn + CrossEntropyLoss. Additionally — augmentation of rare classes via backtranslation (ru→en→ru through MarianMT). Recall for rare classes rose from 0.31 to 0.67 with a slight drop in accuracy (76%→74%). Full NLP development end-to-end took 3 weeks.

Distillation for production. BERT-large gives F1 0.89, but inference on CPU — 180ms. Distillation into DistilBERT or ruBERT-tiny2 reduces latency to 25ms with F1 0.84. Export to ONNX Runtime provides an additional 1.5-2x speedup. DistilBERT achieves 7x lower latency than BERT-large with only a 5% drop in macro F1 – a typical production trade-off.

Model F1 macro Latency (CPU) Size
BERT-large 0.89 180 ms 1.3 GB
DistilBERT 0.84 25 ms 250 MB
ruBERT-tiny2 0.81 12 ms 120 MB
DistilBERT + ONNX 0.84 14 ms 150 MB

How to choose between BERT and LLM for your task?

For most classification and extraction tasks, BERT-sized models offer the best trade-off between cost and performance. Shift to LLMs only when the task demands generation, complex reasoning, or zero-shot generalization.

NER: Named Entity Recognition

NER — extracting persons, organizations, locations, dates, amounts, document numbers. For general categories (PER, ORG, LOC), pre-trained models work well. For specialized ones (medical terms, legal concepts) — fine-tuning is needed.

Data annotation. The main cost of an NER project. For a quality model — 500-2,000 labeled sentences per entity type. Tools: Label Studio (open source) or Prodigy (by spaCy creators). IOB2 format — standard.

Architecture. Token classification on top of BERT: each token gets a label (B-PER, I-PER, O). spaCy 3.x with transformer pipeline — a convenient production choice.

Nested entities. Standard IOB models cannot handle nested entities (organization inside an address). For such tasks — span-based NER: SpanBERT or SpERT. More complex but correct.

Post-processing is mandatory. The model predicts tokens — normalized entities are needed. Date — dateparser. Amounts — regex + validation. Names — deduplication via rapidfuzz. Included in our standard delivery.

Sentiment Analysis and Opinion Mining

Binary classification positive/negative works out of the box with BERT. Complexity — aspect-based sentiment analysis (ABSA): "the restaurant has good food but terrible service." For ABSA: aspect extraction (NER) + sentiment per aspect. Joint models BERT-for-ABSA — quality on Russian data is lower due to dataset scarcity. RuSentiment, SentiRuEval — main resources.

For production with simple positive/negative/neutral: distil models are enough. Three classes, balanced dataset, 2,000+ examples — F1 macro 0.82-0.87 in 1-2 days.

Text Summarization

Extractive summarization (select sentences) — TextRank or BM25 without training. Fast, no hallucinations. Good for long documents.

Abstractive (generates new text) — seq2seq: mT5, mBART, FRED-T5, ruT5-large. For production via LLM API (GPT-4, Claude) — often the best cost/quality/speed trade-off.

Embeddings: Vector Representations of Text

Embeddings are the foundation of semantic search, deduplication, clustering, RAG. Quality critically affects downstream tasks.

Models. E5-large-v2, BGE-M3, multilingual-e5-large — strong multilingual embedders. sentence-transformers/paraphrase-multilingual-mpnet-base-v2 — fast option. For Russian: ru-en-RoSBERTa (Skoltech) performs well on semantic textual similarity.

Embedding quality evaluation uses the MTEB benchmark as standard. But top results on MTEB don't guarantee success on a domain dataset — we build domain-specific eval.

Fine-tuning embeddings. If standard models don't give the required Recall@k — contrastive learning on domain pairs with MultipleNegativesRankingLoss. How to perform this for domain data:

  1. Collect 500–2,000 semantically similar pairs from your domain.
  2. Apply MultipleNegativesRankingLoss with a batch size of 32–64.
  3. Train for 1–3 epochs using AdamW (lr=2e-5).
  4. Evaluate Recall@k on a held-out domain test set.

This approach yields a 5–15% improvement in Recall@k in practice.

Dimensionality and storage. E5-large: 1024 dim, float32 — 4KB per vector. For 10M documents — 40GB. Quantization int8 reduces to 10GB. FAISS IVF_PQ — more compact but with losses. Included in our deployment recommendations.

Information Extraction

Structured extraction is a frequent task. Examples: key contract terms, technical characteristics, dates and amounts from invoices.

  1. Regex + rule-based. For INN, OGRN, amounts, dates — more reliable than neural networks. No data required.
  2. NER + post-processing. For variable formats.
  3. LLM with structured output. GPT‑4 / Claude with JSON schema — for complex documents. Cost: minimal per document. For 10k+ documents/day — we calculate the economics.

We guarantee a hybrid: regex/NER for typical fields + LLM for edge cases. Our guarantee is backed by years of production experience and more than 30 projects.

Work Stages

Stage Duration What's included
Data and metric analysis 3-5 days Class distribution, text lengths, baseline
Baseline (TF‑IDF + LogReg) 1 day Quick estimate of gap with deep models
Training and validation 1-2 weeks k‑fold, early stopping, error analysis
Deployment (ONNX + FastAPI) 1-2 weeks REST API, batching, monitoring
Documentation and training 2-3 days Model card, API docs, team training

Prototype on existing data — 1-3 weeks. Production system with CI/CD — 1.5–2.5 months. Cost is calculated individually — get a consultation for a project estimate.

What's Included

  • Model and pipeline architecture documentation
  • Access to the model via REST API (FastAPI + ONNX)
  • Client team training (2-hour webinar + Q&A)
  • Accuracy guarantee on the agreed test set
  • Months of post-delivery support (bug fixes, adaptation to new data)

Our Experience

Years of NLP projects from classification to RAG systems. The team includes ML engineers experienced with Hugging Face, spaCy, LangChain, MLOps. We use vLLM, Kubeflow, Weights & Biases — a production stack, not toys. Contact us to evaluate your NLP project within two days — request a free consultation on your text processing pipeline.