Skip to content

Frustration lexicon: learn multi-word phrases, not just single tokens #40

Description

@elecnix

Problem

The learned frustration lexicon added in #39 judges one token at a time. Running it over a real corpus made the limitation concrete: a French-speaking user's frustration was not detected at all, despite the vocabulary being nominated correctly.

The terms were judged individually, and individually they are all neutral:

term verdict but in context
laisse neutral / und laisse tomber — "forget it", clear disengagement
tomber neutral / fr
trop neutral / fr trop lent — "too slow", dissatisfaction
lent neutral / und

Each judgement is correct for the token in isolation. The signal simply does not live in any single token — it lives in the bigram.

This is not French-specific. English has the same shape: never mind, forget it, come on, what the hell, not again, same thing. Single-token judgement structurally cannot see any of them.

Why it is not a prompt fix

The token prompt in frustration-lexicon/prompt.ts deliberately asks about a token's habitual usage with no session context, because a term node's recipe is the term alone. Adding an example sentence would make identity dishonest — the recipe would claim "just the word" while the verdict actually turned on one session's sentence, and the first session to nominate the word would silently fix the verdict for everyone. That constraint is correct and should stay.

The fix is to make phrases first-class subjects, not to weaken term identity.

Sketch

Everything needed is already in place — a phrase is just another corpus-keyed subject:

  • lexicon-candidates additionally nominates bigrams (and possibly trigrams) from adjacent tokens in the same message, ranked by frequency, under their own cap.
  • frustration-lexicon judges a phrase exactly as it judges a term. The source ref stays {kind: "term", id: "laisse tomber"} — the existing term source kind already carries arbitrary normalised strings, so a phrase gets the same corpus-wide cache and the same revises lineage for free.
  • turn-frustration matches phrases over the ordered token stream (it already tokenises; matching an n-gram is a windowed compare) and emits the same one-node-per-(turn, signal) hit, so additivity is preserved.

Open questions worth settling before building:

  • Combinatorics. Bigrams are far more numerous than unigrams and most are junk. Frequency ranking plus a per-session cap probably suffices, but it needs measuring against a real corpus before the cap is chosen — the cache is permanent, so junk judged once is junk stored forever.
  • Overlap. When both putain and putain c'est faux match a turn, do we emit both hits, or prefer the longest match? Emitting both is simpler and stays additive; the weight calculation should probably not double-count.
  • Do phrases need their own analyzer version axis, or is bumping lexicon-candidates enough? (Nomination changing is what alters what gets judged.)

Value

Likely the single highest-recall improvement available to the lexicon. Multi-word expressions are how a lot of natural frustration is actually phrased, in every language — and the corpus run shows we currently score all of it at zero.

Follow-up to #39.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions