feat(story-summary): globalize hide settings and stabilize recent L0 recall
This commit is contained in:
@@ -210,11 +210,13 @@ Before generating, observe the USER and analyze carefully:
|
||||
## CRITICAL NOTES
|
||||
- events.id 从 evt-{nextEventId} 开始编号
|
||||
- 仅输出【增量】内容,已有事件绝不重复
|
||||
- /地点、通过什么方式、对谁、做了什么事、结果如何。如果原文有具体道具(如一把枪、一封信),必须在总结中提及。
|
||||
- keywords 是全局关键词,综合已有+新增
|
||||
- causedBy 仅在因果明确时填写,允许为[],0-2个
|
||||
- factUpdates 可为空数组
|
||||
- 合法JSON,字符串值内部避免英文双引号
|
||||
- 用朴实、白描、有烟火气的笔触记录,避免比喻和意象
|
||||
- 用朴实、白描、有烟火气的笔触记录事实,避免比喻和意象
|
||||
- 严谨、注重细节,避免使用模糊的概括性语言,应用具体的动词描述动作,例:谁,在什么时间/地点,通过什么方式,对谁,做了什么事,出现了什么道具,结果如何。
|
||||
</meta_protocol>`,
|
||||
|
||||
assistantCheck: `Content review initiated...
|
||||
|
||||
@@ -18,6 +18,7 @@ import { getSummaryStore, getFacts, isRelationFact } from "../data/store.js";
|
||||
import { getVectorConfig, getSummaryPanelConfig, getSettings } from "../data/config.js";
|
||||
import { recallMemory } from "../vector/retrieval/recall.js";
|
||||
import { getMeta } from "../vector/storage/chunk-store.js";
|
||||
import { getStateAtoms } from "../vector/storage/state-store.js";
|
||||
import { getEngineFingerprint } from "../vector/utils/embedder.js";
|
||||
import { buildTrustedCharacters } from "../vector/retrieval/entity-lexicon.js";
|
||||
|
||||
@@ -540,6 +541,34 @@ function groupL0ByFloor(l0List) {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available L0 atoms in recent window and normalize to evidence shape.
|
||||
* @param {number} recentStart
|
||||
* @param {number} recentEnd
|
||||
* @returns {object[]}
|
||||
*/
|
||||
function getRecentWindowL0Atoms(recentStart, recentEnd) {
|
||||
if (!Number.isFinite(recentStart) || !Number.isFinite(recentEnd) || recentEnd < recentStart) return [];
|
||||
const atoms = getStateAtoms() || [];
|
||||
const out = [];
|
||||
for (const atom of atoms) {
|
||||
const floor = atom?.floor;
|
||||
const atomId = atom?.atomId;
|
||||
const semantic = String(atom?.semantic || '').trim();
|
||||
if (!Number.isFinite(floor)) continue;
|
||||
if (floor < recentStart || floor > recentEnd) continue;
|
||||
if (!atomId || !semantic) continue;
|
||||
out.push({
|
||||
id: atomId,
|
||||
floor,
|
||||
atom,
|
||||
similarity: 0,
|
||||
rerankScore: 0,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// EvidenceGroup(per-floor:N个L0 + 共享一对L1)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -585,6 +614,21 @@ function buildEvidenceGroup(floor, l0AtomsForFloor, l1ByFloor) {
|
||||
return { floor, l0Atoms: l0AtomsForFloor, userL1, aiL1, totalTokens };
|
||||
}
|
||||
|
||||
/**
|
||||
* Build recent-evidence group (L0 only, no L1 attachment).
|
||||
* @param {number} floor
|
||||
* @param {object[]} l0AtomsForFloor
|
||||
* @returns {object}
|
||||
*/
|
||||
function buildRecentEvidenceGroup(floor, l0AtomsForFloor) {
|
||||
let totalTokens = 0;
|
||||
for (const l0 of l0AtomsForFloor) {
|
||||
totalTokens += estimateTokens(buildL0DisplayText(l0));
|
||||
}
|
||||
totalTokens += 10;
|
||||
return { floor, l0Atoms: l0AtomsForFloor, userL1: null, aiL1: null, totalTokens };
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化一个证据组为文本行数组
|
||||
*
|
||||
@@ -1114,7 +1158,11 @@ async function buildVectorPrompt(store, recallResult, causalById, focusCharacter
|
||||
|
||||
const lastSummarized = store.lastSummarizedMesId ?? -1;
|
||||
const lastChunkFloor = meta?.lastChunkFloor ?? -1;
|
||||
const keepVisible = store.keepVisibleCount ?? 3;
|
||||
const uiCfg = getSummaryPanelConfig()?.ui || {};
|
||||
const parsedKeepVisible = Number.parseInt(uiCfg.keepVisibleCount, 10);
|
||||
const keepVisible = Number.isFinite(parsedKeepVisible)
|
||||
? Math.max(0, Math.min(50, parsedKeepVisible))
|
||||
: 6;
|
||||
|
||||
// 收集未被事件消费的 L0,按 rerankScore 降序
|
||||
const focusSetForEvidence = new Set((focusCharacters || []).map(normalize).filter(Boolean));
|
||||
@@ -1171,22 +1219,22 @@ async function buildVectorPrompt(store, recallResult, causalById, focusCharacter
|
||||
const recentEnd = lastChunkFloor - keepVisible;
|
||||
|
||||
if (recentEnd >= recentStart) {
|
||||
const recentL0 = remainingL0
|
||||
const recentAllL0 = getRecentWindowL0Atoms(recentStart, recentEnd);
|
||||
const recentL0 = recentAllL0
|
||||
.filter(l0 => !usedL0Ids.has(l0.id))
|
||||
.filter(l0 => l0.floor >= recentStart && l0.floor <= recentEnd);
|
||||
|
||||
if (recentL0.length) {
|
||||
const recentBudget = { used: 0, max: UNSUMMARIZED_EVIDENCE_MAX };
|
||||
|
||||
// 先按分数挑组(高分优先),再按时间输出(楼层升序)
|
||||
// Pick newest floors first, then output in chronological order.
|
||||
const recentFloorMap = groupL0ByFloor(recentL0);
|
||||
const recentRanked = [];
|
||||
for (const [floor, l0s] of recentFloorMap) {
|
||||
const group = buildEvidenceGroup(floor, l0s, l1ByFloor);
|
||||
const bestScore = Math.max(...l0s.map(l0 => (l0.rerankScore ?? l0.similarity ?? 0)));
|
||||
recentRanked.push({ group, bestScore });
|
||||
const group = buildRecentEvidenceGroup(floor, l0s);
|
||||
recentRanked.push({ group });
|
||||
}
|
||||
recentRanked.sort((a, b) => (b.bestScore - a.bestScore) || (a.group.floor - b.group.floor));
|
||||
recentRanked.sort((a, b) => b.group.floor - a.group.floor);
|
||||
|
||||
const acceptedRecentGroups = [];
|
||||
for (const item of recentRanked) {
|
||||
@@ -1277,6 +1325,8 @@ async function buildVectorPrompt(store, recallResult, causalById, focusCharacter
|
||||
};
|
||||
|
||||
metrics.evidence.tokens = injectionStats.distantEvidence.tokens + injectionStats.recentEvidence.tokens;
|
||||
metrics.evidence.recentSource = 'all_l0_window';
|
||||
metrics.evidence.recentL1Attached = 0;
|
||||
metrics.evidence.assemblyTime = Math.round(
|
||||
performance.now() - T_Start - (metrics.timing.constraintFilter || 0) - metrics.formatting.time
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user