code wiki / (root) / nx_research_verify_synth.nx

nx_research_verify_synth.nx source

↩ module page · 136 lines · 6491 B

1// nx_research_verify_synth.nx -- S1 <-> S2 INTEGRATION: the self-verifying researcher. It runs S1 (grounded 2// synthesis, nx_research_synth_qwen) and then, for every NUMERIC sentence-claim, checks that the model's number 3// is actually PRESENT (within a tight tolerance) in the source that claim cites -- CONFIRMED if present, 4// DISCREDITED if the source has numbers but not that one (the model fabricated/altered it), UNVERIFIABLE if the 5// claim is uncited or the source has no numbers. A synthesized answer thus grades ITS OWN numeric claims against 6// the evidence, at $0. Composes S1 (rsq_synth / rsq_sentence_cite) + S2 (cvc_iabs, the verdict discipline). 7// HONEST SCOPE: this catches numeric FABRICATION/ALTERATION (the common hallucination). Metric MISATTRIBUTION 8// (right number, wrong metric) needs S2's explicit-pattern path (nx_claim_verify_core cvc_verify_text with a 9// caller-supplied pattern, as in nx_research_refute). license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_research_synth_qwen.nx" 12import "nx_claim_verify_core.nx" 13import "nx_text_sentences.nx" 14 15// parse the number at buf[off] as TENTHS and report its end offset in endp[0]. off must point at a digit. 16func rvs_num_at(buf: *u8, blen: i64, off: i64, endp: *i64) -> i64 { 17 var i: i64 = off 18 var whole: i64 = 0 19 var any: i64 = 0 20 var go: i64 = 1 21 while go == 1 { 22 go = 0 23 if i < blen { 24 let c: i64 = buf[i] as i64 & 0xff 25 if c >= 48 { if c <= 57 { whole = whole*10 + (c-48); any = 1; i = i + 1; go = 1 } } 26 else { if c == 44 { if any == 1 { if i+1 < blen { let nd: i64 = buf[i+1] as i64 & 0xff; if nd >= 48 { if nd <= 57 { i = i + 1; go = 1 } } } } } } // thousands separator BETWEEN digits (bench H1): 107,785 -> 107785 27 } 28 } 29 if any == 0 { endp[0] = off + 1; return 0-1 } 30 var tenths: i64 = whole * 10 31 if i < blen { if (buf[i] as i64 & 0xff) == 46 { if i+1 < blen { let d: i64 = buf[i+1] as i64 & 0xff; if d >= 48 { if d <= 57 { tenths = tenths + (d-48); i = i + 2 } } } } } 32 endp[0] = i 33 return tenths 34} 35// first number (tenths) in span [s,e), or -1 if none. 36func rvs_first_number(buf: *u8, s: i64, e: i64) -> i64 { 37 let ep: *i64 = sys_mmap(16) as *i64 38 var i: i64 = s 39 while i < e { 40 let c: i64 = buf[i] as i64 & 0xff 41 if c == 91 { // '[' -- skip the whole citation token so [1] is NOT read as a number 42 var j: i64 = i + 1 43 var go: i64 = 1 44 while go == 1 { go = 0; if j < e { if (buf[j] as i64 & 0xff) != 93 { j = j + 1; go = 1 } } } 45 if j < e { i = j + 1 } else { i = e } 46 } else { 47 if c >= 48 { if c <= 57 { return rvs_num_at(buf, e, i, ep) } } 48 i = i + 1 49 } 50 } 51 return 0-1 52} 53// walk EVERY number in span [s,e) (skipping [n] citation tokens); each must be near-present in the cited source. 54// outp[0]=numbers found, outp[1]=all-near (1 when every number is present; the SECOND-NUMBER LAUNDERING kill -- 55// judgment bench N6: a true first number must not launder a fabricated second one). hp = rvs_src_has_near scratch. 56func rvs_span_numbers_near(ans: *u8, s: i64, e: i64, src: *u8, sl: i64, tol: i64, hp: *i64, outp: *i64) -> i64 { 57 let ep: *i64 = sys_mmap(16) as *i64 58 var found: i64 = 0 59 var allnear: i64 = 1 60 var i: i64 = s 61 while i < e { 62 let c: i64 = ans[i] as i64 & 0xff 63 if c == 91 { // '[' -- skip the citation token 64 var j: i64 = i + 1 65 var go: i64 = 1 66 while go == 1 { go = 0; if j < e { if (ans[j] as i64 & 0xff) != 93 { j = j + 1; go = 1 } } } 67 if j < e { i = j + 1 } else { i = e } 68 } else { 69 if c >= 48 { if c <= 57 { 70 let num: i64 = rvs_num_at(ans, e, i, ep) 71 if num >= 0 { found = found + 1; if rvs_src_has_near(src, sl, num, tol, hp) == 0 { allnear = 0 } } 72 i = ep[0] 73 } else { i = i + 1 } } else { i = i + 1 } 74 } 75 } 76 outp[0] = found 77 outp[1] = allnear 78 return found 79} 80// does the claimed value appear (within tol) among the numbers in src? has[0]=1 if src has ANY number. 81func rvs_src_has_near(src: *u8, slen: i64, claimed: i64, tol: i64, has: *i64) -> i64 { 82 has[0] = 0 83 var near: i64 = 0 84 let ep: *i64 = sys_mmap(16) as *i64 85 var i: i64 = 0 86 while i < slen { 87 let c: i64 = src[i] as i64 & 0xff 88 if c >= 48 { if c <= 57 { 89 let num: i64 = rvs_num_at(src, slen, i, ep) 90 if num >= 0 { has[0] = 1; if cvc_iabs(num - claimed) <= tol { near = 1 } } 91 i = ep[0] 92 } else { i = i + 1 } } else { i = i + 1 } 93 } 94 return near 95} 96// grade each numeric claim against its cited source. out[0]=numeric out[1]=confirmed out[2]=discredited 97// out[3]=unverifiable. tol in tenths. returns numeric-claim count. 98func rvs_verify_answer(ans: *u8, alen: i64, srcv: *i64, nsrc: i64, tol: i64, out: *i64) -> i64 { 99 var numeric: i64 = 0 100 var cf: i64 = 0 101 var ds: i64 = 0 102 var uv: i64 = 0 103 let op: *i64 = sys_mmap(32) as *i64 104 let hp: *i64 = sys_mmap(32) as *i64 105 let np: *i64 = sys_mmap(32) as *i64 106 var s: i64 = 0 107 while s < alen { 108 let e: i64 = txt_sentence_end(ans, s, alen) 109 let claimed: i64 = rvs_first_number(ans, s, e) 110 if claimed >= 0 { 111 numeric = numeric + 1 112 let c: i64 = rsq_sentence_cite(ans, s, e, nsrc, op) 113 if c >= 1 { 114 let src: *u8 = srcv[c-1] as *u8 115 let sl: i64 = rsq_len(src) 116 rvs_span_numbers_near(ans, s, e, src, sl, tol, hp, np) // EVERY number must be near (bench N6 laundering kill) 117 if np[1] == 1 { cf = cf + 1 } else { if hp[0] == 1 { ds = ds + 1 } else { uv = uv + 1 } } 118 } else { uv = uv + 1 } 119 } 120 s = e + 1 121 } 122 out[0] = numeric 123 out[1] = cf 124 out[2] = ds 125 out[3] = uv 126 return numeric 127} 128// the full self-verifying researcher: synthesize (S1) then grade its numeric claims (S2). model-gated. 129// returns the answer byte-length (answer in ans, report in out), or -1 if the model isn't ready. 130func rvs_answer(q: *u8, srcv: *i64, nsrc: i64, tol: i64, ans: *u8, out: *i64) -> i64 { 131 if rsq_ready() == 0 { if rsq_load() != 0 { return 0-1 } } 132 let al: i64 = rsq_synth(q, srcv, nsrc, ans) 133 if al <= 0 { return 0-1 } 134 rvs_verify_answer(ans, al, srcv, nsrc, tol, out) 135 return al 136}