Optimize lexical timing diagnostics and suppress dense WHERE diffusion edges
This commit is contained in:
@@ -1379,7 +1379,6 @@ async function handleChatChanged() {
|
|||||||
const newLength = Array.isArray(chat) ? chat.length : 0;
|
const newLength = Array.isArray(chat) ? chat.length : 0;
|
||||||
|
|
||||||
await rollbackSummaryIfNeeded();
|
await rollbackSummaryIfNeeded();
|
||||||
invalidateLexicalIndex();
|
|
||||||
initButtonsForAll();
|
initButtonsForAll();
|
||||||
|
|
||||||
const store = getSummaryStore();
|
const store = getSummaryStore();
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ const CONFIG = {
|
|||||||
where: 0.15, // location exact match — binary
|
where: 0.15, // location exact match — binary
|
||||||
how: 0.30, // action-term co-occurrence — Jaccard
|
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)
|
// Post-verification (Cosine Gate)
|
||||||
COSINE_GATE: 0.45, // min cosine(queryVector, stateVector)
|
COSINE_GATE: 0.45, // min cosine(queryVector, stateVector)
|
||||||
@@ -275,11 +278,24 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
|
|||||||
|
|
||||||
const features = extractAllFeatures(allAtoms, excludeEntities);
|
const features = extractAllFeatures(allAtoms, excludeEntities);
|
||||||
const { entityIndex, locationIndex } = buildInvertedIndices(features);
|
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
|
// Candidate pairs: share ≥1 entity or same location
|
||||||
const pairSet = new Set();
|
const pairSet = new Set();
|
||||||
collectPairsFromIndex(entityIndex, pairSet, N);
|
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
|
// Compute three-channel edge weights for all candidates
|
||||||
const neighbors = Array.from({ length: N }, () => []);
|
const neighbors = Array.from({ length: N }, () => []);
|
||||||
@@ -294,7 +310,15 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
|
|||||||
const fj = features[j];
|
const fj = features[j];
|
||||||
|
|
||||||
const wWhat = overlapCoefficient(fi.interactionPairs, fj.interactionPairs);
|
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 wHow = jaccard(fi.actionTerms, fj.actionTerms);
|
||||||
|
|
||||||
const weight =
|
const weight =
|
||||||
@@ -318,6 +342,7 @@ function buildGraph(allAtoms, excludeEntities = new Set()) {
|
|||||||
xbLog.info(MODULE_ID,
|
xbLog.info(MODULE_ID,
|
||||||
`Graph: ${N} nodes, ${edgeCount} edges ` +
|
`Graph: ${N} nodes, ${edgeCount} edges ` +
|
||||||
`(what=${channelStats.what} where=${channelStats.where} how=${channelStats.how}) ` +
|
`(what=${channelStats.what} where=${channelStats.where} how=${channelStats.how}) ` +
|
||||||
|
`(whereSkippedGroups=${skippedLocationGroups}) ` +
|
||||||
`(${buildTime}ms)`
|
`(${buildTime}ms)`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export function createMetrics() {
|
|||||||
chunkHits: 0,
|
chunkHits: 0,
|
||||||
eventHits: 0,
|
eventHits: 0,
|
||||||
searchTime: 0,
|
searchTime: 0,
|
||||||
|
indexReadyTime: 0,
|
||||||
eventFilteredByDense: 0,
|
eventFilteredByDense: 0,
|
||||||
floorFilteredByDense: 0,
|
floorFilteredByDense: 0,
|
||||||
},
|
},
|
||||||
@@ -255,6 +256,9 @@ export function formatMetricsLog(metrics) {
|
|||||||
lines.push(`├─ chunk_hits: ${m.lexical.chunkHits}`);
|
lines.push(`├─ chunk_hits: ${m.lexical.chunkHits}`);
|
||||||
lines.push(`├─ event_hits: ${m.lexical.eventHits}`);
|
lines.push(`├─ event_hits: ${m.lexical.eventHits}`);
|
||||||
lines.push(`├─ search_time: ${m.lexical.searchTime}ms`);
|
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) {
|
if (m.lexical.eventFilteredByDense > 0) {
|
||||||
lines.push(`├─ event_filtered_by_dense: ${m.lexical.eventFilteredByDense}`);
|
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_build: ${m.query.buildTime}ms`);
|
||||||
lines.push(`├─ query_refine: ${m.query.refineTime}ms`);
|
lines.push(`├─ query_refine: ${m.query.refineTime}ms`);
|
||||||
lines.push(`├─ anchor_search: ${m.timing.anchorSearch}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(`├─ fusion: ${m.fusion.time}ms`);
|
||||||
lines.push(`├─ constraint_filter: ${m.timing.constraintFilter}ms`);
|
lines.push(`├─ constraint_filter: ${m.timing.constraintFilter}ms`);
|
||||||
lines.push(`├─ event_retrieval: ${m.timing.eventRetrieval}ms`);
|
lines.push(`├─ event_retrieval: ${m.timing.eventRetrieval}ms`);
|
||||||
|
|||||||
@@ -1091,8 +1091,11 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
|
|||||||
eventIds: [], chunkScores: [], searchTime: 0,
|
eventIds: [], chunkScores: [], searchTime: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let indexReadyTime = 0;
|
||||||
try {
|
try {
|
||||||
|
const T_Index_Ready = performance.now();
|
||||||
const index = await getLexicalIndex();
|
const index = await getLexicalIndex();
|
||||||
|
indexReadyTime = Math.round(performance.now() - T_Index_Ready);
|
||||||
if (index) {
|
if (index) {
|
||||||
lexicalResult = searchLexicalIndex(index, bundle.lexicalTerms);
|
lexicalResult = searchLexicalIndex(index, bundle.lexicalTerms);
|
||||||
}
|
}
|
||||||
@@ -1106,7 +1109,8 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
|
|||||||
metrics.lexical.atomHits = lexicalResult.atomIds.length;
|
metrics.lexical.atomHits = lexicalResult.atomIds.length;
|
||||||
metrics.lexical.chunkHits = lexicalResult.chunkIds.length;
|
metrics.lexical.chunkHits = lexicalResult.chunkIds.length;
|
||||||
metrics.lexical.eventHits = lexicalResult.eventIds.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);
|
metrics.lexical.terms = bundle.lexicalTerms.slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1162,7 +1166,7 @@ export async function recallMemory(allEvents, vectorConfig, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xbLog.info(MODULE_ID,
|
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(`R1 weights: [${r1Weights.map(w => w.toFixed(2)).join(', ')}]`);
|
||||||
console.log(`Focus: [${bundle.focusEntities.join(', ')}]`);
|
console.log(`Focus: [${bundle.focusEntities.join(', ')}]`);
|
||||||
console.log(`Round 2 Anchors: ${anchorHits.length} hits → ${anchorFloors_dense.size} floors`);
|
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(`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(`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)`);
|
console.log(`L1: ${metrics.evidence.l1Pulled || 0} pulled → ${metrics.evidence.l1Attached || 0} attached (${metrics.evidence.l1CosineTime || 0}ms)`);
|
||||||
|
|||||||
Reference in New Issue
Block a user