Lingways is a Spanish-learning platform built on computational Construction Grammar rather than vocabulary lists. This note describes how the engine represents what a learner knows, chooses what to teach next, and measures progress — at the level of design, not implementation.
Fluency is the ability to produce and understand form–meaning pairings, not the size of a word list. Construction Grammar (CxG) treats grammar as an inventory of such pairings, from morphemes to idioms to argument-structure patterns. A learner who knows me gusta as a construction with a dative experiencer slot can generalize to nos interesa on first contact; a learner who memorized the phrase cannot.
Most apps model the learner as a set of known words and a difficulty score. Lingways models the learner as a partial copy of a construction inventory, with a separate strength for each construction and for each of the semantic and pragmatic frames it participates in. Everything downstream — flashcard scheduling, which article the Reader surfaces, what the guide says next — reads from that representation.
The bet is that construction-level modeling gives three things word-level modeling cannot: transfer (one construction licenses many sentences), diagnosability (an error localizes to a construction, not a word), and dialect sensitivity (the same frame is realized differently in Mexico City and Madrid, and the model keeps them apart).
The current inventory holds 42 constructions, 26 semantic/pragmatic frames, and 5 dialect variants, hand-built and intended to grow. Each construction record carries:
| Field | What it holds | Example |
|---|---|---|
| Form | Slot structure with morphosyntactic constraints per slot | [EXPERIENCER.dat] gustar [STIMULUS.nom] |
| Frames | The semantic/pragmatic frames the construction evokes | Liking, Evaluation |
| Dialect variants | Realizations that differ by region, with register notes | vosotros vs ustedes forms |
| Dependencies | Constructions a learner should hold first | clitic pronouns, present indicative |
| Cost vector | Per-component difficulty across morphology, lexicon, syntax, semantics, pragmatics | see Target Model generation |
Lexical grounding comes from a 794K-entry Spanish lexicon indexed in Typesense, so a construction slot can be filled with real, frequency-ranked candidates at query time. Dialect commitments were shaped in part by work alongside Tzotzil-speaking colleagues, which is why variant tracking is a first-class field rather than a flag.
The NLP service (Python, FastAPI, Stanza) parses learner input and reading material into dependency structure, then matches spans against construction form templates. A match is a signal about one construction, not about a sentence.
Given a learner's current inventory and a goal — a situation, register, or dialect they want to operate in — the engine builds a Target Model: the smallest set of constructions that closes the gap, ordered so each one is learnable from what came before.
The search runs breadth-first over the construction dependency graph from the learner's frontier. Each edge carries a cost vector rather than a scalar: morphology, lexicon, syntax, semantics, and pragmatics load are tracked separately, and the learner's own component-level weaknesses weight them. The result is a distance vector between current and target competency, not a single difficulty number. Two learners with the same total distance can get different Target Models because the shape of the distance differs.
That vector drives three consumers today:
The exact edge-cost formulation and the pruning rules are the part I keep out of public write-ups. The structure — a BFS over a typed construction graph with vector costs — is the part worth discussing.
Comprehension and production are tracked as independent strengths per construction. A learner who reads se me olvidó correctly but never produces it has one score high and the other low, and the Target Model treats those as different gaps with different exercises. Collapsing them into one mastery number is the most common modeling error in language apps, and the one the construction layer makes easiest to avoid.
Every interaction writes one row to a minimal event schema:
{user_id, construction_id, frame_id, modality, source, signal, latency, timestamp}
modality is comprehension or production; source is which surface produced the event (deck, reader, guide chat); signal is the graded outcome. The schema is deliberately small so that every new activity type can emit it without a migration.
On top of the event stream, the knowledge-tracing layer is in progress. The MVP uses per-construction Bayesian updates. The next design is a hybrid: an attention-based sequence encoder over a learner's interaction history, feeding per-skill Bayesian heads, so the model keeps interpretable per-construction mastery estimates while borrowing strength across related constructions. Cross-construction propagation through the frame graph is deferred until there is enough interaction data to fit it honestly.
The app talks only to the Node API; the API calls the Python engine for parsing and Target Model planning, and both write to the same stores. The engine is stateless per request, so it can be scaled or swapped without touching the app.
Three problems I'd most like to work on with someone:
I'm a solo founder with a background in archaeological science (more here), and I'm looking for an ML/NLP co-founder who wants to own the modeling side of a research-driven product company. Knowledge tracing, LLM agents, or multi-agent simulation experience would all be relevant.
If any of the three problems above made you want to argue, I'd like to hear it.