-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdemand.js
More file actions
162 lines (119 loc) · 9.05 KB
/
Copy pathdemand.js
File metadata and controls
162 lines (119 loc) · 9.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
const memoryIntentPatterns = [
/(?:你|还|是否|能不能|可以).{0,6}记得/,
/(?:从|查|搜).{0,8}(?:记忆|历史记录)/,
/回忆(?:一下|下)?(?:我们|之前|上次|过去)?/,
/\b(?:do you|can you|you)\s+(?:still\s+)?remember\b/i,
/\brecall\s+(?:what|when|how|our|the)\b/i,
/\b(?:from|search)\s+(?:your\s+)?memor(?:y|ies)\b/i,
];
// A bare temporal word is deliberately insufficient: generic history questions must stay on the zero-work path.
const historicalReferencePatterns = [
/(?:之前|上次|上回|以前|先前|当时|昨天|前几天|曾经).{0,20}(?:我们|咱们|你|我).{0,12}(?:说过|聊过|提过|做过|改过|定过|决定过|约定过|讨论过|说的|聊的|提的|做的|改的|定的|决定的|约定的|讨论的|说了|聊了|提了|做了|改了|定了|决定了|约定了|讨论了)/,
/(?:我们|咱们|你|我).{0,12}(?:之前|上次|上回|以前|先前|当时|昨天|前几天|曾经).{0,12}(?:说过|聊过|提过|做过|改过|定过|决定过|约定过|讨论过|说的|聊的|提的|做的|改的|定的|决定的|约定的|讨论的|说了|聊了|提了|做了|改了|定了|决定了|约定了|讨论了)/,
/(?:之前|上次|上回|先前|当时)(?:的)?(?:决定|约定|结论|方案|配置|偏好|进度|版本)/,
/(?:我们|咱们|你|我).{0,12}(?:说过|聊过|提过|做过|改过|定过|决定过|约定过|讨论过|说的|聊的|提的|做的|改的|定的|决定的|约定的|讨论的).{0,24}(?:之前|上次|上回|以前|先前|当时|曾经)/,
/(?:继续|接着)(?:上次|之前|先前|昨天)/,
/(?:我|我们)?(?:之前|一贯|平时|原来)(?:的)?(?:偏好|习惯|风格|约定|方案|配置)/,
/\b(?:last time|previously|in (?:an|our) earlier session)\b.{0,80}\b(?:(?:we|i)\b.{0,40}\b(?:agreed|decided|discussed|worked|changed|configured)|you\b.{0,40}\b(?:said|told|recommended|changed|configured))\b/i,
/\b(?:(?:we|i)\b.{0,80}\b(?:agree|agreed|decide|decided|discuss|discussed|work|worked|change|changed|configure|configured)|you\b.{0,80}\b(?:say|said|tell|told|recommend|recommended|change|changed|configure|configured))\b.{0,40}\b(?:before|previously|last time)\b/i,
/\b(?:my|our)\s+(?:usual|saved|previous)\s+(?:preferences?|style|settings?)\b/i,
];
const ambiguousReferencePatterns = [
/(?:那个|那件事|之前那个|上次那个)(?:项目|插件|问题|方案|配置|任务|东西|事)?[^。!?\n]{0,36}(?:现在|目前|进展|状态|做到哪|哪一步|怎么样|后来)/,
/\b(?:that|the previous)\s+(?:project|plugin|issue|plan|task|setup)\b.{0,80}\b(?:now|status|progress|where|current|later)\b/i,
];
const uncertainContextPatterns = [
/(?:这个|当前|现有|目前|继续|接着|还是|再|重新).{0,24}(?:项目|插件|配置|方案|代码|实现|流程|任务|调优)/,
/(?:帮我|就按|照着|按照)(?:这个|上面|刚才|把)?[^。!?\n]{0,20}(?:落地|继续|修改|调整|优化|完善|实现)/,
/(?:我的|我们的|适合我|按我的|当前配置|现有配置|已有方案)/,
/\b(?:my|our|this|the current)\s+(?:project|plugin|setup|configuration|config|workflow|implementation|codebase)\b/i,
/\b(?:continue|keep going|implement|apply|tune|optimi[sz]e)\s+(?:this|that|it|the current)\b/i,
];
const hybridRoutePatterns = [
/(?:综合|结合|汇总|跨).{0,16}(?:多个|所有|项目|工具|偏好|设备|关系)/,
/(?:多个|所有).{0,12}(?:项目|工具|偏好|目标).{0,18}(?:关系|关联|变化|总结|汇总)/,
/\b(?:across|combine|summari[sz]e|all)\b.{0,60}\b(?:projects?|tools?|preferences?|goals?|relationships?)\b/i,
];
const graphRoutePatterns = [
/(?:关系|关联|依赖|谁在用|哪些项目|属于谁|发布在|运行在|维护工具|上下游|实体)/,
/(?:以前|之前|过去).{0,24}(?:现在|目前|后来|改成|变成|变化|更换)/,
/(?:现在|目前).{0,24}(?:以前|之前|过去|原来)/,
/\b(?:relationship|related|depends? on|maintained with|published on|runs? on|owned by|uses?)\b/i,
/\b(?:before|previously|used to)\b.{0,80}\b(?:now|currently|changed?|switched?)\b/i,
];
const graphDemandPatterns = [
/(?:我的|我们的|我那个|这个|当前|现有|之前|现在).{0,48}(?:关系|关联|依赖|谁在用|哪些项目|属于谁|发布在|运行在|维护工具|上下游)/,
/(?:关系|关联|依赖|谁在用|哪些项目|属于谁|发布在|运行在|维护工具|上下游).{0,48}(?:我的|我们的|我那个|这个|当前|现有|之前|现在)/,
/\b(?:my|our|this|current|previous)\b.{0,80}\b(?:relationship|related|depends? on|maintained with|published on|runs? on|owned by|uses?)\b/i,
/\b[a-z0-9]+[-_][a-z0-9_-]+\b.{0,60}(?:关系|关联|依赖|维护|发布|运行|\b(?:relationship|related|depends? on|maintained|published|runs? on)\b)/i,
];
export function routeRecallQuery(query ) {
if (hybridRoutePatterns.some((pattern) => pattern.test(query))) return "hybrid";
if (graphRoutePatterns.some((pattern) => pattern.test(query))) return "graph";
return "vector";
}
function matchPrefix(input , prefixes ) {
const lower = input.toLocaleLowerCase();
for (const prefix of prefixes) {
const normalizedPrefix = prefix.trim();
if (!normalizedPrefix || !lower.startsWith(normalizedPrefix.toLocaleLowerCase())) continue;
const remainder = input.slice(normalizedPrefix.length);
if (normalizedPrefix.startsWith("/") && remainder && !/^[\s::]/.test(remainder)) continue;
return { matched: true, query: remainder.replace(/^[\s::]+/, "").trim(), prefix: normalizedPrefix };
}
return { matched: false, query: input };
}
function includesKeyword(input , keywords ) {
const lower = input.toLocaleLowerCase();
return keywords.some((keyword) => keyword && lower.includes(keyword.toLocaleLowerCase()));
}
function explicitOptions(query ) {
let remaining = query;
let depth ;
let route ;
for (let index = 0; index < 2; index += 1) {
const match = remaining.match(/^(literal|balanced|deep|vector|graph|hybrid)\b[\s::]*/i);
if (!match) break;
const option = match[1].toLocaleLowerCase();
if (option === "literal" || option === "balanced" || option === "deep") depth = option;
else route = option ;
remaining = remaining.slice(match[0].length).trim();
}
return { depth, route, query: remaining };
}
/** High-precision, zero-LLM first-stage gate. Only uncertain results may use the semantic gate. */
export function evaluateRecallDemand(input , config ) {
const query = input.trim();
if (!query) return { decision: "no", query, reason: "not_needed", confidence: 1, route: "none" };
const suppressed = matchPrefix(query, config.suppressPrefixes);
if (suppressed.matched) return { decision: "no", query: suppressed.query, reason: "suppressed", confidence: 1, depth: "none", route: "none" };
const explicit = matchPrefix(query, config.explicitPrefixes);
if (explicit.matched) {
const parsed = explicitOptions(explicit.query);
return parsed.query.length > 0
? { decision: "yes", query: parsed.query, reason: "explicit_prefix", confidence: 1, depth: parsed.depth, route: parsed.route ?? routeRecallQuery(parsed.query) }
: { decision: "no", query: parsed.query, reason: "not_needed", confidence: 1, depth: "none", route: "none" };
}
if (config.mode === "always") return { decision: "yes", query, reason: "always", confidence: 1, route: routeRecallQuery(query) };
if (includesKeyword(query, config.additionalKeywords)) {
return { decision: "yes", query, reason: "custom_keyword", confidence: 1, route: routeRecallQuery(query) };
}
if (config.mode === "explicit") return { decision: "no", query, reason: "not_needed", confidence: 1, depth: "none", route: "none" };
if (hybridRoutePatterns.some((pattern) => pattern.test(query)) || graphDemandPatterns.some((pattern) => pattern.test(query))) {
return { decision: "yes", query, reason: "user_specific_context", confidence: 0.9, route: routeRecallQuery(query) };
}
if (memoryIntentPatterns.some((pattern) => pattern.test(query))) {
return { decision: "yes", query, reason: "memory_intent", confidence: 0.98, route: routeRecallQuery(query) };
}
if (historicalReferencePatterns.some((pattern) => pattern.test(query))) {
return { decision: "yes", query, reason: "historical_reference", confidence: 0.95, route: routeRecallQuery(query) };
}
if (ambiguousReferencePatterns.some((pattern) => pattern.test(query))) {
return { decision: "yes", query, reason: "ambiguous_reference", confidence: 0.92, route: routeRecallQuery(query) };
}
if (uncertainContextPatterns.some((pattern) => pattern.test(query))) {
return { decision: "uncertain", query, reason: "user_specific_context", route: routeRecallQuery(query) };
}
return { decision: "no", query, reason: "not_needed", confidence: 0.9, depth: "none", route: "none" };
}
//# sourceURL=C:\Users\lenovo\.openclaw\workspace\plugins\active_recall\demand.ts