Files
ST-Bionic-Memory-Ecology/maintenance/smart-trigger.js
2026-04-08 03:03:09 +08:00

122 lines
3.0 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.

import { isSystemMessageForExtraction } from "./chat-history.js";
export const DEFAULT_TRIGGER_KEYWORDS = [
"突然",
"没想到",
"原来",
"其实",
"发现",
"背叛",
"死亡",
"复活",
"恢复记忆",
"失忆",
"告白",
"暴露",
"秘密",
"计划",
"规则",
"契约",
"位置",
"地点",
"离开",
"来到",
];
export function getSmartTriggerDecision(
chat,
lastProcessed,
settings,
endFloor = null,
) {
const safeChat = Array.isArray(chat) ? chat : [];
const startFloor = Math.max(0, (lastProcessed ?? -1) + 1);
const normalizedEndFloor =
endFloor == null || endFloor === ""
? null
: Number.isFinite(Number(endFloor))
? Math.max(startFloor - 1, Math.floor(Number(endFloor)))
: null;
const pendingMessages = safeChat
.slice(
startFloor,
normalizedEndFloor == null ? undefined : normalizedEndFloor + 1,
)
.map((msg, offset) => ({
msg,
index: startFloor + offset,
}))
.filter(({ msg, index }) =>
!isSystemMessageForExtraction(msg, { index, chat: safeChat }),
)
.map(({ msg }) => ({
role: msg.is_user ? "user" : "assistant",
content: msg.mes || "",
}))
.filter((msg) => msg.content.trim().length > 0);
if (pendingMessages.length === 0) {
return { triggered: false, score: 0, reasons: [] };
}
const reasons = [];
let score = 0;
const combinedText = pendingMessages.map((message) => message.content).join("\n");
const keywordHits = DEFAULT_TRIGGER_KEYWORDS.filter((keyword) =>
combinedText.includes(keyword),
);
if (keywordHits.length > 0) {
score += Math.min(2, keywordHits.length);
reasons.push(`关键词: ${keywordHits.slice(0, 3).join(", ")}`);
}
const customPatterns = String(settings?.triggerPatterns || "")
.split(/\r?\n|,/)
.map((item) => item.trim())
.filter(Boolean);
for (const pattern of customPatterns) {
try {
const regex = new RegExp(pattern, "i");
if (regex.test(combinedText)) {
score += 2;
reasons.push(`自定义触发: ${pattern}`);
break;
}
} catch {
// 忽略无效正则,避免影响主流程
}
}
const roleSwitchCount = pendingMessages.reduce((count, message, index) => {
if (index === 0) return count;
return count + (message.role !== pendingMessages[index - 1].role ? 1 : 0);
}, 0);
if (roleSwitchCount >= 2) {
score += 1;
reasons.push("多轮往返互动");
}
const punctuationHits = (combinedText.match(/[!?]/g) || []).length;
if (punctuationHits >= 2) {
score += 1;
reasons.push("情绪/冲突波动");
}
const entityLikeHits =
combinedText.match(
/[A-Z][a-z]{2,}|[\u4e00-\u9fff]{2,6}(先生|小姐|王国|城|镇|村|学院|组织|公司|小队|军团)/g,
) || [];
if (entityLikeHits.length > 0) {
score += 1;
reasons.push("疑似新实体/新地点");
}
const threshold = Math.max(1, settings?.smartTriggerThreshold || 2);
return {
triggered: score >= threshold,
score,
reasons,
};
}