AI Data Analyst – Text-to-SQL Digital Data Analyst

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.
Showing 1 of 1All 1564 services
AI Data Analyst – Text-to-SQL Digital Data Analyst
Medium
from 2 weeks to 3 months
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1357
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    956
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1188
  • image_logo-advance_0.webp
    B2B Advance company logo design
    646
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    929

AI Data Analyst – Text-to-SQL Digital Data Analyst

A marketing team spends 4 hours on a single data request. Two analysts are swamped with tickets, while business waits for reports. This situation is familiar to many. We solved it for an e-commerce project with AI Data Analyst — a digital employee and AI agent that independently generates SQL, executes queries, builds charts, and provides interpretation. All in natural language. No template dashboards — any ad-hoc question turns into an answer in minutes. The digital analyst responds 120 times faster than manual analysis and reduces analytics costs by 60%. Budget savings on analytics reach 60%, and a typical project pays off in 2–3 months.

What Problems Does AI Data Analyst Solve?

Ad-hoc Queries Without an Analyst

Manual analytics stalls on typical questions: "how many orders yesterday?", "what is the cohort retention?", "top products by revenue." BI dashboards cover 20% of needs, the rest are ad-hoc. AI Data Analyst takes over 80% of repetitive ad-hoc queries, freeing analysts for deep research.

Automated Reporting on a Schedule

Daily digests, weekly cohort reports, seasonality monitoring — set up once and run via cron. No human involvement.

Real-Time Anomaly Detection

Drop in conversion, abnormal error rate surge, sudden spike in returns — the system alerts with an interpretation of the cause. The LLM explains what happened and how critical it is. According to research on the Spider dataset for Text-to-SQL, the accuracy of SQL generation on the first attempt reaches 81%.

How AI Data Analyst Solves the Ad-hoc Analytics Problem?

The digital analyst receives a question in Russian or English, turns it into an SQL query to your database, loads data, visualizes, and writes conclusions. Unlike BI tools with fixed dashboards, it works with arbitrary queries — no limitations.

Text-to-SQL Core

Example Implementation of DataAnalystAgent
from openai import AsyncOpenAI
from typing import Optional
import pandas as pd
import json

client = AsyncOpenAI()

class SQLGenerator:

    def __init__(self, schema: dict):
        """
        schema: {
            "table_name": {
                "columns": [{"name": "...", "type": "...", "description": "..."}],
                "description": "...",
                "relationships": [...]
            }
        }
        """
        self.schema = schema
        self.schema_context = self._format_schema()

    def _format_schema(self) -> str:
        parts = []
        for table, info in self.schema.items():
            cols = ", ".join(
                f"{c['name']} {c['type']} -- {c.get('description', '')}"
                for c in info["columns"]
            )
            parts.append(f"-- {info.get('description', '')}\nCREATE TABLE {table} ({cols});")
        return "\n\n".join(parts)

    async def generate_sql(self, question: str) -> dict:
        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "system",
                "content": f"""Ты — аналитик данных. Генерируй только SELECT-запросы.
Схема базы данных:
{self.schema_context}

Правила:
- Всегда используй явные JOIN (не implicit)
- Для временных рядов — GROUP BY дата с нужной гранулярностью
- Если вопрос неоднозначен — выбери наиболее вероятную интерпретацию и укажи допущение
- Верни JSON: {{"sql": "...", "assumption": "...", "chart_type": "bar|line|pie|table"}}"""
            }, {
                "role": "user",
                "content": question,
            }],
            response_format={"type": "json_object"},
        )

        return json.loads(response.choices[0].message.content)


class DataAnalystAgent:

    def __init__(self, db_connection, schema: dict):
        self.db = db_connection
        self.sql_gen = SQLGenerator(schema)

    async def answer(self, question: str) -> dict:
        """Полный цикл: вопрос → SQL → данные → интерпретация"""

        # Генерация SQL
        sql_result = await self.sql_gen.generate_sql(question)
        sql = sql_result["sql"]

        # Выполнение запроса
        try:
            df = await asyncio.get_event_loop().run_in_executor(
                None, pd.read_sql, sql, self.db
            )
        except Exception as e:
            # Попытка исправить SQL
            fixed = await self.fix_sql_error(sql, str(e))
            df = await asyncio.get_event_loop().run_in_executor(
                None, pd.read_sql, fixed, self.db
            )

        # Интерпретация результата
        interpretation = await self.interpret_results(question, df)

        return {
            "question": question,
            "sql": sql,
            "data": df.to_dict("records")[:100],
            "summary": df.describe().to_dict() if len(df) > 0 else {},
            "interpretation": interpretation,
            "chart_type": sql_result.get("chart_type", "table"),
            "assumption": sql_result.get("assumption"),
        }

    async def interpret_results(self, question: str, df: pd.DataFrame) -> str:
        if df.empty:
            return "Запрос не вернул данных. Проверьте условия фильтрации."

        stats = df.describe().to_string() if df.select_dtypes(include="number").shape[1] > 0 else ""
        sample = df.head(10).to_string()

        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "system",
                "content": "Интерпретируй результаты запроса для бизнес-аудитории. Выдели ключевые инсайты, аномалии, тренды. Конкретные числа."
            }, {
                "role": "user",
                "content": f"Вопрос: {question}\nСтатистика:\n{stats}\nПример данных:\n{sample}",
            }],
        )

        return response.choices[0].message.content

Automated Analytics

class AutomatedReportingSystem:
    """Система автоматических аналитических отчётов"""

    REPORT_SCHEDULE = {
        "daily_sales": {
            "cron": "0 8 * * *",
            "questions": [
                "Выручка за вчера vs неделю назад",
                "Топ-10 продуктов по выручке за вчера",
                "Аномалии в транзакциях за вчера",
            ],
            "recipients": ["[email protected]", "[email protected]"],
        },
        "weekly_cohort": {
            "cron": "0 9 * * 1",
            "questions": [
                "Retention когорт за последние 8 недель",
                "LTV по каналам привлечения",
                "Churn rate за неделю vs предыдущие 4 недели",
            ],
            "recipients": ["[email protected]"],
        },
    }

    async def generate_scheduled_report(self, report_name: str) -> str:
        config = self.REPORT_SCHEDULE[report_name]
        analyst = DataAnalystAgent(self.db, self.schema)

        sections = []
        for question in config["questions"]:
            result = await analyst.answer(question)
            chart = await self.create_visualization(result)
            sections.append({
                "question": question,
                "interpretation": result["interpretation"],
                "chart_url": chart,
            })

        return await self.format_report(report_name, sections)

Anomaly Alerts

class AnomalyDetector:

    async def detect_and_alert(self) -> list[dict]:
        """Ежедневное выявление статистических аномалий в ключевых метриках"""

        metrics_to_monitor = [
            {"name": "daily_revenue", "query": "SELECT SUM(amount) FROM orders WHERE date = CURRENT_DATE"},
            {"name": "conversion_rate", "query": "..."},
            {"name": "api_error_rate", "query": "..."},
        ]

        alerts = []
        for metric in metrics_to_monitor:
            current_value = await self.db.fetchval(metric["query"])
            historical = await self.db.fetch(metric["history_query"])

            mean = statistics.mean(historical)
            stdev = statistics.stdev(historical)
            z_score = (current_value - mean) / stdev if stdev > 0 else 0

            if abs(z_score) > 2.5:
                # Запрашиваем у LLM интерпретацию аномалии
                interpretation = await self.interpret_anomaly(metric, current_value, mean, z_score)
                alerts.append({
                    "metric": metric["name"],
                    "current": current_value,
                    "expected_range": (mean - 2 * stdev, mean + 2 * stdev),
                    "z_score": z_score,
                    "interpretation": interpretation,
                })

        return alerts

Comparison: BI Dashboards vs AI Data Analyst

Criteria BI Dashboards AI Data Analyst
Query type Pre-defined Arbitrary ad-hoc
Response time for a new question Days (need developer) Seconds to minutes
Flexibility Fixed filters Natural language
Interpretation Numbers only AI insights
Automated reports Require setup Created via cron

Limitations of Direct GPT-4 Calls

Directly asking GPT-4 "how many orders yesterday?" is a bad idea. The model doesn't know your schema: table names, types, relationships. It will invent names, generate invalid SQL, and hallucinate interpretations. AI Data Analyst wraps the LLM in a custom pipeline: the schema skeleton (table names, columns, types) is fed into the system prompt, the query is executed against a real database, errors are caught and fixed with a retry including the error text. This yields the 81% correctness on the first attempt.

Additionally, we use Retrieval-Augmented Generation (RAG) — if the schema is large (50+ tables), we load only the relevant ones based on the query. This reduces token cost and improves quality.

From Our Practice: E-commerce with 15 Ad-hoc Queries a Day

Our client — a marketing team of 5 people. They sent 15–20 questions to analysts, each answer took an average of 4 hours. We deployed AI Data Analyst on PostgreSQL with 12 tables (orders, customers, products, traffic). Results:

  • Average response time dropped from 4 hours to 2 minutes.
  • SQL correctness on first attempt — 81% (remaining get auto-fixed).
  • Analysts switched to complex analysis and experiments.
  • Team satisfaction rated 4.3/5.0.
  • Budget savings on analytics reached 60%, project paid off in 2 months.

We have been in the market for over 5 years, completed 30+ AI projects, therefore we guarantee quality.

How We Implement AI Data Analyst?

  1. Data source audit — description of schemas, types, relationships, typical queries.
  2. Schema skeleton creation — formatting for system prompt, semantics definition.
  3. Prompt engineering — customizing SQL generation rules, output format, interpretation.
  4. Channel integration — Slack, Teams, Telegram, or web interface.
  5. Launch and iteration — testing on real queries, improving auto-fix mechanisms.

How Long Does Each Stage Take?

Stage Duration
Text-to-SQL for your schema 1–2 weeks
Automated reports and visualizations 1–2 weeks
Slack/Teams integration 1 week
Anomaly detection 1 week
Total 4–6 weeks

What Is Included in the Result

  • Documentation — architecture description, schema, operating instructions.
  • Access to source code — fully transparent implementation in your repository.
  • Team training — 2–3 working days for analysts and engineers.
  • Post-launch support — 2 weeks on-call, bug fixing, fine-tuning.
  • Quality guarantee — SQL accuracy not lower than 80%, stable integration, working alerts.

Contact us — we will tell you how AI Data Analyst can reduce analytics time and save budget in your company. Request a free demo and evaluate the results on your own data.

LLM Development: Fine-Tuning, RAG, Agents, and Production Deployment

Using GPT‑4 or Claude 3.5 Sonnet through a public API is not a solution — it's just a tool. When the requirement is to "make it like ChatGPT, but on our data," there is a real engineering challenge behind it: from prompt engineering to training a 70B model on your own infrastructure. End-to-end LLM solution development is a complex stack, and we have been doing it for over 5 years. During this time, we have completed over 20 projects in generative AI: from RAG systems for legal departments to custom support agents. Where exactly your task falls depends on data, latency requirements, budget, and how critical confidentiality is.

A typical situation: the client has already tried ChatGPT, but results are unstable — sometimes accurate, sometimes hallucinating. Or they need integration into a corporate portal while complying with security policies. Let's break down each layer of the stack in detail — from RAG to production deployment.

Why Do RAG Systems Break and How to Fix It?

RAG (Retrieval-Augmented Generation) looks simple: find relevant documents, put them in context, get an answer. In practice, it fails in several places.

Chunking without overlap. Classic mistake: chunk_size=512, overlap=0. If the answer lies across two chunks, retrieval won't find either with sufficient confidence. Solution: overlap 15–25% of chunk_size, or better yet, sentence-aware splitting with spaCy or NLTK instead of naive character splitting.

Poor embedder. text-embedding-ada-002 is good for general use, but on legal or medical texts, specialized models like E5-large-v2, BGE-M3, or fine-tuned sentence-transformers on domain data outperform it. Recall@5 differences can be 15–25%.

No re-ranking. Vector search optimizes for speed, not relevance. A cross-encoder re-ranker (ms-marco-MiniLM-L-6-v2, bge-reranker-large) after initial retrieval improves top-3 accuracy with acceptable latency (+50–150ms). This is often more impactful than improving the embedding model.

Hybrid search. Dense vectors alone work poorly on exact queries: names, SKUs, codes. BM25 (sparse) finds exact matches but misses semantics. Hybrid via RRF (Reciprocal Rank Fusion) is the optimal compromise. Qdrant, Weaviate, and pgvector 0.7+ support hybrid search natively.

Typical production architecture for a corporate knowledge base
  1. Documents → preprocessing (PyMuPDF, Unstructured)
  2. Chunking → embedding (BGE-M3)
  3. Qdrant (hybrid dense+sparse)
  4. Cross-encoder re-ranking
  5. Context → LLM (vLLM or OpenAI API)
  6. Answer with sources (RAGAS for quality evaluation)

When to Fine-Tune Instead of Prompt Engineering?

Prompt engineering solves ~70% of LLM adaptation tasks for a domain. The remaining 30% require fine-tuning. Three indicators: the model ignores a specific output format even with detailed prompting; the task requires deep knowledge of specialized vocabulary (medicine, law); you need to significantly reduce token costs by replacing a large model with a smaller specialized one.

LoRA and QLoRA are the standard for SFT. LoRA adds trainable low-rank matrices to attention layers. A typical configuration for Llama-3 8B: r=64, lora_alpha=128, target_modules=["q_proj","v_proj","k_proj","o_proj"] yields ~0.8% trainable parameters, training on one A100 40GB. QLoRA adds 4-bit quantization (NF4) and allows fine-tuning 70B models on two A100 40GB, though speed drops by half compared to bf16.

DPO instead of RLHF. Direct Preference Optimization requires only (chosen, rejected) pairs, not scalar reward signals. DPOTrainer from the trl library (Hugging Face) implements it in a few dozen lines.

Common mistake. A dataset of 500 examples, 5 epochs, validation loss 0.8 — seems fine. But on test, the model degrades on general instructions. Cause: catastrophic forgetting. Solution: add 10–20% general instruction-following examples (Alpaca, FLAN) to the training set to preserve original capabilities.

How to Choose a Base Model: 8B or 70B?

Model Parameters Strengths Context
Llama-3.1 8B 8B Quality/speed balance 128k
Llama-3.1 70B 70B Complex reasoning 128k
Mistral 7B / Mixtral 8x7B 7B / 47B Efficiency for size 32k
Qwen2.5 72B 72B Code, multilingual 128k
Gemma 2 27B 27B Open license 8k

For most tasks, fine-tuning an 8B model is sufficient. 70B is needed when deep reasoning is required or the 8B baseline does not reach the required quality even after fine-tuning. Inference cost for Llama-3 8B via vLLM on A100 is efficient; the exact cost depends on volume.

What Does PagedAttention Bring to Production?

vLLM is the first choice for serving open-source models. PagedAttention is the key technical innovation: KV-cache is managed like virtual memory in an OS, without fragmentation. This yields 2–4x higher throughput compared to naive HuggingFace Transformers inference. The vLLM documentation confirms that continuous batching and PagedAttention are the standard for high-load LLM services.

Typical numbers on A100 80GB for Llama-3 8B (bf16): 400–600 req/s, P50 latency 200–400ms, P99 latency 600–900ms at concurrency 64. For 70B on two A100 with tensor parallelism: 80–120 req/s, P99 latency 1.5–2.5s. AWQ or GPTQ quantization reduces memory consumption by 2x with quality loss within 1–3%.

Multi-Agent Systems

Agents are LLMs with access to tools: search, code execution, API calls, database interaction. Common patterns:

  • ReAct (Reason + Act): the model reasons → chooses a tool → observes the result → reasons again. LangChain and LlamaIndex implement it out of the box.
  • Multi-agent orchestration: multiple specialized agents with a coordinator on top. Example: coordinator → researcher (search + summarization) → coder (code generation and execution) → critic (verification). Tools: AutoGen (Microsoft), CrewAI, custom implementation on LangGraph.

In production, agent systems are non-deterministic. Essential: guardrails, step limits, logging of each step, human-in-the-loop for critical actions.

How We Work: Stages, Timeline, Deliverables

Stage Duration What You Get
Audit and data collection 1–2 weeks Eval dataset of 100+ examples, task formalization
Baseline (prompt + RAG) 1–2 weeks Working prototype, quality metrics
Fine-tuning (if needed) 2–4 weeks Trained model, LoRA weights, model card
Deployment and monitoring 1–2 weeks vLLM server, Grafana + Prometheus
Documentation and training 1 week API documentation, team training

What Is Included

We deliver:

  • Technical documentation (model card, configs, deployment instructions)
  • Access to infrastructure (code repository, trained weights)
  • 1 month of post-deployment support (consultations, bug fixes)
  • Customer team training (2–3 sessions on system operation)

Timeline: basic RAG prototype — 1–2 weeks. Fine-tuning with customer data — 3–6 weeks (including data preparation). Production system with monitoring and retraining — 2–4 months. Cost is calculated individually based on data volume, model complexity, and infrastructure requirements.

We guarantee the quality of the final model with performance benchmarks and ongoing monitoring. Our engineers have hands‑on experience with dozens of production LLM systems.

Want to evaluate your project? Leave a request — we will prepare a preliminary summary within 1–2 business days. Or get a consultation on choosing the approach: RAG, fine-tuning, or hybrid — we will tell you what works best for you. Contact us to discuss your LLM development needs. Schedule a free consultation today.