Refactor L0 quality and diffusion graph gating for balanced recall

This commit is contained in:
2026-02-14 17:12:03 +08:00
parent 15640d48f2
commit e8383570e0
3 changed files with 169 additions and 46 deletions

View File

@@ -118,15 +118,22 @@ export function createMetrics() {
seedCount: 0,
graphNodes: 0,
graphEdges: 0,
candidatePairs: 0,
pairsFromWhat: 0,
pairsFromHow: 0,
edgeDensity: 0,
reweightWhoUsed: 0,
reweightWhereUsed: 0,
iterations: 0,
convergenceError: 0,
pprActivated: 0,
cosineGatePassed: 0,
cosineGateFiltered: 0,
cosineGateNoVector: 0,
postGatePassRate: 0,
finalCount: 0,
scoreDistribution: { min: 0, max: 0, mean: 0 },
byChannel: { what: 0, where: 0, how: 0 },
byChannel: { what: 0, where: 0, how: 0, who: 0 },
time: 0,
},
@@ -169,6 +176,7 @@ export function createMetrics() {
eventPrecisionProxy: 0,
l1AttachRate: 0,
rerankRetentionRate: 0,
diffusionEffectiveRate: 0,
potentialIssues: [],
},
};
@@ -368,9 +376,12 @@ export function formatMetricsLog(metrics) {
lines.push('[Diffusion] PPR Spreading Activation');
lines.push(`├─ seeds: ${m.diffusion.seedCount}`);
lines.push(`├─ graph: ${m.diffusion.graphNodes} nodes, ${m.diffusion.graphEdges} edges`);
lines.push(`├─ candidate_pairs: ${m.diffusion.candidatePairs || 0} (what=${m.diffusion.pairsFromWhat || 0}, how=${m.diffusion.pairsFromHow || 0})`);
lines.push(`├─ edge_density: ${m.diffusion.edgeDensity || 0}%`);
if (m.diffusion.graphEdges > 0) {
const ch = m.diffusion.byChannel || {};
lines.push(`─ by_channel: what=${ch.what || 0}, where=${ch.where || 0}, how=${ch.how || 0}`);
lines.push(`─ by_channel: what=${ch.what || 0}, how=${ch.how || 0}, who=${ch.who || 0}, where=${ch.where || 0}`);
lines.push(`│ └─ reweight_used: who=${m.diffusion.reweightWhoUsed || 0}, where=${m.diffusion.reweightWhereUsed || 0}`);
}
if (m.diffusion.iterations > 0) {
lines.push(`├─ ppr: ${m.diffusion.iterations} iterations, ε=${Number(m.diffusion.convergenceError).toExponential(1)}`);
@@ -378,8 +389,10 @@ export function formatMetricsLog(metrics) {
lines.push(`├─ activated (excl seeds): ${m.diffusion.pprActivated}`);
if (m.diffusion.pprActivated > 0) {
lines.push(`├─ cosine_gate: ${m.diffusion.cosineGatePassed} passed, ${m.diffusion.cosineGateFiltered} filtered`);
const passPrefix = m.diffusion.cosineGateNoVector > 0 ? '│ ├─' : '│ └─';
lines.push(`${passPrefix} pass_rate: ${m.diffusion.postGatePassRate || 0}%`);
if (m.diffusion.cosineGateNoVector > 0) {
lines.push(`─ no_vector: ${m.diffusion.cosineGateNoVector}`);
lines.push(`─ no_vector: ${m.diffusion.cosineGateNoVector}`);
}
}
lines.push(`├─ final_injected: ${m.diffusion.finalCount}`);
@@ -435,6 +448,7 @@ export function formatMetricsLog(metrics) {
lines.push(`├─ event_precision_proxy: ${m.quality.eventPrecisionProxy}`);
lines.push(`├─ l1_attach_rate: ${m.quality.l1AttachRate}%`);
lines.push(`├─ rerank_retention_rate: ${m.quality.rerankRetentionRate}%`);
lines.push(`├─ diffusion_effective_rate: ${m.quality.diffusionEffectiveRate}%`);
if (m.quality.potentialIssues && m.quality.potentialIssues.length > 0) {
lines.push(`└─ potential_issues:`);
@@ -642,6 +656,10 @@ export function detectIssues(metrics) {
issues.push('All PPR-activated nodes failed cosine gate - graph structure diverged from query semantics');
}
m.quality.diffusionEffectiveRate = m.diffusion.pprActivated > 0
? Math.round((m.diffusion.finalCount / m.diffusion.pprActivated) * 100)
: 0;
if (m.diffusion.cosineGateNoVector > 5) {
issues.push(`${m.diffusion.cosineGateNoVector} PPR nodes missing vectors - L0 vectorization may be incomplete`);
}
@@ -650,5 +668,9 @@ export function detectIssues(metrics) {
issues.push(`Slow diffusion (${m.diffusion.time}ms) - graph may be too dense`);
}
if (m.diffusion.pprActivated > 0 && (m.diffusion.postGatePassRate < 20 || m.diffusion.postGatePassRate > 60)) {
issues.push(`Diffusion post-gate pass rate out of target (${m.diffusion.postGatePassRate}%)`);
}
return issues;
}