story-summary: facts migration + recall enhancements
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
import { getContext } from "../../../../../../extensions.js";
|
||||
import { xbLog } from "../../../core/debug-core.js";
|
||||
import { getSummaryStore, saveSummaryStore, addSummarySnapshot, mergeNewData } from "../data/store.js";
|
||||
import { getSummaryStore, saveSummaryStore, addSummarySnapshot, mergeNewData, getFacts } from "../data/store.js";
|
||||
import { generateSummary, parseSummaryJson } from "./llm.js";
|
||||
|
||||
const MODULE_ID = 'summaryGenerator';
|
||||
@@ -11,46 +11,48 @@ const SUMMARY_SESSION_ID = 'xb9';
|
||||
const MAX_CAUSED_BY = 2;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// worldUpdate 清洗
|
||||
// factUpdates 清洗
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function sanitizeWorldUpdate(parsed) {
|
||||
function sanitizeFacts(parsed) {
|
||||
if (!parsed) return;
|
||||
|
||||
const wu = Array.isArray(parsed.worldUpdate) ? parsed.worldUpdate : [];
|
||||
const updates = Array.isArray(parsed.factUpdates) ? parsed.factUpdates : [];
|
||||
const ok = [];
|
||||
|
||||
for (const item of wu) {
|
||||
const category = String(item?.category || '').trim().toLowerCase();
|
||||
const topic = String(item?.topic || '').trim();
|
||||
for (const item of updates) {
|
||||
const s = String(item?.s || '').trim();
|
||||
const p = String(item?.p || '').trim();
|
||||
|
||||
if (!category || !topic) continue;
|
||||
if (!s || !p) continue;
|
||||
|
||||
// status/knowledge/relation 必须包含 "::"
|
||||
if (['status', 'knowledge', 'relation'].includes(category) && !topic.includes('::')) {
|
||||
xbLog.warn(MODULE_ID, `丢弃不合格 worldUpdate: ${category}/${topic}`);
|
||||
// 删除操作
|
||||
if (item.retracted === true) {
|
||||
ok.push({ s, p, retracted: true });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.cleared === true) {
|
||||
ok.push({ category, topic, cleared: true });
|
||||
continue;
|
||||
const o = String(item?.o || '').trim();
|
||||
if (!o) continue;
|
||||
|
||||
const fact = { s, p, o };
|
||||
|
||||
// 关系类保留 trend
|
||||
if (/^对.+的/.test(p) && item.trend) {
|
||||
const validTrends = ['破裂', '厌恶', '反感', '陌生', '投缘', '亲密', '交融'];
|
||||
if (validTrends.includes(item.trend)) {
|
||||
fact.trend = item.trend;
|
||||
}
|
||||
}
|
||||
|
||||
const content = String(item?.content || '').trim();
|
||||
if (!content) continue;
|
||||
|
||||
ok.push({ category, topic, content });
|
||||
ok.push(fact);
|
||||
}
|
||||
|
||||
parsed.worldUpdate = ok;
|
||||
parsed.factUpdates = ok;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// causedBy 清洗(事件因果边)
|
||||
// - 允许引用:已存在事件 + 本次新输出事件
|
||||
// - 限制长度:0-2
|
||||
// - 去重、剔除非法ID、剔除自引用
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function sanitizeEventsCausality(parsed, existingEventIds) {
|
||||
@@ -61,7 +63,6 @@ function sanitizeEventsCausality(parsed, existingEventIds) {
|
||||
|
||||
const idRe = /^evt-\d+$/;
|
||||
|
||||
// 本次新输出事件ID集合(允许引用)
|
||||
const newIds = new Set(
|
||||
events
|
||||
.map(e => String(e?.id || '').trim())
|
||||
@@ -73,7 +74,6 @@ function sanitizeEventsCausality(parsed, existingEventIds) {
|
||||
for (const e of events) {
|
||||
const selfId = String(e?.id || '').trim();
|
||||
if (!idRe.test(selfId)) {
|
||||
// id 不合格的话,causedBy 直接清空,避免污染
|
||||
e.causedBy = [];
|
||||
continue;
|
||||
}
|
||||
@@ -117,11 +117,6 @@ export function formatExistingSummaryForAI(store) {
|
||||
parts.push(`\n【主要角色】${names.join("、")}`);
|
||||
}
|
||||
|
||||
if (data.characters?.relationships?.length) {
|
||||
parts.push("【人物关系】");
|
||||
data.characters.relationships.forEach(r => parts.push(`- ${r.from} → ${r.to}:${r.label}(${r.trend})`));
|
||||
}
|
||||
|
||||
if (data.arcs?.length) {
|
||||
parts.push("【角色弧光】");
|
||||
data.arcs.forEach(a => parts.push(`- ${a.name}:${a.trajectory}(进度${Math.round(a.progress * 100)}%)`));
|
||||
@@ -187,7 +182,7 @@ export async function runSummaryGeneration(mesId, config, callbacks = {}) {
|
||||
onStatus?.(`正在总结 ${slice.range}(${slice.count}楼新内容)...`);
|
||||
|
||||
const existingSummary = formatExistingSummaryForAI(store);
|
||||
const existingWorld = store?.json?.world || [];
|
||||
const existingFacts = getFacts();
|
||||
const nextEventId = getNextEventId(store);
|
||||
const existingEventCount = store?.json?.events?.length || 0;
|
||||
const useStream = config.trigger?.useStream !== false;
|
||||
@@ -196,7 +191,7 @@ export async function runSummaryGeneration(mesId, config, callbacks = {}) {
|
||||
try {
|
||||
raw = await generateSummary({
|
||||
existingSummary,
|
||||
existingWorld,
|
||||
existingFacts,
|
||||
newHistoryText: slice.text,
|
||||
historyRange: slice.range,
|
||||
nextEventId,
|
||||
@@ -231,7 +226,7 @@ export async function runSummaryGeneration(mesId, config, callbacks = {}) {
|
||||
return { success: false, error: "parse" };
|
||||
}
|
||||
|
||||
sanitizeWorldUpdate(parsed);
|
||||
sanitizeFacts(parsed);
|
||||
const existingEventIds = new Set((store?.json?.events || []).map(e => e?.id).filter(Boolean));
|
||||
sanitizeEventsCausality(parsed, existingEventIds);
|
||||
|
||||
@@ -245,8 +240,8 @@ export async function runSummaryGeneration(mesId, config, callbacks = {}) {
|
||||
|
||||
xbLog.info(MODULE_ID, `总结完成,已更新至 ${slice.endMesId + 1} 楼`);
|
||||
|
||||
if (parsed.worldUpdate?.length) {
|
||||
xbLog.info(MODULE_ID, `世界状态更新: ${parsed.worldUpdate.length} 条`);
|
||||
if (parsed.factUpdates?.length) {
|
||||
xbLog.info(MODULE_ID, `Facts 更新: ${parsed.factUpdates.length} 条`);
|
||||
}
|
||||
|
||||
const newEventIds = (parsed.events || []).map(e => e.id);
|
||||
@@ -255,7 +250,7 @@ export async function runSummaryGeneration(mesId, config, callbacks = {}) {
|
||||
merged,
|
||||
endMesId: slice.endMesId,
|
||||
newEventIds,
|
||||
l3Stats: { worldUpdate: parsed.worldUpdate?.length || 0 },
|
||||
factStats: { updated: parsed.factUpdates?.length || 0 },
|
||||
});
|
||||
|
||||
return { success: true, merged, endMesId: slice.endMesId, newEventIds };
|
||||
|
||||
Reference in New Issue
Block a user