# Kipinä Agentic Studio — Opas

Hajautettu AI-laskentaverkko jossa kielimallit ajavat koodia suoraan selaimessa.
Tämä opas selittää miten kielimallit toimivat, miten niitä ohjataan, ja miten
tuloksia voi parantaa.

---

## Kielimallit ja niiden koot

Kielimalli on neuroverkko joka ennustaa seuraavan sanan (tokenin) edellisten
perusteella. Mallin "koko" tarkoittaa parametrien (painojen) määrää:

| Malli | Parametrit | Koko levyllä | Nopeus selaimessa | Koodinlaatu |
|-------|-----------|-------------|-------------------|-------------|
| SmolLM 135M | 135 miljoonaa | ~270 MB | ~5 tok/s | Yksinkertainen teksti |
| Qwen2.5-Coder:0.5B | 500 miljoonaa | ~990 MB | ~3-6 tok/s | Pienet funktiot |
| Qwen2.5-Coder:3B | 3 miljardia | ~6.2 GB | ~0.4 tok/s | Kokonaiset tiedostot |
| GPT-4 (vertailu) | ~1800 miljardia | ~3.6 TB | pilvipalvelu | Kokonaiset projektit |

**Parametrien vaikutus:** Jokainen parametri on yksi liukuluku (float16 = 2 tavua)
joka tallentaa opittua tietoa. 0.5B-malli tietää perusrakenteet mutta tekee
loogisia virheitä. 3B-malli ymmärtää kontekstin paremmin. Ero on kuin sanakirjan
ja oppikirjan välillä.

**Miksi selaimessa?** Malli ajetaan käyttäjän omalla laitteella WebAssemblyn
kautta. Data ei lähde koneelta, eikä tarvita pilvipalvelua. Haittapuoli on
hitaus — GPU-palvelimella sama 0.5B-malli tuottaa ~100 tok/s.

---

## Tokenit — kielimallin "sanat"

Malli ei näe tekstiä kirjaimina vaan **tokeneina**. Tokeni on yleensä
sanan osa, kokonainen sana tai välilyönti. Tokenisaatio tehdään
BPE-algoritmilla (Byte Pair Encoding) joka oppii yleisimmät
merkkijonot harjoitusdatasta.

### Esimerkki: koodi

```
"print('Hello')" → [print] [(' ] [Hello] [')]  = 4 tokenia
"tulosta('Hei')" → [tul] [osta] [(' ] [He] [i] [')]  = 6 tokenia
```

Koodi tokenisoidaan tehokkaasti koska `print`, `def`, `return` yms.
ovat kokonaisia tokeneita. Suomenkielinen `tulosta` joudutaan pilkkomaan
osiin koska se ei esiinny harjoitusdatassa kokonaisena.

### Esimerkki: suomi vs. englanti

Sama lause kahdella kielellä Qwen2.5-Coder -tokenisaattorilla:

| | Teksti | Tokenit | Määrä | Merkkejä/token |
|---|---|---|---|---|
| EN | The cat sat on the mat | [The] [ cat] [ sat] [ on] [ the] [ mat] | **6** | 3.7 |
| FI | Kissa istui matolla | [K] [issa] [ ist] [ui] [ mat] [olla] | **6** | 3.2 |
| EN | Distributed computing in the browser | [Dist] [ributed] [ computing] [ in] [ the] [ browser] | **6** | 6.0 |
| FI | Hajautettu laskenta selaimessa | [H] [aj] [au] [tettu] [ las] [kenta] [ sel] [aim] [essa] | **9** | 3.3 |
| EN | Write a function that sorts a list | [Write] [ a] [ function] [ that] [ sorts] [ a] [ list] | **7** | 5.0 |
| FI | Kirjoita funktio joka lajittelee listan | [K] [irj] [oita] [ funkt] [io] [ joka] [ laj] [ittel] [ee] [ listan] | **10** | 4.0 |

**Huomaa miten:**
- Englannin yleiset sanat (`the`, `in`, `a`, `function`) ovat kokonaisia tokeneita
- Suomen sanat pilkotaan pienempiin osiin (`Hajautettu` → 4 tokenia, `Distributed` → 2)
- Suomi vaatii **30-50% enemmän tokeneita** saman merkityksen välittämiseen
- Koodiavainsanat (`function`, `list`, `sort`) ovat tehokkaita molemmilla kielillä

### Miksi tämä merkitsee?

**Jokainen tokeni = yksi laskentakierros.** Jos suomi vaatii 50% enemmän tokeneita:

1. **Hitaampi vastaus:** 100 tokenin englanninkielinen vastaus ≈ 150 tokenia suomeksi
   → 50% pidempi odotusaika
2. **Pienempi konteksti:** Sama merkityssisältö vie enemmän tilaa konteksti-ikkunasta
3. **Huonompi ymmärrys:** Pitkät sanat pilkotaan osiin jotka malli ei välttämättä
   tunnista → hallusinaatiot lisääntyvät

**Siksi tekniset promptit ovat englanniksi** — malli saa enemmän informaatiota
samassa token-budjetissa ja ymmärtää ohjeet paremmin.

**Token-budjetti tässä järjestelmässä:**

| Osa | Tokeneita | Osuus |
|-----|-----------|-------|
| System prompt | ~30 | kiinteä |
| Agent prompt | ~25 | kiinteä |
| Konteksti (aiemmat tiedostot) | 0-300 | kasvaa |
| Käyttäjän prompti | ~20-50 | vaihtelee |
| **Syöte yhteensä** | **~75-400** | |
| Generoitu vastaus (max) | 512 | raja |
| **Yhteensä** | **~600-900** | /32 768 |

Konteksti-ikkuna on reilusti riittävä. Pullonkaula ei ole ikkunan koko
vaan **mallin kyky ymmärtää pitkää kontekstia** — 0.5B-malli alkaa
"unohtaa" ohjeet kun konteksti kasvaa yli ~200 tokenin.

---

## Promptit — miten mallia ohjataan

### Kolmitasoinen prompttirakenne

```mermaid
flowchart TD
    S["System prompt<br/><i>You are a coding assistant. Respond with ONLY code.</i><br/>🔒 Kiinteä, kovakoodattu — malli priorisoi tämän"]
    A["Agent prompt<br/><i>Olet kokenut ohjelmistokehittäjä...</i><br/>✏️ Käyttäjän muokattavissa UI:ssa"]
    U["User prompt<br/><i>Write ONLY the file main.py...</i><br/>📋 Vaihtelee joka kutsussa, sisältää kontekstin"]
    P["Prefill: ``` <br/>🎯 Pakottaa mallin aloittamaan koodilla"]
    S --> A --> U --> P
    P -->|malli jatkaa| R["Generoitu koodi"]

    style S fill:#1a1e2e,stroke:#f85149,color:#c9d1d9
    style A fill:#1a1e2e,stroke:#d29922,color:#c9d1d9
    style U fill:#1a1e2e,stroke:#3fb950,color:#c9d1d9
    style P fill:#1a1e2e,stroke:#a371f7,color:#c9d1d9
    style R fill:#0d1117,stroke:#58a6ff,color:#58a6ff
```

### Miksi promptit ovat englanniksi?

Qwen2.5-Coder on harjoitettu pääosin englanninkielisellä koodilla ja
dokumentaatiolla. Suomenkielinen ohje kuluttaa enemmän tokeneita JA
malli ymmärtää sen huonommin. Agenttien nimet ja käyttöliittymä ovat
suomeksi, mutta tekniset ohjeet mallille englanniksi.

Poikkeus: agenttipromptit ovat suomeksi koska ne menevät user-blokkiin
(ei system-blokkiin) ja niiden tarkoitus on enemmän "persoonallisuus"
kuin tekninen ohje.

---

## Prefill-tekniikka

Normaalisti malli päättää vapaasti miten vastaa:

```
Ilman prefilliä:
  Malli: "Sure! Here is a Python program that prints Hello World:\n```python\nprint('Hello')\n```"
  → 25 tokenia, joista 15 turhia

Prefillin kanssa:
  Me syötämme: ```
  Malli jatkaa: python\nprint('Hello')\n```
  → 5 tokenia, kaikki hyödyllisiä
```

Prefill on kuin aloittaisit lauseen toisen puolesta — malli jatkaa
siitä mihin jäit sen sijaan, että aloittaisi kohteliaalla johdannolla.

**Sivuvaikutus:** Malli tuottaa kielitunnisteen (`python`, `rust`) ja
sulkevan ` ``` `:n. Nämä siivotaan jälkikäteen `strip_markdown_wrapper`-funktiolla.

---

## Sampling — miten malli valitsee seuraavan tokenin

Malli ei "tiedä" oikeaa vastausta. Se laskee jokaiselle mahdolliselle
seuraavalle tokenille todennäköisyyden ja valitsee yhden. Valintaa
ohjataan kolmella parametrilla:

### Temperature (0.7)

Kontrolloi "luovuutta" vs. "varmuutta":

```
Temperature 0.0 (greedy):  Aina todennäköisin tokeni → "def fibonacci(n):"
Temperature 0.7 (oletus):  Painottaa todennäköisiä mutta sallii vaihtelua
Temperature 1.5 (luova):   Lähes satunnainen → "async lambda fib = ..."
```

0.7 on kompromissi: tarpeeksi determinististä tuottamaan toimivaa koodia,
mutta tarpeeksi vaihtelevaa välttämään toistoa.

### Top-k (40)

Rajaa valinnan 40 todennäköisimpään tokeniin. Estää mallia valitsemasta
täysin absurdeja vaihtoehtoja:

```
Ilman top-k:  150 936 vaihtoehtoa → voi valita minkä tahansa
Top-k 40:     40 vaihtoehtoa → järkevät vaihtoehdot
Top-k 1:      1 vaihtoehto → greedy (aina sama vastaus)
```

### Repetition penalty (1.15)

Vähentää jo tuotettujen tokenien todennäköisyyttä. Estää mallia
juuttumasta luuppiin:

```
Ilman rangaistusta:  "print print print print print..."
Penalty 1.15:        "print('Hello')\nprint('World')"
```

1.15 on lievä rangaistus — estää pahimman toiston mutta sallii
saman avainsanan (esim. `return`) esiintymisen useasti.

---

## Stop-sekvenssit — milloin generointi loppuu

Malli generoi tokeneita kunnes jokin näistä tapahtuu:

1. **EOS-tokeni** (151645): Mallin oma "loppu"-merkki
2. **Max tokens** (512): Kovakoodattu raja
3. **Stop-sekvenssi**: Malli alkaa tuottaa selitystä

```
fn fibonacci(n: usize) -> usize {
    if n <= 1 { return n; }
    fibonacci(n-1) + fibonacci(n-2)
}
                                    ← Tähän asti koodia, ok
// Example usage:                   ← Stop! Tämä ei ole enää vastausta
let result = fibonacci(10);         ← Ei generoida
```

Tunnistetut stop-sekvenssit: `### `, `Explanation`, `Note:`, `Output:`,
`// Example`, `# Example`. Generointi katkaistaan ja teksti trimmataan
stop-kohtaan.

---

## Projekti-pipeline — miten agenttitiimi toimii

```mermaid
flowchart TD
    U["Käyttäjä: FastAPI + SQLite REST API for users"] --> M
    M["🟡 Manageri: Pilko tiedostoiksi"] -->|tiedostolista| C1
    C1["🟢 Koodari: models.py"] -->|"konteksti: models.py"| C2
    C2["🟢 Koodari: main.py"] -->|"konteksti: models + main"| C3
    C3["🟢 Koodari: pyproject.toml"] -->|kaikki tiedostot| T1
    T1["🔵 Testaaja: Review"] -->|bugeja löytyi| C4
    T1 -->|LGTM| Done["✅ Projekti valmis"]
    C4["🟡 Koodari: Korjaukset"] --> T2
    T2["🔵 Testaaja: Uudelleenarviointi"] --> Done
```

**Kontekstin ketjutus** on kriittistä: kun koodari kirjoittaa `main.py`:tä,
se saa `models.py`:n sisällön promptissa. Ilman tätä se ei tietäisi
mitä luokkia importata.

**Riippuvuusjärjestys:** Manageria pyydetään listaamaan riippuvuudet ensin
(models.py ennen main.py) jotta kontekstiketju toimii oikeaan suuntaan.

---

## Laadun parantaminen

### 1. Isompi malli (suurin vaikutus)

| | 0.5B | 3B | Pilvi-API |
|---|---|---|---|
| Fibonacci | Joskus virheitä | Yleensä oikein | Aina oikein |
| FastAPI CRUD | Voi käyttää Flaskia | Oikea kirjasto | Täydellinen |
| Monimutkainen logiikka | Hallusinoi | Osaa perusasiat | Syvä ymmärrys |
| Nopeus (selain) | ~5 tok/s | ~0.4 tok/s | — |
| Latauksen koko | 990 MB | 6.2 GB | 0 (API) |

**Käytännössä:** `kpn load 2` lataa 3B-mallin. Hitaampi mutta huomattavasti
parempi koodinlaatu. Suositus monimutkaisiin projekteihin.

### 2. Paremmat promptit (ilmaista)

**Huono:** `"tee fibonacci"`
- Malli ei tiedä kieltä, formaattia tai kontekstia

**Hyvä:** `"Write a fibonacci function in Rust that returns Vec<u64>"`
- Kieli, palautustyyppi ja rakenne määritelty

**Promptin säännöt:**
- Englanniksi (tehokkaampi tokenisointi, parempi ymmärrys)
- Konkreettinen (mainitse kieli, kirjastot, palautustyyppi)
- Lyhyt (jokainen sana kuluttaa tokenin konteksti-ikkunasta)
- Positiivinen ("Write X" ei "Don't write Y")

### 3. Kontekstin hallinta (pipeline-taso)

**Ongelma:** 0.5B-malli "unohtaa" promptin alun kun konteksti kasvaa.

**Ratkaisu:** Pienet, kohdennetut promptit:
- Yksi tiedosto kerrallaan (ei "kirjoita koko projekti")
- Vain relevantit aiemmat tiedostot kontekstina
- Max 4 tiedostoa per projekti

### 4. Iterointi (review-luuppi)

Yksi generointikierros tuottaa harvoin virheetöntä koodia.
Pipeline-arkkitehtuuri mahdollistaa:

1. **Generointi** — ensimmäinen versio
2. **Review** — testaaja löytää ongelmat
3. **Korjaus** — koodari saa palautteen ja korjaa
4. **Uusi review** — tarkistetaan korjaukset

Nykyinen järjestelmä tekee max 1 korjauskierroksen. Useampi
iteraatio parantaisi laatua mutta kasvattaisi laskenta-aikaa.

### 5. Erikoistetut system promptit

Oletuspromptit ovat yleiskäyttöisiä. Projektikohtaiset promptit
parantavat laatua merkittävästi:

```
Oletus: "Olet kokenut ohjelmistokehittäjä."

Parempi: "You are a Python backend developer specializing in FastAPI.
Always use Pydantic models for request/response schemas.
Always use dependency injection for database sessions.
Follow the repository pattern."
```

Agenttikohtaiset promptit voi muokata suoraan UI:ssa.

### 6. Few-shot esimerkit

Malli oppii parhaiten esimerkeistä. Sen sijaan, että sanot "kirjoita
FastAPI endpoint", näytä miltä haluat tuloksen näyttävän:

```
Write a GET endpoint like this example:

@app.get("/items")
def list_items():
    db = SessionLocal()
    return db.query(Item).all()

Now write a similar endpoint for /users.
```

0.5B-malli jäljittelee rakennetta tehokkaasti — se on parempi kopioimaan
kuin keksimään. Nykyinen pyproject.toml-esimerkki promptissa on tätä tekniikkaa.

### 7. Temperature-säätö tehtävän mukaan

Nykyinen temperature 0.7 on kompromissi. Eri tehtävät hyötyisivät eri arvoista:

| Tehtävä | Paras temperature | Miksi |
|---------|-------------------|-------|
| Tarkka koodi (CRUD, boilerplate) | 0.2-0.4 | Determinismi tärkeää |
| Luova koodi (algoritmit, arkkitehtuuri) | 0.6-0.8 | Vaihtelu löytää ratkaisuja |
| Vapaa teksti (kommentit, dokumentaatio) | 0.8-1.0 | Luonnollisempi kieli |

Järjestelmä voisi valita temperaturen automaattisesti tehtävätyypin perusteella.

### 8. Ensemble — sama prompti usealle mallille

Lähetetään sama tehtävä kahdelle solmulle ja valitaan parempi vastaus.
Nykyinen Proof of Compute -arkkitehtuuri tukee tätä periaatteessa:
hub voisi reitittää saman task_id:n kahdelle solmulle ja verrata tuloksia.

Käytännössä tämä kaksinkertaistaa laskenta-ajan mutta parantaa laatua
merkittävästi — virheellinen vastaus harvoin on sama kahdella ajolla
koska sampling on stokastinen.

### 9. Post-processing (nykyinen)

Mallin raakavastaus siivotaan:
1. Kielitunniste poistetaan (`python`, `rust`, ...)
2. Sulkeva ` ``` ` poistetaan
3. Johdantolauseet poistetaan ("Sure!", "Here is...")
4. Selityskommentit poistetaan ("# This is a simple...")
5. Stop-sekvenssit katkaisevat generoinnin

Tämä ei paranna mallin ajattelua mutta poistaa turhan roskan.

### 10. Mallin hienosäätö (fine-tuning)

Qwen2.5-Coder on yleiskäyttöinen koodimalli. Jos sitä hienosäätäisi
omalla koodiaineistolla (esim. yrityksen koodikanta, tietty framework),
se tuottaisi huomattavasti parempaa koodia juuri siihen kontekstiin.

LoRA-hienosäätö 0.5B-mallille vaatii ~4 GB GPU-muistia ja muutaman
tunnin harjoittelua. Tulos on erikoistunut malli joka osaa tuottaa
esimerkiksi juuri FastAPI + SQLAlchemy -koodia luotettavasti.

---

## Välimuistiarkkitehtuuri — miksi toinen lataus on nopea

```
Ensimmäinen lataus (hidas):
  Verkko (HuggingFace CDN) → IndexedDB → RAM → Mallin rakennus
  ~990 MB lataus, ~30-60s

Toinen lataus samalla sivulatauksella (nopea):
  RAM-cache → Mallia ei rakenneta uusiksi, vain KV-cache nollataan
  ~0ms

Refresh jälkeen (keskitaso):
  IndexedDB → RAM → Mallin rakennus
  ~0 MB lataus, ~2-5s rakennus

Uusi selain/laite (hidas):
  Verkko → IndexedDB → RAM → Mallin rakennus
  Kuten ensimmäinen lataus
```

**KV-cache:** Mallin sisäinen muisti joka tallentaa aiempien tokenien
laskenta tulokset. Nollataan (`clear_kv_cache()`) jokaisen promptin
välillä jotta edellinen vastaus ei vuoda seuraavaan.

---

## Lukuja käytännöstä

**Yksittäinen funktio** (esim. fibonacci):
- Input: ~80 tokenia
- Output: ~50-100 tokenia
- Aika: ~10-20s (0.5B, selain)
- Laatu: Yleensä toimiva, joskus loogisia virheitä

**3 tiedoston projekti** (esim. FastAPI CRUD):
- Manageri: ~30 tok out
- Koodari (3x): ~100-150 tok out per tiedosto
- Testeri: ~50 tok out
- Korjaukset: ~100 tok out (jos tarpeen)
- **Yhteensä: ~500-700 tokenia, ~3-5 min**
- Laatu: Rakenne oikein, yksittäisiä bugeja

**Token-kustannus vs. pilvipalvelu:**
- Tässä järjestelmässä: 0 euroa (laskenta omalla koneella)
- GPT-4 API: ~700 tokenia x $0.03/1K = ~$0.02 per projekti
- Claude API: ~700 tokenia x $0.015/1K = ~$0.01 per projekti

Selaimessa ajettava malli on ilmainen mutta huomattavasti hitaampi
ja heikompilaatuinen kuin pilvi-API. Sopii oppimiseen, prototypointiin
ja tilanteisiin joissa data ei saa lähteä omalta koneelta.
