Skip to content

Architecture

The application follows a layered structure with a clear separation between user interface, input processing, calculation core, and output formats. A central design decision is the isolation of the LLM to input processing only: as soon as the parameters are available in structured form, a deterministic calculation core takes over. This makes the calculation results reproducible and independent of the model used.

At a glance

  • Layered model with user interface, LLM-assisted parser layer, deterministic calculation core, session management, and output layer
  • Strict separation of AI-assisted input processing and rule-based calculation
  • State management via a state machine with defined transitions
  • Configuration entirely through environment variables, pay scale data held in separate configuration modules
  • Containerisation via Docker; reverse-proxy-capable through a configurable URL prefix
  • LLM connection via an OpenAI-compatible chat API
  • Import tool for updating pay scale data from external Excel sources

Architecture description

Component overview

flowchart TB
    User([User])

    subgraph UI["User interface"]
        Form[Form]
        Chat[Chat]
        Result[Result display]
    end

    subgraph Session["Session management"]
        State[State machine]
        History[History]
    end

    subgraph Parser["Parser layer"]
        Extractor[Parameter extractor]
        Validator[Schema validator]
        LLMClient[LLM client]
    end

    subgraph Calculator["Calculation core (deterministic)"]
        Core[Calculation logic]
        Periods[Yearly slices]
        Steps[Level progression]
    end

    subgraph Output["Output"]
        Tables[Markdown tables]
        Excel[Excel export]
        PDF[PDF export]
    end

    subgraph Config["Configuration"]
        TVL[Pay scale table]
        AG[Employer contributions]
        SHK[Student assistant rates]
    end

    LLM[(OpenAI-compatible<br/>chat API)]
    Importer[Pay scale importer]
    Excel_Source[(Excel source)]

    User --> Form
    User --> Chat
    Form --> Session
    Chat --> Session
    Session --> State
    Session --> Parser
    Extractor --> LLMClient
    Extractor --> Validator
    LLMClient <--> LLM
    Session --> Calculator
    Calculator --> Core
    Core --> Periods
    Core --> Steps
    Calculator --> Output
    Output --> Result
    Output --> Excel
    Output --> PDF
    Result --> User

    Config -.-> Calculator
    Importer --> Config
    Excel_Source --> Importer

User interface and session management

The user interface uses a two-column layout. The left column contains a form with collapsible sections for settings, position input, and the list of positions; the right column hosts the chat history and shows the calculation result. Both input paths write to the same list of positions and can be combined within a single session.

The session management keeps a per-user state (e.g. initial, parsing, clarifying, calculating, complete, q_and_a, fallback) and a conversation history. A state machine permits only defined transitions; after a configured number of unsuccessful parse attempts, it switches to fall-back mode and activates the form.

Parser layer

The parameter extractor formulates the request to the LLM, extracts the JSON response, and runs schema-based validation through Pydantic. Before the actual validation, a pre-check applies: if the LLM returns a position list with missing required fields, it is converted into a specific clarification question. If processing fails, the extractor invokes the LLM again with an error-oriented prompt; repeated failures are reported to the session management.

The LLM client encapsulates communication with an OpenAI-compatible chat API. Endpoint availability is verified at session start; if the endpoint is unreachable, the system falls back to a mock client, allowing the application to be operated without an active LLM.

After a calculation has been completed, a second prompt configuration takes over the Q&A mode: the LLM answers questions about the calculation based on the already computed result, which is supplied as context. Independent numerical values are not produced in this mode.

Calculation core

The calculation core operates entirely deterministically and independently of the LLM. A calculation comprises multiple positions; each position is first broken down into yearly slices. For each slice, base salary, employer-gross, prorated special payment, and total cost are computed. Pay scale increases are applied cumulatively per year; planned level progressions trigger a transition between the old and new level within the affected year. Student assistants are calculated separately on an hourly basis, with a cap of 80 hours per month and an hourly rate updated yearly.

For the BUND breakdown, pension, health, unemployment, and long-term care insurance contributions are reported separately as fixed shares of the gross. All calculations use decimal arithmetic to avoid rounding deviations.

Configuration and pay scale data

Pay scale data (pay scale table, special payment rates, employer contributions, student assistant hourly rates) is held in separate configuration modules and read by the calculation core at runtime. Application parameters (LLM endpoint, server host and port, URL prefix, default pay scale region, pay scale increases, log level) are controlled through environment variables. A standalone command-line tool imports updated pay scale data from Excel files and generates the configuration modules; a validation option checks plausibility.

Operation

The application runs as a Docker container. The container exposes a single HTTP port; a health check verifies availability of the user interface. The configurable URL prefix allows the application to operate behind a reverse proxy under any path. The default user inside the container does not have root privileges.

Technologies used

  • Web framework: Gradio
  • Schema validation: Pydantic
  • HTTP client: requests
  • Excel processing: openpyxl, pandas, xlrd
  • PDF generation: reportlab
  • LLM interface: OpenAI-compatible chat API
  • Containerisation: Docker
  • Test framework: pytest