a30b62b987
Memind CI / Test, build, and release guards (push) Has been cancelled
Close the chat-session dead zone without flipping production flags: record 0.72 fallbacks, stop C-2 from treating「先聊聊」as execute, and promote「不对你去执行」after a completed direct turn on both h5direct and date sessions. Co-authored-by: Cursor <cursoragent@cursor.com>
108 lines
3.5 KiB
JavaScript
108 lines
3.5 KiB
JavaScript
export const RULE_FALLBACK_CONFIDENCE = 0.72;
|
|
export const RULE_FALLBACK_REASON = '普通对话默认走轻量聊天';
|
|
|
|
function asFiniteNumber(value) {
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
function bucketTextLength(length) {
|
|
if (!length) return 'empty';
|
|
if (length <= 6) return 'short_le6';
|
|
if (length <= 12) return 'medium_le12';
|
|
return 'long_gt12';
|
|
}
|
|
|
|
export function isRuleFallbackClassification(classification = {}) {
|
|
if (classification?.ruleFallback === true) return true;
|
|
const source = String(classification?.source ?? '').trim();
|
|
const reason = String(classification?.reason ?? '').trim();
|
|
const confidence = asFiniteNumber(classification?.confidence);
|
|
return source === 'rule'
|
|
&& confidence != null
|
|
&& Math.abs(confidence - RULE_FALLBACK_CONFIDENCE) < 1e-9
|
|
&& reason.includes(RULE_FALLBACK_REASON);
|
|
}
|
|
|
|
export function summarizeChatIntentObservation(classification = {}, context = {}) {
|
|
const text = String(context.text ?? '').trim();
|
|
const textLength = text.length;
|
|
const confidence = asFiniteNumber(classification.confidence);
|
|
return {
|
|
route: classification.route ?? null,
|
|
source: String(classification.source ?? '').trim() || null,
|
|
confidence,
|
|
ruleFallback: isRuleFallbackClassification(classification),
|
|
memoryRecall: context.memoryRecall === true,
|
|
wouldChangeRoute: Boolean(classification.llmShadow?.wouldChangeRoute),
|
|
llmShadowRoute: classification.llmShadow?.route ?? null,
|
|
textLength,
|
|
textLengthBucket: bucketTextLength(textLength),
|
|
};
|
|
}
|
|
|
|
export function observationFromIntentRoutedData(data = {}, fallbackText = '') {
|
|
if (data?.observation && typeof data.observation === 'object') {
|
|
return data.observation;
|
|
}
|
|
return summarizeChatIntentObservation(data, { text: fallbackText });
|
|
}
|
|
|
|
function increment(map, key) {
|
|
const normalized = key == null ? '(null)' : String(key);
|
|
map[normalized] = (map[normalized] || 0) + 1;
|
|
}
|
|
|
|
export function aggregateChatIntentObservations({
|
|
routed = [],
|
|
escalatedCount = 0,
|
|
contextInjectedCount = 0,
|
|
} = {}) {
|
|
const total = routed.length;
|
|
const sourceCounts = {};
|
|
const routeCounts = {};
|
|
const bucketCounts = {};
|
|
let ruleFallbackCount = 0;
|
|
let memoryRecallCount = 0;
|
|
let memoryRecallNotDirect = 0;
|
|
let shadowObserved = 0;
|
|
let wouldChangeRoute = 0;
|
|
|
|
for (const item of routed) {
|
|
increment(sourceCounts, item.source);
|
|
increment(routeCounts, item.route);
|
|
increment(bucketCounts, item.textLengthBucket);
|
|
if (item.ruleFallback) ruleFallbackCount += 1;
|
|
if (item.memoryRecall) {
|
|
memoryRecallCount += 1;
|
|
if (item.route && item.route !== 'direct_chat') memoryRecallNotDirect += 1;
|
|
}
|
|
if (item.llmShadowRoute != null || item.wouldChangeRoute) {
|
|
shadowObserved += 1;
|
|
}
|
|
if (item.wouldChangeRoute) wouldChangeRoute += 1;
|
|
}
|
|
|
|
const ratio = (count) => (total > 0 ? Number((count / total).toFixed(4)) : 0);
|
|
return {
|
|
total,
|
|
ruleFallbackCount,
|
|
ruleFallbackRatio: ratio(ruleFallbackCount),
|
|
escalatedCount,
|
|
escalationRatio: ratio(escalatedCount),
|
|
contextInjectedCount,
|
|
sourceCounts,
|
|
routeCounts,
|
|
shortMessageCounts: bucketCounts,
|
|
memoryRecallCount,
|
|
memoryRecallDirectRatio: memoryRecallCount > 0
|
|
? Number(((memoryRecallCount - memoryRecallNotDirect) / memoryRecallCount).toFixed(4))
|
|
: 1,
|
|
shadowObserved,
|
|
wouldChangeRoute,
|
|
wouldChangeRouteRatio: shadowObserved > 0
|
|
? Number((wouldChangeRoute / shadowObserved).toFixed(4))
|
|
: 0,
|
|
};
|
|
}
|