fix: broaden WeChat session page continuation phrasing
Memind CI / Test, build, and release guards (push) Successful in 2m37s

Recognize retry, HTML page, page edits, and poem content edits as session continuations so delivery skips fresh-thumbnail forcing and retains agent context for multi-turn page work.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-28 16:54:42 +08:00
parent 6307f974cf
commit 7eedda092d
9 changed files with 397 additions and 42 deletions
+69
View File
@@ -0,0 +1,69 @@
/** WeChat short follow-ups that continue an in-session page or poem without a full new topic. */
const PAGE_DECORATORS =
'(?:精美(?:的)?|漂亮(?:的)?|好看(?:的)?|简约(?:的)?|优雅(?:的)?)';
export const PAGE_RETRY_PATTERN =
/^(?:(?:请|麻烦|可以|能不能)\s*)?(?:(?:帮我|帮忙|给我)\s*)?重试(?:上次|上一个)?页面[。.!\s]*$/iu;
export const PAGE_EDIT_PATTERN =
/^(?:(?:请|麻烦|可以|能不能)\s*)?(?:(?:帮我|帮忙|给我)\s*)?(?:(?:把|将)\s*)?(?:(?:这个|刚才(?:的)?|上面(?:的)?|前面(?:的)?|上一条|它|刚才(?:生成|做|弄)(?:的)?|上面(?:生成|做|弄)(?:的)?)\s*)?(?:的)?\s*(?:页面|网页|html)?\s*(?:的)?\s*(?:内容|标题|背景|样式|排版|封面|页脚|字体|颜色)?\s*(?:改|修改|调整|更新|换|优化|美化)/iu;
export const PAGE_IMMEDIATE_CREATE_PATTERN =
new RegExp(
'^'
+ '(?:(?:请|麻烦|可以|能不能)\\s*)?'
+ '(?:(?:帮我|帮忙|给我)\\s*)?'
+ '(?:(?:把\\s*)?(?:这个|刚才(?:的)?(?:诗|文章|内容)?|上面(?:的)?|前面(?:的)?|上一条|它|这首(?:诗)?|这篇(?:文章)?|这段(?:内容)?)\\s*)?'
+ '(?:继续\\s*)?'
+ '(?:做|弄|改|转|写|排版|生成|制作)(?:成|为)?\\s*'
+ '(?:一个|个)?'
+ '(?:(?:\\s*'
+ PAGE_DECORATORS
+ '){1,3}\\s*(?:h5|html)|(?:\\s*(?:h5|html)))?'
+ '\\s*(?:页面|网页)(?:吧|呀|呢|一下)?[。.!\\s]*'
+ '$',
'iu',
);
export const PAGE_CONTENT_EDIT_PATTERN =
/^(?:(?:请|麻烦|可以|能不能)\s*)?(?:(?:帮我|帮忙|给我)\s*)?(?:(?:把|将)\s*)?(?:(?:这个|刚才(?:的)?|上面(?:的)?|前面(?:的)?|它|这首(?:诗)?|这篇(?:文章)?|这段(?:内容)?|诗(?:里|中)?)\s*)?(?:的)?\s*(?:[\p{L}\p{N}]{0,12}\s*)?(?:改|修改|调整|更新|换|加长|缩短|润色)/iu;
export function isWechatPageRetryText(text) {
const normalized = String(text ?? '').trim();
return Boolean(normalized && PAGE_RETRY_PATTERN.test(normalized));
}
export function isWechatPageEditText(text) {
const normalized = String(text ?? '').trim();
return Boolean(normalized && PAGE_EDIT_PATTERN.test(normalized));
}
export function isWechatContentEditFollowup(text) {
const normalized = String(text ?? '').trim();
return Boolean(normalized && PAGE_CONTENT_EDIT_PATTERN.test(normalized));
}
export function isWechatImmediateContextPageCreate(wechatIntent, text) {
if (wechatIntent?.kind !== 'page.generate') return false;
const normalized = String(text ?? '').trim();
return Boolean(normalized && PAGE_IMMEDIATE_CREATE_PATTERN.test(normalized));
}
export function isWechatSessionPageContinuation(wechatIntent, text) {
const normalized = String(text ?? '').trim();
if (!normalized) return false;
if (isWechatPageRetryText(normalized)) return true;
if (isWechatImmediateContextPageCreate(wechatIntent, normalized)) return true;
if (wechatIntent?.kind === 'page.generate' && isWechatPageEditText(normalized)) return true;
if (wechatIntent?.kind === 'chat.general' && isWechatContentEditFollowup(normalized)) return true;
return false;
}
export function expectsWechatHtmlArtifactDelivery(wechatIntent, text) {
const normalized = String(text ?? '').trim();
return (
wechatIntent?.kind === 'page.generate'
|| isWechatSessionPageContinuation(wechatIntent, normalized)
);
}
+10 -1
View File
@@ -1,5 +1,10 @@
/** WeChat channel intent patterns — not shared with H5 chat-skills routing. */
import {
isWechatPageEditText,
isWechatPageRetryText,
} from './page-continuation.mjs';
export const PAGE_GENERATE_PATTERN =
/(?:生成|创建|做|写|帮我.*(?:生成|创建|做|写)).*(?:html|页面|网页|page|文件)/iu;
@@ -25,7 +30,11 @@ export function isPageGenerateText(text) {
const normalized = String(text ?? '').trim();
if (!normalized) return false;
if (PAGE_GENERATE_NEGATION_PATTERN.test(normalized)) return false;
return PAGE_GENERATE_PATTERN.test(normalized);
return (
PAGE_GENERATE_PATTERN.test(normalized)
|| isWechatPageRetryText(normalized)
|| isWechatPageEditText(normalized)
);
}
export function isTopicResetText(text) {
+14 -1
View File
@@ -8,7 +8,11 @@ import { buildCurrentTimeAgentPrefix } from '../../user-memory-profile.mjs';
import { isPageGenerateText, wantsDocxDownload } from '../intent/patterns.mjs';
import { buildWechatStandaloneImageInstruction } from '../image-generation-policy.mjs';
export function buildWechatAgentPrompt(intent, { grantedSkills = [], imagePolicy = null } = {}) {
export function buildWechatAgentPrompt(intent, {
grantedSkills = [],
imagePolicy = null,
preferSessionPageContinuation = false,
} = {}) {
const msgType = String(intent?.msgType ?? 'text');
const agentText = intent?.agentText ?? intent?.content ?? '';
const autoSkillPrefix =
@@ -56,6 +60,14 @@ export function buildWechatAgentPrompt(intent, { grantedSkills = [], imagePolicy
'',
].join('\n')
: '';
const sessionPageContinuationHint = preferSessionPageContinuation
? [
'【会话页面续作】用户在修改本会话刚生成的页面或诗/文章内容。',
'必须读取当前会话最近 assistant 回复或已落盘的 public/*.html,用 edit_file 更新目标 HTML。',
'禁止切换到无关主题;没有 edit_file/write_file 落盘就不要声称已修改或发送链接。',
'',
].join('\n')
: '';
const scheduleTimezone = process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai';
const currentTimeHint = buildCurrentTimeAgentPrefix({ timezone: scheduleTimezone });
const scheduleAssistantHint = shouldUseScheduleAssistant(agentText)
@@ -156,6 +168,7 @@ export function buildWechatAgentPrompt(intent, { grantedSkills = [], imagePolicy
if (docxDownloadHint) lines.push(docxDownloadHint);
if (pageDataCollectHint) lines.push(pageDataCollectHint);
if (pageDataRepairHint) lines.push(pageDataRepairHint);
if (sessionPageContinuationHint) lines.push(sessionPageContinuationHint);
if (pagePublishHint) lines.push(pagePublishHint);
if (scheduleAssistantHint) lines.push(scheduleAssistantHint);
if (imageGenerationHint) lines.push(imageGenerationHint);
+5 -4
View File
@@ -31,11 +31,12 @@ export function buildPageGenerateAgentPrompt(
});
const immediateContextBlock = preferImmediateContext
? [
'【即时上下文优先】当前需求是对本会话紧邻内容的续作。',
'“这个/刚才/做成页面”等指代只能解析为当前会话最近一条 assistant 回复中的完整正文(如诗全文、文章全文),禁止用长期记忆、历史偏好或旧任务替换主题。',
'【即时上下文优先】当前需求是对本会话紧邻内容的续作(做成页面、修改页面、重试上次页面或改诗/文章内容)。',
'“这个/刚才/做成页面/修改页面/重试上次页面”等指代只能解析为当前会话最近一条 assistant 回复或最近落盘的 public/*.html,禁止用长期记忆、历史偏好或旧任务替换主题。',
'如果当前会话没有可辨识的紧邻内容,必须请用户补充主题,禁止猜测并生成其他页面。',
'【write_file 硬门槛】必须先 `load_skill` → `static-page-publish`,再用 `write_file`/`edit_file` 把紧邻正文完整写入 `public/*.html` 后才能结束。',
'禁止只调用 load_skill、todo_write 或 generate_image 就回复;没有 write_file 落盘 HTML 视为未完成。',
'【write_file 硬门槛】必须先 `load_skill` → `static-page-publish`,再用 `write_file`/`edit_file` 写入或更新 `public/*.html` 后才能结束。',
'修改已有页面时必须 edit_file 更新同一 HTML 或明确的新文件,禁止只回复文字说明。',
'禁止只调用 load_skill、todo_write 或 generate_image 就回复;没有 write_file/edit_file 落盘 HTML 视为未完成。',
'缩略图/配图可选:优先完成 HTML 落盘与 mindspace-cover;没有 hero 图时 cover 可用 CSS 渐变或 omit,不要卡在 generate_image。',
'',
].join('\n')
+39
View File
@@ -0,0 +1,39 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { classifyWechatIntent } from './intent/classifier.mjs';
import {
isWechatSessionPageContinuation,
isWechatPageRetryText,
isWechatPageEditText,
isWechatContentEditFollowup,
} from './intent/page-continuation.mjs';
test('WeChat page continuation detects retry and edit phrasings', () => {
assert.equal(isWechatPageRetryText('重试上次页面'), true);
assert.equal(isWechatPageEditText('修改刚才生成的页面,背景改成浅蓝色'), true);
assert.equal(isWechatContentEditFollowup('把诗里第三段改长一点'), true);
const retryIntent = classifyWechatIntent({ msgType: 'text', agentText: '重试上次页面' });
assert.equal(retryIntent.kind, 'page.generate');
assert.equal(isWechatSessionPageContinuation(retryIntent, '重试上次页面'), true);
const editIntent = classifyWechatIntent({
msgType: 'text',
agentText: '把页面标题改成《七月清晨》',
});
assert.equal(editIntent.kind, 'page.generate');
assert.equal(isWechatSessionPageContinuation(editIntent, editIntent.topic), true);
const poemEdit = classifyWechatIntent({ msgType: 'text', agentText: '把诗里第三段改长一点' });
assert.equal(poemEdit.kind, 'chat.general');
assert.equal(isWechatSessionPageContinuation(poemEdit, '把诗里第三段改长一点'), true);
});
test('WeChat page continuation rejects full new-topic page requests', () => {
const intent = classifyWechatIntent({
msgType: 'text',
agentText: '帮我做一个上海旅游攻略页面',
});
assert.equal(intent.kind, 'page.generate');
assert.equal(isWechatSessionPageContinuation(intent, intent.topic), false);
});