Pipelinen parannuksia building blockeilla

This commit is contained in:
Jaakko Vanhala
2026-04-12 18:48:14 +03:00
parent c1a5f8aff5
commit b2ee8b9031
175 changed files with 13311 additions and 237 deletions

View File

@@ -230,6 +230,144 @@ mitä luokkia importata.
---
## Rakennuspalaset vs. vapaa generointi
Kielimalli voi generoida koodia kahdella perustavanlaatuisesti eri tavalla.
Ymmärtäminen milloin kumpikin toimii on avain luotettavaan koodigenerointi-pipelineen.
### Tapa 1: Vapaa generointi (naivi)
LLM generoi jokaisen tiedoston tyhjästä. Prompti kuvaa mitä halutaan,
malli tuottaa koko tiedoston — importeista lähtien.
```mermaid
flowchart LR
P["Prompti"] --> LLM1["LLM: models.py"]
LLM1 --> V1{"Validointi"}
V1 -->|virhe| LLM1
V1 -->|ok| LLM2["LLM: schemas.py"]
LLM2 --> V2{"Validointi"}
V2 -->|virhe| LLM2
V2 -->|ok| LLM3["LLM: main.py"]
LLM3 --> V3{"..."}
style V1 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
style V2 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
style V3 fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
```
**Ongelma:** Pieni malli (0.5B7B) tekee toistuvia rakenteellisia virheitä:
| Virhe | Esiintymistiheys | Selitys |
|-------|:---:|------|
| Puuttuva import | ~60% | `from datetime import date` unohtuu |
| SQLite `connect_args` | ~80% | Malli ei muista SQLite-erityisyyttä |
| Väärä Enum-käyttö | ~50% | Sekoittaa `sqlalchemy.Enum` ja `enum.Enum` |
| Poetry pyproject.toml:ssa | ~40% | Malli suosii Poetryä vaikka ohje sanoo uv |
| Testit kopioivat koko appin | ~70% | Malli ei osaa importata, luo uudet reitit |
Retry-loopilla (virhe → uusi yritys virheviestin kanssa) osa korjautuu,
mutta **sama malli toistaa samoja virheitä** koska ne johtuvat harjoitusdatasta.
7 tiedoston projekti vaatii 714 LLM-kutsua ja 80120 sekuntia.
### Tapa 2: Rakennuspalaset (template pipeline)
LLM:ltä pyydetään **vain JSON-speksi** — entiteetit, kentät ja tyypit.
Koodi kootaan mekaanisesti valmiista pohjista joiden rakenne on todistettavasti
oikein.
```mermaid
flowchart LR
P["Projektin kuvaus"] --> LLM["LLM: JSON-speksi"]
LLM --> S["{ entities: [...] }"]
S --> T1["Template: models.py"]
S --> T2["Template: schemas.py"]
S --> T3["Template: main.py"]
S --> T4["Template: test_main.py"]
S --> T5["Template: Dockerfile"]
T1 & T2 & T3 & T4 & T5 --> D["Docker build + pytest"]
style LLM fill:#1a1e2e,stroke:#d29922,color:#c9d1d9
style S fill:#1a1e2e,stroke:#3fb950,color:#c9d1d9
style D fill:#1a1e2e,stroke:#58a6ff,color:#c9d1d9
```
**Idea:** Malli on hyvä päättämään *mitä* (entiteetit, kentät), mutta huono
muistamaan *miten* (importit, engine setup, testikonfiguraatio). Annetaan
mallin tehdä se missä se on hyvä, ja hoidetaan loput mekaanisesti.
### LLM:n ainoa tehtävä
Malli tuottaa JSON-rakenteen kuten:
```json
{
"project_name": "todo-app",
"entities": [
{
"name": "Todo",
"table_name": "todos",
"fields": [
{"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false},
{"name": "due_date", "sa_type": "Date", "py_type": "date | None", "nullable": true},
{"name": "status", "sa_type": "String(20)", "py_type": "str", "default": "pending"}
]
}
],
"extra_imports": ["from datetime import date"]
}
```
Tämä on yksinkertainen tehtävä jossa pienikin malli onnistuu luotettavasti:
entiteettien tunnistus projektin kuvauksesta ja kenttätyyppien valinta.
### Template täyttää loput
Jokainen template on kuin madlib — aukot täytetään speksin datalla:
**models.py template (yksinkertaistettu):**
```python
from sqlalchemy import create_engine, Column, Integer, {sa_types}
# ... aina samat importit, engine setup, SessionLocal ...
class {entity.name}(Base):
__tablename__ = "{entity.table_name}"
id = Column(Integer, primary_key=True, index=True)
{field.name} = Column({field.sa_type}, nullable={field.nullable})
# ... jokainen kenttä speksistä ...
```
Tulos: importit ovat aina oikein, `connect_args` on aina mukana,
testit importoivat aina `main.py`:stä eivätkä kopioi sitä.
### Vertailu: mittaustulokset
| | Vapaa generointi | Rakennuspalaset |
|---|:---:|:---:|
| LLM-kutsuja | 714 | **1** |
| Aika | 80120s | **~20s** |
| Syntaksi OK | ~70% | **100%** |
| Docker build | vaihteleva | **100%** |
| Pytest läpi | 0% | **100%** |
| API toimii | ~30% | **100%** |
### Milloin kumpikin toimii
**Rakennuspalaset** kun:
- Projektin rakenne on tunnettu (FastAPI + SQLAlchemy CRUD)
- Laatu ja luotettavuus ovat tärkeitä
- Malli on pieni (0.5B7B)
**Vapaa generointi** kun:
- Projektin rakenne on epätavallinen
- Tarvitaan custom-logiikkaa jota template ei kata
- Malli on riittävän iso (>70B tai pilvi-API)
Paras lopputulos syntyy yhdistelmällä: **rakennuspalaset perusrakenteelle,
vapaa generointi business-logiikalle**.
---
## Laadun parantaminen
### 1. Isompi malli (suurin vaikutus)