212ff3ff80
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>
152 lines
5.3 KiB
JavaScript
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);
|
|
}
|