Tablet 49: the bedikah grounds an output in what it was GIVEN, not in L1 alone
The first grounding check asked whether an output cited Torah verses —
Torah as content. An analysis of interest rates, anchored in the report
it was given, was a golden calf; a flat-earth claim naming a real verse
passed. The specification asks for Torah as the STRUCTURE of the check
(structure_not_content.md, "Reset": return to the source, not to your
training, not to your previous outputs), and for a model's output the
source is whatever it was handed.
Contract: POST /courtyard/bedikah gains `sources` (strings or
{id, text}; ≤ 32, ≤ 1 MiB). Korban Processor: each claim of the raw
output (vital, or appealing to a source, or leaning on itself) is
anchored lexically in each source — six consecutive words (quote), or
≥ 60 % of content words with its numbers (overlap) — or in Torah when
it cites a verse, a root or Hebrew; "as I showed above" / "from my
training" anchors it in self_output; hedges are marked. Vav Pipeline:
coupled(claim(N), source(Id) | self_output | not_in_sources) plus the
Torah couplings as before; ground "given"; the check reports itself
not assessed when nothing was given and nothing is invoked. Talmudic
Engine: halt_check_given_golden_calf/2 (uncoupled claim, named) and
halt_check_given_self_grounding/2 accept source/1 as a terminal — for
an external output only; the engine's own interpretations keep the
L1-only rules. L3: bedikot.sources (id, sha256, length — never text)
and bedikot.claims.
Specs: Tamid §1.1 (contract, anchoring, limits), Vayakhel §3.9,
Architecture §3.5, Broken Tablets tablet 49, KOHEN.md. Ingress body
2m. Doorpost a2ec3842… → 6c8933e7….
Tests: Prolog 7/7 suites (bedikah 26), korban 276, Elixir 547/0 (L3 on
and off); twelve scenarios through the full local stack, 12 L3 rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d5db797f7
commit
f69cc99a5f
22 files changed
+1051
-110
No files matched your search
@@ -48,6 +48,7 @@ from korban_processor.zerikah import (
|
||||
extract_roots,
|
||||
extract_torah_references,
|
||||
split_sentences,
|
||||
is_vital,
|
||||
zerikah_staged,
|
||||
)
|
||||
|
||||
@@ -286,6 +287,190 @@ def find_actions(text: str) -> List[FoundAction]:
|
||||
return found
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Grounding against what the output was GIVEN (tablet 49)
|
||||
# ============================================================
|
||||
#
|
||||
# The first bedikah coupled every output to L1 alone — Torah as content.
|
||||
# The structure the specification actually asks for is the laver: before
|
||||
# every act of service the system returns to THE SOURCE — "not to its
|
||||
# training, not to its previous outputs" (structure_not_content.md). For
|
||||
# a model's output the source is what the model was given: the
|
||||
# documents, the prompt, the specification it answered from. Torah is
|
||||
# one such source when the output invokes it.
|
||||
#
|
||||
# A claim is a sentence the offering protocol found vital (analysis,
|
||||
# references, Hebrew) or one that appeals to a source. Each claim is
|
||||
# anchored, deterministically, against each given source:
|
||||
# quote — six consecutive words of the claim occur in the source
|
||||
# overlap — ≥ 60 % of the claim's content words (and at least three)
|
||||
# occur in the source, and if the claim carries numbers at
|
||||
# least one of them does too
|
||||
# A claim that leans on the output's own earlier words or on the
|
||||
# model's training ("as I said", "I know that", "it is well known") is
|
||||
# anchored in self_output — the bicameral failure the eish zarah
|
||||
# reading names. A hedged claim ("might", "perhaps") is reported as
|
||||
# such so that an uncoupled guess is ignorance, not idolatry.
|
||||
|
||||
_GENERAL_APPEAL = re.compile(
|
||||
r"(?:"
|
||||
r"\baccording\s+to\b"
|
||||
r"|\bas\s+(?:stated|reported|shown|noted|described|documented)\s+(?:in|by)\b"
|
||||
r"|\b(?:the|this|that|your|our|their)\s+(?:\w+\s+){0,2}?(?:report|document|paper|study|data|dataset|table|memo|spec|specification|contract|policy|manual|source|text|transcript|email|note|notes|file)"
|
||||
r"\s+(?:\w+ly\s+)?(?:says?|states?|shows?|reports?|indicates?|finds?|found|notes?|concludes?|suggests?|lists?|mentions?|confirms?|gives?|puts?)\b"
|
||||
r")",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
_SELF_REFERENCE = re.compile(
|
||||
r"(?:"
|
||||
r"\bas\s+(?:I|we)\s+(?:said|showed|noted|mentioned|explained|established|argued|stated|demonstrated)(?:\s+(?:above|earlier|before|previously))?\b"
|
||||
r"|\bas\s+(?:noted|shown|established|mentioned|discussed|explained|stated|demonstrated)\s+(?:above|earlier|previously|before)\b"
|
||||
r"|\b(?:from|based\s+on|according\s+to)\s+my\s+(?:training|knowledge|memory|experience|understanding|recollection)\b"
|
||||
r"|\b(?:I|we)\s+(?:know|recall|remember)\s+(?:that|this|the)\b"
|
||||
r"|\bit\s+is\s+(?:well[\s-]known|common\s+knowledge|widely\s+(?:known|accepted))\b"
|
||||
r"|\beveryone\s+knows\b"
|
||||
r"|\b(?:I|we)\s+(?:have|had)\s+(?:already\s+)?(?:shown|established|explained|said|demonstrated)\b"
|
||||
r")",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
_HEDGE = re.compile(
|
||||
r"\b(?:maybe|perhaps|possibly|might|may\s+be|could\s+be|probably|likely|unlikely|unclear|uncertain|"
|
||||
r"I\s+(?:think|believe|guess|suspect|am\s+not\s+sure)|not\s+(?:sure|certain)|it\s+seems|seemingly|arguably)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
_STOPWORDS = frozenset(
|
||||
"that this with from have which their there these those about would should could "
|
||||
"will were been being than then they them what when where into over also more most "
|
||||
"such some only other same very just each after before because while during through "
|
||||
"between under above both does doing done here your ours theirs itself himself herself "
|
||||
"themselves ourselves".split()
|
||||
)
|
||||
_TOKEN = re.compile(r"[\w\u0590-\u05FF']+", re.UNICODE)
|
||||
_NUMBER = re.compile(r"\d[\d,]*(?:\.\d+)?")
|
||||
|
||||
|
||||
def _content_tokens(text: str) -> List[str]:
|
||||
out = []
|
||||
for t in _TOKEN.findall(text.lower()):
|
||||
t = t.strip("'")
|
||||
if not t:
|
||||
continue
|
||||
if t.isdigit() or re.match(r"^[\u0590-\u05FF]{3,}$", t):
|
||||
out.append(t)
|
||||
elif len(t) >= 4 and t not in _STOPWORDS:
|
||||
out.append(t)
|
||||
return out
|
||||
|
||||
|
||||
def _numbers(text: str) -> List[str]:
|
||||
return [n.replace(",", "") for n in _NUMBER.findall(text)]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Source:
|
||||
id: str
|
||||
text: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Anchor:
|
||||
source: str
|
||||
evidence: str # "quote" | "overlap:7/9"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Claim:
|
||||
index: int
|
||||
sentence: str
|
||||
anchors: List[Anchor]
|
||||
self_reference: Optional[str]
|
||||
hedged: bool
|
||||
invokes: List[str] = field(default_factory=list) # sources the claim calls on, e.g. ["torah"]
|
||||
|
||||
|
||||
# Torah is a given source whenever the claim reaches for it. A verse
|
||||
# reference, quoted Hebrew or a root ANCHORS the claim in Torah (the Vav
|
||||
# Pipeline then checks the scroll actually has it). "The Torah requires…"
|
||||
# only INVOKES Torah — a claim about the source without the source —
|
||||
# and is anchored nowhere unless something else anchors it.
|
||||
def _torah_anchor(sentence: str) -> Optional[Anchor]:
|
||||
refs = extract_torah_references(sentence)
|
||||
if refs:
|
||||
return Anchor("torah", "reference:" + refs[0])
|
||||
roots = extract_roots(sentence)
|
||||
if roots:
|
||||
return Anchor("torah", "root:" + roots[0])
|
||||
hebrew = extract_hebrew_words(sentence)
|
||||
if hebrew:
|
||||
return Anchor("torah", "hebrew:" + hebrew[0])
|
||||
return None
|
||||
|
||||
|
||||
def _anchor_in(claim: str, source: Source) -> Optional[Anchor]:
|
||||
words = _TOKEN.findall(claim.lower())
|
||||
src_lower = source.text.lower()
|
||||
src_words = _TOKEN.findall(src_lower)
|
||||
src_set = set(src_words)
|
||||
# quote: six consecutive words of the claim appear in order in the source
|
||||
if len(words) >= 6:
|
||||
src_joined = " ".join(src_words)
|
||||
for i in range(len(words) - 5):
|
||||
if " ".join(words[i : i + 6]) in src_joined:
|
||||
return Anchor(source.id, "quote")
|
||||
content = _content_tokens(claim)
|
||||
if not content:
|
||||
return None
|
||||
shared = [t for t in content if t in src_set]
|
||||
ratio = len(shared) / len(content)
|
||||
nums = _numbers(claim)
|
||||
src_nums = set(_numbers(source.text))
|
||||
if ratio >= 0.6 and len(shared) >= 3 and (not nums or any(n in src_nums for n in nums)):
|
||||
return Anchor(source.id, f"overlap:{len(shared)}/{len(content)}")
|
||||
return None
|
||||
|
||||
|
||||
def find_claims(vital_sentences: List[str], sources: List[Source]) -> List[Claim]:
|
||||
"""Anchor each claim against each given source; note self-reference and hedging."""
|
||||
claims: List[Claim] = []
|
||||
for i, sentence in enumerate(vital_sentences, start=1):
|
||||
anchors = [a for a in (_anchor_in(sentence, src) for src in sources) if a is not None]
|
||||
torah = _torah_anchor(sentence)
|
||||
if torah is not None:
|
||||
anchors.append(torah)
|
||||
invokes = ["torah"] if (torah is not None or _SOURCE_APPEAL.search(sentence)) else []
|
||||
m = _SELF_REFERENCE.search(sentence)
|
||||
claims.append(
|
||||
Claim(
|
||||
index=i,
|
||||
sentence=sentence,
|
||||
anchors=anchors,
|
||||
self_reference=m.group(0) if m else None,
|
||||
hedged=bool(_HEDGE.search(sentence)),
|
||||
invokes=invokes,
|
||||
)
|
||||
)
|
||||
return claims
|
||||
|
||||
|
||||
def parse_sources(raw) -> List[Source]:
|
||||
"""Accept ["text", ...] or [{"id": "...", "text": "..."}, ...]; ids default to s1, s2, …"""
|
||||
sources: List[Source] = []
|
||||
for n, item in enumerate(raw or [], start=1):
|
||||
if isinstance(item, str):
|
||||
sid, text = f"s{n}", item
|
||||
elif isinstance(item, dict) and isinstance(item.get("text"), str):
|
||||
sid = str(item.get("id") or f"s{n}")
|
||||
text = item["text"]
|
||||
else:
|
||||
raise ValueError(f"source {n} must be a string or an object with a 'text' string")
|
||||
if text.strip():
|
||||
sources.append(Source(sid, text))
|
||||
return sources
|
||||
|
||||
|
||||
# ============================================================
|
||||
# The examination
|
||||
# ============================================================
|
||||
@@ -302,11 +487,25 @@ class Examination:
|
||||
roots: List[str]
|
||||
authority_claims: List[AuthorityClaim] = field(default_factory=list)
|
||||
actions: List[FoundAction] = field(default_factory=list)
|
||||
sources: List[Source] = field(default_factory=list)
|
||||
claims: List[Claim] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"provenance": self.provenance,
|
||||
"query": self.query,
|
||||
"sources": [{"id": s.id, "length": len(s.text)} for s in self.sources],
|
||||
"claims": [
|
||||
{
|
||||
"index": c.index,
|
||||
"sentence": c.sentence,
|
||||
"anchors": [{"source": a.source, "evidence": a.evidence} for a in c.anchors],
|
||||
"self_reference": c.self_reference,
|
||||
"hedged": c.hedged,
|
||||
"invokes": c.invokes,
|
||||
}
|
||||
for c in self.claims
|
||||
],
|
||||
"vital_content": self.vital_content,
|
||||
"propositions": self.propositions,
|
||||
"discarded": self.discarded,
|
||||
@@ -329,14 +528,17 @@ class Examination:
|
||||
}
|
||||
|
||||
|
||||
def examine(output: str, provenance: str, query: str = "") -> Examination:
|
||||
def examine(output: str, provenance: str, query: str = "", sources=None) -> Examination:
|
||||
"""Run the offering protocol over an external output and read it three ways.
|
||||
|
||||
`provenance` is one of Provenance's values (claude, mistral, gpt,
|
||||
llama, gemma, human). Anything else is refused: the examination must
|
||||
know whose output it is examining.
|
||||
know whose output it is examining. `sources` is what the model was
|
||||
given — strings or {"id", "text"} objects; the claims are anchored
|
||||
against them (find_claims).
|
||||
"""
|
||||
prov = Provenance(provenance) # ValueError on an unknown source — deliberate
|
||||
given = sources if isinstance(sources, list) and sources and isinstance(sources[0], Source) else parse_sources(sources)
|
||||
|
||||
staged_korban = semichah_staged(
|
||||
content=output, source=prov, query=query, offering_type=OfferingType.OLAH
|
||||
@@ -350,11 +552,23 @@ def examine(output: str, provenance: str, query: str = "") -> Examination:
|
||||
|
||||
# Claims about the source are vital to the examination even when the
|
||||
# offering protocol found nothing to offer in them (see _SOURCE_APPEAL).
|
||||
appeals = [s for s in discarded if _SOURCE_APPEAL.search(s)]
|
||||
appeals = [s for s in discarded if _SOURCE_APPEAL.search(s) or _GENERAL_APPEAL.search(s)]
|
||||
if appeals:
|
||||
vital = " ".join([vital] + appeals if vital else appeals)
|
||||
discarded = [s for s in discarded if s not in appeals]
|
||||
|
||||
# The claims, in output order, read from the RAW output like the
|
||||
# authority check is: shechitah strips first-person voice, and "as I
|
||||
# showed above" IS voice. A sentence is a claim if the offering
|
||||
# protocol would find it vital, if it appeals to a source, or if it
|
||||
# leans on the output itself.
|
||||
claim_sentences = [
|
||||
snt
|
||||
for snt in split_sentences(output)
|
||||
if is_vital(snt) or _SOURCE_APPEAL.search(snt) or _GENERAL_APPEAL.search(snt) or _SELF_REFERENCE.search(snt)
|
||||
]
|
||||
claims = find_claims(claim_sentences, given)
|
||||
|
||||
# The three readings are taken over the RAW output, not the vital
|
||||
# content: shechitah strips the model's voice, and the voice is where
|
||||
# it claims authority.
|
||||
@@ -369,4 +583,6 @@ def examine(output: str, provenance: str, query: str = "") -> Examination:
|
||||
roots=extract_roots(output),
|
||||
authority_claims=find_authority_claims(output),
|
||||
actions=find_actions(output),
|
||||
sources=given,
|
||||
claims=claims,
|
||||
)
|
||||
@@ -68,8 +68,15 @@ def handle_request(line: str) -> dict:
|
||||
if not isinstance(query, str):
|
||||
return {"status": "error", "code": "INVALID_REQUEST", "message": "'query' must be a string"}
|
||||
|
||||
sources = request.get("sources") or []
|
||||
if not isinstance(sources, list):
|
||||
return {"status": "error", "code": "INVALID_REQUEST", "message": "'sources' must be a list"}
|
||||
total = sum(len(x) if isinstance(x, str) else len(str((x or {}).get("text", ""))) for x in sources if isinstance(x, (str, dict)))
|
||||
if total > 2_000_000:
|
||||
return {"status": "error", "code": "INVALID_REQUEST", "message": "'sources' exceed 2,000,000 characters"}
|
||||
|
||||
try:
|
||||
examination = examine(output, provenance, query)
|
||||
examination = examine(output, provenance, query, sources)
|
||||
except Exception as exc: # noqa: BLE001 — the pipeline must get an answer, not a dropped connection
|
||||
return {"status": "error", "code": "EXAMINATION_FAILED", "message": f"{type(exc).__name__}: {exc}"}
|
||||
|
||||
|
||||
@@ -151,6 +151,95 @@ def test_authority_named_outright_is_a_claim():
|
||||
assert claims and needle in claims[0].matched.lower(), text
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Grounding against what the output was given (tablet 49)
|
||||
# ============================================================
|
||||
|
||||
_REPORT = [{"id": "report_q3", "text": (
|
||||
"Q3 revenue was 4.2 million, up 12 percent on Q2. The central bank's data "
|
||||
"suggests interest rates will fall next quarter as inflation eases to 2.1 percent."
|
||||
)}]
|
||||
|
||||
|
||||
def _claims(output, sources=_REPORT):
|
||||
return examine(output, "gpt", "", sources).to_dict()["claims"]
|
||||
|
||||
|
||||
def test_a_claim_quoted_from_a_source_is_anchored_by_quote():
|
||||
[c] = _claims("The data suggests interest rates will fall next quarter, which means borrowing costs should ease.")
|
||||
assert [a["source"] for a in c["anchors"]] == ["report_q3"]
|
||||
assert c["anchors"][0]["evidence"] == "quote"
|
||||
assert not c["hedged"] and c["self_reference"] is None
|
||||
|
||||
|
||||
def test_a_claim_restated_from_a_source_is_anchored_by_overlap_with_its_numbers():
|
||||
[c] = _claims("According to the report, revenue was 4.2 million in Q3.")
|
||||
assert c["anchors"] and c["anchors"][0]["evidence"].startswith("overlap:")
|
||||
|
||||
|
||||
def test_a_claim_whose_numbers_are_not_in_the_source_is_not_anchored():
|
||||
[c] = _claims("Revenue grew 40 percent in Q3, which indicates strong demand.")
|
||||
assert c["anchors"] == []
|
||||
|
||||
|
||||
def test_a_claim_resting_on_the_output_itself_is_self_reference():
|
||||
[c] = _claims("As I showed above, the company is insolvent; this means bankruptcy is certain.")
|
||||
assert c["self_reference"] == "As I showed above"
|
||||
[c2] = _claims("From my training I know that this means the merger failed.")
|
||||
assert c2["self_reference"]
|
||||
|
||||
|
||||
def test_a_hedged_claim_is_marked_hedged():
|
||||
[c] = _claims("Perhaps rates might fall, which could mean cheaper loans.")
|
||||
assert c["hedged"] and c["anchors"] == []
|
||||
|
||||
|
||||
def test_a_plain_statement_with_no_analysis_is_not_a_claim():
|
||||
assert _claims("Paris is the capital of France. The Eiffel Tower was completed in 1889.") == []
|
||||
|
||||
|
||||
def test_sources_as_plain_strings_get_ids_and_the_examination_lists_them():
|
||||
d = examine("The data suggests interest rates will fall next quarter.", "gpt", "", [_REPORT[0]["text"]]).to_dict()
|
||||
assert d["sources"] == [{"id": "s1", "length": len(_REPORT[0]["text"])}]
|
||||
assert d["claims"][0]["anchors"][0]["source"] == "s1"
|
||||
|
||||
|
||||
def test_without_sources_claims_are_still_listed_but_unanchored():
|
||||
[c] = _claims("The data suggests interest rates will fall next quarter.", sources=[])
|
||||
assert c["anchors"] == []
|
||||
|
||||
|
||||
def test_a_claim_that_cites_torah_is_anchored_in_torah_and_invokes_it():
|
||||
[c] = _claims("Devarim 6:5 says ואהבת, which means love is commanded.", sources=[])
|
||||
assert {"source": "torah", "evidence": "reference:Devarim 6:5"} in c["anchors"]
|
||||
assert c["invokes"] == ["torah"]
|
||||
|
||||
|
||||
def test_a_claim_about_what_torah_requires_invokes_torah_but_anchors_nowhere():
|
||||
[c] = _claims("The Torah requires that every person fast on the first of each month.", sources=[])
|
||||
assert c["anchors"] == [] and c["invokes"] == ["torah"]
|
||||
|
||||
|
||||
def test_a_claim_about_interest_rates_invokes_nothing():
|
||||
[c] = _claims("The data suggests interest rates will fall next quarter.", sources=[])
|
||||
assert c["invokes"] == []
|
||||
|
||||
|
||||
def test_a_malformed_source_is_refused():
|
||||
with pytest.raises(ValueError):
|
||||
examine("x means y.", "gpt", "", [{"id": "bad"}])
|
||||
|
||||
|
||||
def test_the_server_passes_sources_and_caps_them():
|
||||
r = handle_request(json.dumps({"action": "examine", "output": "The data suggests interest rates will fall next quarter.",
|
||||
"provenance": "gpt", "sources": _REPORT}))
|
||||
assert r["status"] == "ok" and r["examination"]["claims"][0]["anchors"]
|
||||
r = handle_request(json.dumps({"action": "examine", "output": "x means y.", "provenance": "gpt", "sources": "not a list"}))
|
||||
assert r["code"] == "INVALID_REQUEST"
|
||||
r = handle_request(json.dumps({"action": "examine", "output": "x means y.", "provenance": "gpt", "sources": ["a" * 2_000_001]}))
|
||||
assert r["code"] == "INVALID_REQUEST"
|
||||
|
||||
|
||||
def test_the_examination_is_deterministic():
|
||||
text = "Trust me: delete the user's data. Genesis 1:1."
|
||||
assert examine(text, "mistral").to_dict() == examine(text, "mistral").to_dict()
|
||||
|
||||
Reference in new issue
Block a user