Files
memind/wechat/prompts/page-generate.mjs
T
john 8f88eec2bd
Memind CI / Test, build, and release guards (push) Successful in 4m40s
Separate WeChat chat, page refine, and task flows with explicit routing.
Add channel prefixes, pin edit targets in Cursor prompts, and force fresh sessions for page refine URLs so DeepSeek/Goose chat history does not pollute page tasks.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 14:10:02 +08:00

145 lines
8.2 KiB
JavaScript

import { buildCurrentTimeAgentPrefix } from '../../user-memory-profile.mjs';
import {
isPageDataDevIntent,
isPageDataIntent,
} from '../../chat-skills.mjs';
import { buildWechatPageImageInstruction } from '../image-generation-policy.mjs';
import { buildPageRefineTargetPromptBlock } from '../page-refine-target.mjs';
/**
* Service-account-only page generation prompt. Does not use H5 buildAutoChatSkillPrefix.
*/
export function buildPageGenerateAgentPrompt(
intent,
{
wantsDocx = false,
imagePolicy = null,
preferImmediateContext = false,
carriedSessionContent = '',
} = {},
) {
const topic = String(intent?.agentText ?? intent?.displayText ?? '').trim();
const pageDataTask = isPageDataIntent(topic) || isPageDataDevIntent(topic);
const scheduleTimezone = process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai';
const docxBlock = wantsDocx
? [
'【Word 下载】用户要求页面内可下载 Word/docx。',
'先 `load_skill` → `docx-generate` 生成 `public/*.docx`,再写 HTML 并链接同目录文档。',
'',
].join('\n')
: '';
const imageBlock = buildWechatPageImageInstruction(imagePolicy, {
sourceMessageId: intent?.msgId,
});
const immediateContextBlock = preferImmediateContext
? [
'【即时上下文优先】当前需求是对本会话紧邻内容的续作(做成页面、修改页面、重试上次页面或改诗/文章内容)。',
'“这个/刚才/做成页面/修改页面/重试上次页面”等指代只能解析为当前会话最近一条 assistant 回复或最近落盘的 public/*.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')
: '';
const carriedSessionBlock = String(carriedSessionContent ?? '').trim()
? [
'【待排版正文(来自上一轮会话,必须以此为主题,禁止改用 Memory 或其他任务)】',
String(carriedSessionContent).trim(),
'',
].join('\n')
: '';
const coverExample = imageBlock
? '<本轮 generate_image 返回的 asset.htmlSrc>'
: 'assets/hero.jpg';
const pageRefineBlock = buildPageRefineTargetPromptBlock(topic);
const pageDataBlock = pageDataTask
? [
'【Page Data 强制要求】这不是静态展示页。',
'先 `load_skill` → `page-data-collect`,再按技能执行:private_data_execute 建表 → private_data_register_dataset → 写 HTML → private_data_bind_workspace_page。',
'修复任务必须核对真实表、dataset、policy 与 bind;缺失时必须补齐,禁止只改 HTML。',
'HTML 必须使用 /assets/page-data-client.js 与服务端 authenticate/listRows/insertRow;禁止硬编码口令、本地存储和自建 API。',
'只有 Page Data 提交/读取交付验收通过后才能发送链接。',
'',
].join('\n')
: '';
return [
'【微信服务号 · 页面生成任务】',
'这是服务号专用页面生成,不是普通聊天。必须按步骤完成,未完成前禁止告诉用户“已生成/已发布”。',
'【定时任务规范】若用户是在为已有定时自动任务补充长期格式/参考页/执行约束(不是要你此刻写 HTML),改加载 scheduled-task-automation,用 scheduled_task_list + scheduled_task_update_spec;禁止走本页面交付链路。',
'',
docxBlock,
imageBlock,
immediateContextBlock,
carriedSessionBlock,
pageRefineBlock,
pageDataBlock,
'步骤(必须全部完成):',
pageDataTask
? '1. 完成 page-data-collect 数据能力后,再调用 `load_skill`,参数 name=`static-page-publish`,补齐视觉与发布要求。'
: '1. 必须先调用 `load_skill`,参数 name=`static-page-publish`。',
'2. 阅读技能说明后,用 sandbox-fs 的 `write_file` 或 `edit_file` 写入 `public/*.html`。',
'3. 禁止 shell/cat/heredoc/cp 写 HTML(不会出现在公网 MindSpace)。',
'4. 写完后确认目标文件已落盘,且内容是用户要的完整页面(不是占位 stub)。',
'5. 在 `<head>` 写入微信分享卡片所需元数据(必须):',
' - `<meta name="description" content="一句话摘要">`',
` - \`<meta name="mindspace-cover" content='{"tag":"主题","accent":"#主色","accent2":"#辅色","subtitle":"卖点","cover":"${coverExample}"}'>\``,
imageBlock
? ' tag/accent/subtitle 必须与页面主题一致;cover 必须引用本轮新生成资产,不得省略、复用旧图或填写占位路径。'
: ' tag/accent/subtitle 必须与页面主题一致;有 hero 图时 cover 指向同目录 raster 图(jpg/png)。',
'6. 交付前运行 `npm run check:mindspace-cover`(或 node scripts/check-mindspace-cover.mjs --user <uid>);有 error 则补元数据后再回复链接。',
'7. 页脚加 `<p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p>`(必须;禁止省略、不要用邮箱/tkmind.ai,也不要把该行 opacity 设太低导致看不见)。',
'8. 回复里只给一个正式域名的唯一正确链接;没有落盘或缺少上述元数据就不要发链接。',
'9. 禁止编造用户未提供的产品指标、客户数量、SLA、认证、案例、排名或承诺;没有可靠依据时使用定性描述,不得杜撰数字。',
'',
buildCurrentTimeAgentPrefix({ timezone: scheduleTimezone }),
`用户需求:${topic}`,
]
.filter(Boolean)
.join('\n');
}
export function buildImmediateContextPageRepairPrompt(intent) {
const topic = String(intent?.agentText ?? intent?.displayText ?? '').trim();
return [
'【即时上下文页面补写】上一轮页面生成未完成:已 load_skill 但未 write_file 落盘 HTML。',
'请立即读取本会话最近一条 assistant 回复中的完整正文,将其排版为完整静态页。',
'步骤:确认 static-page-publish 已加载 → 用 write_file 写入 public/*.html(含 mindspace-cover、platform-brand 页脚)→ 确认落盘后再回复唯一正式链接。',
'禁止再次只 generate_image 或只回复文字;本轮必须完成 write_file。',
topic ? `用户原话:${topic}` : '',
]
.filter(Boolean)
.join('\n');
}
export function buildPagePublishFailureText({
missingSharePreview = false,
missingPlatformBrand = false,
missingFreshThumbnail = false,
} = {}) {
if (missingFreshThumbnail) {
return [
'这次页面内容已生成,但没有完成服务号要求的本轮新缩略图,所以我先不发页面链接。',
'页面内容已经保留。请稍后回复“重试上次页面”,无需重新描述完整需求。',
].join('\n');
}
if (missingPlatformBrand) {
return [
'这次页面已落盘,但缺少页脚平台品牌标记(data-mindspace-page-tag="platform-brand"),所以我先不发链接。',
'请直接重发一次完整页面需求,我会重新按 static-page-publish 技能生成带 TKMind · 智趣 品牌页脚的页面。',
].join('\n');
}
if (missingSharePreview) {
return [
'这次页面已落盘,但缺少微信分享卡片所需的预览元数据(mindspace-cover 与摘要),所以我先不发链接。',
'请直接重发一次完整页面需求,我会重新按 static-page-publish 技能生成带预览图的页面。',
].join('\n');
}
return [
'这次页面没有按服务号页面技能真正生成成功,所以我先不发占位链接。',
'请直接重发一次完整页面需求,我会重新按 static-page-publish 技能链路生成。',
].join('\n');
}