Correct embed(): the index is CLS-pooled, not mean -- a port trusting the old docstring would silently land in a different vector space
999c9f2 verified Download code/taviel_decide.py from OhBeOneKeyNoBe/YahBible: direct link, hf CLI and curl.
- Browser
- Download file 30.8 kB
-
https://huggingface.co/OhBeOneKeyNoBe/YahBible/resolve/main/code/taviel_decide.py
- Command line
-
hf download hf://OhBeOneKeyNoBe/YahBible/code/taviel_decide.py
-
curl -L -o taviel_decide.py https://huggingface.co/OhBeOneKeyNoBe/YahBible/resolve/main/code/taviel_decide.py
30.8 kB
| r"""TAV'IEL-DECIDE -- single-pass typed decisions for YahBible's AI ("Jev mode"). | |
| The current pipeline (taviel_reason.py) makes every decision INSIDE prose | |
| generation: which groundings to pull, what kind of question this is, which | |
| gauntlet round matches, whether a cited verse is real -- all decided while the | |
| model is already writing, across two 360-token autoregressive generations. | |
| This module moves those decisions in front of generation, using the | |
| parallel-decision fork of llama.cpp (D:\llama.cpp-decision, branch | |
| parallel-decision): every field of a finite schema is scored as token paths | |
| forking from ONE shared KV cache, so all questions are answered in a single | |
| llama_decode with a calibrated probability each, and the JSON is assembled by | |
| code -- it cannot be malformed. Same mechanism harshatheg benchmarked at | |
| 5.6-7.0x over autoregressive JSON on this exact base model | |
| (Qwen2.5-1.5B-Instruct). | |
| Server: the fork's llama-server with --decision-seqs, port 8110 (root tier, | |
| Qwen2.5-1.5B-Instruct-Q4_K_M -- Tav'iel's existing mobile-tier GGUF; one | |
| resident model serves BOTH /v1/chat/completions and /v1/decision). | |
| Machine-first: nothing here loads a model into this process; it is an HTTP | |
| client. Start/stop the server with serve_up()/serve_down(). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import subprocess | |
| import time | |
| import urllib.request | |
| PORT = int(os.environ.get("TAV_DECIDE_PORT", "8110")) | |
| URL = f"http://127.0.0.1:{PORT}/v1/decision" | |
| SERVER = r"D:\llama.cpp-decision\build-cuda-mmq\bin\llama-server.exe" | |
| GGUF = os.environ.get("TAV_DECIDE_GGUF", | |
| r"D:\0000_Raw_LLM Models\Qwen2.5-1.5B-Instruct-Q4_K_M.gguf") | |
| NGL = int(os.environ.get("TAV_DECIDE_NGL", "99")) | |
| EMB_PORT = int(os.environ.get("TAV_EMB_PORT", "8111")) | |
| EMB_SERVER = r"D:\0000_Raw_LLM Models\llama.cpp-tools\llama.cpp\llama-server.exe" | |
| # a REAL contrastive embedder: causal-LM mean-pooling (Qwen 0.6B) proved | |
| # recall-blind -- 4/20 paraphrases, the true round absent from top-8. bge-small | |
| # is 33M params, CPU-instant, trained for exactly this. | |
| # bge-BASE (110M) over bge-small (33M): measured on 630 held-out askings, | |
| # R@8 86% -> 88%, spatial 53->59, R@3 83->84. Still CPU-only, still zero | |
| # VRAM taken from the decision model. | |
| EMB_GGUF = os.environ.get( | |
| "TAV_EMB_GGUF", r"D:\0000_Raw_LLM Models\bge-base-en-v1.5-q8_0.gguf") | |
| _proc = None | |
| _emb_proc = None | |
| # ---------------------------------------------------------------- server ---- | |
| def serve_up(ngl=NGL, ctx=4096, seqs=16, wait_s=180): | |
| """Start the decision server (one resident model, 1.5B fits the 1650 whole).""" | |
| global _proc | |
| if _up(): | |
| return True | |
| # the fork asserts (ggml_can_mul_mat) when --embeddings meets the unified | |
| # decision KV, so recall gets its own tiny embedder: the third_eye tier | |
| # (0.6B) on CPU ONLY -- zero VRAM taken from the decision model. | |
| global _emb_proc | |
| _emb_proc = subprocess.Popen( | |
| [EMB_SERVER, "-m", EMB_GGUF, "-ngl", "0", "-c", "512", | |
| "--embeddings", "--port", str(EMB_PORT)], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| # ctx 8192 x 24 unified decision sequences proved unstable on the 4 GB | |
| # card across long runs; 4096 x 16 comfortably covers the router's 8 | |
| # candidates plus instructions and has not died since. | |
| _proc = subprocess.Popen( | |
| [SERVER, "-m", GGUF, "-ngl", str(ngl), "-fa", "on", "-c", str(ctx), | |
| "--decision-seqs", str(seqs), "--port", str(PORT)], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| t0 = time.time() | |
| while time.time() - t0 < wait_s: | |
| if _up() and _emb_up(): | |
| return True | |
| time.sleep(1.0) | |
| raise RuntimeError("server(s) did not come up in %ss (decision=%s embed=%s)" | |
| % (wait_s, _up(), _emb_up())) | |
| _AVAIL = {"at": 0.0, "ok": False} | |
| AVAIL_TTL = 20.0 | |
| def available(timeout=0.4, ttl=AVAIL_TTL): | |
| """Is Jev actually here? A CHEAP probe that never starts anything. | |
| This exists because of what serve_up() costs when the answer is no. Its | |
| retry path waits up to 180 seconds for a server to come up, and decide() | |
| calls it three times -- so on a machine without the fork binary, or with | |
| one that cannot allocate, a single user question could hang for minutes | |
| before falling back. A speedup whose absence costs six minutes is not a | |
| speedup. | |
| So every caller that would LIKE to use the decision server asks here | |
| first. The probe is a HEAD-weight /health with a short timeout, cached for | |
| a few seconds so a chat turn does not pay for it repeatedly, and it never | |
| spawns a process. A machine without Jev behaves exactly as it did before | |
| Jev existed, at the cost of one refused connection every ttl seconds. | |
| Both servers are required: the router needs the decision endpoint to judge | |
| and the embedder to retrieve, and a half-present Jev routes worse than no | |
| Jev at all. | |
| """ | |
| now = time.time() | |
| if now - _AVAIL["at"] < ttl: | |
| return _AVAIL["ok"] | |
| ok = False | |
| try: | |
| urllib.request.urlopen(f"http://127.0.0.1:{PORT}/health", timeout=timeout) | |
| urllib.request.urlopen(f"http://127.0.0.1:{EMB_PORT}/health", | |
| timeout=timeout) | |
| ok = True | |
| except Exception: | |
| ok = False | |
| _AVAIL.update(at=now, ok=ok) | |
| return ok | |
| def unavailable(): | |
| """Mark Jev as gone without waiting for the cache to lapse. | |
| Called when a decision actually fails mid-turn, so the next question does | |
| not repeat a doomed round-trip. | |
| """ | |
| _AVAIL.update(at=time.time(), ok=False) | |
| def serve_down(): | |
| global _proc, _emb_proc | |
| for p in (_proc, _emb_proc): | |
| if p: | |
| p.terminate() | |
| _proc = _emb_proc = None | |
| def _up(): | |
| try: | |
| urllib.request.urlopen(f"http://127.0.0.1:{PORT}/health", timeout=2) | |
| return True | |
| except Exception: | |
| return False | |
| def _emb_up(): | |
| try: | |
| urllib.request.urlopen(f"http://127.0.0.1:{EMB_PORT}/health", timeout=2) | |
| return True | |
| except Exception: | |
| return False | |
| # ---------------------------------------------------------------- client ---- | |
| def decide(schema, contexts, instructions="", timeout=120, _tries=3): | |
| """POST /v1/decision. contexts: str or list of 1-256 strings sharing one | |
| schema + one cached instruction prefix. Returns the parsed response. | |
| RESILIENT BY DESIGN: the fork's server has died mid-run three times | |
| across long evaluations, and each time the numbers it corrupted looked | |
| like a model failure rather than a dead socket (the always-answer law | |
| once scored 11/40 purely from connection-refused). A refused connection | |
| now restarts the server and retries, so a measurement reports the system | |
| rather than the server's mood.""" | |
| if isinstance(contexts, str): | |
| contexts = [contexts] | |
| body = json.dumps({"instructions": instructions, "schema": schema, | |
| "contexts": contexts}).encode() | |
| last = None | |
| for attempt in range(_tries): | |
| try: | |
| req = urllib.request.Request( | |
| URL, data=body, headers={"Content-Type": "application/json"}) | |
| with urllib.request.urlopen(req, timeout=timeout) as r: | |
| return json.loads(r.read()) | |
| except Exception as ex: | |
| last = ex | |
| if attempt == _tries - 1: | |
| break | |
| import sys | |
| import time as _t | |
| print(f"[taviel_decide] decision server unreachable " | |
| f"({type(ex).__name__}); restarting", file=sys.stderr) | |
| try: | |
| if _proc: | |
| _proc.terminate() | |
| except Exception: | |
| pass | |
| globals()["_proc"] = None | |
| _t.sleep(2) | |
| serve_up() | |
| raise last | |
| def decisions_of(resp): | |
| """[{field: value}] per context, in order.""" | |
| return [r["decision"] for r in resp["results"]] | |
| def fields_of(resp, i=0): | |
| """{field: {value, probability, ...}} for context i.""" | |
| return resp["results"][i]["fields"] | |
| # ---------------------------------------------------------------- schemas --- | |
| # 1) THE ANSWER PLAN -- the full shape of the answer, decided before the first | |
| # generated token. One decode answers all nine. | |
| # There is deliberately NO "route" question. v1 asked the model to route with | |
| # bare labels and "decline" won at p=0.96 on a core objection; v2's descriptive | |
| # phrases still lost at p=0.61. Routing is a COMPOUND judgment a 1.5B fumbles, | |
| # while the primitive judgments below measure strong (genre=objection p=0.97). | |
| # So the model answers primitives, and plan() COMPOSES the route in code -- | |
| # "with code owning the action", as the aeon-pack doctrine has it. | |
| ANSWER_PLAN = { | |
| "wants_passage": {"type": "boolean", | |
| "description": "Do they only want a Bible passage's text " | |
| "quoted to them, not an explanation?"}, | |
| "genre": {"type": "enum", | |
| "choices": ["objection", "word_study", "doctrine", "comfort", | |
| "history", "practice"], | |
| "description": "What kind of question is this?"}, | |
| "register": {"type": "enum", "choices": ["skeptic", "believer", "child"], | |
| "description": "Who is asking, by their wording?"}, | |
| "depth": {"type": "enum", "choices": ["brief", "moderate", "full"], | |
| "description": "How thorough must the answer be to satisfy the asker?"}, | |
| "need_scripture": {"type": "boolean", | |
| "description": "Would verse quotations strengthen the answer?"}, | |
| "need_hebrew_greek": {"type": "boolean", | |
| "description": "Does answering require the original " | |
| "Hebrew or Greek behind a word?"}, | |
| "need_apologetics": {"type": "boolean", | |
| "description": "Is this an objection or challenge to the " | |
| "faith that a recorded argument answers?"}, | |
| "christ_first": {"type": "boolean", | |
| "description": "Should the answer open with what Yeshua the " | |
| "Christ Himself said or did on this?"}, | |
| "in_scope": {"type": "boolean", | |
| "description": "Is this within Scripture study, doctrine, or the " | |
| "life of faith at all?"}, | |
| } | |
| PLAN_INSTRUCTIONS = ("You are Tav'iel, the study guide of YahBible, a Scripture " | |
| "study app. Judge the user's question below and answer each " | |
| "field from the question's own wording.") | |
| def _true(v): | |
| return v in (True, "true") | |
| def plan(question): | |
| """One decode of primitive judgments; the route is COMPOSED in code.""" | |
| r = decide(ANSWER_PLAN, "Question: " + question, PLAN_INSTRUCTIONS) | |
| dec, fl = decisions_of(r)[0], fields_of(r) | |
| if not _true(dec.get("in_scope")) and fl["in_scope"]["probability"] >= 0.6: | |
| dec["route"] = "decline" | |
| elif _true(dec.get("wants_passage")): | |
| dec["route"] = "verse_lookup" | |
| elif dec.get("genre") == "objection" or _true(dec.get("need_apologetics")): | |
| dec["route"] = "vetted" # the router confirms; its NONE -> reason | |
| else: | |
| dec["route"] = "reason" | |
| return dec, fl, r.get("timings", {}) | |
| # 2) THE GAUNTLET ROUTER -- which of the 100 vetted rounds answers this | |
| # question. v1 (a 100-way enum of round IDS) proved 9/20: id tokens carry no | |
| # meaning. v2 (100 independent booleans) proved 1/20 at 4.7s: "covers" says | |
| # yes to everything topical, and independent judgments cannot discriminate. | |
| # v3: the existing LEXICAL net shortlists a handful, and the decision is one | |
| # enum whose CHOICES ARE THE CANDIDATE QUESTIONS THEMSELVES (plus NONE) -- | |
| # the candidates compete inside one softmax, which is where a decision | |
| # model's discrimination actually lives. | |
| _KB = None | |
| def _kb(): | |
| global _KB | |
| if _KB is None: | |
| p = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gauntlet_kb.json") | |
| _KB = json.load(open(p, encoding="utf-8")) | |
| return _KB | |
| _STOP = set("a an the is are was be been do does did what why how who when where " | |
| "isn didn doesn can could would should i you it they of in on to for " | |
| "with about that this if not no or and but there s t re".split()) | |
| def _shortlist(question, k=8): | |
| qw = {w for w in "".join(c if c.isalnum() else " " for c in question.lower()) | |
| .split() if w not in _STOP} | |
| scored = [] | |
| for e in _kb(): | |
| ew = set() | |
| for field in ("words", "keys"): | |
| v = e.get(field) or [] | |
| ew |= {str(x).lower() for x in (v if isinstance(v, list) else [v])} | |
| ew |= {w for w in "".join(c if c.isalnum() else " " for c in | |
| e["question"].lower()).split() if w not in _STOP} | |
| s = len(qw & ew) | |
| if s: | |
| scored.append((s, e)) | |
| scored.sort(key=lambda x: -x[0]) | |
| return [e for _, e in scored[:k]] | |
| def embed(texts): | |
| """CLS-pooled embeddings from the bge server (/v1/embeddings). | |
| NOT mean-pooled -- this docstring said so and was wrong. The GGUF declares | |
| `bert.pooling_type = 2`, which is CLS, and serve_up passes no --pooling | |
| flag, so the model's own default stands. Every vector in the precomputed | |
| index was therefore built with CLS. | |
| This matters far beyond a docstring: transformers.js and most ONNX | |
| examples default to MEAN pooling. A port that accepts that default puts | |
| its queries in a different space from the keys and recall collapses with | |
| no error and no exception -- just quietly worse answers. Any | |
| reimplementation must pool CLS, must truncate at 512 tokens (`-c 512`), | |
| and must NOT prepend bge's "Represent this sentence for searching | |
| relevant passages: " instruction, because llama-server does not. | |
| """ | |
| if isinstance(texts, str): | |
| texts = [texts] | |
| body = json.dumps({"input": texts}).encode() | |
| last = None | |
| for attempt in range(3): | |
| try: | |
| req = urllib.request.Request( | |
| f"http://127.0.0.1:{EMB_PORT}/v1/embeddings", data=body, | |
| headers={"Content-Type": "application/json"}) | |
| with urllib.request.urlopen(req, timeout=180) as r: | |
| data = json.loads(r.read())["data"] | |
| return [d["embedding"] for d in sorted(data, key=lambda d: d["index"])] | |
| except Exception as ex: | |
| # same lesson as decide(): building the key cache issues ~180 | |
| # batched calls, and one dropped connection used to abort a whole | |
| # proof run with a bare URLError. | |
| last = ex | |
| if attempt == 2: | |
| break | |
| import sys | |
| import time as _t | |
| print(f"[taviel_decide] embedder unreachable ({type(ex).__name__}); " | |
| "restarting", file=sys.stderr) | |
| try: | |
| if _emb_proc: | |
| _emb_proc.terminate() | |
| except Exception: | |
| pass | |
| globals()["_emb_proc"] = None | |
| _t.sleep(2) | |
| serve_up() | |
| raise last | |
| def _cos(a, b): | |
| num = sum(x * y for x, y in zip(a, b)) | |
| da = sum(x * x for x in a) ** 0.5 | |
| db = sum(x * x for x in b) ** 0.5 | |
| return num / (da * db + 1e-9) | |
| _ROUND_EMB = None | |
| def _round_embs(): | |
| """Views per round: the question, its key words, and -- when the variant | |
| file exists -- every generated way of asking it (registers, synonyms, | |
| antonym framings, symbolic/metaphorical, ballpark). A user question | |
| routes by its NEAREST view, so nobody needs the exact wording. Variant | |
| embeddings are cached to disk keyed on the variant file's mtime.""" | |
| global _ROUND_EMB | |
| if _ROUND_EMB is None: | |
| base = os.path.dirname(os.path.abspath(__file__)) | |
| views = {} # n -> [text, ...] | |
| for e in _kb(): | |
| words = e.get("words") or [] | |
| views[e["n"]] = [e["question"].strip('"'), | |
| " ".join(str(w) for w in words[:16]) or | |
| e["question"].strip('"')] | |
| vfile = os.path.join(base, "gauntlet_variants.json") | |
| if os.path.exists(vfile): | |
| var = json.load(open(vfile, encoding="utf-8")) | |
| for k_, vs in var.items(): | |
| views.setdefault(int(k_), []).extend(vs) | |
| # flat embed with a disk cache (numpy npz keyed by content signature) | |
| import hashlib | |
| import numpy as np | |
| flat, owners, isvar = [], [], [] | |
| nbase = {n: 0 for n in views} | |
| for n in sorted(views): | |
| for i, t in enumerate(views[n]): | |
| flat.append(t) | |
| owners.append(n) | |
| isvar.append(0 if i < 2 else 1) # first two are question+words | |
| sig = hashlib.sha1("\x1f".join(flat).encode("utf-8")).hexdigest()[:16] | |
| cache = os.path.join(base, f"round_embs_{sig}.npz") | |
| if os.path.exists(cache): | |
| z = np.load(cache) | |
| arr = z["e"] | |
| else: | |
| out = [] | |
| for i in range(0, len(flat), 64): | |
| out.extend(embed(flat[i:i + 64])) | |
| arr = np.asarray(out, dtype="float32") | |
| arr /= (np.linalg.norm(arr, axis=1, keepdims=True) + 1e-9) | |
| np.savez_compressed(cache, e=arr) | |
| _ROUND_EMB = (np.asarray(owners), np.asarray(isvar), arr) | |
| return _ROUND_EMB | |
| # MEASURED on the paraphrase set + off-scope traps (tune_recall.py): | |
| # knn k10 p6 ranks best (19/20 R@1) but its scores do NOT separate scope | |
| # (worst on-topic 0.202 BELOW best off-scope 0.417); | |
| # base_max (question+words only) ranks worse (17/20) but is the only | |
| # strategy with positive separation (0.616 on-topic vs 0.583 off-scope). | |
| # So the two jobs take the two signals: BASE-MAX GATES, KNN RANKS. Variants | |
| # then help recall without letting one noisy generated line admit an | |
| # off-scope question. | |
| KNN_K, KNN_POWER = 10, 6 | |
| def _semantic_shortlist(question, k=8): | |
| """[(knn_score, entry)] best-first. The gate score rides on the list as | |
| .scope (see _scope_of) so callers can judge in/out of scope separately.""" | |
| import numpy as np | |
| owners, isvar, arr = _round_embs() | |
| q = np.asarray(embed(question)[0], dtype="float32") | |
| q /= (np.linalg.norm(q) + 1e-9) | |
| sims = arr @ q | |
| base_mask = isvar == 0 | |
| scope = float(sims[base_mask].max()) if base_mask.any() else 0.0 | |
| kk = min(KNN_K, sims.shape[0]) | |
| idx = np.argpartition(-sims, kk - 1)[:kk] | |
| acc = {} | |
| for i in idx: | |
| acc[int(owners[i])] = acc.get(int(owners[i]), 0.0) + \ | |
| float(sims[i]) ** KNN_POWER | |
| byn = {e["n"]: e for e in _kb()} | |
| top = sorted(acc.items(), key=lambda x: -x[1])[:k] | |
| out = [(s, byn[n]) for n, s in top] | |
| _semantic_shortlist.last_scope = scope | |
| return out | |
| def _scope_of(question=None): | |
| """The gate score of the most recent shortlist (base-view max cosine).""" | |
| return getattr(_semantic_shortlist, "last_scope", 0.0) | |
| def suggest(question, k=3, exclude=None): | |
| """Better questions near this one -- ALWAYS k of them, from k DISTINCT | |
| rounds. 'Poor answering without better suggestions for questions is poor | |
| answering.' | |
| It cannot reuse the router's shortlist: that ranks by a knn vote over | |
| individual vectors, so a near-verbatim question fills all ten neighbours | |
| with ONE round's own variants. Excluding the answered round then left | |
| nothing to suggest -- measured, seven of forty perfect vetted answers | |
| arrived with no follow-up questions at all, the best matches being the | |
| emptiest. Suggestions therefore rank whole rounds by their best view, | |
| which always yields distinct alternatives.""" | |
| import numpy as np | |
| try: | |
| owners, isvar, arr = _round_embs() | |
| q = np.asarray(embed(question)[0], dtype="float32") | |
| q /= (np.linalg.norm(q) + 1e-9) | |
| sims = arr @ q | |
| best = {} | |
| for n, s in zip(owners, sims): | |
| n = int(n) | |
| if s > best.get(n, -1.0): | |
| best[n] = float(s) | |
| except Exception: | |
| return [] | |
| byn = {e["n"]: e for e in _kb()} | |
| out = [] | |
| for n, _ in sorted(best.items(), key=lambda x: -x[1]): | |
| if n == exclude or n not in byn: | |
| continue | |
| out.append((n, byn[n]["question"].strip('"'))) | |
| if len(out) >= k: | |
| break | |
| return out | |
| def route(question, threshold=0.60, cos_gate=0.60, k=8, direct_floor=0.70): | |
| """(round_number|None, probability, timings). None = fall through to reason(). | |
| v6 -- two successive questions, each GATED where its signal lives: | |
| recall asks "what is this ABOUT" (embeddings -- lexical overlap capped it | |
| at 3/20 and causal-LM pooling was recall-blind); the judge asks "which one | |
| EXACTLY" among the top-k candidates' own text. A NONE choice no longer | |
| sits in the judge's softmax: as candidates grow plausible the mass SPLITS | |
| among them and NONE wins by default (measured -- the refined suffering | |
| question lost to NONE at p=0.43 with round 2 sitting at recall rank 1). | |
| Out-of-scope is the RECALL's signal: an off-topic question embeds far from | |
| every round, so the cosine gate rejects it before any judging. The judge's | |
| threshold is a margin over uniform (1/k), not a majority bar.""" | |
| try: | |
| scored = _semantic_shortlist(question, k) | |
| except Exception as ex: | |
| # fall back, but NEVER silently -- a masked recall failure cost two | |
| # proof runs that were secretly re-measuring the lexical net. | |
| import sys | |
| print(f"[taviel_decide] semantic recall failed ({ex!r}); lexical fallback", | |
| file=sys.stderr) | |
| scored = [(1.0, e) for e in _shortlist(question, k)] | |
| # the GATE is the base-view cosine (the only separating signal); the | |
| # ORDER is the knn vote. Two jobs, two signals. | |
| scope = _scope_of() | |
| if not scored or scope < cos_gate: | |
| return None, scope, {} | |
| bynq = {e["question"].strip('"'): e["n"] for _, e in scored} | |
| schema = {"same": {"type": "enum", "choices": list(bynq), | |
| "description": "Which recorded objection is asking the " | |
| "same thing as the user, in other words?"}} | |
| r = decide(schema, "User's question: " + question, | |
| "The user's question is a REWORDING of one recorded objection. " | |
| "Pick the objection that means the same thing even when the " | |
| "words differ.") | |
| f = fields_of(r)["same"] | |
| n = bynq.get(f["value"]) | |
| p = f["probability"] | |
| # TWO WITNESSES. To commit is to hand the inquirer a RECORDED answer -- | |
| # and if it is the wrong one, an answer to a question they never asked. | |
| # So retrieval and the judge must INDEPENDENTLY name the same round, and | |
| # the judge must be sure. Measured on 225 held-out askings | |
| # (sweep_thresholds.py): the old permissive rule (p>=0.30, pick anywhere | |
| # in the top 8, with a dominance override) answered 70.2% correctly but | |
| # was OFF TOPIC 17.8% of the time -- one asking in six got another | |
| # question's answer. Requiring agreement at p>=0.60 gives 54.2% correct | |
| # and 1.8% wrong: a tenfold cut in the only truly bad outcome, at 96.8% | |
| # precision. Deferral is not silence -- the front door reasons from | |
| # scripture grounding and offers better questions, which under his law | |
| # is a good answer of a different kind. Bounding WRONG is the objective; | |
| # maximising CORRECT is not. | |
| if n is None or n != scored[0][1]["n"]: | |
| return None, p, r.get("timings", {}) | |
| if p < threshold: | |
| return None, p, r.get("timings", {}) | |
| # A THIRD WITNESS THAT NEVER SAW THE SHORTLIST. | |
| # | |
| # The two-witness rule assumed retrieval and the judge were independent. | |
| # They are not: retrieval hands the judge its candidate list. Measured on | |
| # 600 held-out askings, 10 of the 14 worst wrong commits had the true | |
| # round ABSENT from the top-8 entirely -- so the judge chose confidently, | |
| # several at p=1.00, among candidates that could not contain the right | |
| # answer, and both witnesses agreed on the same wrong page. No threshold | |
| # on the judge can reach that failure. | |
| # | |
| # So the last check compares the asking DIRECTLY against the chosen | |
| # round's own text, which depends on nothing the shortlist did. Measured | |
| # separation: correct commits median 0.761, wrong 0.663. A floor of 0.70 | |
| # keeps 77% of correct commits and blocks 73% of wrong ones, taking the | |
| # off-topic rate from 3.5% to about 1%. The commits it blocks are not | |
| # lost -- they become deferrals, which answer by reasoning with better | |
| # questions offered, and under his law that is a good answer of a | |
| # different kind. | |
| if not _resembles(question, scored[0][1], floor=direct_floor): | |
| return None, p, {**(r.get("timings") or {}), "blocked": "direct_floor"} | |
| return n, p, r.get("timings", {}) | |
| def _resembles(question, entry, floor=0.70): | |
| """Does the CHOSEN round actually look like what was asked?""" | |
| try: | |
| qv, cv = embed([question, entry["question"].strip('"')]) | |
| except Exception: | |
| return True # no embedder: do not block on a check we cannot run | |
| num = sum(x * y for x, y in zip(qv, cv)) | |
| na = sum(x * x for x in qv) ** .5 | |
| nb = sum(x * x for x in cv) ** .5 | |
| return (num / (na * nb + 1e-9)) >= floor | |
| def literalize(question, timeout=120): | |
| """Say plainly what is being asked underneath a figurative asking.""" | |
| prompt = ("Someone asked this in a figurative or roundabout way:\n" | |
| f"\"{question}\"\n\n" | |
| "Restate ONLY the plain question underneath it, in one short " | |
| "line, using ordinary words about God, the Bible, or faith. " | |
| "No metaphors, no preamble, no quotation marks.") | |
| body = json.dumps({ | |
| "messages": [{"role": "system", | |
| "content": "You restate questions plainly. One line."}, | |
| {"role": "user", "content": prompt}], | |
| "max_tokens": 48, "temperature": 0.1}).encode() | |
| req = urllib.request.Request( | |
| f"http://127.0.0.1:{PORT}/v1/chat/completions", data=body, | |
| headers={"Content-Type": "application/json"}) | |
| with urllib.request.urlopen(req, timeout=timeout) as r: | |
| t = json.loads(r.read())["choices"][0]["message"]["content"] | |
| return t.strip().strip('"').split("\n")[0][:200] | |
| def route_best(question, k=8, anchor=3, lit_threshold=0.70): | |
| """route(), and when it defers, ASK A BETTER QUESTION OF THE QUESTION. | |
| His principle turned inward: "asking better questions, or questions that | |
| build on questions, is what leads to the best answer." Metaphorical | |
| askings fail retrieval because their words sit far from every recorded | |
| question -- musical 44%, spatial 58% recall@1 -- so the system restates | |
| the asking plainly and routes on the restatement, the way a librarian | |
| says "so what you're really asking is whether God is just." | |
| Three attempts to close this gap by moving the KEYS toward the question | |
| all failed (intelligence-field variants, the KB's own prose as views, a | |
| bigger embedder). Moving the QUESTION toward the keys works. | |
| THE THIRD WITNESS: a restatement may only land on a round the ORIGINAL | |
| asking already had in view. Restating is interpretation, and | |
| interpretation can invent -- unanchored, it turned 7 deferrals into | |
| confident wrong answers. Anchored, it keeps 22 of 24 rescues and breaks | |
| 2. Measured on 160 held-out askings from the four weakest fields: | |
| correct 34.4% -> 48.1%, WRONG 2.5% -> 3.8%, with spatial 22% -> 40% at | |
| zero wrong. | |
| Returns (round|None, p, {source}). Fires only on deferral, so a question | |
| both witnesses already agreed on is never second-guessed. | |
| """ | |
| n, p, t = route(question, k=k) | |
| if n is not None: | |
| return n, p, {"via": "direct", **(t or {})} | |
| try: | |
| # ANCHOR DEPTH 3, not 8. Swept offline over 300 held-out askings | |
| # (collect_literalize + sweep_literalize): a top-8 anchor at p>=0.60 | |
| # scored correct 58.0% / WRONG 2.7%, while top-3 at p>=0.70 scores | |
| # 57.7% / 2.3% -- and 2.3% is EXACTLY the baseline wrongness without | |
| # literalization at all. So restating buys +7 points of correct | |
| # answers at ZERO added off-topic risk, which is the only version of | |
| # this feature his standard permits. Giving up 0.3 points of correct | |
| # to add no risk is the right trade when the rule is "never off | |
| # topic"; the 3% bar also sits near the sampling noise floor, so the | |
| # rule that ties baseline is the robust one, not the nominal best. | |
| near = [e["n"] for _, e in _semantic_shortlist(question, k)][:anchor] | |
| plain = literalize(question) | |
| n2, p2, t2 = route(plain, k=k) | |
| if n2 is not None and n2 in near and p2 >= lit_threshold: | |
| return n2, p2, {"via": "literalized", "plain": plain, **(t2 or {})} | |
| except Exception: | |
| pass | |
| return None, p, {"via": "deferred"} | |
| # 3) VERSE VERIFICATION -- every candidate ref of a draft, one batched round. | |
| # Replaces the second 360-token "refine" generation on drafts whose only sin | |
| # is a doubtful citation. | |
| VERIFY_SCHEMA = { | |
| "supports": {"type": "boolean", | |
| "description": "Does the verse, as quoted, actually say what the " | |
| "claim uses it to say?"}, | |
| "quoted_exactly": {"type": "boolean", | |
| "description": "Is the quotation word-for-word from the " | |
| "verse text given?"}, | |
| } | |
| def verify_refs(claims_and_verses): | |
| """claims_and_verses: [(claim, ref, verse_text)] -> [{ref, supports, exact, p}]. | |
| All pairs are scored in ONE decision round (shared schema + cached prefix). | |
| NOT SHIPPED as the refine replacement: prove_taviel_decide2 measured 3/6 | |
| discrimination at the 1.5B tier (embedding cosine also 3/6) -- the refine | |
| generation stays until a bigger tier or a true NLI cross-encoder retries.""" | |
| contexts = [f"Claim: {c}\nVerse ({ref}): {v}" for c, ref, v in claims_and_verses] | |
| r = decide(VERIFY_SCHEMA, contexts, | |
| "Judge each claim against the verse quoted with it.") | |
| out = [] | |
| for (c, ref, v), res in zip(claims_and_verses, r["results"]): | |
| f = res["fields"] | |
| out.append({"ref": ref, | |
| "supports": f["supports"]["value"] in (True, "true"), | |
| "exact": f["quoted_exactly"]["value"] in (True, "true"), | |
| "p": min(f["supports"]["probability"], | |
| f["quoted_exactly"]["probability"])}) | |
| return out | |