158 lines
5.2 KiB
Markdown
158 lines
5.2 KiB
Markdown
# 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).
|