- Staattinen tekstitokenisointiesimerkki korvattu kuvalla joka näyttää värikoodatut tokenit EN/FI-vertailussa - Markdown-renderöijään lisätty  kuvatuki - Kuva: static/images/tokenization-example.png Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
414 lines
14 KiB
Markdown
414 lines
14 KiB
Markdown
# 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: suomi vs. englanti
|
|
|
|
Alla oikea tokenisointitulos Qwen2.5-Coder-tokenisaattorilla. Jokainen
|
|
värikoodattu lohko on yksi tokeni — huomaa miten suomi vaatii enemmän
|
|
tokeneita saman merkityksen välittämiseen:
|
|
|
|

|
|
|
|
**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.
|