nx_convo.nx source
↩ module page · 83 lines · 3384 B
1// nx_convo.nx -- WRITING arc, rung W-CONVO-1: the CONVERSATIONAL TUTOR core.
2// Serves two operator asks with one engine:
3// (1) "a couple doing language learning -- keep topics going"
4// (2) "young people learn positive social skills to overcome anxiety, fun way"
5//
6// Three deterministic, data-driven primitives (the generation seat -- the LLM that
7// actually speaks -- plugs in on top; this organ keeps the SESSION honest):
8//
9// cv_next_topic -- KEEP TOPICS GOING: from a topic bank (each topic has a
10// difficulty + the turn it was last used), pick the eligible
11// topic (difficulty <= level+window) that is LEAST RECENTLY
12// used -> fresh, level-appropriate, never an immediate repeat.
13// cv_rubric -- COACHING SCORE, RESPECT-GATED BY CONSTRUCTION: a weighted
14// score across axes (warmth/authenticity/respect/confidence),
15// BUT if the gate axis (respect / consent-reading) is below its
16// minimum the attempt is DISQUALIFIED (-1) no matter how high
17// "confidence" is. "Positive" is enforced in the math: you
18// cannot win by being pushy. This is the anti-manipulation law.
19// cv_level_adjust-- ADAPTIVE DIFFICULTY: nudge the target level up/down within
20// bounds from the last score (overcome-anxiety pacing).
21//
22// All thresholds/weights/axes are operator DATA (rule 11), passed in -- nothing
23// hardcoded. Pure integer, NO syscalls, NO imports, caller owns buffers.
24//
25// license_tier: ORIGINAL
26// module: nishi-core.write.convo
27// depends:
28// capability: WRITE_CONVO_TUTOR
29
30const CV_DISQUALIFIED: i64 = 0 - 1
31
32// KEEP TOPICS GOING: index of the eligible (diff <= level+window) least-recently-
33// used topic; ties -> lowest index; -1 if none eligible.
34func cv_next_topic(last_used: *i64, diff: *i64, ntopics: i64, level: i64, window: i64) -> i64 {
35 var best: i64 = 0 - 1
36 var best_lu: i64 = 0
37 var i: i64 = 0
38 while i < ntopics {
39 if diff[i] <= level + window {
40 if best < 0 {
41 best = i
42 best_lu = last_used[i]
43 } else {
44 if last_used[i] < best_lu {
45 best = i
46 best_lu = last_used[i]
47 }
48 }
49 }
50 i = i + 1
51 }
52 return best
53}
54
55// RESPECT-GATED COACHING SCORE: weighted average over axes, or CV_DISQUALIFIED if
56// the gate axis is below gate_min (anti-manipulation, by construction).
57func cv_rubric(scores: *i64, weights: *i64, naxes: i64, gate_idx: i64, gate_min: i64) -> i64 {
58 if scores[gate_idx] < gate_min { return CV_DISQUALIFIED }
59 var num: i64 = 0
60 var den: i64 = 0
61 var i: i64 = 0
62 while i < naxes {
63 num = num + (scores[i] * weights[i])
64 den = den + weights[i]
65 i = i + 1
66 }
67 if den < 1 { den = 1 }
68 return num / den
69}
70
71// ADAPTIVE DIFFICULTY: raise the level on a strong score, lower it on a weak one,
72// clamped to [lvl_min, lvl_max].
73func cv_level_adjust(level: i64, score: i64, up_th: i64, down_th: i64, lvl_min: i64, lvl_max: i64) -> i64 {
74 var lv: i64 = level
75 if score >= up_th {
76 if lv < lvl_max { lv = lv + 1 }
77 } else {
78 if score < down_th {
79 if lv > lvl_min { lv = lv - 1 }
80 }
81 }
82 return lv
83}