# 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).