mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
refactor: 合并精确对照+记忆进化为统一记忆整合模块 (consolidator.js)
This commit is contained in:
341
consolidator.js
Normal file
341
consolidator.js
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
// ST-BME: 统一记忆整合引擎
|
||||||
|
// 合并 Mem0 精确对照 + A-MEM 记忆进化为单一阶段
|
||||||
|
// 每个新节点只需 1 次 embed + 1 次 LLM 调用
|
||||||
|
|
||||||
|
import { addEdge, createEdge, getActiveNodes, getNode } from './graph.js';
|
||||||
|
import { callLLMForJSON } from './llm.js';
|
||||||
|
import {
|
||||||
|
buildNodeVectorText,
|
||||||
|
findSimilarNodesByText,
|
||||||
|
validateVectorConfig,
|
||||||
|
} from './vector-index.js';
|
||||||
|
|
||||||
|
function createAbortError(message = '操作已终止') {
|
||||||
|
const error = new Error(message);
|
||||||
|
error.name = 'AbortError';
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAbortError(error) {
|
||||||
|
return error?.name === 'AbortError';
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwIfAborted(signal) {
|
||||||
|
if (signal?.aborted) {
|
||||||
|
throw signal.reason instanceof Error ? signal.reason : createAbortError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一记忆整合系统提示词
|
||||||
|
* 同时完成 Mem0 冲突判定 + A-MEM 进化分析
|
||||||
|
*/
|
||||||
|
const CONSOLIDATION_SYSTEM_PROMPT = `你是一个记忆整合分析器。当新记忆加入知识图谱时,你需要同时完成两项任务:
|
||||||
|
|
||||||
|
**任务一:冲突检测**
|
||||||
|
判断新记忆与最近邻的已有记忆是否冲突或重复:
|
||||||
|
- skip: 新记忆与已有记忆完全重复,应丢弃
|
||||||
|
- merge: 新记忆是对旧记忆的修正或补充,应合并
|
||||||
|
- keep: 新记忆是全新信息,应保留
|
||||||
|
|
||||||
|
**任务二:进化分析**(仅当 action=keep 时需要)
|
||||||
|
分析新记忆是否揭示了关于旧记忆的新信息:
|
||||||
|
- 建立有意义的关联连接
|
||||||
|
- 反向更新旧记忆的描述或分类
|
||||||
|
|
||||||
|
输出严格 JSON:
|
||||||
|
{
|
||||||
|
"action": "keep" | "merge" | "skip",
|
||||||
|
"merge_target_id": "仅 action=merge 时必填:要合并到的旧节点 ID",
|
||||||
|
"merged_fields": { "仅 action=merge 时可选:合并后的字段更新" },
|
||||||
|
"reason": "判定理由(简述)",
|
||||||
|
"evolution": {
|
||||||
|
"should_evolve": true/false,
|
||||||
|
"connections": ["需要建立链接的旧记忆 ID 列表"],
|
||||||
|
"neighbor_updates": [
|
||||||
|
{
|
||||||
|
"nodeId": "需更新的旧节点 ID",
|
||||||
|
"newContext": "基于新信息修正后的描述(不需修改则为 null)",
|
||||||
|
"newTags": ["更新后的分类标签,不需修改则为 null"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
整合规则:
|
||||||
|
- 当 action=skip 时,evolution 可省略或设 should_evolve=false
|
||||||
|
- 当 action=merge 时,evolution 可省略或设 should_evolve=false
|
||||||
|
- 仅当 action=keep 且新信息确实改变了对旧记忆的理解时,才设 should_evolve=true
|
||||||
|
- 例如:揭露卧底身份 → 修正该角色之前事件中的动机描述
|
||||||
|
- 例如:发现地点的隐藏特性 → 更新地点节点的描述
|
||||||
|
- 不要对无关记忆强行建立联系
|
||||||
|
- neighbor_updates 中每条必须有实际意义的修改`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一记忆整合主函数
|
||||||
|
*
|
||||||
|
* 合并了原先的 mem0ConflictCheck(精确对照)和 evolveMemories(进化),
|
||||||
|
* 实现"1 次 embed + 1 次 LLM"完成冲突检测 + 进化分析。
|
||||||
|
*
|
||||||
|
* @param {object} params
|
||||||
|
* @param {object} params.graph - 当前图状态
|
||||||
|
* @param {string[]} params.newNodeIds - 本次新创建的节点 ID 列表
|
||||||
|
* @param {object} params.embeddingConfig - Embedding API 配置
|
||||||
|
* @param {object} [params.options]
|
||||||
|
* @param {number} [params.options.neighborCount=5] - 近邻搜索数量
|
||||||
|
* @param {number} [params.options.conflictThreshold=0.85] - 冲突判定阈值(低于此值跳过冲突检测)
|
||||||
|
* @param {string} [params.customPrompt] - 自定义提示词
|
||||||
|
* @param {AbortSignal} [params.signal]
|
||||||
|
* @returns {Promise<{merged: number, skipped: number, kept: number, evolved: number, connections: number, updates: number}>}
|
||||||
|
*/
|
||||||
|
export async function consolidateMemories({
|
||||||
|
graph,
|
||||||
|
newNodeIds,
|
||||||
|
embeddingConfig,
|
||||||
|
options = {},
|
||||||
|
customPrompt,
|
||||||
|
signal,
|
||||||
|
}) {
|
||||||
|
const neighborCount = options.neighborCount ?? 5;
|
||||||
|
const conflictThreshold = options.conflictThreshold ?? 0.85;
|
||||||
|
const stats = {
|
||||||
|
merged: 0,
|
||||||
|
skipped: 0,
|
||||||
|
kept: 0,
|
||||||
|
evolved: 0,
|
||||||
|
connections: 0,
|
||||||
|
updates: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newNodeIds || newNodeIds.length === 0) return stats;
|
||||||
|
if (!validateVectorConfig(embeddingConfig).valid) {
|
||||||
|
console.log('[ST-BME] 记忆整合跳过:向量配置不可用');
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeNodes = getActiveNodes(graph).filter(n => {
|
||||||
|
const text = buildNodeVectorText(n);
|
||||||
|
return typeof text === 'string' && text.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeNodes.length < 2) return stats;
|
||||||
|
|
||||||
|
for (const newId of newNodeIds) {
|
||||||
|
throwIfAborted(signal);
|
||||||
|
const newNode = getNode(graph, newId);
|
||||||
|
if (!newNode || newNode.archived) continue;
|
||||||
|
|
||||||
|
const queryText = buildNodeVectorText(newNode);
|
||||||
|
if (!queryText) continue;
|
||||||
|
|
||||||
|
// 排除自身的候选池
|
||||||
|
const candidates = activeNodes.filter(n => n.id !== newId);
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
stats.kept++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ── 1次 Embed:查近邻 ──
|
||||||
|
const neighbors = await findSimilarNodesByText(
|
||||||
|
graph,
|
||||||
|
queryText,
|
||||||
|
embeddingConfig,
|
||||||
|
neighborCount,
|
||||||
|
candidates,
|
||||||
|
signal,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (neighbors.length === 0) {
|
||||||
|
stats.kept++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建近邻描述文本
|
||||||
|
const neighborsContext = neighbors.map(n => {
|
||||||
|
const node = getNode(graph, n.nodeId);
|
||||||
|
if (!node) return null;
|
||||||
|
const fieldsStr = Object.entries(node.fields)
|
||||||
|
.map(([k, v]) => `${k}: ${v}`)
|
||||||
|
.join(', ');
|
||||||
|
return `[${node.id}] 类型=${node.type}, ${fieldsStr}, 相似度=${n.score.toFixed(3)}${
|
||||||
|
(node.clusters || []).length > 0 ? `, 分类=${node.clusters.join('/')}` : ''
|
||||||
|
}`;
|
||||||
|
}).filter(Boolean).join('\n');
|
||||||
|
|
||||||
|
const newNodeFieldsStr = Object.entries(newNode.fields)
|
||||||
|
.map(([k, v]) => `${k}: ${v}`)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
// 检查是否有高相似度命中(决定是否启用冲突检测部分的提示)
|
||||||
|
const hasHighSimilarity = neighbors[0].score > conflictThreshold;
|
||||||
|
|
||||||
|
const userPrompt = [
|
||||||
|
'## 新加入的记忆',
|
||||||
|
`[${newNode.id}] 类型=${newNode.type}, ${newNodeFieldsStr}`,
|
||||||
|
'',
|
||||||
|
'## 最近邻的已有记忆',
|
||||||
|
neighborsContext,
|
||||||
|
'',
|
||||||
|
`共 ${neighbors.length} 条近邻记忆。`,
|
||||||
|
hasHighSimilarity
|
||||||
|
? `最高相似度 ${neighbors[0].score.toFixed(3)} 超过阈值 ${conflictThreshold},请先判断是否冲突/重复,再分析进化关系。`
|
||||||
|
: '相似度均较低,请重点分析新记忆是否揭示了关于旧记忆的新信息。',
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
// ── 1次 LLM:统一判定 ──
|
||||||
|
const decision = await callLLMForJSON({
|
||||||
|
systemPrompt: customPrompt || CONSOLIDATION_SYSTEM_PROMPT,
|
||||||
|
userPrompt,
|
||||||
|
maxRetries: 1,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!decision) {
|
||||||
|
stats.kept++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 处理 action ──
|
||||||
|
switch (decision.action) {
|
||||||
|
case 'skip': {
|
||||||
|
console.log(`[ST-BME] 记忆整合: skip (重复) — ${newId}`);
|
||||||
|
newNode.archived = true;
|
||||||
|
stats.skipped++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'merge': {
|
||||||
|
const targetId = decision.merge_target_id;
|
||||||
|
const targetNode = targetId ? getNode(graph, targetId) : null;
|
||||||
|
|
||||||
|
if (targetNode && !targetNode.archived) {
|
||||||
|
console.log(`[ST-BME] 记忆整合: merge ${newId} → ${targetId}`);
|
||||||
|
|
||||||
|
// 合并字段到旧节点
|
||||||
|
if (decision.merged_fields && typeof decision.merged_fields === 'object') {
|
||||||
|
for (const [key, value] of Object.entries(decision.merged_fields)) {
|
||||||
|
if (value != null && value !== '') {
|
||||||
|
targetNode.fields[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没提供 merged_fields,将新节点的非空字段补充到旧节点
|
||||||
|
for (const [key, value] of Object.entries(newNode.fields)) {
|
||||||
|
if (value != null && value !== '' && !targetNode.fields[key]) {
|
||||||
|
targetNode.fields[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新旧节点的 seq 为更新的值
|
||||||
|
if (Number.isFinite(newNode.seq) && newNode.seq > (targetNode.seq || 0)) {
|
||||||
|
targetNode.seq = newNode.seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记旧节点需要 re-embed
|
||||||
|
targetNode.embedding = null;
|
||||||
|
|
||||||
|
// 归档新节点
|
||||||
|
newNode.archived = true;
|
||||||
|
stats.merged++;
|
||||||
|
} else {
|
||||||
|
// merge target 无效,回退为 keep
|
||||||
|
console.warn(`[ST-BME] 记忆整合: merge target ${targetId} 不存在,回退为 keep`);
|
||||||
|
stats.kept++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'keep':
|
||||||
|
default: {
|
||||||
|
stats.kept++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 处理 evolution(仅 keep 时有意义,但也容错处理其它 action) ──
|
||||||
|
const evolution = decision.evolution;
|
||||||
|
if (evolution?.should_evolve && !newNode.archived) {
|
||||||
|
stats.evolved++;
|
||||||
|
console.log(`[ST-BME] 记忆整合/进化触发: ${decision.reason || '(无理由)'}`);
|
||||||
|
|
||||||
|
// 建立关联边
|
||||||
|
if (Array.isArray(evolution.connections)) {
|
||||||
|
for (const targetId of evolution.connections) {
|
||||||
|
if (!getNode(graph, targetId)) continue;
|
||||||
|
const edge = createEdge({
|
||||||
|
fromId: newId,
|
||||||
|
toId: targetId,
|
||||||
|
relation: 'related',
|
||||||
|
strength: 0.7,
|
||||||
|
});
|
||||||
|
if (addEdge(graph, edge)) {
|
||||||
|
stats.connections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反向更新旧节点
|
||||||
|
if (Array.isArray(evolution.neighbor_updates)) {
|
||||||
|
for (const update of evolution.neighbor_updates) {
|
||||||
|
if (!update.nodeId) continue;
|
||||||
|
const oldNode = getNode(graph, update.nodeId);
|
||||||
|
if (!oldNode || oldNode.archived) continue;
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
// 更新 context/state 字段
|
||||||
|
if (update.newContext && typeof update.newContext === 'string') {
|
||||||
|
if (oldNode.fields.state !== undefined) {
|
||||||
|
oldNode.fields.state = update.newContext;
|
||||||
|
changed = true;
|
||||||
|
} else if (oldNode.fields.summary !== undefined) {
|
||||||
|
oldNode.fields.summary = update.newContext;
|
||||||
|
changed = true;
|
||||||
|
} else if (oldNode.fields.core_note !== undefined) {
|
||||||
|
oldNode.fields.core_note = update.newContext;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新分类标签
|
||||||
|
if (update.newTags && Array.isArray(update.newTags)) {
|
||||||
|
oldNode.clusters = update.newTags;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
oldNode.embedding = null;
|
||||||
|
if (!oldNode._evolutionHistory) oldNode._evolutionHistory = [];
|
||||||
|
oldNode._evolutionHistory.push({
|
||||||
|
triggeredBy: newId,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
reason: decision.reason || '',
|
||||||
|
});
|
||||||
|
stats.updates++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (isAbortError(e)) throw e;
|
||||||
|
console.error(`[ST-BME] 记忆整合失败 (${newId}):`, e);
|
||||||
|
stats.kept++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionSummary = [];
|
||||||
|
if (stats.merged > 0) actionSummary.push(`合并 ${stats.merged}`);
|
||||||
|
if (stats.skipped > 0) actionSummary.push(`跳过 ${stats.skipped}`);
|
||||||
|
if (stats.kept > 0) actionSummary.push(`保留 ${stats.kept}`);
|
||||||
|
if (stats.evolved > 0) actionSummary.push(`进化 ${stats.evolved}`);
|
||||||
|
if (stats.connections > 0) actionSummary.push(`新链接 ${stats.connections}`);
|
||||||
|
if (stats.updates > 0) actionSummary.push(`回溯更新 ${stats.updates}`);
|
||||||
|
|
||||||
|
if (actionSummary.length > 0) {
|
||||||
|
console.log(`[ST-BME] 记忆整合完成: ${actionSummary.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
226
evolution.js
226
evolution.js
@@ -1,226 +0,0 @@
|
|||||||
// ST-BME: 记忆进化引擎(A-MEM 启发)
|
|
||||||
// 新节点写入后触发,回溯更新相关旧节点的 context/tags/links
|
|
||||||
|
|
||||||
import { getActiveNodes, getNode, createEdge, addEdge } from './graph.js';
|
|
||||||
import { callLLMForJSON } from './llm.js';
|
|
||||||
import {
|
|
||||||
buildNodeVectorText,
|
|
||||||
findSimilarNodesByText,
|
|
||||||
validateVectorConfig,
|
|
||||||
} from './vector-index.js';
|
|
||||||
|
|
||||||
function createAbortError(message = '操作已终止') {
|
|
||||||
const error = new Error(message);
|
|
||||||
error.name = 'AbortError';
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAbortError(error) {
|
|
||||||
return error?.name === 'AbortError';
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwIfAborted(signal) {
|
|
||||||
if (signal?.aborted) {
|
|
||||||
throw signal.reason instanceof Error ? signal.reason : createAbortError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 进化系统提示词
|
|
||||||
* 参考 A-MEM process_memory() 的进化决策 Prompt
|
|
||||||
*/
|
|
||||||
const EVOLUTION_SYSTEM_PROMPT = `你是一个记忆进化分析器。当新的记忆加入知识图谱时,你需要分析它与现有记忆的关系。
|
|
||||||
|
|
||||||
你的任务:
|
|
||||||
1. 判断新记忆是否揭示了与旧记忆相关的新信息
|
|
||||||
2. 如果是,决定如何更新旧记忆的描述和分类
|
|
||||||
3. 建立新旧记忆之间的有意义连接
|
|
||||||
|
|
||||||
输出严格 JSON:
|
|
||||||
{
|
|
||||||
"should_evolve": true/false,
|
|
||||||
"reason": "进化理由(简述)",
|
|
||||||
"suggested_connections": ["需要建立链接的旧记忆ID列表"],
|
|
||||||
"neighbor_updates": [
|
|
||||||
{
|
|
||||||
"nodeId": "需更新的旧节点ID",
|
|
||||||
"newContext": "基于新信息修正后的描述(如不需修改则为null)",
|
|
||||||
"newTags": ["更新后的分类标签,如不需修改则为null"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
进化规则:
|
|
||||||
- 仅当新信息确实改变了对旧记忆的理解时才触发进化
|
|
||||||
- 例如:揭露卧底身份 → 修正该角色之前事件中的动机描述
|
|
||||||
- 例如:发现地点的隐藏特性 → 更新地点节点的描述
|
|
||||||
- 不要对无关记忆强行建立联系
|
|
||||||
- neighbor_updates 中每条必须有实际意义的修改`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记忆进化主函数
|
|
||||||
*
|
|
||||||
* @param {object} params
|
|
||||||
* @param {object} params.graph - 当前图状态
|
|
||||||
* @param {string[]} params.newNodeIds - 本次新创建的节点 ID 列表
|
|
||||||
* @param {object} params.embeddingConfig - Embedding API 配置
|
|
||||||
* @param {object} [params.options]
|
|
||||||
* @returns {Promise<{evolved: number, connections: number, updates: number}>}
|
|
||||||
*/
|
|
||||||
export async function evolveMemories({
|
|
||||||
graph,
|
|
||||||
newNodeIds,
|
|
||||||
embeddingConfig,
|
|
||||||
options = {},
|
|
||||||
customPrompt,
|
|
||||||
signal,
|
|
||||||
}) {
|
|
||||||
const neighborCount = options.neighborCount ?? 5;
|
|
||||||
const stats = { evolved: 0, connections: 0, updates: 0 };
|
|
||||||
|
|
||||||
if (!newNodeIds || newNodeIds.length === 0) return stats;
|
|
||||||
if (!validateVectorConfig(embeddingConfig).valid) {
|
|
||||||
console.log('[ST-BME] 记忆进化跳过:向量配置不可用');
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeNodes = getActiveNodes(graph);
|
|
||||||
if (activeNodes.length < 2) return stats; // 至少需要 2 个节点才有进化意义
|
|
||||||
|
|
||||||
for (const newId of newNodeIds) {
|
|
||||||
throwIfAborted(signal);
|
|
||||||
const newNode = getNode(graph, newId);
|
|
||||||
if (!newNode) continue;
|
|
||||||
|
|
||||||
const queryText = buildNodeVectorText(newNode);
|
|
||||||
if (!queryText) continue;
|
|
||||||
|
|
||||||
const candidates = activeNodes.filter(n => n.id !== newId);
|
|
||||||
if (candidates.length === 0) continue;
|
|
||||||
|
|
||||||
const neighbors = await findSimilarNodesByText(
|
|
||||||
graph,
|
|
||||||
queryText,
|
|
||||||
embeddingConfig,
|
|
||||||
neighborCount,
|
|
||||||
candidates,
|
|
||||||
signal,
|
|
||||||
);
|
|
||||||
if (neighbors.length === 0) continue;
|
|
||||||
|
|
||||||
// 构建 LLM 上下文
|
|
||||||
const neighborsContext = neighbors.map(n => {
|
|
||||||
const node = getNode(graph, n.nodeId);
|
|
||||||
if (!node) return null;
|
|
||||||
const fieldsStr = Object.entries(node.fields)
|
|
||||||
.map(([k, v]) => `${k}: ${v}`)
|
|
||||||
.join(', ');
|
|
||||||
return `[${node.id}] 类型=${node.type}, ${fieldsStr}, 分类=${(node.clusters || []).join('/')}`;
|
|
||||||
}).filter(Boolean).join('\n');
|
|
||||||
|
|
||||||
const newNodeFieldsStr = Object.entries(newNode.fields)
|
|
||||||
.map(([k, v]) => `${k}: ${v}`)
|
|
||||||
.join(', ');
|
|
||||||
|
|
||||||
const userPrompt = [
|
|
||||||
'## 新加入的记忆',
|
|
||||||
`[${newNode.id}] 类型=${newNode.type}, ${newNodeFieldsStr}`,
|
|
||||||
'',
|
|
||||||
'## 最近邻的已有记忆',
|
|
||||||
neighborsContext,
|
|
||||||
'',
|
|
||||||
`共 ${neighbors.length} 条近邻记忆。请分析新记忆是否揭示了关于旧记忆的新信息。`,
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const decision = await callLLMForJSON({
|
|
||||||
systemPrompt: customPrompt || EVOLUTION_SYSTEM_PROMPT,
|
|
||||||
userPrompt,
|
|
||||||
maxRetries: 1,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!decision || !decision.should_evolve) continue;
|
|
||||||
|
|
||||||
stats.evolved++;
|
|
||||||
console.log(`[ST-BME] 记忆进化触发: ${decision.reason || '(无理由)'}`);
|
|
||||||
|
|
||||||
// 1. 建立链接(strengthen)
|
|
||||||
if (decision.suggested_connections && Array.isArray(decision.suggested_connections)) {
|
|
||||||
for (const targetId of decision.suggested_connections) {
|
|
||||||
// 验证目标节点存在
|
|
||||||
if (!getNode(graph, targetId)) continue;
|
|
||||||
const edge = createEdge({
|
|
||||||
fromId: newId,
|
|
||||||
toId: targetId,
|
|
||||||
relation: 'related',
|
|
||||||
strength: 0.7,
|
|
||||||
});
|
|
||||||
if (addEdge(graph, edge)) {
|
|
||||||
stats.connections++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 反向更新旧节点(update_neighbor)
|
|
||||||
if (decision.neighbor_updates && Array.isArray(decision.neighbor_updates)) {
|
|
||||||
for (const update of decision.neighbor_updates) {
|
|
||||||
if (!update.nodeId) continue;
|
|
||||||
const oldNode = getNode(graph, update.nodeId);
|
|
||||||
if (!oldNode) continue;
|
|
||||||
|
|
||||||
let changed = false;
|
|
||||||
|
|
||||||
// 更新 context/state 字段
|
|
||||||
if (update.newContext && typeof update.newContext === 'string') {
|
|
||||||
// 根据节点类型选择更新哪个字段
|
|
||||||
if (oldNode.fields.state !== undefined) {
|
|
||||||
oldNode.fields.state = update.newContext;
|
|
||||||
changed = true;
|
|
||||||
} else if (oldNode.fields.summary !== undefined) {
|
|
||||||
oldNode.fields.summary = update.newContext;
|
|
||||||
changed = true;
|
|
||||||
} else if (oldNode.fields.core_note !== undefined) {
|
|
||||||
oldNode.fields.core_note = update.newContext;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新分类标签
|
|
||||||
if (update.newTags && Array.isArray(update.newTags)) {
|
|
||||||
oldNode.clusters = update.newTags;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
// 标记需要重新生成 embedding
|
|
||||||
oldNode.embedding = null;
|
|
||||||
// 记录进化历史
|
|
||||||
if (!oldNode._evolutionHistory) oldNode._evolutionHistory = [];
|
|
||||||
oldNode._evolutionHistory.push({
|
|
||||||
triggeredBy: newId,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
reason: decision.reason || '',
|
|
||||||
});
|
|
||||||
stats.updates++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
if (isAbortError(e)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
console.error(`[ST-BME] 记忆进化失败 (${newId}):`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stats.evolved > 0) {
|
|
||||||
console.log(
|
|
||||||
`[ST-BME] 记忆进化完成: ${stats.evolved} 次进化, ` +
|
|
||||||
`${stats.connections} 条新链接, ${stats.updates} 个节点回溯更新`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
104
extractor.js
104
extractor.js
@@ -22,9 +22,7 @@ import {
|
|||||||
import { RELATION_TYPES } from "./schema.js";
|
import { RELATION_TYPES } from "./schema.js";
|
||||||
import {
|
import {
|
||||||
buildNodeVectorText,
|
buildNodeVectorText,
|
||||||
findSimilarNodesByText,
|
|
||||||
isDirectVectorConfig,
|
isDirectVectorConfig,
|
||||||
validateVectorConfig,
|
|
||||||
} from "./vector-index.js";
|
} from "./vector-index.js";
|
||||||
|
|
||||||
function createAbortError(message = "操作已终止") {
|
function createAbortError(message = "操作已终止") {
|
||||||
@@ -69,7 +67,6 @@ export async function extractMemories({
|
|||||||
schema,
|
schema,
|
||||||
embeddingConfig,
|
embeddingConfig,
|
||||||
extractPrompt,
|
extractPrompt,
|
||||||
v2Options = {},
|
|
||||||
signal = undefined,
|
signal = undefined,
|
||||||
}) {
|
}) {
|
||||||
throwIfAborted(signal);
|
throwIfAborted(signal);
|
||||||
@@ -84,8 +81,7 @@ export async function extractMemories({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const enablePreciseConflict = v2Options.enablePreciseConflict ?? true;
|
|
||||||
const conflictThreshold = v2Options.conflictThreshold ?? 0.85;
|
|
||||||
|
|
||||||
const effectiveStartSeq = Number.isFinite(startSeq)
|
const effectiveStartSeq = Number.isFinite(startSeq)
|
||||||
? startSeq
|
? startSeq
|
||||||
@@ -154,17 +150,7 @@ export async function extractMemories({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== v2: Mem0 精确对照阶段 ==========
|
|
||||||
if (enablePreciseConflict && validateVectorConfig(embeddingConfig).valid) {
|
|
||||||
await mem0ConflictCheck(
|
|
||||||
graph,
|
|
||||||
result.operations,
|
|
||||||
embeddingConfig,
|
|
||||||
conflictThreshold,
|
|
||||||
effectiveEndSeq,
|
|
||||||
signal,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行操作
|
// 执行操作
|
||||||
const stats = { newNodes: 0, updatedNodes: 0, newEdges: 0 };
|
const stats = { newNodes: 0, updatedNodes: 0, newEdges: 0 };
|
||||||
@@ -570,92 +556,6 @@ function buildDefaultExtractPrompt(schema) {
|
|||||||
|
|
||||||
// ==================== v2 增强功能 ====================
|
// ==================== v2 增强功能 ====================
|
||||||
|
|
||||||
/**
|
|
||||||
* Mem0 启发的精确对照
|
|
||||||
* 对每条 create 操作搜索近邻,高相似度时让 LLM 判断 add/update/skip
|
|
||||||
*/
|
|
||||||
async function mem0ConflictCheck(
|
|
||||||
graph,
|
|
||||||
operations,
|
|
||||||
embeddingConfig,
|
|
||||||
threshold,
|
|
||||||
fallbackSeq,
|
|
||||||
signal,
|
|
||||||
) {
|
|
||||||
const activeNodes = getActiveNodes(graph).filter((node) => {
|
|
||||||
const text = buildNodeVectorText(node);
|
|
||||||
return typeof text === "string" && text.length > 0;
|
|
||||||
});
|
|
||||||
if (activeNodes.length === 0) return;
|
|
||||||
|
|
||||||
for (const op of operations) {
|
|
||||||
if (op.action !== "create") continue;
|
|
||||||
|
|
||||||
const factText =
|
|
||||||
op.fields?.summary || op.fields?.name || op.fields?.title || "";
|
|
||||||
if (!factText) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
throwIfAborted(signal);
|
|
||||||
const similar = await findSimilarNodesByText(
|
|
||||||
graph,
|
|
||||||
factText,
|
|
||||||
embeddingConfig,
|
|
||||||
3,
|
|
||||||
activeNodes,
|
|
||||||
signal,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (similar.length > 0 && similar[0].score > threshold) {
|
|
||||||
const topMatch = graph.nodes.find((n) => n.id === similar[0].nodeId);
|
|
||||||
if (!topMatch) continue;
|
|
||||||
|
|
||||||
const topFields = Object.entries(topMatch.fields)
|
|
||||||
.map(([k, v]) => `${k}: ${v}`)
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
const decision = await callLLMForJSON({
|
|
||||||
systemPrompt: [
|
|
||||||
"判断新信息与已有记忆的关系。输出严格 JSON:",
|
|
||||||
'{"action": "add"|"update"|"skip", "targetId": "旧节点ID", "mergedFields": {}}',
|
|
||||||
"- add: 新信息完全不同,应新建",
|
|
||||||
"- update: 新信息是对旧记忆的修正/补充",
|
|
||||||
"- skip: 与旧记忆完全重复",
|
|
||||||
].join("\n"),
|
|
||||||
userPrompt: [
|
|
||||||
`新信息: [${op.type}] ${factText}`,
|
|
||||||
`最相似旧记忆: [${topMatch.id}] 类型=${topMatch.type}, ${topFields}`,
|
|
||||||
`相似度: ${similar[0].score.toFixed(3)}`,
|
|
||||||
].join("\n"),
|
|
||||||
maxRetries: 1,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (decision?.action === "update" && decision.targetId) {
|
|
||||||
console.log(
|
|
||||||
`[ST-BME] Mem0对照: create->update (${decision.targetId})`,
|
|
||||||
);
|
|
||||||
op.action = "update";
|
|
||||||
op.nodeId = decision.targetId;
|
|
||||||
op.sourceNodeId = op.sourceNodeId || topMatch.id;
|
|
||||||
op.seq = Number.isFinite(op.seq) ? op.seq : fallbackSeq;
|
|
||||||
if (decision.mergedFields) {
|
|
||||||
op.fields = { ...op.fields, ...decision.mergedFields };
|
|
||||||
}
|
|
||||||
} else if (decision?.action === "skip") {
|
|
||||||
console.log("[ST-BME] Mem0对照: create->skip (重复)");
|
|
||||||
op.action = "_skip";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (isAbortError(e)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
console.warn("[ST-BME] Mem0对照失败,保持原操作:", e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局故事概要生成(MemoRAG 启发)
|
* 全局故事概要生成(MemoRAG 启发)
|
||||||
* 基于图中事件/角色/主线自动生成/更新 synopsis 节点
|
* 基于图中事件/角色/主线自动生成/更新 synopsis 节点
|
||||||
|
|||||||
44
index.js
44
index.js
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "../../../extensions.js";
|
} from "../../../extensions.js";
|
||||||
|
|
||||||
import { compressAll, sleepCycle } from "./compressor.js";
|
import { compressAll, sleepCycle } from "./compressor.js";
|
||||||
import { evolveMemories } from "./evolution.js";
|
import { consolidateMemories } from "./consolidator.js";
|
||||||
import {
|
import {
|
||||||
extractMemories,
|
extractMemories,
|
||||||
generateReflection,
|
generateReflection,
|
||||||
@@ -122,14 +122,10 @@ const defaultSettings = {
|
|||||||
|
|
||||||
// ====== v2 增强设置 ======
|
// ====== v2 增强设置 ======
|
||||||
|
|
||||||
// ③ A-MEM 记忆进化
|
// ③ 记忆整合(合并精确对照 + 记忆进化)
|
||||||
enableEvolution: true, // 启用记忆进化
|
enableConsolidation: true, // 启用记忆整合
|
||||||
evoNeighborCount: 5, // 近邻搜索数量
|
consolidationNeighborCount: 5, // 近邻搜索数量
|
||||||
evoConsolidateEvery: 50, // 每 N 次进化后整理
|
consolidationThreshold: 0.85, // 冲突判定相似度阈值
|
||||||
|
|
||||||
// ② Mem0 精确对照
|
|
||||||
enablePreciseConflict: true, // 启用精确对照
|
|
||||||
conflictThreshold: 0.85, // 相似度阈值
|
|
||||||
|
|
||||||
// ⑨ 全局故事概要
|
// ⑨ 全局故事概要
|
||||||
enableSynopsis: true, // 启用全局概要
|
enableSynopsis: true, // 启用全局概要
|
||||||
@@ -1642,20 +1638,23 @@ async function handleExtractionSuccess(
|
|||||||
currentGraph.historyState.extractionCount = extractionCount;
|
currentGraph.historyState.extractionCount = extractionCount;
|
||||||
updateLastExtractedItems(result.newNodeIds || []);
|
updateLastExtractedItems(result.newNodeIds || []);
|
||||||
|
|
||||||
if (settings.enableEvolution && result.newNodeIds?.length > 0) {
|
if (settings.enableConsolidation && result.newNodeIds?.length > 0) {
|
||||||
try {
|
try {
|
||||||
await evolveMemories({
|
await consolidateMemories({
|
||||||
graph: currentGraph,
|
graph: currentGraph,
|
||||||
newNodeIds: result.newNodeIds,
|
newNodeIds: result.newNodeIds,
|
||||||
embeddingConfig: getEmbeddingConfig(),
|
embeddingConfig: getEmbeddingConfig(),
|
||||||
options: { neighborCount: settings.evoNeighborCount },
|
options: {
|
||||||
customPrompt: settings.evolutionPrompt || undefined,
|
neighborCount: settings.consolidationNeighborCount,
|
||||||
|
conflictThreshold: settings.consolidationThreshold,
|
||||||
|
},
|
||||||
|
customPrompt: settings.consolidationPrompt || undefined,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
postProcessArtifacts.push("evolution");
|
postProcessArtifacts.push("consolidation");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (isAbortError(e)) throw e;
|
if (isAbortError(e)) throw e;
|
||||||
console.error("[ST-BME] 记忆进化失败:", e);
|
console.error("[ST-BME] 记忆整合失败:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2173,10 +2172,6 @@ async function executeExtractionBatch({
|
|||||||
schema: getSchema(),
|
schema: getSchema(),
|
||||||
embeddingConfig: getEmbeddingConfig(),
|
embeddingConfig: getEmbeddingConfig(),
|
||||||
extractPrompt: settings.extractPrompt || undefined,
|
extractPrompt: settings.extractPrompt || undefined,
|
||||||
v2Options: {
|
|
||||||
enablePreciseConflict: settings.enablePreciseConflict,
|
|
||||||
conflictThreshold: settings.conflictThreshold,
|
|
||||||
},
|
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3356,18 +3351,21 @@ async function onManualEvolve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const beforeSnapshot = cloneGraphSnapshot(currentGraph);
|
const beforeSnapshot = cloneGraphSnapshot(currentGraph);
|
||||||
const result = await evolveMemories({
|
const result = await consolidateMemories({
|
||||||
graph: currentGraph,
|
graph: currentGraph,
|
||||||
newNodeIds: candidateIds,
|
newNodeIds: candidateIds,
|
||||||
embeddingConfig: getEmbeddingConfig(),
|
embeddingConfig: getEmbeddingConfig(),
|
||||||
options: { neighborCount: getSettings().evoNeighborCount },
|
options: {
|
||||||
|
neighborCount: getSettings().consolidationNeighborCount,
|
||||||
|
conflictThreshold: getSettings().consolidationThreshold,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
await recordGraphMutation({
|
await recordGraphMutation({
|
||||||
beforeSnapshot,
|
beforeSnapshot,
|
||||||
artifactTags: ["evolution"],
|
artifactTags: ["consolidation"],
|
||||||
});
|
});
|
||||||
toastr.success(
|
toastr.success(
|
||||||
`进化完成:${result.evolved} 次进化,${result.connections} 条链接,${result.updates} 个回溯更新`,
|
`整合完成:合并 ${result.merged},跳过 ${result.skipped},保留 ${result.kept},进化 ${result.evolved},新链接 ${result.connections},回溯更新 ${result.updates}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
63
panel.html
63
panel.html
@@ -551,19 +551,12 @@
|
|||||||
</span>
|
</span>
|
||||||
<input id="bme-setting-recall-enabled" type="checkbox" />
|
<input id="bme-setting-recall-enabled" type="checkbox" />
|
||||||
</label>
|
</label>
|
||||||
<label class="bme-toggle-item" for="bme-setting-evolution-enabled">
|
<label class="bme-toggle-item" for="bme-setting-consolidation-enabled">
|
||||||
<span class="bme-toggle-copy">
|
<span class="bme-toggle-copy">
|
||||||
<span class="bme-toggle-title">启用记忆进化</span>
|
<span class="bme-toggle-title">启用记忆整合</span>
|
||||||
<span class="bme-toggle-desc">根据新提取结果反向修正既有记忆。</span>
|
<span class="bme-toggle-desc">去重/冲突检测 + 反向修正既有记忆 + 建立关联边。</span>
|
||||||
</span>
|
</span>
|
||||||
<input id="bme-setting-evolution-enabled" type="checkbox" />
|
<input id="bme-setting-consolidation-enabled" type="checkbox" />
|
||||||
</label>
|
|
||||||
<label class="bme-toggle-item" for="bme-setting-precise-conflict-enabled">
|
|
||||||
<span class="bme-toggle-copy">
|
|
||||||
<span class="bme-toggle-title">启用精确对照</span>
|
|
||||||
<span class="bme-toggle-desc">识别与既有记忆冲突或需要修订的片段。</span>
|
|
||||||
</span>
|
|
||||||
<input id="bme-setting-precise-conflict-enabled" type="checkbox" />
|
|
||||||
</label>
|
</label>
|
||||||
<label class="bme-toggle-item" for="bme-setting-synopsis-enabled">
|
<label class="bme-toggle-item" for="bme-setting-synopsis-enabled">
|
||||||
<span class="bme-toggle-copy">
|
<span class="bme-toggle-copy">
|
||||||
@@ -782,42 +775,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bme-config-card bme-guarded-card" data-guard-settings="enableEvolution">
|
<div class="bme-config-card bme-guarded-card" data-guard-settings="enableConsolidation">
|
||||||
<div class="bme-config-card-head">
|
<div class="bme-config-card-head">
|
||||||
<div>
|
<div>
|
||||||
<div class="bme-config-card-title">进化</div>
|
<div class="bme-config-card-title">记忆整合</div>
|
||||||
<div class="bme-config-card-subtitle">控制邻域搜索规模和整理频率。</div>
|
<div class="bme-config-card-subtitle">控制近邻搜索规模和冲突判定阈值。</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-config-guard-note">在“功能开关”中启用后生效。</div>
|
<div class="bme-config-guard-note">在“功能开关”中启用后生效。</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-config-row">
|
<div class="bme-config-row">
|
||||||
<label for="bme-setting-evo-neighbor-count">近邻数量</label>
|
<label for="bme-setting-consolidation-neighbor-count">近邻数量</label>
|
||||||
<input id="bme-setting-evo-neighbor-count" class="bme-config-input" type="number" min="1" max="20" />
|
<input id="bme-setting-consolidation-neighbor-count" class="bme-config-input" type="number" min="1" max="20" />
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-config-row">
|
<div class="bme-config-row">
|
||||||
<label for="bme-setting-evo-consolidate-every">整理频率</label>
|
<label for="bme-setting-consolidation-threshold">冲突判定阈值</label>
|
||||||
<input
|
<input
|
||||||
id="bme-setting-evo-consolidate-every"
|
id="bme-setting-consolidation-threshold"
|
||||||
class="bme-config-input"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bme-config-card bme-guarded-card" data-guard-settings="enablePreciseConflict">
|
|
||||||
<div class="bme-config-card-head">
|
|
||||||
<div>
|
|
||||||
<div class="bme-config-card-title">冲突对照</div>
|
|
||||||
<div class="bme-config-card-subtitle">调整对照阶段的判定阈值。</div>
|
|
||||||
</div>
|
|
||||||
<div class="bme-config-guard-note">在“功能开关”中启用后生效。</div>
|
|
||||||
</div>
|
|
||||||
<div class="bme-config-row">
|
|
||||||
<label for="bme-setting-conflict-threshold">对照阈值</label>
|
|
||||||
<input
|
|
||||||
id="bme-setting-conflict-threshold"
|
|
||||||
class="bme-config-input"
|
class="bme-config-input"
|
||||||
type="number"
|
type="number"
|
||||||
min="0.5"
|
min="0.5"
|
||||||
@@ -986,26 +959,26 @@
|
|||||||
<textarea id="bme-setting-recall-prompt" class="bme-config-textarea" placeholder="留空 = 默认召回 prompt"></textarea>
|
<textarea id="bme-setting-recall-prompt" class="bme-config-textarea" placeholder="留空 = 默认召回 prompt"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bme-prompt-card" data-setting-key="evolutionPrompt" data-default-prompt="evolution">
|
<div class="bme-prompt-card" data-setting-key="consolidationPrompt" data-default-prompt="consolidation">
|
||||||
<div class="bme-prompt-card-head">
|
<div class="bme-prompt-card-head">
|
||||||
<div>
|
<div>
|
||||||
<div class="bme-config-card-title">记忆进化</div>
|
<div class="bme-config-card-title">记忆整合</div>
|
||||||
<div class="bme-config-card-subtitle">用于分析新记忆如何修正或连接既有节点。</div>
|
<div class="bme-config-card-subtitle">用于分析新记忆与旧记忆的冲突、去重和进化关系。</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-prompt-card-actions">
|
<div class="bme-prompt-card-actions">
|
||||||
<span class="bme-prompt-status"></span>
|
<span class="bme-prompt-status"></span>
|
||||||
<button
|
<button
|
||||||
class="menu_button bme-prompt-reset"
|
class="menu_button bme-prompt-reset"
|
||||||
data-setting-key="evolutionPrompt"
|
data-setting-key="consolidationPrompt"
|
||||||
data-default-prompt="evolution"
|
data-default-prompt="consolidation"
|
||||||
data-target-id="bme-setting-evolution-prompt"
|
data-target-id="bme-setting-consolidation-prompt"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
恢复默认
|
恢复默认
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="bme-setting-evolution-prompt" class="bme-config-textarea" placeholder="留空 = 默认进化 prompt"></textarea>
|
<textarea id="bme-setting-consolidation-prompt" class="bme-config-textarea" placeholder="留空 = 默认整合 prompt"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bme-prompt-card" data-setting-key="compressPrompt" data-default-prompt="compress">
|
<div class="bme-prompt-card" data-setting-key="compressPrompt" data-default-prompt="compress">
|
||||||
|
|||||||
81
panel.js
81
panel.js
@@ -54,28 +54,30 @@ const DEFAULT_PROMPTS = {
|
|||||||
'{"selected_ids": ["id1", "id2", ...], "reason": "简要说明选择理由"}',
|
'{"selected_ids": ["id1", "id2", ...], "reason": "简要说明选择理由"}',
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
|
|
||||||
evolution: [
|
consolidation: [
|
||||||
"你是一个记忆进化分析器。当新的记忆加入知识图谱时,你需要分析它与现有记忆的关系。",
|
"你是一个记忆整合分析器。当新记忆加入知识图谱时,你需要同时完成两项任务:",
|
||||||
"",
|
"",
|
||||||
"你的任务:",
|
"任务一:冲突检测",
|
||||||
"1. 判断新记忆是否揭示了与旧记忆相关的新信息",
|
"- skip: 新记忆与已有记忆完全重复",
|
||||||
"2. 如果是,决定如何更新旧记忆的描述和分类",
|
"- merge: 新记忆是对旧记忆的修正/补充",
|
||||||
"3. 建立新旧记忆之间的有意义连接",
|
"- keep: 新记忆是全新信息",
|
||||||
|
"",
|
||||||
|
"任务二:进化分析(仅 action=keep 时)",
|
||||||
|
"- 建立关联连接",
|
||||||
|
"- 反向更新旧记忆",
|
||||||
"",
|
"",
|
||||||
"输出严格 JSON:",
|
"输出严格 JSON:",
|
||||||
"{",
|
"{",
|
||||||
' "should_evolve": true/false,',
|
' "action": "keep"|"merge"|"skip",',
|
||||||
' "reason": "进化理由",',
|
' "merge_target_id": "旧节点ID",',
|
||||||
' "suggested_connections": ["旧记忆ID"],',
|
' "merged_fields": {},',
|
||||||
' "neighbor_updates": [',
|
' "reason": "理由",',
|
||||||
' {"nodeId": "旧节点ID", "newContext": "修正描述", "newTags": ["标签"]}',
|
' "evolution": {',
|
||||||
" ]",
|
' "should_evolve": true/false,',
|
||||||
|
' "connections": ["旧记忆ID"],',
|
||||||
|
' "neighbor_updates": [{"nodeId": "旧节点ID", "newContext": "修正描述", "newTags": ["标签"]}]',
|
||||||
|
" }",
|
||||||
"}",
|
"}",
|
||||||
"",
|
|
||||||
"进化规则:",
|
|
||||||
"- 仅当新信息确实改变了对旧记忆的理解时才触发进化",
|
|
||||||
"- 例如:揭露卧底身份 → 修正该角色之前事件中的动机描述",
|
|
||||||
"- 不要对无关记忆强行建立联系",
|
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
|
|
||||||
compress: [
|
compress: [
|
||||||
@@ -732,11 +734,7 @@ function _refreshConfigTab() {
|
|||||||
"bme-setting-recall-graph-diffusion-enabled",
|
"bme-setting-recall-graph-diffusion-enabled",
|
||||||
settings.recallEnableGraphDiffusion ?? true,
|
settings.recallEnableGraphDiffusion ?? true,
|
||||||
);
|
);
|
||||||
_setCheckboxValue("bme-setting-evolution-enabled", settings.enableEvolution ?? true);
|
_setCheckboxValue("bme-setting-consolidation-enabled", settings.enableConsolidation ?? true);
|
||||||
_setCheckboxValue(
|
|
||||||
"bme-setting-precise-conflict-enabled",
|
|
||||||
settings.enablePreciseConflict ?? true,
|
|
||||||
);
|
|
||||||
_setCheckboxValue("bme-setting-synopsis-enabled", settings.enableSynopsis ?? true);
|
_setCheckboxValue("bme-setting-synopsis-enabled", settings.enableSynopsis ?? true);
|
||||||
_setCheckboxValue(
|
_setCheckboxValue(
|
||||||
"bme-setting-visibility-enabled",
|
"bme-setting-visibility-enabled",
|
||||||
@@ -790,16 +788,12 @@ function _refreshConfigTab() {
|
|||||||
settings.importanceWeight ?? 0.1,
|
settings.importanceWeight ?? 0.1,
|
||||||
);
|
);
|
||||||
_setInputValue(
|
_setInputValue(
|
||||||
"bme-setting-evo-neighbor-count",
|
"bme-setting-consolidation-neighbor-count",
|
||||||
settings.evoNeighborCount ?? 5,
|
settings.consolidationNeighborCount ?? 5,
|
||||||
);
|
);
|
||||||
_setInputValue(
|
_setInputValue(
|
||||||
"bme-setting-evo-consolidate-every",
|
"bme-setting-consolidation-threshold",
|
||||||
settings.evoConsolidateEvery ?? 50,
|
settings.consolidationThreshold ?? 0.85,
|
||||||
);
|
|
||||||
_setInputValue(
|
|
||||||
"bme-setting-conflict-threshold",
|
|
||||||
settings.conflictThreshold ?? 0.85,
|
|
||||||
);
|
);
|
||||||
_setInputValue("bme-setting-synopsis-every", settings.synopsisEveryN ?? 5);
|
_setInputValue("bme-setting-synopsis-every", settings.synopsisEveryN ?? 5);
|
||||||
_setInputValue(
|
_setInputValue(
|
||||||
@@ -855,7 +849,7 @@ function _refreshConfigTab() {
|
|||||||
|
|
||||||
_setInputValue("bme-setting-extract-prompt", settings.extractPrompt || DEFAULT_PROMPTS.extract);
|
_setInputValue("bme-setting-extract-prompt", settings.extractPrompt || DEFAULT_PROMPTS.extract);
|
||||||
_setInputValue("bme-setting-recall-prompt", settings.recallPrompt || DEFAULT_PROMPTS.recall);
|
_setInputValue("bme-setting-recall-prompt", settings.recallPrompt || DEFAULT_PROMPTS.recall);
|
||||||
_setInputValue("bme-setting-evolution-prompt", settings.evolutionPrompt || DEFAULT_PROMPTS.evolution);
|
_setInputValue("bme-setting-consolidation-prompt", settings.consolidationPrompt || DEFAULT_PROMPTS.consolidation);
|
||||||
_setInputValue("bme-setting-compress-prompt", settings.compressPrompt || DEFAULT_PROMPTS.compress);
|
_setInputValue("bme-setting-compress-prompt", settings.compressPrompt || DEFAULT_PROMPTS.compress);
|
||||||
_setInputValue("bme-setting-synopsis-prompt", settings.synopsisPrompt || DEFAULT_PROMPTS.synopsis);
|
_setInputValue("bme-setting-synopsis-prompt", settings.synopsisPrompt || DEFAULT_PROMPTS.synopsis);
|
||||||
_setInputValue("bme-setting-reflection-prompt", settings.reflectionPrompt || DEFAULT_PROMPTS.reflection);
|
_setInputValue("bme-setting-reflection-prompt", settings.reflectionPrompt || DEFAULT_PROMPTS.reflection);
|
||||||
@@ -901,12 +895,8 @@ function _bindConfigControls() {
|
|||||||
_patchSettings({ recallEnableGraphDiffusion: checked });
|
_patchSettings({ recallEnableGraphDiffusion: checked });
|
||||||
_refreshStageCardStates();
|
_refreshStageCardStates();
|
||||||
});
|
});
|
||||||
bindCheckbox("bme-setting-evolution-enabled", (checked) => {
|
bindCheckbox("bme-setting-consolidation-enabled", (checked) => {
|
||||||
_patchSettings({ enableEvolution: checked });
|
_patchSettings({ enableConsolidation: checked });
|
||||||
_refreshGuardedConfigStates();
|
|
||||||
});
|
|
||||||
bindCheckbox("bme-setting-precise-conflict-enabled", (checked) => {
|
|
||||||
_patchSettings({ enablePreciseConflict: checked });
|
|
||||||
_refreshGuardedConfigStates();
|
_refreshGuardedConfigStates();
|
||||||
});
|
});
|
||||||
bindCheckbox("bme-setting-synopsis-enabled", (checked) => {
|
bindCheckbox("bme-setting-synopsis-enabled", (checked) => {
|
||||||
@@ -969,14 +959,11 @@ function _bindConfigControls() {
|
|||||||
bindFloat("bme-setting-importance-weight", 0.1, 0, 1, (value) =>
|
bindFloat("bme-setting-importance-weight", 0.1, 0, 1, (value) =>
|
||||||
_patchSettings({ importanceWeight: value }),
|
_patchSettings({ importanceWeight: value }),
|
||||||
);
|
);
|
||||||
bindNumber("bme-setting-evo-neighbor-count", 5, 1, 20, (value) =>
|
bindNumber("bme-setting-consolidation-neighbor-count", 5, 1, 20, (value) =>
|
||||||
_patchSettings({ evoNeighborCount: value }),
|
_patchSettings({ consolidationNeighborCount: value }),
|
||||||
);
|
);
|
||||||
bindNumber("bme-setting-evo-consolidate-every", 50, 1, 500, (value) =>
|
bindFloat("bme-setting-consolidation-threshold", 0.85, 0.5, 0.99, (value) =>
|
||||||
_patchSettings({ evoConsolidateEvery: value }),
|
_patchSettings({ consolidationThreshold: value }),
|
||||||
);
|
|
||||||
bindFloat("bme-setting-conflict-threshold", 0.85, 0.5, 0.99, (value) =>
|
|
||||||
_patchSettings({ conflictThreshold: value }),
|
|
||||||
);
|
);
|
||||||
bindNumber("bme-setting-synopsis-every", 5, 1, 100, (value) =>
|
bindNumber("bme-setting-synopsis-every", 5, 1, 100, (value) =>
|
||||||
_patchSettings({ synopsisEveryN: value }),
|
_patchSettings({ synopsisEveryN: value }),
|
||||||
@@ -1061,9 +1048,9 @@ function _bindConfigControls() {
|
|||||||
"recall",
|
"recall",
|
||||||
);
|
);
|
||||||
bindPromptText(
|
bindPromptText(
|
||||||
"bme-setting-evolution-prompt",
|
"bme-setting-consolidation-prompt",
|
||||||
"evolutionPrompt",
|
"consolidationPrompt",
|
||||||
"evolution",
|
"consolidation",
|
||||||
);
|
);
|
||||||
bindPromptText(
|
bindPromptText(
|
||||||
"bme-setting-compress-prompt",
|
"bme-setting-compress-prompt",
|
||||||
|
|||||||
Reference in New Issue
Block a user