fix(chat-intent): 隐式攻略/页面生成请求直接路由到 static-page-publish

短句如"苏州攻略页面"此前会被规则/LLM 误判为 product-campaign-page(先追问
促销信息),导致用户体验成"卡住不动"。规则层扩大页面生成意图识别,并新增
coercePageGenerationSkill 在 LLM 判为 product-campaign-page 但明显是内容页
时强制纠正为 static-page-publish,避免不必要的追问。同步更新两个技能文档
的适用边界说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-07 10:55:20 +08:00
parent 7edafefc56
commit ec8d0464a3
6 changed files with 121 additions and 15 deletions
+42 -11
View File
@@ -3,6 +3,8 @@ import {
buildWebNewsSkillPrompt,
extractSelectedChatSkillName,
hasExplicitChatSkillPrompt,
isPageGenerationIntent,
isProductCampaignIntent,
} from './chat-skills.mjs';
import { isDirectChatSessionId } from './direct-chat-service.mjs';
import {
@@ -111,6 +113,26 @@ function buildRealtimeInfoClassification() {
}, { source: 'rule' });
}
export function coercePageGenerationSkill(classification, text, { grantedSkills = [] } = {}) {
if (!classification || classification.route !== CHAT_INTENT_ROUTE.AGENT) return classification;
if (!isPageGenerationIntent(text) || isProductCampaignIntent(text)) return classification;
if (grantedSkills.length > 0 && !grantedSkills.includes('static-page-publish')) return classification;
const shouldUsePublishSkill =
!classification.suggestedSkill || classification.suggestedSkill === 'product-campaign-page';
if (!shouldUsePublishSkill) return classification;
const reasonSuffix = classification.suggestedSkill === 'product-campaign-page'
? '(内容页意图,改用 static-page-publish'
: '(内容页生成意图)';
return {
...classification,
suggestedSkill: 'static-page-publish',
agentBrief: '生成或更新 MindSpace 公开内容页面,并返回可访问链接;信息不足时使用合理默认,不要停在追问。',
reason: `${classification.reason}${reasonSuffix}`,
};
}
export function coerceRealtimeWebSkill(classification, text, { grantedSkills = [] } = {}) {
if (!classification || classification.route !== CHAT_INTENT_ROUTE.AGENT) return classification;
if (!isRealtimeInfoQuestion(text)) return classification;
@@ -276,6 +298,10 @@ function buildRouterSystemPrompt(grantedSkills = []) {
? '若走 agent_orchestration,可在 suggested_skill 中填写最匹配的 skill 名称(须来自上述列表),否则填 null。'
: 'suggested_skill 通常填 null。',
'',
'skill 选择补充:',
'- static-page-publish:攻略/游记/主题内容页、城市介绍、活动介绍、任何「做页面/生成链接」且无商品购买跳转需求。',
'- product-campaign-page:仅当用户明确提供商品链接或要求购买按钮/电商转化时使用,不要用于旅游攻略或纯内容页。',
'',
'只输出 JSON,不要 markdown,不要解释:',
'{"route":"direct_chat|agent_orchestration","confidence":0.0,"reason":"一句话","suggested_skill":null,"agent_brief":"给 Agent 的执行要点,direct_chat 时可为空"}',
].join('\n');
@@ -713,14 +739,15 @@ export function classifyWithRules({
if (
includeIntentPatterns &&
normalized &&
OBVIOUS_AGENT_PATTERNS.some((pattern) => pattern.test(normalized))
(OBVIOUS_AGENT_PATTERNS.some((pattern) => pattern.test(normalized))
|| isPageGenerationIntent(normalized))
) {
return finalizeRouterClassification(normalizeClassification({
route: CHAT_INTENT_ROUTE.AGENT,
confidence: 0.95,
reason: '明确需要生成或发布页面/文件',
suggested_skill: 'static-page-publish',
agent_brief: '生成或更新 MindSpace 公开页面,并返回可访问链接。',
agent_brief: '生成或更新 MindSpace 公开页面,并返回可访问链接;信息不足时使用合理默认,不要停在追问。',
}, { source: 'rule' }), decisionContext);
}
if (includeIntentPatterns && normalized && isRealtimeInfoQuestion(normalized)) {
@@ -848,12 +875,16 @@ export function createChatIntentRouter(options = {}) {
sessionId,
sessionMessageCount,
};
const finalizeWithRealtimeCoercion = (classification) => {
const finalizeWithCoercion = (classification) => {
if (!classification) return classification;
const base = { ...classification };
delete base.decision;
return finalizeRouterClassification(
coerceRealtimeWebSkill(base, text, { grantedSkills }),
coercePageGenerationSkill(
coerceRealtimeWebSkill(base, text, { grantedSkills }),
text,
{ grantedSkills },
),
decisionContext,
);
};
@@ -867,9 +898,9 @@ export function createChatIntentRouter(options = {}) {
includeIntentPatterns: true,
llmRouterEnabled: Boolean(policy.enabled),
});
if (ruleResult) return finalizeWithRealtimeCoercion(ruleResult);
if (ruleResult) return finalizeWithCoercion(ruleResult);
if (!isEnabled()) {
return finalizeWithRealtimeCoercion(normalizeClassification({
return finalizeWithCoercion(normalizeClassification({
route: policy.fallbackRoute,
confidence: 0.5,
reason: '意图路由未启用,走默认通道',
@@ -905,7 +936,7 @@ export function createChatIntentRouter(options = {}) {
'Chat intent router',
);
} catch (err) {
return finalizeWithRealtimeCoercion(normalizeClassification({
return finalizeWithCoercion(normalizeClassification({
route: policy.fallbackRoute,
confidence: 0,
reason: err instanceof Error ? err.message : '意图路由失败,走默认通道',
@@ -913,7 +944,7 @@ export function createChatIntentRouter(options = {}) {
}, { source: 'fallback', fallbackRoute: policy.fallbackRoute }));
}
if (!completion?.ok) {
return finalizeWithRealtimeCoercion(normalizeClassification({
return finalizeWithCoercion(normalizeClassification({
route: policy.fallbackRoute,
confidence: 0,
reason: completion?.message ?? '意图路由失败,走默认通道',
@@ -923,7 +954,7 @@ export function createChatIntentRouter(options = {}) {
const parsed = parseRouterJson(completion.reply);
if (!parsed) {
return finalizeWithRealtimeCoercion(normalizeClassification({
return finalizeWithCoercion(normalizeClassification({
route: policy.fallbackRoute,
confidence: 0,
reason: '意图路由响应无法解析,走默认通道',
@@ -931,7 +962,7 @@ export function createChatIntentRouter(options = {}) {
}, { source: 'fallback', fallbackRoute: policy.fallbackRoute }));
}
const classification = finalizeWithRealtimeCoercion({
const classification = finalizeWithCoercion({
...normalizeClassification(parsed, { source: 'llm', fallbackRoute: policy.fallbackRoute }),
providerKeyId: completion.providerKeyId ?? policy.modelProviderKeyId ?? null,
model: completion.model ?? policy.model ?? null,
@@ -941,7 +972,7 @@ export function createChatIntentRouter(options = {}) {
classification.route === CHAT_INTENT_ROUTE.DIRECT_CHAT &&
classification.confidence < policy.minConfidence
) {
return finalizeWithRealtimeCoercion(normalizeClassification({
return finalizeWithCoercion(normalizeClassification({
...classification,
route: policy.fallbackRoute,
reason: `${classification.reason}(置信度 ${classification.confidence} 低于阈值,走默认通道)`,