From 68c7195d541c8c823a0d766c61717ff62b924e52 Mon Sep 17 00:00:00 2001 From: Jaakko Vanhala Date: Mon, 13 Apr 2026 06:59:12 +0300 Subject: [PATCH] TEMPLATING.md: periaatteet rakennuspalapohjaiselle koodigeneroinnille --- TEMPLATING.md | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 TEMPLATING.md diff --git a/TEMPLATING.md b/TEMPLATING.md new file mode 100644 index 0000000..b159a95 --- /dev/null +++ b/TEMPLATING.md @@ -0,0 +1,157 @@ +# Templating — rakennuspalaset koodigeneroinnissa + +## Perusperiaate + +Kielimalli päättää **mitä** rakennetaan (entiteetit, kentät, tyypit, yhteydet). +Template-funktiot päättävät **miten** se rakennetaan (importit, engine setup, testikonfiguraatio). + +``` +Projektikuvaus → LLM → JSON-speksi → Templateit → Koodi → Validointi +``` + +LLM:n kontribuutio on yksi JSON-rakenne. Kaikki muu on determinististä — +sama speksi tuottaa aina saman koodin. + +## Miksi tämä toimii + +Pienen kielimallin (0.5B–7B) vahvuudet ja heikkoudet ovat epäsymmetrisiä: + +| Tehtävä | LLM:n kyky | Ratkaisu | +|---------|-----------|----------| +| Tunnista entiteetit kuvauksesta | Hyvä | LLM tekee | +| Valitse kenttätyypit | Hyvä | LLM tekee | +| Muista importit oikein | Huono | Template tekee | +| SQLite connect_args | Huono | Template tekee | +| Testikonfiguraatio | Huono | Template tekee | +| Dockerfile-rakenne | Huono | Template tekee | + +Annetaan mallin tehdä se missä se on hyvä. Hoidetaan loput mekaanisesti. + +## JSON-speksi + +Kielimallin ainoa tuotos on JSON joka kuvaa projektin rakenteen: + +```json +{ + "project_name": "library-app", + "entities": [ + { + "name": "Author", + "table_name": "authors", + "fields": [ + {"name": "name", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null} + ] + }, + { + "name": "Book", + "table_name": "books", + "fields": [ + {"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null}, + {"name": "author_id", "sa_type": "Integer", "py_type": "int", "nullable": false, "default": null} + ] + } + ], + "relationships": [ + {"from": "Book", "field": "author_id", "to": "Author", "type": "many-to-one"} + ], + "extra_imports": [] +} +``` + +Speksin laatu ratkaisee kaiken. Hyvä speksi → hyvä projekti. Huono speksi → +teknisesti toimiva mutta sisällöllisesti väärä projekti. + +## Architect-promptin rooli + +Architect-agentti (JSON-speksin generoija) on kriittisin kohta koko pipelinessa. +Sitä ohjataan neljällä keinolla: + +1. **Chain-of-thought** — malli miettii ensin entiteetit, sitten kentät, + sitten yhteydet, vasta lopuksi JSON +2. **Domain-esimerkit** — Todo, verkkokauppa, blogi — malli näkee miltä + hyvä speksi näyttää eri domaineissa +3. **Anti-patternit** — turhat ID-kentät, Enum-tyypit, suomenkieliset nimet +4. **Yhteyssäännöt** — jokainen `_id`-kenttä tarvitsee relationship-merkinnän + +Isompi malli tässä yhdessä kohdassa parantaisi kaikkien projektien laatua. + +## Templateit + +Jokainen template on funktio joka ottaa speksin ja palauttaa koodia: + +``` +tmplModels(spec) → models.py (SQLAlchemy, ForeignKey, relationship) +tmplSchemas(spec) → schemas.py (Pydantic Create/Response/Detail) +tmplMain(spec) → main.py (FastAPI CRUD + nested endpoints + FK-validointi) +tmplTests(spec) → test_main.py (pytest + TestClient + helper-funktiot) +tmplPyproject(spec) → pyproject.toml (PEP 621) +tmplDockerfile() → Dockerfile (uv + non-root user) +``` + +Templateit generoivat automaattisesti: +- ForeignKey-constraintit ja relationship()-määrittelyt +- Nested endpointit (`GET /authors/{id}/books/`) +- FK-validointi (404 jos parent-entiteettiä ei ole) +- Detail-schemat (Book + author-data mukana) +- Test-helperit jotka luovat parent-entiteetit ensin +- Bad FK -testit (varmistaa että orpo-validointi toimii) + +## Validointi + +Generoitu koodi validoidaan mekaanisesti ennen käyttöä: + +- Syntaksitarkistus (AST parse) +- Projektin sisäiset importit (löytyykö nimi lähdetiedostosta) +- SQLite connect_args +- Relatiiviset importit (kielletty) +- Testien rakenne (ei saa kopioida appia) +- pyproject.toml (ei poetryä) +- Dockerfile (ei poetryä, uv cache -oikeudet) + +Docker-testi ajaa koko projektin: build → pytest → API smoke test. + +## Rajoitukset + +Templateit kattavat rakenteellisesti tunnetut projektit: + +| Stack | Kattavuus | +|-------|-----------| +| FastAPI + SQLAlchemy CRUD | Toimii hyvin | +| Streamlit + DuckDB dashboard | Toimii hyvin | +| Muu | Ei templatea → ei toimi | + +**Ei kata:** +- Custom business-logiikka (algoritmit, laskenta, ML) +- Epätyypilliset arkkitehtuurit (WebSocket, graafit, tapahtumapohjaiset) +- Frontend-sovellukset (React, Vue) +- Mikä tahansa mitä template ei tunne + +Arvio: templateit kattavat ~20% kaikista mahdollisista projekteista, mutta juuri +sen 20% mitä opiskelu- ja prototyyppiympäristöissä tarvitaan useimmin. + +## Laajentaminen + +Uuden stackin lisääminen vaatii: + +1. Uudet template-funktiot (käsityö, ~200–400 riviä per stack) +2. JSON-speksin laajennos (uudet kentät jos tarvitaan) +3. Validointisäännöt uudelle stackille +4. Docker-testikonfiguraatio + +Jokainen template on staattinen — se ei opi eikä sopeudu. Kattavuus kasvaa +vain kirjoittamalla lisää templateja. + +## Hybridi: seuraava askel + +Paras lopputulos syntyisi yhdistelmällä: + +``` +Speksi → Template (runko) → LLM (business-logiikka) → Validointi +``` + +Template tuottaa toimivan CRUD-pohjan. LLM lisää domain-kohtaisen logiikan +pienissä palasissa (yksi funktio kerrallaan). Mekaaninen validointi +tarkistaa jokaisen lisäyksen. + +Tämä palauttaa LLM:n epäluotettavuuden takaisin peliin, mutta rajattuna: +virheet ovat paikallisia (yksi funktio) eivätkä rakenteellisia (koko projekti).