Files
memind/temporal-recall-service/keyword-rules.mjs
T
john 212ff3ff80 Add User Model Service and Temporal Recall for MeMind V0.1.
Introduce UMS ingest/snapshot pipeline, Context Planner with multi-source recall, runtime context injection, canonical user mapping, and session snapshot loading on auth/me.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 23:25:18 +08:00

152 lines
5.3 KiB
JavaScript

const CALENDAR_KEYWORDS = [
'行程', '会议', '约会', '日历', '几点', '安排', '预约', '档期', '出发', '航班', '火车',
];
const MEINPUT_KEYWORDS = [
'输入', '打字', '说过', '原话', '上屏', '写过', '输入过', '发了', '微信', '消息',
];
const CHAT_KEYWORDS = [
'聊天', '对话', '讨论', '聊过', '问过', '你记得', '我们说过', '上次聊', 'agent', '助手',
];
const MEMORY_KEYWORDS = [
'决定', '定了', '之前定的', '记得', '记住', '上次', '当时', '承诺', '约定',
];
const COMMITMENT_KEYWORDS = [
'待办', '要做', '记得', '别忘了', '跟进', '确认', '截止', '交付', '完成', '处理',
];
const IMPORTANCE_KEYWORDS = ['重要', '关键', '紧急', '必须', '大事', '要紧'];
const SCHEDULE_QUESTION = /有什么|哪些|什么事|干嘛|做了什么|发生/;
const TEMPORAL_QUESTION = /什么时候|何时|几点|哪天/;
/**
* @param {string} query
*/
export function analyzeKeywords(query) {
const text = String(query ?? '');
const rule_hits = [];
const sources = {
calendar: 0.35,
chat: 0.55,
meinput: 0.55,
memory_v2: 0.35,
};
for (const kw of CALENDAR_KEYWORDS) {
if (text.includes(kw)) {
sources.calendar = Math.min(1, sources.calendar + 0.15);
rule_hits.push(`keyword:calendar:${kw}`);
}
}
for (const kw of MEINPUT_KEYWORDS) {
if (text.includes(kw)) {
sources.meinput = Math.min(1, sources.meinput + 0.12);
rule_hits.push(`keyword:meinput:${kw}`);
}
}
for (const kw of CHAT_KEYWORDS) {
if (text.includes(kw)) {
sources.chat = Math.min(1, sources.chat + 0.12);
rule_hits.push(`keyword:chat:${kw}`);
}
}
for (const kw of MEMORY_KEYWORDS) {
if (text.includes(kw)) {
sources.memory_v2 = Math.min(1, sources.memory_v2 + 0.12);
rule_hits.push(`keyword:memory:${kw}`);
}
}
if (SCHEDULE_QUESTION.test(text)) {
sources.calendar += 0.1;
sources.chat += 0.08;
sources.meinput += 0.08;
rule_hits.push('pattern:schedule_question');
}
const importance_min = IMPORTANCE_KEYWORDS.some((kw) => text.includes(kw)) ? 0.55 : 0.35;
if (importance_min > 0.35) rule_hits.push('filter:importance');
return { sources, rule_hits, importance_min, commitmentKeywords: COMMITMENT_KEYWORDS };
}
/**
* @param {string} query
* @param {{ sources: Record<string, number>, importance_min: number, commitmentKeywords: string[] }} kw
*/
export function inferTargets(query, kw) {
const text = String(query ?? '');
const targets = [];
if (/行程|会议|约会|日历|安排/.test(text)) {
targets.push({ type: 'calendar_event', weight: 0.9 }, { type: 'schedule', weight: 0.85 });
}
if (/待办|要做|完成|跟进|截止/.test(text)) {
targets.push({ type: 'todo', weight: 0.95 });
}
if (/决定|定了|承诺|约定/.test(text)) {
targets.push({ type: 'commitment', weight: 0.95 }, { type: 'decision', weight: 0.85 });
}
if (/重要|关键|紧急/.test(text)) {
targets.push({ type: 'important_event', weight: 0.9 });
}
if (!targets.length || /什么事|做了什么|发生|输入|说过/.test(text)) {
targets.push({ type: 'mention', weight: 0.75 }, { type: 'event', weight: 0.7 });
}
const seen = new Set();
return targets.filter((t) => {
if (seen.has(t.type)) return false;
seen.add(t.type);
return true;
});
}
/**
* @param {string} query
*/
export function inferTemporalMode(query) {
const text = String(query ?? '');
if (/说过|提到|安排|定了|计划|要做|记得|别忘了/.test(text) && /昨天|前天|上周|这周/.test(text)) {
if (/做了什么|发生了什么|干嘛了/.test(text)) return 'AMBIGUOUS';
if (/说过|提到|定了|安排/.test(text)) return 'MENTIONED_IN';
}
if (/有什么安排|行程|会议|几点|今天|明天/.test(text)) return 'PLANNED_IN';
if (/定了哪些|创建|记录/.test(text)) return 'CREATED_IN';
if (/截止|due|要交|到期/.test(text)) return 'DUE_IN';
if (/做了什么|发生了什么|干嘛|什么事/.test(text)) return 'OCCURRED_IN';
if (/重要|什么事/.test(text)) return 'AMBIGUOUS';
return 'AMBIGUOUS';
}
/**
* @param {string} query
*/
export function isTemporalRecallQuery(query) {
const text = String(query ?? '').trim();
if (!text) return false;
if (TEMPORAL_QUESTION.test(text)) return true;
if (/今天|昨天|前天|明天|这周|上周|本月|最近|近期|这几天/.test(text)) return true;
if (/做了什么|发生了什么|什么事|有什么|哪些|安排|行程|输入过|说过/.test(text)) return true;
return false;
}
export function expandQueries(source, query, commitmentKeywords) {
const base = [];
const text = String(query ?? '');
if (source === 'meinput') {
base.push('会议', '安排', '明天', '今天', '项目', '确认', '跟进', '截止', '记得');
} else if (source === 'chat') {
base.push('约', '安排', '明天', '会议', '确认', '跟进', '截止', '记得', '待办');
} else if (source === 'memory_v2') {
base.push('commitment', 'decision', 'task', 'todo');
} else if (source === 'calendar') {
base.push('会议', '预约', '行程');
}
for (const kw of commitmentKeywords) {
if (text.includes(kw) && !base.includes(kw)) base.push(kw);
}
for (const kw of IMPORTANCE_KEYWORDS) {
if (text.includes(kw) && !base.includes(kw)) base.push(kw);
}
return base.slice(0, 12);
}