@evomap/evolver-core 2.0.0-beta.19 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/gep/genes.jsonl +5 -5
- package/dist/algo/bans.d.ts +10 -1
- package/dist/algo/bans.js +56 -6
- package/dist/algo/candidateAssembly.d.ts +25 -2
- package/dist/algo/candidateAssembly.js +143 -20
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/cycleEngine.d.ts +40 -5
- package/dist/algo/cycleEngine.js +277 -37
- package/dist/algo/evolutionEvent.d.ts +26 -0
- package/dist/algo/evolutionEvent.js +32 -0
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +34 -1
- package/dist/algo/geneHealth.js +42 -0
- package/dist/algo/geneIntake.d.ts +46 -1
- package/dist/algo/geneIntake.js +121 -10
- package/dist/algo/geneSelection.d.ts +115 -6
- package/dist/algo/geneSelection.js +459 -32
- package/dist/algo/index.d.ts +4 -1
- package/dist/algo/index.js +4 -1
- package/dist/algo/kautoProjection.d.ts +41 -0
- package/dist/algo/kautoProjection.js +95 -0
- package/dist/algo/kautoValidator.d.ts +68 -0
- package/dist/algo/kautoValidator.js +256 -0
- package/dist/algo/orchestrator.d.ts +14 -1
- package/dist/algo/orchestrator.js +9 -2
- package/dist/algo/solidify.d.ts +11 -2
- package/dist/algo/solidify.js +37 -7
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetrepair/hubRejection.d.ts +12 -0
- package/dist/assetrepair/hubRejection.js +109 -0
- package/dist/assetrepair/index.d.ts +2 -0
- package/dist/assetrepair/index.js +2 -0
- package/dist/assetrepair/repair.d.ts +33 -0
- package/dist/assetrepair/repair.js +155 -0
- package/dist/assetstore/assetSidecarRecords.js +5 -1
- package/dist/assetstore/assetStoreHealth.d.ts +2 -3
- package/dist/assetstore/assetStoreHealth.js +4 -10
- package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
- package/dist/assetstore/foreignJsonlSource.js +150 -0
- package/dist/assetstore/index.d.ts +3 -0
- package/dist/assetstore/index.js +3 -0
- package/dist/assetstore/learningHistory.js +3 -3
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +8 -0
- package/dist/assetstore/localJsonl.js +128 -3
- package/dist/assetstore/provenance.d.ts +38 -3
- package/dist/assetstore/provenance.js +103 -45
- package/dist/assetstore/provider.d.ts +4 -0
- package/dist/assetstore/reviewFilter.d.ts +19 -1
- package/dist/assetstore/reviewFilter.js +36 -0
- package/dist/assetstore/unionReadStore.d.ts +25 -0
- package/dist/assetstore/unionReadStore.js +119 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
- package/dist/bootstrap/lifecycleBootstrap.js +433 -0
- package/dist/bootstrap/v1EnvCompat.d.ts +3 -0
- package/dist/bootstrap/v1EnvCompat.js +48 -1
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +2 -1
- package/dist/events/ingest.js +6 -0
- package/dist/exec/autoExec.d.ts +30 -3
- package/dist/exec/autoExec.js +294 -29
- package/dist/exec/autonomousCycle.d.ts +7 -0
- package/dist/exec/autonomousCycle.js +2 -0
- package/dist/exec/claudeBridge.d.ts +28 -6
- package/dist/exec/claudeBridge.js +411 -258
- package/dist/exec/executionBinding.d.ts +414 -0
- package/dist/exec/executionBinding.js +588 -0
- package/dist/exec/index.d.ts +1 -0
- package/dist/exec/index.js +1 -0
- package/dist/exec/proofOfWork.d.ts +1 -1
- package/dist/exec/proofOfWork.js +2 -2
- package/dist/exec/runnerRegistry.d.ts +5 -0
- package/dist/exec/runnerRegistry.js +54 -14
- package/dist/hub/capability.d.ts +40 -1
- package/dist/hub/conversationDistiller.d.ts +19 -0
- package/dist/hub/conversationDistiller.js +115 -37
- package/dist/hub/fake.d.ts +1 -0
- package/dist/hub/fake.js +1 -0
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/hub/recipeCompose.d.ts +27 -0
- package/dist/hub/recipeCompose.js +90 -0
- package/dist/hub/sanitize.js +119 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mailbox/ipcServer.d.ts +2 -0
- package/dist/mailbox/ipcServer.js +20 -0
- package/dist/mailbox/store.d.ts +52 -2
- package/dist/mailbox/store.js +546 -81
- package/dist/modelCompatibility.d.ts +164 -0
- package/dist/modelCompatibility.js +309 -0
- package/dist/observers/valueDigestObserver.d.ts +9 -0
- package/dist/observers/valueDigestObserver.js +35 -2
- package/dist/ops/cleanup.js +1 -1
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/selfUpdate.d.ts +8 -0
- package/dist/ops/selfUpdate.js +24 -8
- package/dist/ops/valueOutreach.d.ts +3 -1
- package/dist/ops/valueOutreach.js +5 -1
- package/dist/personality/schema.d.ts +18 -18
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/proofOfWork.d.ts +125 -6
- package/dist/schema/proofOfWork.js +102 -4
- package/dist/shadow/shadowHub.js +1 -0
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +148 -0
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/scopeVocabulary.d.ts +75 -0
- package/dist/signals/scopeVocabulary.js +91 -0
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/trace/learningTrace.d.ts +7 -0
- package/dist/trace/learningTrace.js +22 -0
- package/dist/trace/trajectory.d.ts +8 -0
- package/dist/trace/trajectory.js +14 -2
- package/dist/util/fileLock.d.ts +92 -5
- package/dist/util/fileLock.js +809 -70
- package/dist/util/index.d.ts +2 -1
- package/dist/util/index.js +1 -1
- package/dist/verify/index.d.ts +2 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/sandboxRunner.d.ts +2 -0
- package/dist/verify/sandboxRunner.js +154 -10
- package/dist/verify/sandboxedValidation.d.ts +7 -2
- package/dist/verify/sandboxedValidation.js +102 -24
- package/dist/verify/validation.d.ts +24 -3
- package/dist/verify/validation.js +141 -24
- package/dist/wire/geneHints.d.ts +88 -16
- package/dist/wire/geneHints.js +124 -15
- package/dist/wire/index.d.ts +8 -3
- package/dist/wire/index.js +2 -2
- package/dist/wire/schemaGate.d.ts +21 -0
- package/dist/wire/schemaGate.js +119 -10
- package/package.json +5 -4
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { StrategyPoint } from '../strategy/strategyPoint.js';
|
|
2
2
|
import { geneHealthScore, HEALTH_WEIGHTS_VERSION } from './geneHealth.js';
|
|
3
|
-
import { tagOverlapScore,
|
|
4
|
-
import { driftSelect } from './exploration.js';
|
|
3
|
+
import { bagCosine, buildSemanticIdfProfile, geneTags, idfBagCosine, idfTagOverlapScore, tagOverlapScore, } from '../signals/expand.js';
|
|
4
|
+
import { driftSelect, explorationWindowSize } from './exploration.js';
|
|
5
5
|
import { isDistilledGeneId } from './geneIntake.js';
|
|
6
|
+
import { chooseUcb1Arm, ucb1StatsForCandidate, UCB1_REWARD_POLICY_VERSION, UCB1_SELECTION_POLICY_VERSION, } from './ucb1.js';
|
|
7
|
+
import { resolveTaskDomainSignals, withoutTaskDomainSignals, } from '../signals/taskDomain.js';
|
|
8
|
+
export const SELECTION_GUARD_VERSION = 'relevance-guard-v1';
|
|
6
9
|
function decisionWithWarnings(input, decision) {
|
|
7
10
|
const selected = decision.selectedGeneId
|
|
8
|
-
? decision.candidates.find((candidate) => candidate.geneId === decision.selectedGeneId
|
|
11
|
+
? decision.candidates.find((candidate) => (candidate.geneId === decision.selectedGeneId
|
|
12
|
+
|| (decision.selectedAssetId !== undefined && candidate.assetId === decision.selectedAssetId)))
|
|
9
13
|
: undefined;
|
|
10
14
|
const enriched = {
|
|
11
15
|
...decision,
|
|
@@ -17,11 +21,13 @@ function decisionWithWarnings(input, decision) {
|
|
|
17
21
|
: enriched;
|
|
18
22
|
}
|
|
19
23
|
function signalMatchScore(signals, match) {
|
|
20
|
-
|
|
24
|
+
const genericSignals = withoutTaskDomainSignals(signals);
|
|
25
|
+
const genericMatch = withoutTaskDomainSignals(match);
|
|
26
|
+
if (genericSignals.length === 0 || genericMatch.length === 0)
|
|
21
27
|
return 0;
|
|
22
|
-
const set = new Set(
|
|
23
|
-
const hit =
|
|
24
|
-
return hit /
|
|
28
|
+
const set = new Set(genericSignals);
|
|
29
|
+
const hit = genericMatch.filter((m) => set.has(m)).length;
|
|
30
|
+
return hit / genericMatch.length; // 覆盖率
|
|
25
31
|
}
|
|
26
32
|
// Weight of the bag-of-words cosine path when it fills the recall gap left by literal + tag overlap.
|
|
27
33
|
const SEMANTIC_WEIGHT = 0.5;
|
|
@@ -39,12 +45,88 @@ export const CONFIDENCE_WEIGHT = 0.15;
|
|
|
39
45
|
export const REUSE_WEIGHT = 0.1;
|
|
40
46
|
/** Weight of scoped local MemoryGraph outcome evidence. */
|
|
41
47
|
export const MEMORY_GRAPH_WEIGHT = 0.12;
|
|
48
|
+
/** Bounded weight for a canonical task-domain signal match (#628). */
|
|
49
|
+
export const TASK_DOMAIN_WEIGHT = 0.08;
|
|
50
|
+
/** signals_match is weak domain evidence; its maximum score contribution is 0.08 * 0.5 = 0.04. */
|
|
51
|
+
export const TASK_DOMAIN_SIGNAL_EVIDENCE = 0.5;
|
|
52
|
+
/**
|
|
53
|
+
* Soft boost for strict K_auto members. Smaller than CONFIDENCE_WEIGHT: membership is a writer-side property,
|
|
54
|
+
* not verified cycle history, so it only breaks near-ties between already-eligible candidates.
|
|
55
|
+
*/
|
|
56
|
+
export const KAUTO_WEIGHT = 0.05;
|
|
57
|
+
/** λ is intentionally NOT inlined into SELECTION_WEIGHTS_VERSION text:
|
|
58
|
+
* decision.gene_selected root events already sit within ~2B of the 4096B line budget
|
|
59
|
+
* under UCB1/plateau fixtures; a `,kauto=0.05` suffix drops selectionPolicy. */
|
|
60
|
+
const SELECTION_GUARD_MATCH_EPS = 1e-6;
|
|
42
61
|
/**
|
|
43
62
|
* Version of the full engine-health weight vector (health 0.6 + signal-match 0.4 − epigenetic penalty
|
|
44
|
-
* + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment
|
|
45
|
-
* weight snapshots track the change. Composed from the
|
|
63
|
+
* + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment + KAUTO_WEIGHT × kauto-member).
|
|
64
|
+
* Bumped whenever a factor is added so golden weight snapshots track the change. Composed from the
|
|
65
|
+
* health-weights version so a change to either layer shows.
|
|
46
66
|
*/
|
|
47
|
-
export const
|
|
67
|
+
export const LEGACY_SELECTION_WEIGHTS_VERSION = `sel-6-domain(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT},domain=${TASK_DOMAIN_WEIGHT})`;
|
|
68
|
+
export const SELECTION_WEIGHTS_VERSION = `sel-7-idf-domain(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT},domain=${TASK_DOMAIN_WEIGHT})`;
|
|
69
|
+
const SEMANTIC_CORPUS_LIMIT = 1_000;
|
|
70
|
+
const SEMANTIC_SIGNAL_LIMIT = 16;
|
|
71
|
+
const SEMANTIC_SIGNAL_CHARS = 96;
|
|
72
|
+
const SEMANTIC_GENE_ID_CHARS = 128;
|
|
73
|
+
const SEMANTIC_CATEGORY_CHARS = 96;
|
|
74
|
+
const SEMANTIC_SUMMARY_CHARS = 1_024;
|
|
75
|
+
const SEMANTIC_IDENTITY_CHARS = 256;
|
|
76
|
+
function legacyCandidateSemanticText(candidate) {
|
|
77
|
+
return [
|
|
78
|
+
...withoutTaskDomainSignals(candidate.signalsMatch),
|
|
79
|
+
candidate.category ?? '',
|
|
80
|
+
candidate.summary ?? '',
|
|
81
|
+
].join(' ');
|
|
82
|
+
}
|
|
83
|
+
function boundedSignals(signals) {
|
|
84
|
+
const out = [];
|
|
85
|
+
const genericSignals = withoutTaskDomainSignals(signals);
|
|
86
|
+
const limit = Math.min(genericSignals.length, SEMANTIC_SIGNAL_LIMIT);
|
|
87
|
+
for (let index = 0; index < limit; index += 1) {
|
|
88
|
+
const signal = genericSignals[index];
|
|
89
|
+
if (typeof signal === 'string')
|
|
90
|
+
out.push(signal.slice(0, SEMANTIC_SIGNAL_CHARS));
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function semanticCandidateProjection(candidate) {
|
|
95
|
+
const signalsMatch = boundedSignals(candidate.signalsMatch);
|
|
96
|
+
const geneId = candidate.geneId.slice(0, SEMANTIC_GENE_ID_CHARS);
|
|
97
|
+
const category = candidate.category?.slice(0, SEMANTIC_CATEGORY_CHARS);
|
|
98
|
+
const summary = candidate.summary?.slice(0, SEMANTIC_SUMMARY_CHARS);
|
|
99
|
+
return {
|
|
100
|
+
tagInput: {
|
|
101
|
+
signalsMatch,
|
|
102
|
+
geneId,
|
|
103
|
+
...(category ? { category } : {}),
|
|
104
|
+
...(summary ? { summary } : {}),
|
|
105
|
+
},
|
|
106
|
+
text: [...signalsMatch, category ?? '', summary ?? ''].join(' '),
|
|
107
|
+
hasContent: signalsMatch.some((signal) => signal.trim().length > 0)
|
|
108
|
+
|| Boolean(category?.trim())
|
|
109
|
+
|| Boolean(summary?.trim()),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function buildSelectionSemanticProfile(corpus) {
|
|
113
|
+
const documents = [];
|
|
114
|
+
const identities = new Set();
|
|
115
|
+
for (const candidate of corpus) {
|
|
116
|
+
if (documents.length >= SEMANTIC_CORPUS_LIMIT)
|
|
117
|
+
break;
|
|
118
|
+
const identityValue = candidate.assetId ?? candidate.geneId;
|
|
119
|
+
const identity = `${candidate.assetId ? 'asset' : 'gene'}:${identityValue.slice(0, SEMANTIC_IDENTITY_CHARS)}`;
|
|
120
|
+
if (identities.has(identity))
|
|
121
|
+
continue;
|
|
122
|
+
identities.add(identity);
|
|
123
|
+
const projection = semanticCandidateProjection(candidate);
|
|
124
|
+
if (!projection.hasContent)
|
|
125
|
+
continue;
|
|
126
|
+
documents.push({ tags: geneTags(projection.tagInput), text: projection.text });
|
|
127
|
+
}
|
|
128
|
+
return buildSemanticIdfProfile(documents);
|
|
129
|
+
}
|
|
48
130
|
/**
|
|
49
131
|
* Match score with semantic signal expansion (ported from v1): literal coverage first, then fill the
|
|
50
132
|
* recall gap with semantic tag overlap, then with bag-of-words cosine similarity. A perfect literal match
|
|
@@ -52,22 +134,43 @@ export const SELECTION_WEIGHTS_VERSION = `sel-4(${HEALTH_WEIGHTS_VERSION},conf=$
|
|
|
52
134
|
* for 'repair'), and cosine catches lexical similarity the curated tags miss (e.g. a 'timeout_slow' signal
|
|
53
135
|
* vs a gene summarized "fix the slow timeout"). Each layer only fills the gap the previous one left.
|
|
54
136
|
*/
|
|
55
|
-
function expandedMatchScore(signals, c) {
|
|
137
|
+
function expandedMatchScore(signals, c, semanticProfile) {
|
|
56
138
|
const literal = signalMatchScore(signals, c.signalsMatch);
|
|
57
139
|
if (literal >= 1)
|
|
58
140
|
return { score: 1, literal, tag: 0, cos: 0 };
|
|
59
|
-
const
|
|
141
|
+
const projection = semanticProfile ? semanticCandidateProjection(c) : undefined;
|
|
142
|
+
const semanticSignals = semanticProfile ? boundedSignals(signals) : withoutTaskDomainSignals(signals);
|
|
143
|
+
const tag = semanticProfile
|
|
144
|
+
? idfTagOverlapScore(semanticSignals, projection.tagInput, semanticProfile)
|
|
145
|
+
: tagOverlapScore(semanticSignals, {
|
|
146
|
+
signalsMatch: withoutTaskDomainSignals(c.signalsMatch),
|
|
147
|
+
geneId: c.geneId,
|
|
148
|
+
category: c.category,
|
|
149
|
+
summary: c.summary,
|
|
150
|
+
});
|
|
60
151
|
const base = Math.min(1, literal + (1 - literal) * tag);
|
|
61
152
|
if (base >= 1)
|
|
62
153
|
return { score: 1, literal, tag, cos: 0 };
|
|
63
|
-
const
|
|
64
|
-
|
|
154
|
+
const cos = semanticProfile
|
|
155
|
+
? idfBagCosine(semanticSignals.join(' '), projection.text, semanticProfile)
|
|
156
|
+
: bagCosine(semanticSignals.join(' '), legacyCandidateSemanticText(c));
|
|
65
157
|
return { score: Math.min(1, base + (1 - base) * cos * SEMANTIC_WEIGHT), literal, tag, cos };
|
|
66
158
|
}
|
|
67
|
-
|
|
68
|
-
|
|
159
|
+
function taskDomainEvidence(queryDomain, candidateSignals) {
|
|
160
|
+
if (queryDomain.status !== 'resolved')
|
|
161
|
+
return 0;
|
|
162
|
+
const candidateDomain = resolveTaskDomainSignals(candidateSignals);
|
|
163
|
+
return candidateDomain.status === 'resolved' && candidateDomain.slug === queryDomain.slug
|
|
164
|
+
? TASK_DOMAIN_SIGNAL_EVIDENCE
|
|
165
|
+
: 0;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Score one candidate under the engine-health weight vector, including bounded task-domain evidence.
|
|
169
|
+
* `kautoWeight` defaults to production KAUTO_WEIGHT; ablation passes λ ∈ {0,0.01,0.05,0.1}.
|
|
170
|
+
*/
|
|
171
|
+
function scoreCandidate(signals, c, semanticProfile, queryDomain, kautoWeight = KAUTO_WEIGHT) {
|
|
69
172
|
const health = geneHealthScore(c.view, { reuseCount: c.reuseCount, antiPatternCount: c.antiPatternCount });
|
|
70
|
-
const m = expandedMatchScore(signals, c);
|
|
173
|
+
const m = expandedMatchScore(signals, c, semanticProfile);
|
|
71
174
|
const epi = c.epigeneticPenalty ?? 0;
|
|
72
175
|
// Confidence is the fourth (positive) factor: a [0,1] preferred-gene edge for this signal fingerprint.
|
|
73
176
|
// Clamped defensively so an upstream bug cannot turn the soft nudge into a dominant term.
|
|
@@ -76,7 +179,15 @@ function scoreCandidate(signals, c) {
|
|
|
76
179
|
// into a dominant (or unbounded) term. Absent → 0 → no effect (default-off until a caller injects it).
|
|
77
180
|
const reuse = Math.max(-1, Math.min(1, c.reuseAdjust ?? 0));
|
|
78
181
|
const memory = Math.max(-1, Math.min(1, c.memoryBoost ?? 0));
|
|
79
|
-
const
|
|
182
|
+
const domainEvidence = taskDomainEvidence(queryDomain, c.signalsMatch);
|
|
183
|
+
const domainContribution = TASK_DOMAIN_WEIGHT * domainEvidence;
|
|
184
|
+
// Soft K_auto membership preference: binary, already-admitted candidates only.
|
|
185
|
+
const kauto = c.kautoMember === true ? 1 : 0;
|
|
186
|
+
const kautoContribution = kautoWeight * kauto;
|
|
187
|
+
// score_base is everything except the K_auto soft term — used by λ ablation.
|
|
188
|
+
const scoreBase = 0.6 * health.score + 0.4 * m.score - epi + CONFIDENCE_WEIGHT * conf
|
|
189
|
+
+ MEMORY_GRAPH_WEIGHT * memory + REUSE_WEIGHT * reuse + domainContribution;
|
|
190
|
+
const score = scoreBase + kautoContribution;
|
|
80
191
|
const breakdown = m.tag > 0 || m.cos > 0 ? `(literal=${m.literal.toFixed(2)}+tag=${m.tag.toFixed(2)}+cos=${m.cos.toFixed(2)})` : '';
|
|
81
192
|
const reasons = [`health=${health.score.toFixed(3)}(succ=${health.successRate.toFixed(2)},reuse=${health.reuseCount})`, `信号匹配=${m.score.toFixed(2)}${breakdown}`];
|
|
82
193
|
if (epi > 0)
|
|
@@ -87,7 +198,119 @@ function scoreCandidate(signals, c) {
|
|
|
87
198
|
reasons.push(`scoped memory-graph outcome ${memory >= 0 ? '+' : ''}${(MEMORY_GRAPH_WEIGHT * memory).toFixed(3)} (boost=${memory.toFixed(2)})`);
|
|
88
199
|
if (reuse !== 0)
|
|
89
200
|
reasons.push(`cross-runtime reuse ${reuse >= 0 ? '+' : ''}${(REUSE_WEIGHT * reuse).toFixed(3)} (sentiment=${reuse.toFixed(2)})`);
|
|
90
|
-
|
|
201
|
+
if (domainContribution > 0)
|
|
202
|
+
reasons.push(`task_domain match +${domainContribution.toFixed(3)} (evidence=${domainEvidence.toFixed(2)})`);
|
|
203
|
+
if (kautoContribution > 0)
|
|
204
|
+
reasons.push(`K_auto member +${kautoContribution.toFixed(3)}`);
|
|
205
|
+
// Keep scoreBase/kautoContribution off ScoredCandidate so decision.gene_selected
|
|
206
|
+
// root-event candidate payloads retain the pre-K_auto 4096B line budget.
|
|
207
|
+
return {
|
|
208
|
+
geneId: c.geneId,
|
|
209
|
+
...(c.assetId ? { assetId: c.assetId } : {}),
|
|
210
|
+
score,
|
|
211
|
+
matchScore: m.score,
|
|
212
|
+
health,
|
|
213
|
+
reasons,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function candidateIdentity(c) {
|
|
217
|
+
return c.assetId ? `asset:${c.assetId}` : `gene:${c.geneId}`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Offline λ ablation over an already-admitted candidate pool.
|
|
221
|
+
* Does not run hard gates / force / distilled fallback — those stay outside the soft preference.
|
|
222
|
+
* Pure ranking sensitivity for score_T2 = score_base + λ · 1[k_a ∈ K_auto].
|
|
223
|
+
*/
|
|
224
|
+
export function ablateKautoLambda(input, lambdas = [0, 0.01, 0.05, 0.1], opts = {}) {
|
|
225
|
+
const topK = opts.topK ?? 3;
|
|
226
|
+
const floor = opts.floor ?? 0;
|
|
227
|
+
const semanticProfile = input.disableSemanticIdf || input.semanticCorpus === undefined
|
|
228
|
+
? undefined
|
|
229
|
+
: buildSelectionSemanticProfile(input.semanticCorpus);
|
|
230
|
+
const queryDomain = resolveTaskDomainSignals(input.signals);
|
|
231
|
+
const pools = lambdas.map((lambda) => {
|
|
232
|
+
const scored = input.candidates
|
|
233
|
+
.map((c) => {
|
|
234
|
+
const s = scoreCandidate(input.signals, c, semanticProfile, queryDomain, lambda);
|
|
235
|
+
const kautoContribution = (c.kautoMember === true ? 1 : 0) * lambda;
|
|
236
|
+
return {
|
|
237
|
+
geneId: s.geneId,
|
|
238
|
+
...(s.assetId ? { assetId: s.assetId } : {}),
|
|
239
|
+
score: s.score,
|
|
240
|
+
scoreBase: s.score - kautoContribution,
|
|
241
|
+
kautoContribution,
|
|
242
|
+
kautoMember: c.kautoMember === true,
|
|
243
|
+
};
|
|
244
|
+
})
|
|
245
|
+
.sort((a, b) => b.score - a.score || (a.assetId ?? a.geneId).localeCompare(b.assetId ?? b.geneId));
|
|
246
|
+
const eligible = scored.filter((s) => s.score > floor);
|
|
247
|
+
const chosen = eligible[0];
|
|
248
|
+
const bases = scored.map((s) => s.scoreBase);
|
|
249
|
+
const scoreBaseMin = bases.length ? Math.min(...bases) : 0;
|
|
250
|
+
const scoreBaseMax = bases.length ? Math.max(...bases) : 0;
|
|
251
|
+
const scoreBaseMean = bases.length ? bases.reduce((a, b) => a + b, 0) / bases.length : 0;
|
|
252
|
+
return {
|
|
253
|
+
lambda,
|
|
254
|
+
ranking: scored.map((s, index) => ({
|
|
255
|
+
rank: index + 1,
|
|
256
|
+
geneId: s.geneId,
|
|
257
|
+
...(s.assetId ? { assetId: s.assetId } : {}),
|
|
258
|
+
score: s.score,
|
|
259
|
+
scoreBase: s.scoreBase,
|
|
260
|
+
kautoContribution: s.kautoContribution,
|
|
261
|
+
kautoMember: s.kautoMember,
|
|
262
|
+
})),
|
|
263
|
+
selectedGeneId: chosen?.geneId ?? null,
|
|
264
|
+
...(chosen?.assetId ? { selectedAssetId: chosen.assetId } : {}),
|
|
265
|
+
scoreBaseMin,
|
|
266
|
+
scoreBaseMax,
|
|
267
|
+
scoreBaseMean,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
const baseline = pools.find((p) => p.lambda === 0) ?? pools[0];
|
|
271
|
+
const baselineRank = new Map(baseline?.ranking.map((r) => [candidateIdentity(r), r.rank]) ?? []);
|
|
272
|
+
const baselineTopK = new Set((baseline?.ranking.slice(0, topK) ?? []).map((r) => candidateIdentity(r)));
|
|
273
|
+
const baselineSelected = baseline
|
|
274
|
+
? candidateIdentity({
|
|
275
|
+
geneId: baseline.selectedGeneId ?? '',
|
|
276
|
+
...(baseline.selectedAssetId ? { assetId: baseline.selectedAssetId } : {}),
|
|
277
|
+
})
|
|
278
|
+
: '';
|
|
279
|
+
const rankChangesByLambda = {};
|
|
280
|
+
const selectedChangesByLambda = {};
|
|
281
|
+
const topKChangesByLambda = {};
|
|
282
|
+
for (const pool of pools) {
|
|
283
|
+
const key = String(pool.lambda);
|
|
284
|
+
let rankChanges = 0;
|
|
285
|
+
for (const row of pool.ranking) {
|
|
286
|
+
const id = candidateIdentity(row);
|
|
287
|
+
if ((baselineRank.get(id) ?? -1) !== row.rank)
|
|
288
|
+
rankChanges += 1;
|
|
289
|
+
}
|
|
290
|
+
rankChangesByLambda[key] = rankChanges;
|
|
291
|
+
const selectedId = candidateIdentity({
|
|
292
|
+
geneId: pool.selectedGeneId ?? '',
|
|
293
|
+
...(pool.selectedAssetId ? { assetId: pool.selectedAssetId } : {}),
|
|
294
|
+
});
|
|
295
|
+
selectedChangesByLambda[key] = selectedId !== baselineSelected;
|
|
296
|
+
const topSet = new Set(pool.ranking.slice(0, topK).map((r) => candidateIdentity(r)));
|
|
297
|
+
let topDiff = 0;
|
|
298
|
+
for (const id of topSet)
|
|
299
|
+
if (!baselineTopK.has(id))
|
|
300
|
+
topDiff += 1;
|
|
301
|
+
for (const id of baselineTopK)
|
|
302
|
+
if (!topSet.has(id))
|
|
303
|
+
topDiff += 1;
|
|
304
|
+
topKChangesByLambda[key] = topDiff;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
lambdas,
|
|
308
|
+
pools,
|
|
309
|
+
rankChangesByLambda,
|
|
310
|
+
selectedChangesByLambda,
|
|
311
|
+
topKChangesByLambda,
|
|
312
|
+
topK,
|
|
313
|
+
};
|
|
91
314
|
}
|
|
92
315
|
function matchesForcedGeneId(c, forcedGeneId) {
|
|
93
316
|
return c.geneId === forcedGeneId || c.assetId === forcedGeneId;
|
|
@@ -101,6 +324,23 @@ function matchesScoredIdentity(a, b) {
|
|
|
101
324
|
function withoutCandidateIdentity(scored, c) {
|
|
102
325
|
return scored.filter((s) => !matchesCandidateIdentity(s, c));
|
|
103
326
|
}
|
|
327
|
+
function candidateForScore(input, scored) {
|
|
328
|
+
// Eligibility is attached to one assembled candidate, not to every logical alias of the same content.
|
|
329
|
+
// Match the exact scored row so an untrusted alias cannot borrow a trusted candidate's UCB1 admission bit.
|
|
330
|
+
return input.candidates.find((candidate) => (candidate.geneId === scored.geneId && candidate.assetId === scored.assetId));
|
|
331
|
+
}
|
|
332
|
+
function compactMetric(value) {
|
|
333
|
+
return Math.round(value * 1_000_000) / 1_000_000;
|
|
334
|
+
}
|
|
335
|
+
function compactChoice(choice) {
|
|
336
|
+
return {
|
|
337
|
+
...choice,
|
|
338
|
+
armId: choice.armId.slice(0, 160),
|
|
339
|
+
meanReward: compactMetric(choice.meanReward),
|
|
340
|
+
...(choice.bonus !== null ? { bonus: compactMetric(choice.bonus) } : { bonus: null }),
|
|
341
|
+
...(choice.index !== null ? { index: compactMetric(choice.index) } : { index: null }),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
104
344
|
function isReusableGenerationSource(source) {
|
|
105
345
|
return source === 'distilled' || source === 'evolved';
|
|
106
346
|
}
|
|
@@ -146,20 +386,150 @@ function forcedSelection(input, scored, scoreForcedFallback, floor) {
|
|
|
146
386
|
forcedFallback.reasons.push(`forced gene selected despite floor ${floor}`);
|
|
147
387
|
return { selectedGeneId: fallback.geneId, ...(fallback.assetId ? { selectedAssetId: fallback.assetId } : {}), scoredForChoice: scoredCandidatesOnly };
|
|
148
388
|
}
|
|
389
|
+
/** Refs #626: identify selections whose relevance is absent or cannot discriminate during a plateau. */
|
|
390
|
+
export function assessSelectionGuard(scored, plateauActive) {
|
|
391
|
+
if (scored.length === 0) {
|
|
392
|
+
return { wouldAbstain: true, reason: 'no_match', maxMatch: 0, matchSpread: 0 };
|
|
393
|
+
}
|
|
394
|
+
const matches = scored.map((candidate) => candidate.matchScore ?? 0);
|
|
395
|
+
const maxMatch = Math.max(...matches);
|
|
396
|
+
const minMatch = Math.min(...matches);
|
|
397
|
+
const matchSpread = maxMatch - minMatch;
|
|
398
|
+
const metrics = {
|
|
399
|
+
maxMatch: compactMetric(maxMatch),
|
|
400
|
+
matchSpread: compactMetric(matchSpread),
|
|
401
|
+
};
|
|
402
|
+
if (maxMatch <= SELECTION_GUARD_MATCH_EPS) {
|
|
403
|
+
return { wouldAbstain: true, reason: 'no_match', ...metrics };
|
|
404
|
+
}
|
|
405
|
+
if (plateauActive && scored.length > 1 && matchSpread <= SELECTION_GUARD_MATCH_EPS) {
|
|
406
|
+
return { wouldAbstain: true, reason: 'plateau_flat_match', ...metrics };
|
|
407
|
+
}
|
|
408
|
+
return { wouldAbstain: false, ...metrics };
|
|
409
|
+
}
|
|
149
410
|
/** 实现1: engine 健康分主导(health 0.6 + 信号匹配 0.4). */
|
|
150
411
|
export const engineHealthSelection = {
|
|
151
412
|
name: 'engine-health',
|
|
152
413
|
version: '1',
|
|
153
414
|
run(input, ctx) {
|
|
154
|
-
const
|
|
415
|
+
const semanticProfile = input.disableSemanticIdf || input.semanticCorpus === undefined
|
|
416
|
+
? undefined
|
|
417
|
+
: buildSelectionSemanticProfile(input.semanticCorpus);
|
|
418
|
+
const weightsVersion = semanticProfile ? SELECTION_WEIGHTS_VERSION : LEGACY_SELECTION_WEIGHTS_VERSION;
|
|
419
|
+
const semanticMetadata = semanticProfile
|
|
420
|
+
? {
|
|
421
|
+
semanticProfileVersion: semanticProfile.version,
|
|
422
|
+
semanticDocumentCount: semanticProfile.documentCount,
|
|
423
|
+
}
|
|
424
|
+
: {};
|
|
425
|
+
const queryDomain = resolveTaskDomainSignals(input.signals);
|
|
426
|
+
const scoreForSelection = (candidate) => (scoreCandidate(input.signals, candidate, semanticProfile, queryDomain));
|
|
427
|
+
const scored = input.candidates.map(scoreForSelection).sort((a, b) => b.score - a.score);
|
|
155
428
|
const floor = input.floor ?? 0;
|
|
156
|
-
const forced = forcedSelection(input, scored,
|
|
429
|
+
const forced = forcedSelection(input, scored, scoreForSelection, floor);
|
|
157
430
|
const eligible = forced.scoredForChoice.filter((s) => s.score > floor);
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
431
|
+
const guardMode = input.selectionGuard ?? 'legacy';
|
|
432
|
+
const plateauActive = input.exploration?.plateau?.active === true;
|
|
433
|
+
let guardAssessment = assessSelectionGuard(eligible, plateauActive);
|
|
434
|
+
if (eligible.length === 0) {
|
|
435
|
+
const fullAssessment = assessSelectionGuard(forced.scoredForChoice, plateauActive);
|
|
436
|
+
if (fullAssessment.reason !== 'no_match') {
|
|
437
|
+
guardAssessment = {
|
|
438
|
+
wouldAbstain: false,
|
|
439
|
+
...(fullAssessment.maxMatch !== undefined ? { maxMatch: fullAssessment.maxMatch } : {}),
|
|
440
|
+
...(fullAssessment.matchSpread !== undefined ? { matchSpread: fullAssessment.matchSpread } : {}),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const policyEligible = guardMode === 'enforce' && guardAssessment.reason === 'no_match'
|
|
445
|
+
? []
|
|
446
|
+
: eligible;
|
|
447
|
+
// Deterministic top by default. Plateau exploration either preserves legacy random drift or, when explicitly
|
|
448
|
+
// requested, evaluates UCB1 over the same adaptive top-N window. Forced selection always wins below.
|
|
449
|
+
const requestedPolicy = input.exploration?.policy ?? 'engine-health';
|
|
450
|
+
const ucb1Active = input.exploration?.plateau?.active === true;
|
|
451
|
+
let drift = { driftMode: 'deterministic', index: 0, intensity: 0 };
|
|
452
|
+
let chosen;
|
|
453
|
+
let selectionPolicy;
|
|
454
|
+
if (forced.selectedGeneId === undefined && ucb1Active && requestedPolicy !== 'engine-health') {
|
|
455
|
+
// Canonicalize ties before slicing the UCB window. The legacy/default ranked list intentionally keeps its
|
|
456
|
+
// stable input-order tie behavior; UCB1 must produce the same arm for any permutation of the same pool.
|
|
457
|
+
const ucbRanked = [...policyEligible].sort((left, right) => {
|
|
458
|
+
const leftScore = Number.isFinite(left.score) ? left.score : Number.NEGATIVE_INFINITY;
|
|
459
|
+
const rightScore = Number.isFinite(right.score) ? right.score : Number.NEGATIVE_INFINITY;
|
|
460
|
+
return rightScore !== leftScore
|
|
461
|
+
? rightScore - leftScore
|
|
462
|
+
: (left.assetId ?? left.geneId).localeCompare(right.assetId ?? right.geneId);
|
|
463
|
+
});
|
|
464
|
+
const window = ucbRanked.slice(0, explorationWindowSize(ucbRanked.length, input.exploration));
|
|
465
|
+
const logicalIdCounts = new Map();
|
|
466
|
+
for (const scored of window) {
|
|
467
|
+
logicalIdCounts.set(scored.geneId, (logicalIdCounts.get(scored.geneId) ?? 0) + 1);
|
|
468
|
+
}
|
|
469
|
+
const history = input.exploration?.ucb1History;
|
|
470
|
+
const result = history
|
|
471
|
+
? chooseUcb1Arm(window.map((scored) => {
|
|
472
|
+
const candidate = candidateForScore(input, scored);
|
|
473
|
+
const armId = scored.assetId ?? scored.geneId;
|
|
474
|
+
return {
|
|
475
|
+
armId,
|
|
476
|
+
baseScore: scored.score,
|
|
477
|
+
explorationEligible: candidate?.explorationEligible === true,
|
|
478
|
+
stats: candidate
|
|
479
|
+
? ucb1StatsForCandidate(history, candidate.geneId, candidate.assetId, (logicalIdCounts.get(candidate.geneId) ?? 0) === 1)
|
|
480
|
+
: { armId, pulls: 0, completedPulls: 0, rewardSum: 0, meanReward: 0 },
|
|
481
|
+
};
|
|
482
|
+
}), history.totalPulls)
|
|
483
|
+
: { choice: null, fallbackReason: 'missing_history' };
|
|
484
|
+
const ucbChosen = result.choice
|
|
485
|
+
? window.find((candidate) => (candidate.assetId ?? candidate.geneId) === result.choice.armId)
|
|
486
|
+
: undefined;
|
|
487
|
+
if (requestedPolicy === 'ucb1' && ucbChosen) {
|
|
488
|
+
chosen = ucbChosen;
|
|
489
|
+
const choice = compactChoice(result.choice);
|
|
490
|
+
chosen.reasons.push('ucb1(index=' + (choice.index ?? 'cold') + ',mean=' + choice.meanReward.toFixed(3) + ',pulls=' + choice.pulls + ')');
|
|
491
|
+
selectionPolicy = {
|
|
492
|
+
requested: requestedPolicy,
|
|
493
|
+
effective: 'ucb1',
|
|
494
|
+
selectionPolicyVersion: UCB1_SELECTION_POLICY_VERSION,
|
|
495
|
+
rewardPolicyVersion: UCB1_REWARD_POLICY_VERSION,
|
|
496
|
+
arm: choice,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
drift = driftSelect(eligible.length, input.exploration, ctx?.rng ?? Math.random);
|
|
501
|
+
chosen = eligible[drift.index] ?? eligible[0];
|
|
502
|
+
selectionPolicy = {
|
|
503
|
+
requested: requestedPolicy,
|
|
504
|
+
effective: 'engine-health',
|
|
505
|
+
selectionPolicyVersion: UCB1_SELECTION_POLICY_VERSION,
|
|
506
|
+
rewardPolicyVersion: UCB1_REWARD_POLICY_VERSION,
|
|
507
|
+
...(result.choice ? {
|
|
508
|
+
arm: compactChoice(result.choice),
|
|
509
|
+
shadowArmId: result.choice.armId.slice(0, 160),
|
|
510
|
+
shadowDisagrees: Boolean(chosen && (chosen.assetId ?? chosen.geneId) !== result.choice.armId),
|
|
511
|
+
} : {}),
|
|
512
|
+
...(result.fallbackReason ? { fallbackReason: result.fallbackReason } : {}),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
drift = driftSelect(eligible.length, input.exploration, ctx?.rng ?? Math.random);
|
|
518
|
+
chosen = eligible[drift.index] ?? eligible[0];
|
|
519
|
+
}
|
|
520
|
+
const forcedSelected = forced.selectedGeneId !== undefined;
|
|
521
|
+
const ucb1ResolvedPlateau = guardAssessment.reason === 'plateau_flat_match'
|
|
522
|
+
&& selectionPolicy?.effective === 'ucb1';
|
|
523
|
+
const guardApplied = guardMode === 'enforce'
|
|
524
|
+
&& !forcedSelected
|
|
525
|
+
&& guardAssessment.wouldAbstain
|
|
526
|
+
&& !ucb1ResolvedPlateau;
|
|
527
|
+
if (guardApplied) {
|
|
528
|
+
chosen = undefined;
|
|
529
|
+
}
|
|
161
530
|
let selectedGeneId = forced.selectedGeneId ?? (chosen ? chosen.geneId : null);
|
|
162
531
|
let selectedAssetId = forced.selectedAssetId ?? chosen?.assetId;
|
|
532
|
+
let fallbackSelected = false;
|
|
163
533
|
if (!selectedGeneId) {
|
|
164
534
|
// Issue #97 (ported from v1 selector.js): no candidate matched any live signal (every gene scored <= 0). Rather
|
|
165
535
|
// than fall straight through to a blind innovate, reuse a *distilled* (or evolved) gene if one is available.
|
|
@@ -176,11 +546,14 @@ export const engineHealthSelection = {
|
|
|
176
546
|
// scored above zero, NOT merely when nothing cleared a positive `floor`. Otherwise a real but weak signal match
|
|
177
547
|
// (0 < score <= floor) would be preempted by an unrelated zero-evidence distilled gene; V1 (which has no floor)
|
|
178
548
|
// keeps such a match. When candidates scored > 0 but below floor, defer to V2's floor policy (innovate).
|
|
179
|
-
const
|
|
549
|
+
const normalPoolHasMatch = forced.scoredForChoice.some((candidate) => (candidate.matchScore ?? 0) > SELECTION_GUARD_MATCH_EPS);
|
|
550
|
+
const noSignalMatch = !forced.forceRejected
|
|
551
|
+
&& ((guardApplied && guardAssessment.reason === 'no_match' && !normalPoolHasMatch)
|
|
552
|
+
|| forced.scoredForChoice.every((candidate) => candidate.score <= 0));
|
|
180
553
|
const fallback = noSignalMatch
|
|
181
554
|
? (input.distilledFallback ?? [])
|
|
182
555
|
.filter((candidate) => isReusableFallbackCandidate(candidate) && (candidate.epigeneticPenalty ?? 0) === 0)
|
|
183
|
-
.map((candidate) => ({ candidate, scored:
|
|
556
|
+
.map((candidate) => ({ candidate, scored: scoreForSelection(candidate) }))
|
|
184
557
|
.sort((left, right) => right.scored.score - left.scored.score || left.candidate.geneId.localeCompare(right.candidate.geneId))[0]
|
|
185
558
|
: undefined;
|
|
186
559
|
if (fallback) {
|
|
@@ -189,15 +562,48 @@ export const engineHealthSelection = {
|
|
|
189
562
|
scored.push(fbScored);
|
|
190
563
|
selectedGeneId = fb.geneId;
|
|
191
564
|
selectedAssetId = fb.assetId;
|
|
565
|
+
fallbackSelected = true;
|
|
192
566
|
}
|
|
193
|
-
else {
|
|
194
|
-
forced.scoredForChoice.forEach((
|
|
567
|
+
else if (!guardApplied) {
|
|
568
|
+
forced.scoredForChoice.forEach((candidate) => candidate.reasons.push(`< floor ${floor} → innovate`));
|
|
195
569
|
}
|
|
196
570
|
}
|
|
197
|
-
else if (forced.selectedGeneId === undefined && drift.driftMode !== 'deterministic') {
|
|
571
|
+
else if (forced.selectedGeneId === undefined && selectionPolicy?.effective !== 'ucb1' && drift.driftMode !== 'deterministic') {
|
|
198
572
|
scored.find((s) => s.geneId === selectedGeneId)?.reasons.push(`drift(${drift.driftMode},intensity=${drift.intensity.toFixed(2)})`);
|
|
199
573
|
}
|
|
200
|
-
|
|
574
|
+
if (selectionPolicy?.requested === 'ucb1-shadow' && selectionPolicy.shadowArmId) {
|
|
575
|
+
selectionPolicy.shadowDisagrees = (selectedAssetId ?? selectedGeneId) !== selectionPolicy.shadowArmId;
|
|
576
|
+
}
|
|
577
|
+
const selectionGuard = guardMode === 'legacy'
|
|
578
|
+
? undefined
|
|
579
|
+
: {
|
|
580
|
+
mode: guardMode,
|
|
581
|
+
version: SELECTION_GUARD_VERSION,
|
|
582
|
+
status: forcedSelected
|
|
583
|
+
? 'forced'
|
|
584
|
+
: ucb1ResolvedPlateau
|
|
585
|
+
? 'ucb1'
|
|
586
|
+
: fallbackSelected
|
|
587
|
+
? 'fallback'
|
|
588
|
+
: guardApplied
|
|
589
|
+
? 'innovate'
|
|
590
|
+
: guardAssessment.wouldAbstain
|
|
591
|
+
? 'shadow'
|
|
592
|
+
: 'allowed',
|
|
593
|
+
...(guardAssessment.wouldAbstain && guardAssessment.reason ? { reason: guardAssessment.reason } : {}),
|
|
594
|
+
...(guardAssessment.maxMatch !== undefined ? { maxMatch: guardAssessment.maxMatch } : {}),
|
|
595
|
+
...(guardAssessment.matchSpread !== undefined ? { matchSpread: guardAssessment.matchSpread } : {}),
|
|
596
|
+
};
|
|
597
|
+
return decisionWithWarnings(input, {
|
|
598
|
+
selectedGeneId,
|
|
599
|
+
...(selectedAssetId ? { selectedAssetId } : {}),
|
|
600
|
+
candidates: scored,
|
|
601
|
+
weightsVersion,
|
|
602
|
+
...semanticMetadata,
|
|
603
|
+
strategyName: 'engine-health',
|
|
604
|
+
...(selectionPolicy ? { selectionPolicy } : {}),
|
|
605
|
+
...(selectionGuard ? { selectionGuard } : {}),
|
|
606
|
+
});
|
|
201
607
|
},
|
|
202
608
|
};
|
|
203
609
|
/** 实现2: 纯信号匹配采样(忽略 health, 对照基线 — 经验主义要可对比). */
|
|
@@ -229,14 +635,35 @@ export function agentLedSelection(pick) {
|
|
|
229
635
|
run(input, ctx) {
|
|
230
636
|
const baseDecision = engineHealthSelection.run(input, ctx);
|
|
231
637
|
const base = baseDecision.candidates;
|
|
638
|
+
const decisionMetadata = {
|
|
639
|
+
weightsVersion: baseDecision.weightsVersion,
|
|
640
|
+
...(baseDecision.semanticProfileVersion
|
|
641
|
+
? { semanticProfileVersion: baseDecision.semanticProfileVersion }
|
|
642
|
+
: {}),
|
|
643
|
+
...(baseDecision.semanticDocumentCount !== undefined
|
|
644
|
+
? { semanticDocumentCount: baseDecision.semanticDocumentCount }
|
|
645
|
+
: {}),
|
|
646
|
+
};
|
|
232
647
|
const forced = input.forcedGeneId ? base.find((s) => s.reasons.some((r) => r.includes('forced gene'))) : undefined;
|
|
233
648
|
if (forced?.reasons.some((r) => r.includes('forced gene selected'))) {
|
|
234
|
-
return decisionWithWarnings(input, {
|
|
649
|
+
return decisionWithWarnings(input, {
|
|
650
|
+
selectedGeneId: forced.geneId,
|
|
651
|
+
...(forced.assetId ? { selectedAssetId: forced.assetId } : {}),
|
|
652
|
+
candidates: base,
|
|
653
|
+
...decisionMetadata,
|
|
654
|
+
strategyName: 'agent-led',
|
|
655
|
+
});
|
|
235
656
|
}
|
|
236
657
|
const candidatesForPick = forced?.reasons.some((r) => r.includes('forced gene rejected')) ? base.filter((s) => !matchesScoredIdentity(s, forced)) : base;
|
|
237
658
|
const choice = pick(candidatesForPick, input);
|
|
238
659
|
const selected = candidatesForPick.find((s) => s.geneId === choice) ?? base.find((s) => s.geneId === choice);
|
|
239
|
-
return decisionWithWarnings(input, {
|
|
660
|
+
return decisionWithWarnings(input, {
|
|
661
|
+
selectedGeneId: choice,
|
|
662
|
+
...(selected?.assetId ? { selectedAssetId: selected.assetId } : {}),
|
|
663
|
+
candidates: base,
|
|
664
|
+
...decisionMetadata,
|
|
665
|
+
strategyName: 'agent-led',
|
|
666
|
+
});
|
|
240
667
|
},
|
|
241
668
|
};
|
|
242
669
|
}
|
package/dist/algo/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './geneHealth.js';
|
|
2
2
|
export * from './exploration.js';
|
|
3
|
+
export * from './ucb1.js';
|
|
3
4
|
export * from './epigenetics.js';
|
|
4
5
|
export * from './confidence.js';
|
|
5
6
|
export * from './memoryGraph.js';
|
|
@@ -18,4 +19,6 @@ export * from './solidify.js';
|
|
|
18
19
|
export * from './evolutionEvent.js';
|
|
19
20
|
export * from './cycleEngine.js';
|
|
20
21
|
export * from './strategyPresets.js';
|
|
21
|
-
export * from './cycleFailureClassifier.js';
|
|
22
|
+
export * from './cycleFailureClassifier.js';
|
|
23
|
+
export * from './kautoValidator.js';
|
|
24
|
+
export * from './kautoProjection.js';
|
package/dist/algo/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './geneHealth.js';
|
|
2
2
|
export * from './exploration.js';
|
|
3
|
+
export * from './ucb1.js';
|
|
3
4
|
export * from './epigenetics.js';
|
|
4
5
|
export * from './confidence.js';
|
|
5
6
|
export * from './memoryGraph.js';
|
|
@@ -18,4 +19,6 @@ export * from './solidify.js';
|
|
|
18
19
|
export * from './evolutionEvent.js';
|
|
19
20
|
export * from './cycleEngine.js';
|
|
20
21
|
export * from './strategyPresets.js';
|
|
21
|
-
export * from './cycleFailureClassifier.js';
|
|
22
|
+
export * from './cycleFailureClassifier.js';
|
|
23
|
+
export * from './kautoValidator.js';
|
|
24
|
+
export * from './kautoProjection.js';
|