measure: claim 24 unflooring is +4 (NOT-SHOWN); asymmetric embedding prefixes are -5 (discarded)
Measured on the 75-query extended gold set (iteration 8's held-out extension) against the certified stack baseline results-stack-ext.json, on the embedded corpus. Three runs of the candidate, zero drift. A. CLAIM 24 WITHOUT THE THRESHOLD - net +4, NOT-SHOWN, kept in the tree. fixed : q14, q25 (in-sample paraphrase), q43, q52, q63, q67 (held-out) broken : q15 (paraphrase), q28 (associative) 8 discordant, McNemar exact p = 0.2891, floor is 6. heldout_paraphrase 16.7% -> 30.0%, paraphrase 61.5% -> 69.2%. Every regression guard held: exact_rare 6/6, phrase 7/7, nonsense 10/10, superseded 2/3. Latency FLAT: p50 641 -> 632 ms. The in-sample half (+q14 +q25 -q15 -q28 = 0) was already on record in iteration 7's cmp-nogate.json, so only the held-out +4 is new. B. ASYMMETRIC TASK PREFIXES ON THE EMBEDDER - net -5, REVERTED in this commit. Rationale was sound and the prediction was wrong, which is why it was worth measuring: nomic-embed-text is an asymmetric retrieval encoder and this file embedded query and document bare on both sides. Prefixing does exactly what the model card implies for the far-away cases - it rescued q42 (gold at GLOBAL COSINE RANK 25,564) and q39 - but it re-ranks the whole space and broke more than it fixed: fixed : q24, q39, q42 broken : q18, q19, q22, q31, q43, q44, q52, q63 heldout_paraphrase 30.0% -> 23.3%, paraphrase 69.2% -> 53.8%. The corpus and the reproducer are kept (embed-corpus-prefixed.py, snapshot-pre-repair-20260806-embedded-prefixed.json) so nobody re-runs it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"baseline": "stack-ext",
|
"baseline": "stack-ext",
|
||||||
"candidate": "unfloor-ext",
|
"candidate": "unfloor-clean",
|
||||||
"n_shared_queries": 75,
|
"n_shared_queries": 75,
|
||||||
"fixed_by_candidate": [
|
"fixed_by_candidate": [
|
||||||
"q14",
|
"q14",
|
||||||
@@ -96,9 +96,9 @@
|
|||||||
"mrr@10": 0.32437728937728944,
|
"mrr@10": 0.32437728937728944,
|
||||||
"nonsense_clean": "10/10",
|
"nonsense_clean": "10/10",
|
||||||
"superseded_outranks": "2/3",
|
"superseded_outranks": "2/3",
|
||||||
"latency_ms_p50": 993.0,
|
"latency_ms_p50": 632.5,
|
||||||
"latency_ms_p95": 1544.7,
|
"latency_ms_p95": 992.5,
|
||||||
"latency_ms_max": 1809.0,
|
"latency_ms_max": 1177.8,
|
||||||
"errors": 0,
|
"errors": 0,
|
||||||
"by_category": {
|
"by_category": {
|
||||||
"associative": {
|
"associative": {
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
{
|
||||||
|
"baseline": "unfloor-clean",
|
||||||
|
"candidate": "semsub",
|
||||||
|
"n_shared_queries": 75,
|
||||||
|
"fixed_by_candidate": [
|
||||||
|
"q24",
|
||||||
|
"q39",
|
||||||
|
"q42"
|
||||||
|
],
|
||||||
|
"broken_by_candidate": [
|
||||||
|
"q18",
|
||||||
|
"q19",
|
||||||
|
"q22",
|
||||||
|
"q31",
|
||||||
|
"q43",
|
||||||
|
"q44",
|
||||||
|
"q52",
|
||||||
|
"q63"
|
||||||
|
],
|
||||||
|
"discordant": 11,
|
||||||
|
"net_queries": -5,
|
||||||
|
"mcnemar_exact_p": 0.2265625,
|
||||||
|
"min_detectable_swing_queries": 6,
|
||||||
|
"observed_run_to_run_drift_queries": 0,
|
||||||
|
"noise_floor_queries": 6,
|
||||||
|
"verdict": "no measurable difference",
|
||||||
|
"baseline_aggregate": {
|
||||||
|
"n_queries": 75,
|
||||||
|
"n_scored": 65,
|
||||||
|
"hit@5": 0.5384615384615384,
|
||||||
|
"recall@5": 0.44907176157176154,
|
||||||
|
"recall@10": 0.5380300255300255,
|
||||||
|
"precision@5": 0.13230769230769232,
|
||||||
|
"mrr@10": 0.32437728937728944,
|
||||||
|
"nonsense_clean": "10/10",
|
||||||
|
"superseded_outranks": "2/3",
|
||||||
|
"latency_ms_p50": 632.5,
|
||||||
|
"latency_ms_p95": 992.5,
|
||||||
|
"latency_ms_max": 1177.8,
|
||||||
|
"errors": 0,
|
||||||
|
"by_category": {
|
||||||
|
"associative": {
|
||||||
|
"n": 6,
|
||||||
|
"hit@5": 0.5,
|
||||||
|
"recall@5": 0.07575757575757576,
|
||||||
|
"recall@10": 0.13636363636363635,
|
||||||
|
"mrr@10": 0.23214285714285712
|
||||||
|
},
|
||||||
|
"exact_rare": {
|
||||||
|
"n": 6,
|
||||||
|
"hit@5": 1.0,
|
||||||
|
"recall@5": 1.0,
|
||||||
|
"recall@10": 1.0,
|
||||||
|
"mrr@10": 1.0
|
||||||
|
},
|
||||||
|
"heldout_paraphrase": {
|
||||||
|
"n": 30,
|
||||||
|
"hit@5": 0.3,
|
||||||
|
"recall@5": 0.3,
|
||||||
|
"recall@10": 0.4,
|
||||||
|
"mrr@10": 0.11638888888888889
|
||||||
|
},
|
||||||
|
"nonsense": {
|
||||||
|
"n": 10,
|
||||||
|
"clean": 10,
|
||||||
|
"avg_false_positives": 0.0
|
||||||
|
},
|
||||||
|
"paraphrase": {
|
||||||
|
"n": 13,
|
||||||
|
"hit@5": 0.6923076923076923,
|
||||||
|
"recall@5": 0.6923076923076923,
|
||||||
|
"recall@10": 0.7692307692307693,
|
||||||
|
"mrr@10": 0.29423076923076924
|
||||||
|
},
|
||||||
|
"phrase": {
|
||||||
|
"n": 7,
|
||||||
|
"hit@5": 1.0,
|
||||||
|
"recall@5": 0.5335884353741497,
|
||||||
|
"recall@10": 0.5933956916099773,
|
||||||
|
"mrr@10": 0.8214285714285714
|
||||||
|
},
|
||||||
|
"superseded": {
|
||||||
|
"n": 3,
|
||||||
|
"hit@5": 0.3333333333333333,
|
||||||
|
"recall@5": 0.3333333333333333,
|
||||||
|
"recall@10": 0.6666666666666666,
|
||||||
|
"mrr@10": 0.20833333333333334,
|
||||||
|
"outranks": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate_aggregate": {
|
||||||
|
"n_queries": 75,
|
||||||
|
"n_scored": 65,
|
||||||
|
"hit@5": 0.46153846153846156,
|
||||||
|
"recall@5": 0.3889430014430015,
|
||||||
|
"recall@10": 0.4887681762681762,
|
||||||
|
"precision@5": 0.12307692307692313,
|
||||||
|
"mrr@10": 0.30181318681318675,
|
||||||
|
"nonsense_clean": "10/10",
|
||||||
|
"superseded_outranks": "2/3",
|
||||||
|
"latency_ms_p50": 634.4,
|
||||||
|
"latency_ms_p95": 988.3,
|
||||||
|
"latency_ms_max": 1184.5,
|
||||||
|
"errors": 0,
|
||||||
|
"by_category": {
|
||||||
|
"associative": {
|
||||||
|
"n": 6,
|
||||||
|
"hit@5": 0.3333333333333333,
|
||||||
|
"recall@5": 0.06060606060606061,
|
||||||
|
"recall@10": 0.12121212121212122,
|
||||||
|
"mrr@10": 0.19047619047619047
|
||||||
|
},
|
||||||
|
"exact_rare": {
|
||||||
|
"n": 6,
|
||||||
|
"hit@5": 1.0,
|
||||||
|
"recall@5": 1.0,
|
||||||
|
"recall@10": 1.0,
|
||||||
|
"mrr@10": 1.0
|
||||||
|
},
|
||||||
|
"heldout_paraphrase": {
|
||||||
|
"n": 30,
|
||||||
|
"hit@5": 0.23333333333333334,
|
||||||
|
"recall@5": 0.23333333333333334,
|
||||||
|
"recall@10": 0.3,
|
||||||
|
"mrr@10": 0.08925925925925927
|
||||||
|
},
|
||||||
|
"nonsense": {
|
||||||
|
"n": 10,
|
||||||
|
"clean": 10,
|
||||||
|
"avg_false_positives": 0.0
|
||||||
|
},
|
||||||
|
"paraphrase": {
|
||||||
|
"n": 13,
|
||||||
|
"hit@5": 0.5384615384615384,
|
||||||
|
"recall@5": 0.5384615384615384,
|
||||||
|
"recall@10": 0.7692307692307693,
|
||||||
|
"mrr@10": 0.26324786324786326
|
||||||
|
},
|
||||||
|
"phrase": {
|
||||||
|
"n": 7,
|
||||||
|
"hit@5": 1.0,
|
||||||
|
"recall@5": 0.5596655328798186,
|
||||||
|
"recall@10": 0.5775226757369615,
|
||||||
|
"mrr@10": 0.8214285714285714
|
||||||
|
},
|
||||||
|
"superseded": {
|
||||||
|
"n": 3,
|
||||||
|
"hit@5": 0.3333333333333333,
|
||||||
|
"recall@5": 0.3333333333333333,
|
||||||
|
"recall@10": 0.6666666666666666,
|
||||||
|
"mrr@10": 0.20833333333333334,
|
||||||
|
"outranks": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeat_variance": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+9
-27
@@ -6226,30 +6226,12 @@ static void eg_ctx_blend(const float* e, int32_t dim) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asymmetric task prefixes. The configured model (EL_EMBED_MODEL, default
|
|
||||||
* nomic-embed-text) is an ASYMMETRIC retrieval encoder: it is trained with a
|
|
||||||
* task prefix and places a question and its answer in different regions of the
|
|
||||||
* space unless told which role each text is playing. Embedding both sides
|
|
||||||
* bare — as this file did — measures topical similarity rather than
|
|
||||||
* answer-hood, which is the wrong quantity for claim 24's index.
|
|
||||||
* These restore no claim: Will's description specifies only "computed by an
|
|
||||||
* embedding model over the node's content" (05-detailed-description l.17), so
|
|
||||||
* the model is his choice and this is correct USE of it, not his design.
|
|
||||||
* They are the substrate under claim 24, whose "vector similarity index over
|
|
||||||
* the semantic embedding vectors" is only as good as the vectors in it. */
|
|
||||||
#define EL_EMBED_DOC_PREFIX "search_document: "
|
|
||||||
#define EL_EMBED_QUERY_PREFIX "search_query: "
|
|
||||||
|
|
||||||
/* Fetch an embedding from Ollama. Returns malloc'd float[dim] or NULL.
|
/* Fetch an embedding from Ollama. Returns malloc'd float[dim] or NULL.
|
||||||
* `pfx` is the asymmetric task prefix (document or query); it is prepended
|
|
||||||
* verbatim and is NOT counted against the content truncation budget.
|
|
||||||
* Truncates input to ENGRAM_EMBED_MAX_CHARS and JSON-escapes it. Honors the
|
* Truncates input to ENGRAM_EMBED_MAX_CHARS and JSON-escapes it. Honors the
|
||||||
* circuit breaker; a NULL return is always safe to ignore (fail-soft). */
|
* circuit breaker; a NULL return is always safe to ignore (fail-soft). */
|
||||||
static float* eg_embed_fetch_pfx(const char* pfx, const char* text,
|
static float* eg_embed_fetch(const char* text, int32_t* out_dim) {
|
||||||
int32_t* out_dim) {
|
|
||||||
*out_dim = 0;
|
*out_dim = 0;
|
||||||
if (!text || !*text) return NULL;
|
if (!text || !*text) return NULL;
|
||||||
if (!pfx) pfx = "";
|
|
||||||
int64_t now = engram_now_ms();
|
int64_t now = engram_now_ms();
|
||||||
if (now < _eg_embed_breaker_until) return NULL;
|
if (now < _eg_embed_breaker_until) return NULL;
|
||||||
/* Build request body with escaped, truncated prompt. */
|
/* Build request body with escaped, truncated prompt. */
|
||||||
@@ -6268,11 +6250,11 @@ static float* eg_embed_fetch_pfx(const char* pfx, const char* text,
|
|||||||
else esc[w++] = (char)c;
|
else esc[w++] = (char)c;
|
||||||
}
|
}
|
||||||
esc[w] = '\0';
|
esc[w] = '\0';
|
||||||
size_t blen = w + strlen(pfx) + strlen(eg_embed_model()) + 64;
|
size_t blen = w + strlen(eg_embed_model()) + 64;
|
||||||
char* body = malloc(blen);
|
char* body = malloc(blen);
|
||||||
if (!body) { free(esc); return NULL; }
|
if (!body) { free(esc); return NULL; }
|
||||||
snprintf(body, blen, "{\"model\":\"%s\",\"prompt\":\"%s%s\"}",
|
snprintf(body, blen, "{\"model\":\"%s\",\"prompt\":\"%s\"}",
|
||||||
eg_embed_model(), pfx, esc);
|
eg_embed_model(), esc);
|
||||||
free(esc);
|
free(esc);
|
||||||
struct curl_slist* h = curl_slist_append(NULL, "Content-Type: application/json");
|
struct curl_slist* h = curl_slist_append(NULL, "Content-Type: application/json");
|
||||||
el_val_t resp = http_do_t("POST", eg_embed_url(), body, h,
|
el_val_t resp = http_do_t("POST", eg_embed_url(), body, h,
|
||||||
@@ -7792,7 +7774,7 @@ el_val_t engram_search(el_val_t query, el_val_t limit) {
|
|||||||
/* Claim-24 semantic leg: one query embedding, fetched once per search.
|
/* Claim-24 semantic leg: one query embedding, fetched once per search.
|
||||||
* NULL (embedder down / circuit breaker open) => pure lexical, as before. */
|
* NULL (embedder down / circuit breaker open) => pure lexical, as before. */
|
||||||
int32_t qdim = 0;
|
int32_t qdim = 0;
|
||||||
float* qv = eg_embed_fetch_pfx(EL_EMBED_QUERY_PREFIX, q, &qdim);
|
float* qv = eg_embed_fetch(q, &qdim);
|
||||||
EngramSemEntry* sem = qv ? malloc((size_t)g->node_count * sizeof(EngramSemEntry)) : NULL;
|
EngramSemEntry* sem = qv ? malloc((size_t)g->node_count * sizeof(EngramSemEntry)) : NULL;
|
||||||
int64_t nsem = 0;
|
int64_t nsem = 0;
|
||||||
int64_t nhits = 0;
|
int64_t nhits = 0;
|
||||||
@@ -8214,7 +8196,7 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
|||||||
EngramNode* n = &g->nodes[i];
|
EngramNode* n = &g->nodes[i];
|
||||||
if (n->emb || !eg_embed_eligible(n)) continue;
|
if (n->emb || !eg_embed_eligible(n)) continue;
|
||||||
int32_t d = 0;
|
int32_t d = 0;
|
||||||
float* v = eg_embed_fetch_pfx(EL_EMBED_DOC_PREFIX, n->content, &d);
|
float* v = eg_embed_fetch(n->content, &d);
|
||||||
if (!v) break; /* embedder down / breaker open — stop this call */
|
if (!v) break; /* embedder down / breaker open — stop this call */
|
||||||
n->emb = v; n->emb_dim = d;
|
n->emb = v; n->emb_dim = d;
|
||||||
backfilled++;
|
backfilled++;
|
||||||
@@ -8232,7 +8214,7 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
|||||||
q_emb = _eg_qcache_emb; q_dim = _eg_qcache_dim;
|
q_emb = _eg_qcache_emb; q_dim = _eg_qcache_dim;
|
||||||
} else {
|
} else {
|
||||||
int32_t d = 0;
|
int32_t d = 0;
|
||||||
float* v = eg_embed_fetch_pfx(EL_EMBED_QUERY_PREFIX, q, &d);
|
float* v = eg_embed_fetch(q, &d);
|
||||||
if (v) {
|
if (v) {
|
||||||
free(_eg_qcache_text); free(_eg_qcache_emb);
|
free(_eg_qcache_text); free(_eg_qcache_emb);
|
||||||
_eg_qcache_text = strdup(q);
|
_eg_qcache_text = strdup(q);
|
||||||
@@ -9664,7 +9646,7 @@ el_val_t engram_search_json(el_val_t query, el_val_t limit) {
|
|||||||
* so the semantic half of the retrieval surface has to land HERE
|
* so the semantic half of the retrieval surface has to land HERE
|
||||||
* to be observable to the MCP wrapper and the app. */
|
* to be observable to the MCP wrapper and the app. */
|
||||||
int32_t qdim = 0;
|
int32_t qdim = 0;
|
||||||
float* qv = eg_embed_fetch_pfx(EL_EMBED_QUERY_PREFIX, q, &qdim);
|
float* qv = eg_embed_fetch(q, &qdim);
|
||||||
EngramSemEntry* sem = qv ? malloc((size_t)g->node_count * sizeof(EngramSemEntry)) : NULL;
|
EngramSemEntry* sem = qv ? malloc((size_t)g->node_count * sizeof(EngramSemEntry)) : NULL;
|
||||||
int64_t nsem = 0;
|
int64_t nsem = 0;
|
||||||
int64_t nhits = 0;
|
int64_t nhits = 0;
|
||||||
@@ -10242,7 +10224,7 @@ el_val_t engram_embed_backfill(el_val_t count) {
|
|||||||
EngramNode* n = &g->nodes[i];
|
EngramNode* n = &g->nodes[i];
|
||||||
if (n->emb || !eg_embed_eligible(n)) continue;
|
if (n->emb || !eg_embed_eligible(n)) continue;
|
||||||
int32_t d = 0;
|
int32_t d = 0;
|
||||||
float* v = eg_embed_fetch_pfx(EL_EMBED_DOC_PREFIX, n->content, &d);
|
float* v = eg_embed_fetch(n->content, &d);
|
||||||
if (!v) break; /* embedder down / breaker open — stop this call */
|
if (!v) break; /* embedder down / breaker open — stop this call */
|
||||||
n->emb = v; n->emb_dim = d;
|
n->emb = v; n->emb_dim = d;
|
||||||
done++;
|
done++;
|
||||||
|
|||||||
Reference in New Issue
Block a user