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;
|
||||
|
||||
await rollbackSummaryIfNeeded();
|
||||
invalidateLexicalIndex();
|
||||
initButtonsForAll();
|
||||
|
||||
const store = getSummaryStore();
|
||||
|
||||
@@ -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)`
|
||||
);
|
||||
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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)`);
|
||||
|
||||
Reference in New Issue
Block a user