Files
memind/chat-intent-router-rules.mjs
T
john 43bc8bbc2b feat(h5): add LLM intent router admin controls and shadow verification.
Expose shadow/canary router policy in ops admin, add FAQ rule fast-path,
and tighten router defaults (1200ms timeout, 0.65 confidence).
Includes verify-h5-llm-router-shadow for production canary rollout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 16:48:01 +08:00

125 lines
4.8 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.
/**
* H5 Chat Intent Router — FAQ / fast-path rule library.
*
* Add patterns here for messages that should bypass LLM routing latency.
* Consumed by chat-intent-router.mjs → classifyWithRules().
*
* Keep rules conservative: task execution, page generation, and realtime lookup
* must stay on Agent paths (checked via FAQ_EXCLUSION_PATTERNS).
*/
/** Block FAQ match when the message clearly needs Agent / tools. */
export const FAQ_EXCLUSION_PATTERNS = [
/\bpublic\/[^\s"'<>]+\.html\b/i,
/MindSpace\/[^/\s]+\/public\/[^\s"'<>]+\.html/i,
/(?:生成|创建|制作|做|写|设计|发布|搭建).{0,24}(?:H5|h5|HTML|html|网页|页面|活动页|宣传页|落地页|分享页|链接)/u,
/(?:H5|h5|HTML|html|网页|页面).{0,24}(?:生成|创建|制作|做|写|设计|发布|链接)/u,
/(?:写|做|生成|制作|设计).{0,16}主题(?:页面|文章)/u,
/(?:服务号|公众号).{0,16}(?:页面|文章|推文|图文)/u,
/(?:今天|今日|现在|最新|当前|实时).{0,12}(?:新闻|赛况|比分|天气|行情|股价|汇率|热点)/u,
/(?:查(?:一下|查)?|搜索|打听).{0,16}(?:今天|今日|现在|最新|当前)/u,
/(?:世界杯|欧冠|NBA|英超).{0,16}(?:赛况|赛程|比分)/u,
/(?:load_skill|page-data|sqlite|express|后端服务)/iu,
/(?:修|改|测试|回归|verify).{0,12}(?:bug|问卷|policy|public)/iu,
];
/**
* @typedef {{ id: string, reason: string, patterns: RegExp[] }} DirectChatFaqRule
*/
/** @type {DirectChatFaqRule[]} */
export const DIRECT_CHAT_FAQ_RULES = [
{
id: 'concept_what_is',
reason: '概念解释类问答(FAQ',
patterns: [
/^什么是[\s\S]{1,48}[?]?$/u,
/^何为[\s\S]{1,48}[?]?$/u,
/^什么叫[\s\S]{1,48}[?]?$/u,
/^[\s\S]{1,40}是什么[?]?$/u,
/^[\s\S]{1,40}是什么意思[?]?$/u,
/^[\s\S]{1,40}(?:是什么东西|指什么|啥意思)[?]?$/u,
],
},
{
id: 'concept_explain',
reason: '解释说明类问答(FAQ',
patterns: [
/^(?:请)?(?:解释|说明|介绍|讲解|阐述)(?:一下|下)?[\s\S]{1,48}[?]?$/u,
/^帮我(?:解释|说明|介绍|讲解)(?:一下|下)?[\s\S]{1,48}[?]?$/u,
/^能(?:不能|否)(?:解释|说明|介绍)(?:一下|下)?[\s\S]{1,40}[?]?$/u,
],
},
{
id: 'concept_compare',
reason: '概念对比类问答(FAQ',
patterns: [
/^[\s\S]{1,32}(?:和|与|跟)[\s\S]{1,32}(?:的)?(?:区别|差异|不同|对比|比较)[是什么]?[?]?$/u,
/^(?:对比|比较)(?:一下|下)?[\s\S]{1,48}[?]?$/u,
/^[\s\S]{1,24}(?:vs|VS|Vs)[\s\S]{1,24}(?:区别|差异|不同)?[?]?$/u,
],
},
{
id: 'concept_how_works',
reason: '原理/机制类问答(FAQ',
patterns: [
/^[\s\S]{1,40}(?:是怎么|是如何)(?:工作|运行|实现|运作)的[?]?$/u,
/^[\s\S]{1,40}(?:原理|机制|流程)(?:是什么|是怎样的)[?]?$/u,
/^为什么[\s\S]{1,48}[?]?$/u,
],
},
{
id: 'text_transform',
reason: '纯文字处理(总结/翻译/改写)',
patterns: [
/^(?:请)?(?:帮我|帮忙)?(?:总结|概括|归纳|提炼)(?:一下|下)?[\s\S]{1,80}[?]?$/u,
/^(?:请)?(?:帮我|帮忙)?(?:翻译|改写|润色|扩写|缩写|校对)(?:一下|下)?[\s\S]{1,80}[?]?$/u,
],
},
{
id: 'tech_acronym',
reason: '常见技术名词解释(FAQ',
patterns: [
/^(?:什么是|解释一下|介绍一下)?\s*(?:SSE|WebSocket|REST(?:ful)?|GraphQL|JWT|OAuth|RAG|Embedding|向量数据库|Server-Sent Events)[\s\S]{0,24}[?]?$/iu,
/^(?:SSE|WebSocket|REST(?:ful)?|GraphQL|JWT|OAuth|RAG)(?:是什么|什么意思|怎么用)[?]?$/iu,
],
},
{
id: 'learning_advice',
reason: '学习/选型建议(纯文字)',
patterns: [
/^(?:学|学习|入门).{0,24}(?:应该|该|推荐|建议).{0,32}(?:先|从哪|哪个)[?]?$/u,
/^(?:初学者|新手|入门).{0,24}(?:应该|该|推荐|建议).{0,32}[?]?$/u,
/^.{0,24}(?:先学|从哪学|怎么学).{0,32}[?]?$/u,
],
},
{
id: 'casual_opinion',
reason: '主观建议/闲聊(无任务执行)',
patterns: [
/^你觉得.{1,48}[?]?$/u,
/^你认为.{1,48}[?]?$/u,
/^(?:随便|随意)(?:聊|说说|谈谈).{0,24}[?]?$/u,
/^.{0,16}(?:有什么建议|给点建议|怎么看)[?]?$/u,
],
},
];
const MAX_FAQ_TEXT_LENGTH = 200;
/**
* @param {string} text
* @returns {{ id: string, reason: string } | null}
*/
export function matchDirectChatFaqRule(text) {
const normalized = String(text ?? '').trim();
if (!normalized || normalized.length > MAX_FAQ_TEXT_LENGTH) return null;
if (FAQ_EXCLUSION_PATTERNS.some((pattern) => pattern.test(normalized))) return null;
for (const rule of DIRECT_CHAT_FAQ_RULES) {
if (rule.patterns.some((pattern) => pattern.test(normalized))) {
return { id: rule.id, reason: rule.reason };
}
}
return null;
}