Optimize lexical timing diagnostics and suppress dense WHERE diffusion edges

This commit is contained in:
2026-02-14 15:13:00 +08:00
parent 39aa7431c6
commit 15640d48f2
4 changed files with 40 additions and 7 deletions

View File

@@ -53,6 +53,9 @@ const CONFIG = {
where: 0.15, // location exact match — binary
how: 0.30, // action-term co-occurrence — Jaccard
},
WHERE_MAX_GROUP_SIZE: 16, // skip location-only pair expansion for over-common places
WHERE_FREQ_DAMP_PIVOT: 6, // location freq <= pivot keeps full WHERE score
WHERE_FREQ_DAMP_MIN: 0.20, // lower bound for damped WHERE contribution
// Post-verification (Cosine Gate)
COSINE_GATE: 0.45, // min cosine(queryVector, stateVector)
@@ -275,11 +278,24 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
const features = extractAllFeatures(allAtoms, excludeEntities);
const { entityIndex, locationIndex } = buildInvertedIndices(features);
const locationFreq = new Map();
for (const [loc, indices] of locationIndex.entries()) {
locationFreq.set(loc, indices.length);
}
// Candidate pairs: share ≥1 entity or same location
const pairSet = new Set();
collectPairsFromIndex(entityIndex, pairSet, N);
collectPairsFromIndex(locationIndex, pairSet, N);
let skippedLocationGroups = 0;
for (const [loc, indices] of locationIndex.entries()) {
if (!loc) continue;
if (indices.length > CONFIG.WHERE_MAX_GROUP_SIZE) {
skippedLocationGroups++;
continue;
}
const oneLocMap = new Map([[loc, indices]]);
collectPairsFromIndex(oneLocMap, pairSet, N);
}
// Compute three-channel edge weights for all candidates
const neighbors = Array.from({ length: N }, () => []);
@@ -294,7 +310,15 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
const fj = features[j];
const wWhat = overlapCoefficient(fi.interactionPairs, fj.interactionPairs);
const wWhere = (fi.location && fi.location === fj.location) ? 1.0 : 0.0;
let wWhere = 0.0;
if (fi.location && fi.location === fj.location) {
const freq = locationFreq.get(fi.location) || 1;
const damp = Math.max(
CONFIG.WHERE_FREQ_DAMP_MIN,
Math.min(1, CONFIG.WHERE_FREQ_DAMP_PIVOT / Math.max(1, freq))
);
wWhere = damp;
}
const wHow = jaccard(fi.actionTerms, fj.actionTerms);
const weight =
@@ -318,6 +342,7 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
xbLog.info(MODULE_ID,
`Graph: ${N} nodes, ${edgeCount} edges ` +
`(what=${channelStats.what} where=${channelStats.where} how=${channelStats.how}) ` +
`(whereSkippedGroups=${skippedLocationGroups}) ` +
`(${buildTime}ms)`
);

View File

@@ -49,6 +49,7 @@ export function createMetrics() {
chunkHits: 0,
eventHits: 0,
searchTime: 0,
indexReadyTime: 0,
eventFilteredByDense: 0,
floorFilteredByDense: 0,
},
@@ -255,6 +256,9 @@ export function formatMetricsLog(metrics) {
lines.push(`├─ chunk_hits: ${m.lexical.chunkHits}`);
lines.push(`├─ event_hits: ${m.lexical.eventHits}`);
lines.push(`├─ search_time: ${m.lexical.searchTime}ms`);
if (m.lexical.indexReadyTime > 0) {
lines.push(`├─ index_ready_time: ${m.lexical.indexReadyTime}ms`);
}
if (m.lexical.eventFilteredByDense > 0) {
lines.push(`├─ event_filtered_by_dense: ${m.lexical.eventFilteredByDense}`);
}
@@ -411,7 +415,8 @@ export function formatMetricsLog(metrics) {
lines.push(`├─ query_build: ${m.query.buildTime}ms`);
lines.push(`├─ query_refine: ${m.query.refineTime}ms`);
lines.push(`├─ anchor_search: ${m.timing.anchorSearch}ms`);
lines.push(`├─ lexical_search: ${m.lexical.searchTime}ms`);
const lexicalTotal = (m.lexical.searchTime || 0) + (m.lexical.indexReadyTime || 0);
lines.push(`├─ lexical_search: ${lexicalTotal}ms (query=${m.lexical.searchTime || 0}ms, index_ready=${m.lexical.indexReadyTime || 0}ms)`);
lines.push(`├─ fusion: ${m.fusion.time}ms`);
lines.push(`├─ constraint_filter: ${m.timing.constraintFilter}ms`);
lines.push(`├─ event_retrieval: ${m.timing.eventRetrieval}ms`);

View File

@@ -1091,8 +1091,11 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
eventIds: [], chunkScores: [], searchTime: 0,
};
let indexReadyTime = 0;
try {
const T_Index_Ready = performance.now();
const index = await getLexicalIndex();
indexReadyTime = Math.round(performance.now() - T_Index_Ready);
if (index) {
lexicalResult = searchLexicalIndex(index, bundle.lexicalTerms);
}
@@ -1106,7 +1109,8 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
metrics.lexical.atomHits = lexicalResult.atomIds.length;
metrics.lexical.chunkHits = lexicalResult.chunkIds.length;
metrics.lexical.eventHits = lexicalResult.eventIds.length;
metrics.lexical.searchTime = lexTime;
metrics.lexical.searchTime = lexicalResult.searchTime || 0;
metrics.lexical.indexReadyTime = indexReadyTime;
metrics.lexical.terms = bundle.lexicalTerms.slice(0, 10);
}
@@ -1162,7 +1166,7 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
}
xbLog.info(MODULE_ID,
`Lexical: chunks=${lexicalResult.chunkIds.length} events=${lexicalResult.eventIds.length} mergedEvents=+${lexicalEventCount} filteredByDense=${lexicalEventFilteredByDense} floorFiltered=${metrics.lexical.floorFilteredByDense || 0} (${lexTime}ms)`
`Lexical: chunks=${lexicalResult.chunkIds.length} events=${lexicalResult.eventIds.length} mergedEvents=+${lexicalEventCount} filteredByDense=${lexicalEventFilteredByDense} floorFiltered=${metrics.lexical.floorFilteredByDense || 0} (indexReady=${indexReadyTime}ms search=${lexicalResult.searchTime || 0}ms total=${lexTime}ms)`
);
// ═══════════════════════════════════════════════════════════════════
@@ -1292,7 +1296,7 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
console.log(`R1 weights: [${r1Weights.map(w => w.toFixed(2)).join(', ')}]`);
console.log(`Focus: [${bundle.focusEntities.join(', ')}]`);
console.log(`Round 2 Anchors: ${anchorHits.length} hits → ${anchorFloors_dense.size} floors`);
console.log(`Lexical: chunks=${lexicalResult.chunkIds.length} events=${lexicalResult.eventIds.length} evtMerged=+${lexicalEventCount} evtFiltered=${lexicalEventFilteredByDense} floorFiltered=${metrics.lexical.floorFilteredByDense || 0}`);
console.log(`Lexical: chunks=${lexicalResult.chunkIds.length} events=${lexicalResult.eventIds.length} evtMerged=+${lexicalEventCount} evtFiltered=${lexicalEventFilteredByDense} floorFiltered=${metrics.lexical.floorFilteredByDense || 0} (idx=${indexReadyTime}ms search=${lexicalResult.searchTime || 0}ms total=${lexTime}ms)`);
console.log(`Fusion (floor, weighted): dense=${metrics.fusion.denseFloors} lex=${metrics.fusion.lexFloors} → cap=${metrics.fusion.afterCap} (${metrics.fusion.time}ms)`);
console.log(`Floor Rerank: ${metrics.evidence.beforeRerank || 0}${metrics.evidence.floorsSelected || 0} floors → L0=${metrics.evidence.l0Collected || 0} (${metrics.evidence.rerankTime || 0}ms)`);
console.log(`L1: ${metrics.evidence.l1Pulled || 0} pulled → ${metrics.evidence.l1Attached || 0} attached (${metrics.evidence.l1CosineTime || 0}ms)`);