mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-14 02:40:45 +08:00
Refine automatic consolidation and compression triggers
This commit is contained in:
147
consolidator.js
147
consolidator.js
@@ -111,6 +111,153 @@ const CONSOLIDATION_SYSTEM_PROMPT = `你是一个记忆整合分析器。当新
|
||||
- 不要对无关记忆强行建立联系
|
||||
- neighbor_updates 中每条必须有实际意义的修改`;
|
||||
|
||||
function normalizeLatestOnlyIdentityValue(value) {
|
||||
return String(value ?? "")
|
||||
.trim()
|
||||
.replace(/\s+/g, " ")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
export async function analyzeAutoConsolidationGate({
|
||||
graph,
|
||||
newNodeIds,
|
||||
embeddingConfig,
|
||||
schema = [],
|
||||
conflictThreshold = 0.85,
|
||||
signal,
|
||||
} = {}) {
|
||||
const normalizedThreshold = Number.isFinite(Number(conflictThreshold))
|
||||
? Math.max(0, Math.min(1, Number(conflictThreshold)))
|
||||
: 0.85;
|
||||
const safeNewNodeIds = Array.isArray(newNodeIds) ? newNodeIds : [];
|
||||
|
||||
if (!graph || safeNewNodeIds.length === 0) {
|
||||
return {
|
||||
triggered: false,
|
||||
reason: "本批新增少且无明显重复风险,跳过自动整合",
|
||||
matchedScore: null,
|
||||
matchedNodeId: "",
|
||||
detection: "none",
|
||||
};
|
||||
}
|
||||
|
||||
const schemaByType = new Map(
|
||||
(Array.isArray(schema) ? schema : [])
|
||||
.filter((typeDef) => typeDef?.id)
|
||||
.map((typeDef) => [String(typeDef.id), typeDef]),
|
||||
);
|
||||
const activeNodes = getActiveNodes(graph).filter((node) => !node?.archived);
|
||||
const vectorConfigValid = validateVectorConfig(embeddingConfig).valid;
|
||||
let bestVectorMatch = null;
|
||||
|
||||
for (const newNodeId of safeNewNodeIds) {
|
||||
throwIfAborted(signal);
|
||||
const node = getNode(graph, newNodeId);
|
||||
if (!node || node.archived) continue;
|
||||
|
||||
const typeDef = schemaByType.get(String(node.type || ""));
|
||||
const scopedCandidates = activeNodes.filter(
|
||||
(candidate) =>
|
||||
candidate?.id !== node.id && canMergeScopedMemories(node, candidate),
|
||||
);
|
||||
|
||||
if (typeDef?.latestOnly) {
|
||||
for (const field of ["name", "title"]) {
|
||||
const normalizedIdentity = normalizeLatestOnlyIdentityValue(
|
||||
node?.fields?.[field],
|
||||
);
|
||||
if (!normalizedIdentity) continue;
|
||||
const matchedNode = scopedCandidates.find(
|
||||
(candidate) =>
|
||||
candidate?.type === node.type &&
|
||||
normalizeLatestOnlyIdentityValue(candidate?.fields?.[field]) ===
|
||||
normalizedIdentity,
|
||||
);
|
||||
if (matchedNode) {
|
||||
return {
|
||||
triggered: true,
|
||||
reason: `本批仅新增 ${safeNewNodeIds.length} 个节点,但 latestOnly 的 ${field} 与旧记忆完全一致,已触发自动整合`,
|
||||
matchedScore: 1,
|
||||
matchedNodeId: matchedNode.id,
|
||||
detection: `latestOnly:${field}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!vectorConfigValid) continue;
|
||||
const text = buildNodeVectorText(node);
|
||||
if (!text) continue;
|
||||
|
||||
try {
|
||||
const neighbors = await findSimilarNodesByText(
|
||||
graph,
|
||||
text,
|
||||
embeddingConfig,
|
||||
1,
|
||||
scopedCandidates,
|
||||
signal,
|
||||
);
|
||||
const topNeighbor = Array.isArray(neighbors) ? neighbors[0] : null;
|
||||
if (!topNeighbor?.nodeId) continue;
|
||||
|
||||
if (
|
||||
!bestVectorMatch ||
|
||||
Number(topNeighbor.score || 0) > Number(bestVectorMatch.score || 0)
|
||||
) {
|
||||
bestVectorMatch = {
|
||||
score: Number(topNeighbor.score || 0),
|
||||
nodeId: topNeighbor.nodeId,
|
||||
};
|
||||
}
|
||||
|
||||
if (Number(topNeighbor.score || 0) >= normalizedThreshold) {
|
||||
return {
|
||||
triggered: true,
|
||||
reason: `本批仅新增 ${safeNewNodeIds.length} 个节点,但与旧记忆高度相似(${Number(topNeighbor.score || 0).toFixed(3)} >= ${normalizedThreshold.toFixed(2)}),已触发自动整合`,
|
||||
matchedScore: Number(topNeighbor.score || 0),
|
||||
matchedNodeId: topNeighbor.nodeId,
|
||||
detection: "vector",
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
if (isAbortError(error)) throw error;
|
||||
console.warn(
|
||||
`[ST-BME] 自动整合门禁近邻查询失败 (${newNodeId}):`,
|
||||
error?.message || error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (bestVectorMatch) {
|
||||
return {
|
||||
triggered: false,
|
||||
reason: `本批新增少且最高相似度 ${bestVectorMatch.score.toFixed(3)} 未达到阈值 ${normalizedThreshold.toFixed(2)},跳过自动整合`,
|
||||
matchedScore: bestVectorMatch.score,
|
||||
matchedNodeId: bestVectorMatch.nodeId,
|
||||
detection: "vector",
|
||||
};
|
||||
}
|
||||
|
||||
if (!vectorConfigValid) {
|
||||
return {
|
||||
triggered: false,
|
||||
reason: "本批新增少且当前向量不可用,未检测到明确重复风险,跳过自动整合",
|
||||
matchedScore: null,
|
||||
matchedNodeId: "",
|
||||
detection: "vector-unavailable",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
triggered: false,
|
||||
reason: "本批新增少且无明显重复风险,跳过自动整合",
|
||||
matchedScore: null,
|
||||
matchedNodeId: "",
|
||||
detection: "none",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一记忆整合主函数(批量化版)
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user