Files
agentic-studio/network-poc/frontend/public/GUIDE.md
jaakko 2d1b1d3ec6 initial commit: agentic office
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 13:14:39 +03:00

21 KiB
Raw Blame History

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:

Tokenisointivertailu EN/FI

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

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: &#96;&#96;&#96; <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

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.


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.

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.

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:

{
  "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.

Speksi sisältää myös taulujen väliset yhteydet (relationships):

{
  "entities": [
    {"name": "Author", "table_name": "authors", "fields": [...]},
    {"name": "Book", "table_name": "books", "fields": [
      {"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false},
      {"name": "author_id", "sa_type": "Integer", "py_type": "int", "nullable": false}
    ]}
  ],
  "relationships": [
    {"from": "Book", "field": "author_id", "to": "Author", "type": "many-to-one"}
  ]
}

Templateit generoivat yhteyksistä automaattisesti:

  • ForeignKey('authors.id') models.py:hin
  • relationship("Book", back_populates="author") molempiin suuntiin
  • BookDetail-schema jossa author-data mukana
  • GET /authors/{id}/books/ nested endpoint
  • FK-validointi: 404 jos parent-entiteettiä ei ole

Architect-agentti: speksin laatu ratkaisee

Arkkitehti on kriittisin agentti koko pipelinessa. Jos speksi on hyvä (oikeat taulut, kentät, yhteydet), kaikki muu seuraa automaattisesti. Jos speksi on huono, templateitkaan eivät pelasta.

Arkkitehtia ohjataan:

  1. Chain-of-thought: "Mieti ensin taulut, sitten kentät, sitten yhteydet"
  2. Domain-esimerkit: Todo, verkkokauppa, blogi — malli näkee miltä hyvä speksi näyttää
  3. Anti-patternit: "Ei turhia ID-kenttiä, ei Enumeita, ei suomenkielisiä nimiä koodissa"
  4. Yhteyssäännöt: "Jokainen _id-kenttä tarvitsee vastaavan relationship-merkinnän"

Isompi malli (tai API) tässä yhdessä kohdassa parantaa kaikkien projektien laatua koska speksi on ainoa paikka jossa LLM:n ymmärrys vaikuttaa.

Template täyttää loput

Jokainen template on kuin madlib — aukot täytetään speksin datalla:

models.py template (yksinkertaistettu):

from sqlalchemy import create_engine, Column, Integer, {sa_types}, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
# ... 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})
    # FK-kentät: ForeignKey + relationship automaattisesti
    {fk_field} = Column(Integer, ForeignKey('{parent_table}.id'))
    {parent_lower} = relationship("{Parent}", back_populates="{children}")

Tulos: importit ovat aina oikein, connect_args on aina mukana, taulujen yhteydet generoituvat oikein, testit importoivat main.py:stä eivätkä kopioi sitä.

Vertailu: mittaustulokset

Vapaa generointi Rakennuspalaset
LLM-kutsuja 714 3 (speksi + requirements + README)
Aika 80120s ~25s
Syntaksi OK ~70% 100%
Docker build vaihteleva 100%
Pytest läpi 0% 100%
API toimii ~30% 100%
Taulujen yhteydet (FK) ei koskaan 100%
Nested endpointit ei koskaan automaattisesti

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)

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.