# 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: ``` "print('Hello')" → ["print", "('", "Hello", "')"] = 4 tokenia "tulosta('Hei')" → ["tul", "osta", "('", "He", "i", "')"] = 6 tokenia ``` **Miksi tällä on väliä?** 1. **Kustannus:** Jokainen tokeni vaatii laskentaa. 100 tokenin vastaus kestää 0.5B-mallilla ~15-30 sekuntia selaimessa. 2. **Konteksti-ikkuna:** Malli näkee kerrallaan rajallisen määrän tokeneita. Qwen2.5:ssa ikkuna on 32 768 tokenia. Tähän mahtuu prompti + vastaus. 3. **Kielen tehokkuus:** Englanti tokenisoidaan tehokkaammin kuin suomi. Sama lause vaatii suomeksi ~30-70% enemmän tokeneita. Siksi promptit ovat englanniksi — malli saa enemmän informaatiota samassa token-budjetissa. **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.