#!/usr/bin/env node /** * W1 补丁 — 2026-04-16 audit-2026-04-16 WARNING * * 问题: simulateSignals 中 context/project/workflow 三维梯度恒为 0, * bm25/semantic 单向累积导致权重持续漂移 (当前 bm25=0.598, 距 DEFAULT 0.40 漂移 +0.198)。 * 虽有 cooldown 防 reset 抖动,但根因未修,长期仍会趋近 MAX_WEIGHT 边界。 * * 修复方案: 在两处梯度更新后追加 L2 权重衰减项 (正则化拉回 DEFAULT_WEIGHTS)。 * delta_new = LR * gradient + DECAY * (DEFAULT - current) * 当 current 远离 DEFAULT → 反向拉力加大 * 当 current 等于 DEFAULT → 无影响 * 稳态: 梯度力 = 衰减力 → spread 有界 * * 常量: WEIGHT_DECAY = 0.02 (与 LEARNING_RATE 同量级) * 数学估算: 当前 bm25=0.598 → 每批衰减 0.02*(0.598-0.4)=0.004, * 足以抵消 simulateSignals 给 bm25 的单向梯度 (~0.02 量级) * * 两处更新点: * - L283 learnFusionWeights 主路径 * - L541 applyImplicitWeights 隐式反馈路径 * * 幂等: sentinel = 'WEIGHT_DECAY' */ 'use strict'; const fs = require('fs'); const path = require('path'); const TARGET = path.join(__dirname, '..', 'fusion-weight-learner.js'); const SENTINEL = 'WEIGHT_DECAY'; function main() { if (!fs.existsSync(TARGET)) { console.error('[patch-w1] 目标文件不存在'); process.exit(1); } const before = fs.readFileSync(TARGET, 'utf8'); if (before.includes(SENTINEL)) { console.log('[patch-w1] 已打过补丁 (发现 ' + SENTINEL + '),跳过'); process.exit(0); } // ==== 注入 1: 在 LEARNING_RATE 后追加 WEIGHT_DECAY 常量 ==== const constAnchor = 'const LEARNING_RATE = 0.02;'; const constInjection = 'const LEARNING_RATE = 0.02;\n' + '// W1 修复 (patch-w1-weight-decay, 2026-04-16): L2 正则化强度\n' + '// 每批次将权重按比例拉回 DEFAULT_WEIGHTS,防止 simulateSignals 三维=0 导致的单向漂移\n' + 'const WEIGHT_DECAY = 0.02;'; if (!before.includes(constAnchor)) { console.error('[patch-w1] 找不到 LEARNING_RATE 常量锚点'); process.exit(2); } let after = before.replace(constAnchor, constInjection); // ==== 注入 2: learnFusionWeights L283 梯度更新循环 ==== const mainAnchor = ' // 梯度更新\n' + ' const newWeights = { ...weights };\n' + ' for (const key of Object.keys(newWeights)) {\n' + ' newWeights[key] += LEARNING_RATE * totalGradient[key];\n' + ' }'; const mainReplacement = ' // 梯度更新 + W1 权重衰减 (L2 正则化拉回 DEFAULT_WEIGHTS)\n' + ' const newWeights = { ...weights };\n' + ' for (const key of Object.keys(newWeights)) {\n' + ' newWeights[key] += LEARNING_RATE * totalGradient[key];\n' + ' newWeights[key] += WEIGHT_DECAY * (DEFAULT_WEIGHTS[key] - newWeights[key]);\n' + ' }'; if (!after.includes(mainAnchor)) { console.error('[patch-w1] 找不到 learnFusionWeights 梯度更新锚点'); process.exit(3); } after = after.replace(mainAnchor, mainReplacement); // ==== 注入 3: applyImplicitWeights L541 梯度更新循环 ==== const implicitAnchor = ' // 应用梯度更新(保守学习率,使用 LEARNING_RATE 的一半避免与 learnFusionWeights 叠加)\n' + ' const implicitLR = LEARNING_RATE * 0.5;\n' + ' const newWeights = { ...currentWeights };\n' + ' for (const key of Object.keys(newWeights)) {\n' + ' newWeights[key] += implicitLR * (totalGradient[key] || 0);\n' + ' }'; const implicitReplacement = ' // 应用梯度更新(保守学习率,使用 LEARNING_RATE 的一半避免与 learnFusionWeights 叠加)\n' + ' // W1: 同步使用 WEIGHT_DECAY 的一半,与 implicitLR 成比例\n' + ' const implicitLR = LEARNING_RATE * 0.5;\n' + ' const implicitDecay = WEIGHT_DECAY * 0.5;\n' + ' const newWeights = { ...currentWeights };\n' + ' for (const key of Object.keys(newWeights)) {\n' + ' newWeights[key] += implicitLR * (totalGradient[key] || 0);\n' + ' newWeights[key] += implicitDecay * (DEFAULT_WEIGHTS[key] - newWeights[key]);\n' + ' }'; if (!after.includes(implicitAnchor)) { console.error('[patch-w1] 找不到 applyImplicitWeights 梯度更新锚点'); process.exit(4); } after = after.replace(implicitAnchor, implicitReplacement); // ==== 校验 ==== if (after === before) { console.error('[patch-w1] 替换无效果'); process.exit(5); } const decayCount = (after.match(/WEIGHT_DECAY/g) || []).length; if (decayCount < 4) { console.error('[patch-w1] WEIGHT_DECAY 引用数异常:', decayCount, '(期望 >= 4)'); process.exit(6); } // ==== 备份并原子写回 ==== const backup = TARGET + '.bak.w1.' + Date.now(); fs.writeFileSync(backup, before); const _tmp = TARGET + '.tmp.' + process.pid; fs.writeFileSync(_tmp, after); fs.renameSync(_tmp, TARGET); console.log('[patch-w1] ✓ 补丁应用成功'); console.log('[patch-w1] 备份: ' + path.basename(backup)); console.log('[patch-w1] 变更: 新增 WEIGHT_DECAY=0.02 + 两处梯度循环加衰减项'); console.log('[patch-w1] WEIGHT_DECAY 引用数: ' + decayCount); } try { main(); } catch (e) { console.error('[patch-w1] 异常:', e.message); process.exit(99); }