Files
ST-Bionic-Memory-Ecology/evolution.js
2026-03-24 12:38:46 +08:00

203 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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';
/**
* 进化系统提示词
* 参考 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 = {},
}) {
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) {
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,
);
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: EVOLUTION_SYSTEM_PROMPT,
userPrompt,
maxRetries: 1,
});
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) {
console.error(`[ST-BME] 记忆进化失败 (${newId}):`, e);
}
}
if (stats.evolved > 0) {
console.log(
`[ST-BME] 记忆进化完成: ${stats.evolved} 次进化, ` +
`${stats.connections} 条新链接, ${stats.updates} 个节点回溯更新`,
);
}
return stats;
}