Enable optional SEO/GEO injection and discovery routes for confirmed public pages while keeping private pages noindex. Add premium page template skills, portal catalog API, template shop UI, and Baidu push gated by memind_adm config. Co-authored-by: Cursor <cursoragent@cursor.com>
18 KiB
页面模板 Skill 设计(Agent 可调用 + 授权勾选 + 二期购买)
日期: 2026-08-04
状态: 已实现(Phase 1 + 余额购买) — 见 page-template-catalog.mjs、三个 page-template-* skill、Portal/Admin API。
1. 背景与目标
当前 H5 页面由 Goose Agent 按 static-page-publish 规范从零生成 HTML,平台只提供约束(cover 元数据、CSP、页脚标记等),没有可复用的 HTML 骨架库。导致:
- 样式与结构不稳定,同类页面差异大
- 易漏写
mindspace-cover、平台页脚、Page Data 脚本引用 - 用户无法选择「旅游 Hero / 商品促销 / 问卷壳」等固定视觉风格
目标:
- 提供一套平台维护的 HTML 模板,Agent 通过 skill 读取并填充后落盘
public/*.html - 不影响现有
static-page-publish→write_file→ finish guard 主链路 - 一期:管理员在 memind_adm 授权,用户在 H5 聊天勾选模板 skill
- 二期:用户可在模板商城购买,支付成功后自动写 skill grant
2. 核心结论
| 层级 | 负责方 | 说明 |
|---|---|---|
| HTML 骨架 | Memind 平台 skills/page-template-* |
预置合规 HTML + 占位符 |
| 文案 / 章节内容 | Goose + LLM(常见 DeepSeek) | 填占位符,不发明骨架 |
| 授权(谁能用) | h5_user_skill_grants |
与现有 skill 体系一致 |
| 使用(这次用哪个) | H5 聊天 skill 芯片 / 模板选择器 | 注入 templateId 到 prompt |
| 发布与守卫 | 不变 | finish guard、cover 检查、Page Data bind |
一句话:
模板是可选 skill 资产;未授权 = 行为与今天完全一致;授权且勾选 = Agent 读模板再 write_file。
3. 对现有链路的影响(硬约束)
3.1 不变的部分
static-page-publish仍是默认发布 skill(creator 角色或static_publish能力)- finish guard、
mindspace-cover检查、Page Data finish sync、sandbox MCP 路径规则 syncSkillsToWorkspace只同步已授权 skill;未授权模板不会出现在工作区- 意图路由:
coercePageGenerationSkill/page-data-collect优先级不变
3.2 增量部分
- 新增若干
page-template-*platform skill(默认 关闭) - 可选:
page-templates聚合 skill(目录索引 + 共用 SKILL.md),或每个模板独立 skill 便于单独定价 CHAT_SKILL_DEFINITIONS增加模板入口(仅grantedSkills包含时可见)- Agent 工作流多一步:
read_file模板 → 替换占位符 →write_file
3.3 禁止事项
- 禁止把
static-page-publish改为「必须选模板」 - 禁止模板 skill 绕过
page-data-collect的 bind / policy 流程 - 禁止在模板 HTML 内写
localStorage/ 外链<script src="https://..."> - Page Data 类模板必须预置
/assets/page-data-client.js引用位,Agent 只填 dataset 相关占位符
4. 目录结构
4.1 推荐:一模板一 skill(便于授权与定价)
skills/
├── page-template-travel/
│ ├── SKILL.md
│ ├── skill.yaml
│ └── templates/
│ └── travel-hero.html
├── page-template-campaign/
│ ├── SKILL.md
│ ├── skill.yaml
│ └── templates/
│ └── campaign-cta.html
├── page-template-report/
│ ├── SKILL.md
│ ├── skill.yaml
│ └── templates/
│ └── report-editorial.html
└── page-template-survey/
├── SKILL.md
├── skill.yaml
└── templates/
├── survey-basic.html # 前台问卷壳
└── survey-admin-read.html # 后台只读壳(password read)
同步后位于用户工作区:
MindSpace/<userId>/.agents/skills/page-template-travel/templates/travel-hero.html
4.2 可选:聚合 skill(一期快速试点)
skills/page-templates/
├── SKILL.md # 索引:templateId → 文件路径 + 适用场景
├── skill.yaml
└── templates/
├── travel-hero.html
├── campaign-cta.html
└── ...
权衡:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 一模板一 skill | 单独 grant/定价;商城 SKU 一一对应 | skill 数量多 |
| 聚合 skill | 实现快,一个 grant 全开 | 无法按模板单独售卖 |
建议: 一期用聚合 page-templates 试点;二期拆成 page-template-* 便于商城。
5. 模板文件规范
5.1 占位符约定
使用 Mustache 风格,Agent 替换后不得残留 {{:
<title>{{PAGE_TITLE}}</title>
<meta name="description" content="{{PAGE_DESCRIPTION}}">
<meta name="mindspace-cover" content='{{MINDSPACE_COVER_JSON}}'>
<h1>{{HERO_HEADLINE}}</h1>
<div class="content">{{MAIN_CONTENT_HTML}}</div>
| 占位符 | 必填 | 说明 |
|---|---|---|
PAGE_TITLE |
是 | 浏览器标题 |
PAGE_DESCRIPTION |
是 | description + cover subtitle 来源 |
MINDSPACE_COVER_JSON |
是 | 合法 JSON 字符串(tag/accent/cover 等) |
HERO_HEADLINE |
视模板 | 首屏标题 |
MAIN_CONTENT_HTML |
是 | Agent 生成的正文 HTML 片段(已 escape 或由 Agent 保证安全) |
ACCENT / ACCENT2 |
推荐 | 与 CSS 主色一致 |
Page Data 问卷模板额外占位符:
| 占位符 | 说明 |
|---|---|
DATASET_ID |
注册后的 dataset id |
FORM_FIELDS_HTML |
表单字段块 |
ADMIN_PAGE_FILENAME |
后台页相对路径 hint |
5.2 模板自检清单(入库前)
- 完整
<!DOCTYPE html>,lang="zh-CN",viewport <meta name="mindspace-cover">占位符或示例结构- 页脚含
data-mindspace-page-tag="platform-brand"→TKMind · 智趣 - 无 CDN script;Chart 等用
/assets/chart.umd.min.js或相对路径 - 问卷类含
<script src="/assets/page-data-client.js"> - 运行
npm run check:mindspace-cover对填充样例通过
5.3 SKILL.md 工作流摘要(Agent 必读)
## 何时使用
- 用户在本轮对话中**明确选择**了「旅游模板 / 促销模板 / …」
- 或 prompt 前缀含 `templateId=travel-hero`
## 工作流
1. 确认用户已授权本 skill(load_skill 成功)
2. read_file → `.agents/skills/page-template-travel/templates/travel-hero.html`
3. 根据用户需求生成文案与 MAIN_CONTENT_HTML
4. 替换全部占位符,自检无残留 `{{`
5. write_file → public/<slug>.html
6. 若需 Page Data:仍走 page-data-collect(建表 → register → bind),不得跳过
7. 按 static-page-publish 回复 Markdown 链接
## 何时不用
- 用户未选模板 → 继续 static-page-publish 从零写 HTML
- 用户要求完全自定义版式且与模板冲突 → 从零写,说明原因
6. skill.yaml 示例
# skills/page-template-travel/skill.yaml
name: page-template-travel
version: 1.0.0
label: 旅游 Hero 模板
description: 深色沉浸 + Hero 图 + 卡片区块,适用于旅游/美食/城市攻略
catalog:
previewImage: /assets/template-previews/travel-hero.png
priceCents: 990 # 二期商城展示;一期忽略
category: page-template
trigger:
keywords:
- 旅游模板
- 攻略模板
- travel-hero
router:
promptKey: page-template-travel
priority: 25
requires:
- static-page-publish # 文档约定;运行时仍靠 grant 控制
executors:
- goose
skills-registry.mjs 的 parseSkillManifest 已支持扩展字段;catalog / requires 为二期预留,一期 parser 可忽略未知键。
7. 授权模型(复用现有 skill grant)
7.1 现有表(无需改 schema — 一期)
-- 已有:h5_user_skill_grants
-- subject_type: 'role' | 'user'
-- subject_id: 'user' | <userId>
-- skill_name: 如 'page-templates' 或 'page-template-travel'
-- enabled: 0 | 1
解析链(已实现):
listSkillGrants('role','user') + listSkillGrants('user', userId)
→ resolveSkillMap()
→ grantedSkillNames()
→ syncSkillsToWorkspace() // 仅 enabled skill 复制到 .agents/skills/
→ filterChatSkills() // H5 聊天入口过滤
→ resolveSkillPrompt() // 意图路由不注入未授权 skill
7.2 默认策略(一期)
在 skills-registry.mjs 的 DEFAULT_USER_SKILLS 中:
'page-templates': false, // 聚合 skill,试点
'page-template-travel': false, // 拆分后逐项默认关
'page-template-campaign': false,
'page-template-survey': false,
creator 预设可选开启:
creator: {
...DEFAULT_USER_SKILLS,
'page-templates': true,
},
7.3 memind_adm 操作(一期)
复用已有 Admin API(Memind admin-routes.mjs):
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /admin-api/skills/catalog |
列出含新 template skill |
| GET | /admin-api/users/:userId/skills |
查看用户有效 skill |
| PUT | /admin-api/users/:userId/skills |
{ "skills": { "page-templates": true } } |
| PUT | /admin-api/skills/role/user |
角色默认批量开启 |
setUserSkills 成功后已调用 syncUserSkillsForUser,下次会话工作区即有模板文件。
8. H5 聊天勾选(复用 skill 芯片)
8.1 扩展 CHAT_SKILL_DEFINITIONS
在 chat-skills.mjs 增加(示例):
{
id: 'page-template-travel',
label: '旅游模板',
icon: 'page',
skillName: 'page-template-travel',
requiresSkill: 'page-template-travel',
requiresPublish: true,
promptKey: 'page-template-travel',
},
filterChatSkills 逻辑已满足:
requiresSkill→ 必须在grantedSkillsrequiresPublish→ 需canPublish或已 grantpage-data-collect(页面类统一规则)
8.2 Prompt 前缀
在 buildChatSkillPrompt 增加:
case 'page-template-travel':
return (
'请使用 page-template-travel 技能:先 load_skill,再 read_file 读取 templates/travel-hero.html,' +
'按模板占位符填充内容(templateId=travel-hero),write_file 到 public/页面.html,' +
'并遵守 static-page-publish 的 mindspace-cover 与链接格式。页面主题:'
);
用户点击芯片 → pendingSkillRef → 发送带前缀的消息 → Agent 走模板流程。
8.3 二期:独立模板选择器(可选 UI)
在「生成页面」芯片点击后展开二级面板:
- 展示
GET /api/mindspace/v1/template-catalog返回的已购模板列表 - 单选 templateId → 合并进 prompt
一期可仅用聊天芯片,不做二级 UI。
9. Agent 执行时序
sequenceDiagram
participant U as 用户
participant H5 as H5 Chat
participant P as Portal
participant G as Goose
participant WS as 用户工作区
U->>H5: 点击「旅游模板」+ 输入主题
H5->>P: POST /agent/run (含 skill 前缀)
P->>P: grantedSkills 含 page-template-travel?
P->>G: 会话 constraints + 用户消息
G->>G: load_skill(page-template-travel)
G->>WS: read_file templates/travel-hero.html
G->>G: LLM 生成文案 + 填占位符
G->>WS: write_file public/xxx.html
G->>P: Finish + 链接
P->>P: finish guard / cover 检查
P->>U: 可点击公网链接
10. 二期:购买与商城(新增)
10.1 新增表(建议)
CREATE TABLE IF NOT EXISTS h5_template_catalog (
skill_name VARCHAR(64) PRIMARY KEY COMMENT '与 platform skill name 一致',
label VARCHAR(128) NOT NULL,
description TEXT,
preview_url VARCHAR(512) NULL,
price_cents INT NOT NULL DEFAULT 0,
currency VARCHAR(8) NOT NULL DEFAULT 'CNY',
billing_mode ENUM('free','one_time','subscription') NOT NULL DEFAULT 'one_time',
status ENUM('draft','active','archived') NOT NULL DEFAULT 'draft',
sort_order INT NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS h5_template_purchases (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
skill_name VARCHAR(64) NOT NULL,
order_id VARCHAR(64) NULL COMMENT '关联支付单',
source ENUM('purchase','admin_grant','promo') NOT NULL,
purchased_at BIGINT NOT NULL,
expires_at BIGINT NULL COMMENT '订阅型模板到期;NULL=永久',
UNIQUE KEY uq_user_template (user_id, skill_name),
KEY idx_template_user (user_id),
CONSTRAINT fk_template_purchase_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
与 skill grant 关系:
- 购买成功 / 管理员赠送 → 写
h5_template_purchases+h5_user_skill_grants(enabled=1) - 退款 / 到期 →
enabled=0+ 可选删除工作区.agents/skills/page-template-* resolveUserSkillMap可扩展:有效 purchase 视为 grant(即使无 user override 行)
10.2 Portal 用户 API(草案)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/mindspace/v1/template-catalog |
商城列表(含是否已购) |
| GET | /api/mindspace/v1/template-catalog/mine |
当前用户已购/已授权模板 |
| POST | /api/mindspace/v1/template-catalog/:skillName/checkout |
创建支付单(对接现有 billing) |
| POST | /api/mindspace/v1/template-catalog/:skillName/activate |
支付回调内部:写 purchase + grant |
响应示例:
{
"items": [
{
"skillName": "page-template-travel",
"label": "旅游 Hero 模板",
"description": "深色沉浸 + Hero 图",
"previewUrl": "https://m.tkmind.cn/assets/template-previews/travel-hero.png",
"priceCents": 990,
"owned": true,
"chatSkillId": "page-template-travel"
}
]
}
10.3 memind_adm 商城管理(UI 边界)
按 AGENTS.md:商城配置页在 memind_adm(5174),Memind 本仓只实现 API 与 skill 资产。
memind_adm 需新增:
- 模板 SKU 配置(映射
h5_template_catalog) - 用户已购模板查询
- 手动赠送 / 撤销(调 Memind admin API 或直接写 grant)
10.4 支付回调伪代码
async function onTemplatePaymentSuccess({ userId, skillName, orderId }) {
await pool.query(
`INSERT INTO h5_template_purchases (id, user_id, skill_name, order_id, source, purchased_at)
VALUES (?, ?, ?, ?, 'purchase', ?)
ON DUPLICATE KEY UPDATE order_id = VALUES(order_id), purchased_at = VALUES(purchased_at)`,
[uuid(), userId, skillName, orderId, Date.now()],
);
await userAuth.setUserSkills(userId, { [skillName]: true });
}
11. 代码改动清单(实施顺序)
Phase 1 — 试点(无购买)
| # | 仓库 | 文件 / 模块 | 改动 |
|---|---|---|---|
| 1 | Memind | skills/page-templates/ |
新增 SKILL.md + 2~3 个 HTML 模板 |
| 2 | Memind | skills-registry.mjs |
DEFAULT_USER_SKILLS['page-templates']=false |
| 3 | Memind | chat-skills.mjs |
CHAT_SKILL_DEFINITIONS + buildChatSkillPrompt |
| 4 | Memind | chat-skills.test.mjs |
filterChatSkills / prompt 单测 |
| 5 | memind_adm | 用户技能页 | 勾选 page-templates(若尚未暴露 catalog 新项,刷新 catalog 即可) |
| 6 | Memind | scripts/verify-page-template-samples.mjs |
可选:占位符填充样例 + cover 检查 |
验收:
- 未授权用户:聊天无模板芯片,Agent 行为与现网一致
- 管理员授权后:出现「页面模板」芯片,生成页使用模板骨架且 cover 检查通过
npm run verify:mindspace-publish-guards仍通过
Phase 2 — 商城与购买
| # | 模块 | 改动 |
|---|---|---|
| 1 | schema.sql + migration |
h5_template_catalog / h5_template_purchases |
| 2 | Portal routes | template-catalog API + checkout |
| 3 | billing 集成 | 模板 SKU 入账 |
| 4 | memind_adm | 商城 UI + 赠送 |
| 5 | 拆分 skill | page-templates → page-template-* 独立定价 |
12. 测试计划
| 场景 | 预期 |
|---|---|
| 无 grant | 无芯片;自然语言「做旅游页」仍走 static-page-publish |
| 有 grant,未点芯片 | 可走模板或从零(SKILL 约定优先响应用户明确指定) |
| 有 grant,点芯片 | read_file 模板 → write_file;链接可访问 |
| Page Data 模板 | 仍完成 bind;verify:page-data 通过 |
| 撤销 grant | 下次 sync 删除 .agents/skills/page-template-* |
| 购买回调 | purchase 行 + grant;芯片即时可见(需 refresh capabilities) |
13. 风险与缓解
| 风险 | 缓解 |
|---|---|
| Agent 占位符漏替换 | SKILL 强调自检;verify 脚本 grep {{ |
| 模板与 Page Data 流程脱节 | survey 模板仅提供壳;bind 步骤写入 SKILL 强制清单 |
| skill 过多难维护 | 一期聚合;二期按销量拆包 |
| 购买与 grant 不一致 | purchase 表为 source of truth;定时 reconcile job |
14. 相关文档与代码
| 主题 | 位置 |
|---|---|
| 静态页发布规范 | skills/static-page-publish/SKILL.md |
| Skill 同步 | skills-registry.mjs → syncSkillsToWorkspace |
| Skill grant | user-auth.mjs → resolveUserSkillMap |
| 聊天 skill 芯片 | chat-skills.mjs → CHAT_SKILL_DEFINITIONS |
| Admin skill API | admin-routes.mjs /skills/* |
| Page Data 交付 | docs/regression-guards/page-data-delivery-contract.md |
| 管理后台 UI 边界 | AGENTS.md → memind_adm 5174 |
15. 开放问题(产品确认)
- 一期聚合 skill 还是直接按模板拆 skill?
- 模板定价:买断 vs 订阅 vs 套餐捆绑?
- 是否在「生成页面」芯片下做二级模板预览 UI,还是仅聊天芯片?
- creator 角色是否默认赠送全部模板?
确认后可按 Phase 1 清单开分支实现。