Skip to content

culi v0.5.0 — Excelsa

Choose a tag to compare

@hung12ct hung12ct released this 26 Jul 18:02
· 30 commits to main since this release

Culi can finally tell whether a card earned its tokens.

Usage attribution — the missing feedback loop

Culi had no automatic positive signal. expanded requires an MCP expand_card call that in practice never happens; referenced was written in exactly one place — the miner re-observing a lesson, which records that a lesson recurred, not that a card was ever used. Lifetime totals across a whole store were:

total_expanded  total_referenced
0.0             0.0

So every card classified uncertain, every UtilityMultiplier stayed pinned at 1.0, and ranking never learned anything from months of injections.

At SessionEnd the miner now re-reads the finished transcript and credits cards whose own prose reappears in the assistant's replies.

Word overlap was tried first and rejected. Measuring term rarity against the card corpus marks ordinary English as distinctive — card summaries are short and technical, so "also", "keeps" and "development" look rare. On real sessions it credited 10 of 18 injected cards on matches like [also keeps]. Useless as evidence, and actively harmful: any positive marks a card helpful permanently.

Phrase reuse replaced it — five-word prose sequences, with guards:

  • only cards injected at summary/body granularity (a pointer the model never expanded cannot have been used)
  • phrases the user typed are excluded, or a card earns credit for the topic that retrieved it
  • code fences, inline code, URLs and paths stripped — two texts quoting the same path prove no influence
  • at least one substantial word, so "all clean and the" cannot match

Four words still collided on stock English ("starting a new feature"); five credits 1–2 cards per session on real transcripts. Credit is 0.5, below the 1.0 a confirmed re-observation earns, and lands at most once per session.

The embedding arm was down more than you'd think

The hot path gives Ollama 100ms. Warm, nomic-embed-text answers in 36–62ms — but Ollama unloads idle models after ~5 minutes and the cold reload takes seconds. Every gap in a working session was a guaranteed timeout, and three trip the retrieve breaker for a 30s cooldown during which retrieval is BM25-only.

keep_alive now rides on every embed call, refreshing residency on use. Observed live: ollama ps UNTIL moved 4min → 29min and candidates went from bm25 to hybr.

Console fix

The overview's "Noisy cards" panel always rendered empty — it read a downvotes-only top-3 while its label described the effectiveness bucket. Also removes two seed-data fallbacks that displayed invented numbers when the real payload was empty.

Upgrading

git pull && make install

Schema migration v3 → v5, applied automatically. Forward-only ALTERs under BEGIN IMMEDIATE; runtime history (injections, card_stats, session_state, meta) is preserved, and a user_version newer than the binary fails closed. Verified on a live store with 198 injections, 23 card stats and 18 session rows intact.

New default: ollama.keep_alive: 30m, which keeps ~270MB resident for the embedding model. Set "-1" to never unload, or a shorter duration to trade hit rate for memory.

Hard-refresh the console after upgrading — web assets are served from embed.FS, so restarting culi serve alone shows a stale UI.