BUILDING_BLOCKS.md: rakennuspalaset ja työnkulut jatkokehitystä varten
Dokumentoi kaikki arkkitehtuuripatternit, UI-komponentit ja työnkulut: - WebSocket-reaaliaikakommunikaatio (broadcast, reititys, busy-state, työjono) - Wasm-laskentasolmun elinkaari ja kolmitasoinen cache - LLM-inferenssipipeline (prefill, sampling, stop-sekvenssit, streaming) - Terminaaliemulaattori (tab-completion, dropdown, historia) - Status-palkit ja tilaindikaattorit - Tietoturva (XSS, rate limiting, viestityyppivalidointi, gamification-esto) - Agenttien orkestrointi (pipeline, promptien hallinta) - Teknologiapino ja jatkokehitysideat Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
525
network-poc/BUILDING_BLOCKS.md
Normal file
525
network-poc/BUILDING_BLOCKS.md
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
# Kipinä Agentic Studio — Rakennuspalaset
|
||||||
|
|
||||||
|
Tämä dokumentti kuvaa projektin UI-komponentit, arkkitehtuuripatternit ja työnkulut niin, että vastaavan hajautetun AI-laskentaverkon ja agenttipohjaisen käyttöliittymän voi rakentaa alusta asti.
|
||||||
|
|
||||||
|
## Yleiskuva
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Selain (käyttäjä) │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
|
||||||
|
│ │ Verkko- │ │ Koodi- │ │ Agents-näkymä │ │
|
||||||
|
│ │ näkymä │ │ labra │ │ ┌───────────────┐ │ │
|
||||||
|
│ │ │ │ │ │ │ Terminaali │ │ │
|
||||||
|
│ │ Stats │ │ Editor │ │ │ Tab-complete │ │ │
|
||||||
|
│ │ Chat │ │ Pipeline │ │ │ Dropdown │ │ │
|
||||||
|
│ │ Tokenit │ │ Tulokset │ │ │ Historia │ │ │
|
||||||
|
│ └────┬─────┘ └────┬─────┘ │ └───────────────┘ │ │
|
||||||
|
│ │ │ └────────┬──────────┘ │
|
||||||
|
│ └──────────┬───┘ │ │
|
||||||
|
│ UI WebSocket HTTP API │
|
||||||
|
│ │ /api/v1/chat │
|
||||||
|
│ ┌───────────────┴──────────────┐ │ │
|
||||||
|
│ │ Wasm Compute Node │ │ │
|
||||||
|
│ │ (Candle + Burn) │ │ │
|
||||||
|
│ │ ┌─────────┐ ┌────────────┐ │ │ │
|
||||||
|
│ │ │ RAM │ │ IndexedDB │ │ │ │
|
||||||
|
│ │ │ Cache │ │ Cache │ │ │ │
|
||||||
|
│ │ └─────────┘ └────────────┘ │ │ │
|
||||||
|
│ │ ┌─────────────────────────┐ │ │ │
|
||||||
|
│ │ │ Model Cache (QwenModel) │ │ │ │
|
||||||
|
│ │ └─────────────────────────┘ │ │ │
|
||||||
|
│ └──────────────┬───────────────┘ │ │
|
||||||
|
│ │ WS │ │
|
||||||
|
└─────────────────┼──────────────────────┼─────────────┘
|
||||||
|
│ │
|
||||||
|
┌────────┴──────────────────────┴──┐
|
||||||
|
│ Hub (Axum + Tokio) │
|
||||||
|
│ ┌────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ Broadcast │ │ Node │ │
|
||||||
|
│ │ Channel │ │ Registry │ │
|
||||||
|
│ └────────────┘ └─────────────┘ │
|
||||||
|
│ ┌────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ Busy-State │ │ Rate Limit │ │
|
||||||
|
│ │ Tracker │ │ + Auth │ │
|
||||||
|
│ └────────────┘ └─────────────┘ │
|
||||||
|
│ ┌─────────────────────────────┐ │
|
||||||
|
│ │ SQLite (sessiot, tulokset) │ │
|
||||||
|
│ └─────────────────────────────┘ │
|
||||||
|
└──────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. WebSocket-reaaliaikakommunikaatio
|
||||||
|
|
||||||
|
### 1.1 Hub ↔ Node broadcast-kanava
|
||||||
|
|
||||||
|
**Tarkoitus:** Jakaa tehtäviä ja vastaanottaa tuloksia kaikilta laskentasolmuilta.
|
||||||
|
|
||||||
|
**Työnkulku:**
|
||||||
|
1. Hub luo `tokio::sync::broadcast::channel(100)`
|
||||||
|
2. Jokainen solmu saa oman `rx = stats_tx.subscribe()`
|
||||||
|
3. Hub broadcastaa tehtävät: `stats_tx.send(json)`
|
||||||
|
4. Solmut suodattavat viestin tyypin ja `selected_task`:n perusteella
|
||||||
|
|
||||||
|
**Viestityupit:**
|
||||||
|
|
||||||
|
| Tyyppi | Suunta | Sisältö |
|
||||||
|
|--------|--------|---------|
|
||||||
|
| `stats` | Hub → kaikki | nodes, vram_gb, tasks |
|
||||||
|
| `pair_task` | Hub → tokenize-solmut | en, fi tekstiparit |
|
||||||
|
| `llm_prompt` | Hub → valittu solmu | prompt, model, task_id |
|
||||||
|
| `llm_chunk` | Solmu → Hub → UI | token (1 kerrallaan) |
|
||||||
|
| `llm_done` | Solmu → Hub → UI | response, tokens_generated, duration_ms |
|
||||||
|
| `llm_error` | Solmu → Hub → UI | error, task_id |
|
||||||
|
| `task_routed` | Hub → UI | status (routed/queued), node_id, message |
|
||||||
|
|
||||||
|
**Lagged-viestien käsittely:**
|
||||||
|
```rust
|
||||||
|
match rx.recv().await {
|
||||||
|
Ok(msg) => { /* käsittele */ }
|
||||||
|
Err(broadcast::error::RecvError::Lagged(n)) => {
|
||||||
|
// Ohitetaan vanhat viestit, ei katkaista yhteyttä
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(_) => break, // Kanava suljettu
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Kohdennettu reititys (Direct Channel)
|
||||||
|
|
||||||
|
**Tarkoitus:** Lähetä tehtävä yhdelle tietylle solmulle broadcastin sijaan.
|
||||||
|
|
||||||
|
**Työnkulku:**
|
||||||
|
1. Jokainen solmu saa `mpsc::unbounded_channel` yhdistyessään
|
||||||
|
2. Hub tallentaa `node_channels: HashMap<u64, UnboundedSender>`
|
||||||
|
3. API-pyyntö → valitaan vapaa solmu → lähetetään suoraan kanavaan
|
||||||
|
4. Broadcast-kanavaa käytetään vain tuloksen välittämiseen UI:lle
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let channels = state.node_channels.read().await;
|
||||||
|
if let Some(tx) = channels.get(&target_node_id) {
|
||||||
|
tx.send(msg.to_string());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Busy-state ja työjono
|
||||||
|
|
||||||
|
**Tarkoitus:** Estä tehtävien reititys varatuille solmuille.
|
||||||
|
|
||||||
|
**Rakenne:**
|
||||||
|
- `node_busy: HashSet<u64>` — solmut joilla on aktiivinen tehtävä
|
||||||
|
- Asetetaan kun tehtävä reititetään, vapautetaan `llm_done`/`llm_error`:ssa
|
||||||
|
- Jos kaikki solmut varattuja → pollaa 500ms välein, max 30s
|
||||||
|
|
||||||
|
**UI-palaute:**
|
||||||
|
```json
|
||||||
|
{"type": "task_routed", "status": "queued", "message": "Kaikki 2 solmua varattuja — odotetaan..."}
|
||||||
|
{"type": "task_routed", "status": "routed", "node_id": 3, "message": "Solmu #3 vapautui (2.5s jonossa)"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Wasm-laskentasolmu
|
||||||
|
|
||||||
|
### 2.1 Elinkaari
|
||||||
|
|
||||||
|
```
|
||||||
|
init() → start_agent_node(ws_url, has_webgpu, device_info, task_id)
|
||||||
|
│
|
||||||
|
├─ Avaa WebSocket hubiin
|
||||||
|
├─ Lähettää auth-viestin (laitetiedot, selected_task)
|
||||||
|
├─ Rekisteröityy onmessage-käsittelijä
|
||||||
|
│ ├─ pair_task → tokenize
|
||||||
|
│ ├─ llm_prompt → inference
|
||||||
|
│ └─ ai_task → tensor matmul
|
||||||
|
└─ Odottaa tehtäviä loopissa
|
||||||
|
```
|
||||||
|
|
||||||
|
**Globaali tila (atominen, lukitsematon):**
|
||||||
|
```rust
|
||||||
|
static GPU_LOAD_PERCENT: AtomicU32 = AtomicU32::new(50);
|
||||||
|
static LLM_BUSY: AtomicBool = AtomicBool::new(false);
|
||||||
|
static SELECTED_TASK: AtomicU32 = AtomicU32::new(0);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Kolmitasoinen cache
|
||||||
|
|
||||||
|
```
|
||||||
|
Pyyntö → [1] RAM-cache (thread_local HashMap)
|
||||||
|
│ miss
|
||||||
|
▼
|
||||||
|
[2] IndexedDB (selaimen pysyvä tallennus)
|
||||||
|
│ miss
|
||||||
|
▼
|
||||||
|
[3] Verkko (HuggingFace CDN, streaming + 5% progressi)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Tallenna → IndexedDB → RAM-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
| Taso | Nopeus | Koko | Pysyvyys |
|
||||||
|
|------|--------|------|----------|
|
||||||
|
| RAM | ~0ms | Rajaton | Sivulataus |
|
||||||
|
| IndexedDB | ~50ms | ~50GB | Pysyvä |
|
||||||
|
| Verkko | ~10s/100MB | ∞ | — |
|
||||||
|
|
||||||
|
**Malliinstanssin cache (neljäs taso):**
|
||||||
|
```rust
|
||||||
|
thread_local! {
|
||||||
|
static MODEL_CACHE: RefCell<Option<CachedModel>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
// clear_kv_cache() promptien välillä — ei tarvitse rakentaa mallia uusiksi
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Warmup-esilataus
|
||||||
|
|
||||||
|
**Tarkoitus:** Lataa malli valmiiksi ennen ensimmäistä oikeaa promptia.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Lähetetään 1 tokenin warmup heti kun WS on auki
|
||||||
|
uiSocket.send(JSON.stringify({
|
||||||
|
type: 'user_text',
|
||||||
|
text: '{"prompt":"warmup","max_tokens":1}',
|
||||||
|
task_type: 'qwen-coder'
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. LLM-inferenssipipeline
|
||||||
|
|
||||||
|
### 3.1 Prompt-formaatti (ChatML + prefill)
|
||||||
|
|
||||||
|
```
|
||||||
|
<|im_start|>system
|
||||||
|
You are a coding assistant. Respond with ONLY code.<|im_end|>
|
||||||
|
<|im_start|>user
|
||||||
|
hello world in python<|im_end|>
|
||||||
|
<|im_start|>assistant
|
||||||
|
``` ← PREFILL: pakottaa mallin aloittamaan koodilla
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prefill-tekniikka:** Lisäämällä ` ``` ` assistantin vastauksen alkuun malli jatkaa suoraan koodilla eikä tuota "Sure! Here is..." -johdantoa. Säästää 10-20 tokenia per vastaus.
|
||||||
|
|
||||||
|
### 3.2 Sampling-parametrit
|
||||||
|
|
||||||
|
| Parametri | Arvo | Tarkoitus |
|
||||||
|
|-----------|------|-----------|
|
||||||
|
| `temperature` | 0.7 | Pehmentää jakaumaa, vähentää toistoa |
|
||||||
|
| `top_k` | 40 | Rajaa valinnan 40 todennäköisimpään tokeniin |
|
||||||
|
| `repetition_penalty` | 1.15 | Rankaisee jo generoitujen tokenien uudelleenvalintaa |
|
||||||
|
| `max_tokens` | 128 | Oletusraja, JSON-promptilla konfiguroitavissa |
|
||||||
|
|
||||||
|
**Sampling-funktio (top-k + temperature + repetition penalty):**
|
||||||
|
```rust
|
||||||
|
fn sample_top_k_with_penalty(logits, k, temperature, generated_tokens, penalty) -> u32 {
|
||||||
|
// 1. Repetition penalty: vähennä aiempien tokenien logitteja
|
||||||
|
// 2. Temperature scaling: jaa logitit temperaturella
|
||||||
|
// 3. Top-k: ota k suurinta
|
||||||
|
// 4. Softmax top-k:lle
|
||||||
|
// 5. Satunnaisvalinta kumulatiivisella todennäköisyydellä (XorShift RNG)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Stop-sekvenssit
|
||||||
|
|
||||||
|
Generointi katkaistaan ja teksti trimmataan kun malli alkaa selittää:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let stop_patterns = ["\n###", "\nExplanation", "\nNote:", "\nOutput:", "\n```\n\n"];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Vastauksen siivous
|
||||||
|
|
||||||
|
```
|
||||||
|
Raakavastaus: "Sure! Here is...\n```python\n# This is a simple program\nprint('hi')\n```"
|
||||||
|
│
|
||||||
|
strip_markdown: "# This is a simple program\nprint('hi')"
|
||||||
|
│
|
||||||
|
strip_preamble: "print('hi')"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tunnistettavat selityskommentit:** `# This is`, `# simple`, `# program that`, `# here is`, `# the following`, `# below`
|
||||||
|
|
||||||
|
### 3.5 Streaming
|
||||||
|
|
||||||
|
Jokainen generoitu token lähetetään heti `llm_chunk`-viestinä:
|
||||||
|
```json
|
||||||
|
{"type": "llm_chunk", "token": "print", "prompt": "...", "model": "Qwen2.5-Coder", "task_id": "uuid"}
|
||||||
|
```
|
||||||
|
|
||||||
|
UI päivittää streaming-korttia reaaliaikaisesti appendaamalla tokeneita.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Terminaaliemulaattori
|
||||||
|
|
||||||
|
### 4.1 Rakenne
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="agent-hub-status"> <!-- Status-palkki (Hub + Laskenta) -->
|
||||||
|
<div id="agent-terminal"> <!-- Scrollaava tulosalue, max 100 riviä -->
|
||||||
|
<div> <!-- Input-rivi -->
|
||||||
|
<span>$</span>
|
||||||
|
<input id="term-input">
|
||||||
|
<div id="term-dropdown"> <!-- Autocompletion-valikko -->
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Komentojen käsittely
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function termExec(cmd) {
|
||||||
|
// Parsitaan: "kpn" + alikomento + argumentit
|
||||||
|
// Tuetut: help, run, pipeline, load, status, models, hello, clear
|
||||||
|
// Agenttinimi → malli-mapping: "coder" → "qwen-coder"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Tab-completion (kolmitasoinen)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const kpnCommands = {
|
||||||
|
'kpn': ['help', 'run', 'pipeline', 'load', ...],
|
||||||
|
'kpn run': ['coder', 'manager', 'qwen-coder', ...],
|
||||||
|
};
|
||||||
|
const kpnExamples = {
|
||||||
|
'kpn run coder': ['"hello world in python"', ...],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Käyttö:**
|
||||||
|
|
||||||
|
| Näppäin | Toiminto |
|
||||||
|
|---------|----------|
|
||||||
|
| TAB | Täydennä seuraava sana tai avaa dropdown |
|
||||||
|
| Shift-TAB | Poista viimeinen sana (lainausmerkit kokonaisuutena) |
|
||||||
|
| ↑ / ↓ | Navigoi dropdownissa (tai komentohistoriassa) |
|
||||||
|
| Enter | Valitse dropdownista tai suorita komento |
|
||||||
|
| Esc | Sulje dropdown |
|
||||||
|
|
||||||
|
### 4.4 Dropdown-valikko
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function showDropdown(items, prefix) {
|
||||||
|
// Luo div.term-dd-item per vaihtoehto
|
||||||
|
// Positio: absolute, bottom: 100% (inputin yläpuolella)
|
||||||
|
// Mouseenter → highlight, click → valinta
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Komentohistoria
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const termHistory = []; // Kaikki ajetut komennot (viimeisin ensin)
|
||||||
|
let termHistIdx = -1; // Nykyinen positio historiassa
|
||||||
|
// ArrowUp: termHistIdx++, ArrowDown: termHistIdx--
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Status-palkit ja tilaindikaattorit
|
||||||
|
|
||||||
|
### 5.1 Hub-yhteyden tila
|
||||||
|
|
||||||
|
| Tila | Väri | Teksti | Tooltip |
|
||||||
|
|------|------|--------|---------|
|
||||||
|
| Yhdistetään | 🟡 | "Yhdistetään..." | WebSocket-yhteys Kipinä Hubiin |
|
||||||
|
| Yhdistetty | 🟢 | "Yhdistetty" | Tehtävien jakelu aktiivinen |
|
||||||
|
| Katkennut | 🔴 | "Yhteys katkennut" | Tarkista verkko, lataa uudelleen |
|
||||||
|
|
||||||
|
### 5.2 Laskentasolmun tila
|
||||||
|
|
||||||
|
| Tila | Väri | Teksti | Nappi |
|
||||||
|
|------|------|--------|-------|
|
||||||
|
| Ei käynnissä | ⚫ | "—" | `[Alusta laskentasolmu]` sininen |
|
||||||
|
| Lataa | 🟡 | "Ladataan..." | `[Peruuta]` punainen |
|
||||||
|
| Valmis | 🟢 | "Qwen2.5-Coder" | `[✓ Valmis]` vihreä |
|
||||||
|
|
||||||
|
### 5.3 Pipeline-tilakone (Codelab)
|
||||||
|
|
||||||
|
```
|
||||||
|
Step 1: WebAssembly-ytimen lataus [◯ → ◷ → ✓]
|
||||||
|
Step 2: Tokenizer (7 MB) [◯ → ◷ → ✓]
|
||||||
|
Step 3: Mallipainot (990 MB) [◯ → ◷ 45% → ✓ cache]
|
||||||
|
Step 4: Mallin rakentaminen [◯ → ◷ → ✓]
|
||||||
|
Step 5: Valmis generoimaan [◯ → ✓]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Seuranta console.log-viesteistä:**
|
||||||
|
```javascript
|
||||||
|
if (msg.includes('[Coder]') && msg.includes('Malli ladattu')) {
|
||||||
|
// Merkkaa kaikki vaiheet valmiiksi (myös cache-hitillä)
|
||||||
|
setStep('step-wasm', 'done');
|
||||||
|
setStep('step-tokenizer', 'done');
|
||||||
|
setStep('step-model', 'done', 'cache');
|
||||||
|
setStep('step-build', 'done');
|
||||||
|
setStep('step-ready', 'done');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Tietoturva
|
||||||
|
|
||||||
|
### 6.1 XSS-suojaus
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function esc(str) {
|
||||||
|
return String(str).replace(/&/g,'&').replace(/</g,'<')
|
||||||
|
.replace(/>/g,'>').replace(/"/g,'"');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Käyttöpaikat:** Kaikki `innerHTML`-insertoinnit joissa on käyttäjä- tai backend-dataa.
|
||||||
|
|
||||||
|
### 6.2 System prompt -piilotus
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function stripSystemPrompt(prompt) {
|
||||||
|
const parts = prompt.split('\n\n');
|
||||||
|
return parts[parts.length - 1] || prompt;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Viestityyppivalidointi (backend)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const ALLOWED_MSG_TYPES: &[&str] = &[
|
||||||
|
"auth", "result", "pair_done", "llm_chunk", "llm_done",
|
||||||
|
"llm_error", "download_progress", "user_text", "single_tokenize_done"
|
||||||
|
];
|
||||||
|
|
||||||
|
fn validate_message(text: &str) -> Result<Value, &'static str> {
|
||||||
|
// 1. JSON-parsinta
|
||||||
|
// 2. "type"-kenttä pakollinen
|
||||||
|
// 3. Tyyppi sallittujen listalla
|
||||||
|
// 4. Tyyppikohtainen validointi (esim. pair_done: token_count <= 10000)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 Rate limiting
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Per-IP liukuva ikkuna: max 10 pyyntöä per 60s
|
||||||
|
let entry = limits.entry(addr.ip()).or_insert((now, 0));
|
||||||
|
if now.duration_since(entry.0).as_secs() >= 60 {
|
||||||
|
*entry = (now, 1);
|
||||||
|
} else {
|
||||||
|
entry.1 += 1;
|
||||||
|
if entry.1 > 10 { return 429 Too Many Requests; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 Gamification-huijauksen esto
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Hub jakaa task_id:n → tallentaa pending_task_ids:hen
|
||||||
|
// Merkkejä jaetaan VAIN jos llm_done sisältää validin task_id:n
|
||||||
|
let valid_task = state.pending_task_ids.lock().unwrap().remove(tid);
|
||||||
|
if active_incentives && valid_task {
|
||||||
|
*balance += 20;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Syntaksikorostus
|
||||||
|
|
||||||
|
### 7.1 Highlight.js-integraatio
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function highlightCode(code) {
|
||||||
|
if (typeof hljs !== 'undefined') {
|
||||||
|
return hljs.highlightAuto(code).value; // Automaattinen kielentunnistus
|
||||||
|
}
|
||||||
|
return esc(code); // Fallback
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Käyttöpaikat:** Codelab-tulokset, agents-terminaalin vastaukset, network-chat.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Agenttien orkestrointi
|
||||||
|
|
||||||
|
### 8.1 Multi-agent pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||||
|
│ Manageri │ ──→ │ Koodari │ ──→ │ Testaaja │
|
||||||
|
│ Analysoi │ │ Koodaa │ │ Arvioi │
|
||||||
|
│ tehtävä │ │ ratkaisu │ │ koodi │
|
||||||
|
└──────────┘ └──────────┘ └──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
async function kpnPipeline(task) {
|
||||||
|
const plan = await kpnRun('qwen-coder', `Analysoi: ${task}`);
|
||||||
|
if (!plan) return;
|
||||||
|
const code = await kpnRun('qwen-coder', `Koodaa: ${plan}`);
|
||||||
|
if (!code) return;
|
||||||
|
await kpnRun('smollm-135m', `Arvioi: ${code}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Agenttien promptien hallinta
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const agentPrompts = {
|
||||||
|
manager: { model: 'qwen-coder', prompt: 'Olet projektipäällikkö...' },
|
||||||
|
coder: { model: 'qwen-coder', prompt: 'Olet ohjelmistokehittäjä...' },
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
// Tallennetaan localStorage:en per agentti
|
||||||
|
localStorage.setItem('kpn-agent-prompt-coder', customPrompt);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 Yhteinen promptikonteksti
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
async function kpnRun(model, prompt) {
|
||||||
|
const parts = [];
|
||||||
|
if (sharedPrompt) parts.push(sharedPrompt); // Kaikille yhteinen
|
||||||
|
if (agent.prompt) parts.push(agent.prompt); // Agenttikohtainen
|
||||||
|
parts.push(prompt); // Käyttäjän pyyntö
|
||||||
|
const fullPrompt = parts.join('\n\n');
|
||||||
|
// → HTTP POST /api/v1/chat/completions
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Teknologiapino
|
||||||
|
|
||||||
|
| Kerros | Teknologia | Tarkoitus |
|
||||||
|
|--------|------------|-----------|
|
||||||
|
| Frontend | Vanilla JS + HTML + CSS | Ei build-steppiä, toimii suoraan |
|
||||||
|
| Wasm | Rust + wasm-bindgen | Inferenssi selaimessa |
|
||||||
|
| LLM | Candle (Rust) | Transformer-inferenssi CPU:lla |
|
||||||
|
| Tensorit | Burn (Rust) | GPU-tensorilaskenta (WebGPU/NdArray) |
|
||||||
|
| Backend | Axum + Tokio (Rust) | Async WebSocket + HTTP -palvelin |
|
||||||
|
| Tietokanta | SQLite (rusqlite) | Sessiot ja tulokset |
|
||||||
|
| Cache | IndexedDB | Mallipainot selaimen pysyvässä muistissa |
|
||||||
|
| Korostus | Highlight.js (CDN) | Syntaksikorostus, automaattinen kielentunnistus |
|
||||||
|
| Tokenizer | HuggingFace tokenizers | BPE-tokenisaatio Wasmissa |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Jatkokehitysideoita
|
||||||
|
|
||||||
|
Näiden rakennuspalasten pohjalta voi rakentaa:
|
||||||
|
|
||||||
|
- **Oma chat-UI:** WebSocket + streaming + syntaksikorostus
|
||||||
|
- **Hajautettu laskentaverkko:** Hub + node-rekisteri + busy-state + työjono
|
||||||
|
- **Selain-LLM:** Wasm + Candle + IndexedDB-cache + warmup
|
||||||
|
- **Agenttipohjainen työnkulku:** Pipeline + prompt-orkestrointi + reititys
|
||||||
|
- **Terminaaliemulasttori:** Input + historia + tab-completion + dropdown
|
||||||
|
- **Reaaliaikadashboard:** WebSocket broadcast + tilaindikaattorit + metriikat
|
||||||
Reference in New Issue
Block a user