Files
memind/health-intent-rules.mjs
T
john 2baf29b3ae feat(health): complete P0 health channel — baseline engine, page-data, MindSpace UI
Deliver encrypted health zone, observation API, baseline maturity pipeline,
page-data bindings, and H5/WeChat channel integration for health P0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:04:57 +08:00

154 lines
4.5 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 { extractBloodPressurePair } from './health-bp-parse.mjs';
import { looksLikeHealthReportPageRequest, looksLikeHealthQuickAssess } from './health-channel-aliases.mjs';
export { looksLikeHealthReportPageRequest, looksLikeHealthQuickAssess } from './health-channel-aliases.mjs';
export const HEALTH_RULE_INTENTS = Object.freeze({
BLOOD_PRESSURE: 'blood_pressure',
WEIGHT: 'weight',
HEART_RATE: 'heart_rate',
SPO2: 'spo2',
TEMPERATURE: 'temperature',
SLEEP: 'sleep',
SYMPTOM: 'symptom',
ASSESS: 'assess',
ARCHIVE: 'archive',
});
const BP_PAIR = /(\d{2,3})\s*[/]\s*(\d{2,3})/;
export function matchHealthIdleRules(text) {
const raw = String(text ?? '').trim();
if (!raw) return { matched: false, confidence: 0 };
const bpPair = extractBloodPressurePair(raw);
if (bpPair?.systolic != null && bpPair?.diastolic != null) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE,
confidence: bpPair.source === 'chinese' ? 0.88 : 0.95,
extracted: { systolic: bpPair.systolic, diastolic: bpPair.diastolic },
};
}
const bp = raw.match(BP_PAIR);
if (bp) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE,
confidence: 0.95,
extracted: { systolic: Number(bp[1]), diastolic: Number(bp[2]) },
};
}
if (/血压/.test(raw) && /\d{2,3}/.test(raw)) {
const nums = [...raw.matchAll(/\d{2,3}/g)].map((m) => Number(m[0]));
if (nums.length >= 2) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE,
confidence: 0.8,
extracted: { systolic: nums[0], diastolic: nums[1] },
};
}
}
const weight = raw.match(/体重\s*(\d{2,3}(?:\.\d)?)/);
if (weight) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.WEIGHT,
confidence: 0.9,
extracted: { weight: Number(weight[1]) },
};
}
const hr = raw.match(/(?:心率|脉搏)\s*(\d{2,3})/);
if (hr) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.HEART_RATE,
confidence: 0.9,
extracted: { hr: Number(hr[1]) },
};
}
const spo2 = raw.match(/(?:血氧|SpO2|spo2)\s*(\d{2,3})/i);
if (spo2) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.SPO2,
confidence: 0.9,
extracted: { spo2: Number(spo2[1]) },
};
}
const temp = raw.match(/(?:体温|发烧)\s*(3\d(?:\.\d)?)/);
if (temp) {
return {
matched: true,
intent: HEALTH_RULE_INTENTS.TEMPERATURE,
confidence: 0.9,
extracted: { temperature: Number(temp[1]) },
};
}
if (/睡了|睡眠/.test(raw) && /\d/.test(raw)) {
return { matched: true, intent: HEALTH_RULE_INTENTS.SLEEP, confidence: 0.75, extracted: {} };
}
if (/头晕|胸闷|乏力|心慌|头痛|气短/.test(raw)) {
return { matched: true, intent: HEALTH_RULE_INTENTS.SYMPTOM, confidence: 0.8, extracted: {} };
}
if (looksLikeHealthReportPageRequest(raw) || looksLikeHealthQuickAssess(raw)) {
return { matched: true, intent: HEALTH_RULE_INTENTS.ASSESS, confidence: 0.85, extracted: {} };
}
if (/我的档案|历史记录/.test(raw) || (/报告/.test(raw) && !looksLikeHealthReportPageRequest(raw))) {
return { matched: true, intent: HEALTH_RULE_INTENTS.ARCHIVE, confidence: 0.8, extracted: {} };
}
return { matched: false, confidence: 0 };
}
export function shouldForceActionChoice(ruleResult, llm = null) {
if (ruleResult?.matched && ruleResult.confidence >= 0.75) return false;
const confidence = Number(llm?.confidence ?? 0);
const gap = Number(llm?.top1 ?? 0) - Number(llm?.top2 ?? 0);
if (confidence < 0.75 || gap < 0.15) return true;
return false;
}
export function shouldRedirectOrdinaryChatToHealth(text) {
const hit = matchHealthIdleRules(text);
if (!hit.matched) return false;
return [
HEALTH_RULE_INTENTS.BLOOD_PRESSURE,
HEALTH_RULE_INTENTS.WEIGHT,
HEALTH_RULE_INTENTS.HEART_RATE,
HEALTH_RULE_INTENTS.SPO2,
HEALTH_RULE_INTENTS.TEMPERATURE,
HEALTH_RULE_INTENTS.SLEEP,
HEALTH_RULE_INTENTS.SYMPTOM,
HEALTH_RULE_INTENTS.ASSESS,
].includes(hit.intent);
}
export const HEALTH_REDIRECT_COPY =
'你想记录健康数据吗?健康数据需要在健康助手通道里录入,这样才能保证记录准确并纳入你的健康基线分析。';
const HEALTH_ENTER_PATTERNS = [
/健康助手/,
/健康小助手/,
/进入健康/,
/健康通道/,
/录血压/,
];
export function isHealthEnterText(text) {
const raw = String(text ?? '').trim();
if (!raw) return false;
return HEALTH_ENTER_PATTERNS.some((pattern) => pattern.test(raw));
}