21 KiB
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:
- Hitaampi vastaus: 100 tokenin englanninkielinen vastaus ≈ 150 tokenia suomeksi → 50% pidempi odotusaika
- Pienempi konteksti: Sama merkityssisältö vie enemmän tilaa konteksti-ikkunasta
- 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: ``` <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:
- EOS-tokeni (151645): Mallin oma "loppu"-merkki
- Max tokens (512): Kovakoodattu raja
- 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.5B–7B) 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 7–14 LLM-kutsua ja 80–120 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 relaatiot entiteettien välillä:
{
"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 relaatioista automaattisesti:
ForeignKey('authors.id')models.py:hinrelationship("Book", back_populates="author")molempiin suuntiinBookDetail-schema jossa author-data mukanaGET /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 entiteetit, kentät, relaatiot), kaikki muu seuraa automaattisesti. Jos speksi on huono, templateitkaan eivät pelasta.
Arkkitehtia ohjataan:
- Chain-of-thought: "Mieti ensin entiteetit, sitten kentät, sitten relaatiot"
- Domain-esimerkit: Todo, verkkokauppa, blogi — malli näkee miltä hyvä speksi näyttää
- Anti-patternit: "Ei turhia ID-kenttiä, ei Enumeita, ei suomenkielisiä nimiä koodissa"
- Relaatiosää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,
relaatiot generoituvat oikein, testit importoivat main.py:stä eivätkä kopioi sitä.
Vertailu: mittaustulokset
| Vapaa generointi | Rakennuspalaset | |
|---|---|---|
| LLM-kutsuja | 7–14 | 3 (speksi + requirements + README) |
| Aika | 80–120s | ~25s |
| Syntaksi OK | ~70% | 100% |
| Docker build | vaihteleva | 100% |
| Pytest läpi | 0% | 100% |
| API toimii | ~30% | 100% |
| Relaatiot (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.5B–7B)
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:
- Generointi — ensimmäinen versio
- Review — testaaja löytää ongelmat
- Korjaus — koodari saa palautteen ja korjaa
- 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:
- Kielitunniste poistetaan (
python,rust, ...) - Sulkeva
```poistetaan - Johdantolauseet poistetaan ("Sure!", "Here is...")
- Selityskommentit poistetaan ("# This is a simple...")
- 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.
