# 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
You are a coding assistant. Respond with ONLY code.
🔒 Kiinteä, kovakoodattu — malli priorisoi tämän"] A["Agent prompt
Olet kokenut ohjelmistokehittäjä...
✏️ Käyttäjän muokattavissa UI:ssa"] U["User prompt
Write ONLY the file main.py...
📋 Vaihtelee joka kutsussa, sisältää kontekstin"] P["Prefill: ```
🎯 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"` - 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.