SEO 与元数据
让 Agent 为每个可索引路由维护唯一 title、meta description、canonical,并成对维护 Open Graph 与 Twitter 卡片字段;与 hreflang、结构化数据及 robots/sitemap 策略一致。
SPA 与 SSR 混合站点需在 SKILL 中说明首屏 HTML 是否含关键元数据;对仅客户端渲染的路由考虑预渲染或动态 meta API。
结构化数据(JSON-LD)仅用于真实可见内容,避免堆砌关键词;多语言站点配置 hreflang 与 x-default,与国内备案域名的规范 URL 一致。
元数据流水线(skill-flow)
[ 路由 / 构建 确定页面身份 ]
│
▼
┌────────────────────┐
│ HTML head: │──── title、meta description、canonical
│ 唯一性与规范化 URL │ (与最终用户可见 URL 一致)
└────────────────────┘
│
▼
┌────────────────────┐
│ 社交层: │──── og:title / og:description / og:url
│ OG + Twitter 对齐 │ twitter:* 与 OG 同义或显式覆盖
└────────────────────┘ og:image、twitter:image:绝对 HTTPS
│
▼
┌────────────────────┐
│ 扩展: │──── JSON-LD(仅对应可见内容)
│ hreflang / 结构化 │ 多语言 alternate 与 x-default
└────────────────────┘
│
▼
┌────────────────────┐
│ 抓取策略: │──── robots.txt、sitemap、noindex
│ staging / 账户 / 参数 │ Search Console 类工具验证
└────────────────────┘
生产环境应为 og:image 与 twitter:image 提供绝对 HTTPS 地址(常见 1200×630),并配 og:image:alt;本仓库 head 未写死图片 URL,避免指向不存在资源。
唯一性与 canonical
- 标题与描述长度符合搜索引擎展示习惯,避免全站重复。
- 带查询参数、分页、追踪参数的 URL 明确 canonical 指向主规范地址。
- 核心 Web 指标与 SEO 分列,不在 SKILL 中混为一谈。
HTML head 中完整的必要 meta 标签示例(含 OG / Twitter Card):
<!-- HTML head 完整示例:SEO + Open Graph + Twitter Card -->
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 核心 SEO:title 55-65字符,description 150-160字符 -->
<title>产品名称 - 简洁的价值主张 | 品牌名</title>
<meta name="description"
content="不超过160字符的摘要,包含核心关键词,鼓励点击。避免与其他页面重复。" />
<!-- canonical:消除重复索引(含追踪参数、分页等) -->
<link rel="canonical" href="https://www.example.com/products/widget" />
<!-- Open Graph(社交分享)-->
<meta property="og:type" content="article" />
<meta property="og:url" content="https://www.example.com/products/widget" />
<meta property="og:title" content="产品名称 - 价值主张" />
<meta property="og:description" content="用于社交分享的摘要,可与 meta description 不同。" />
<meta property="og:image" content="https://www.example.com/og/widget.jpg" />
<meta property="og:image:alt" content="产品图片的文字描述(可访问性)" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="zh_CN" />
<meta property="og:site_name" content="品牌名" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="产品名称(可比 og:title 更短)" />
<meta name="twitter:description" content="Twitter 分享摘要。" />
<meta name="twitter:image" content="https://www.example.com/og/widget.jpg" />
<meta name="twitter:image:alt" content="产品图片描述" />
<meta name="twitter:site" content="@yourbrand" />
<!-- 多语言 hreflang -->
<link rel="alternate" hreflang="zh-CN"
href="https://www.example.com/products/widget" />
<link rel="alternate" hreflang="en"
href="https://www.example.com/en/products/widget" />
<link rel="alternate" hreflang="x-default"
href="https://www.example.com/products/widget" />
</head>
Open Graph 与 Twitter
og:url 与 canonical 应一致;twitter:card 为 summary_large_image 时需同时提供大图 URL 与合适 alt。
- 社交分享图尺寸与 alt 文案单独维护,与页面主图不必相同。
- Twitter 专用标签可在与 OG 冲突时覆盖(例如更短的分享标题)。
结构化数据与 hreflang
JSON-LD 类型与页面实际类型一致(文章、产品、面包屑等);勿为不可见内容生成虚假结构化字段。
JSON-LD 结构化数据示例(Article / Product / BreadcrumbList):
<!-- Article JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "文章标题(<110 字符)",
"description": "文章摘要,与 meta description 可一致。",
"author": {
"@type": "Person",
"name": "作者姓名",
"url": "https://www.example.com/authors/jane"
},
"publisher": {
"@type": "Organization",
"name": "发布机构",
"logo": {
"@type": "ImageObject",
"url": "https://www.example.com/logo.png"
}
},
"datePublished": "2024-01-15T08:00:00+08:00",
"dateModified": "2024-03-20T12:00:00+08:00",
"image": "https://www.example.com/articles/hero.jpg",
"url": "https://www.example.com/articles/my-article"
}
</script>
<!-- Product JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "产品名称",
"image": "https://www.example.com/products/widget.jpg",
"description": "产品描述。",
"sku": "WIDGET-001",
"offers": {
"@type": "Offer",
"price": "299.00",
"priceCurrency": "CNY",
"availability": "https://schema.org/InStock",
"url": "https://www.example.com/products/widget"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "120"
}
}
</script>
<!-- BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "首页",
"item": "https://www.example.com/" },
{ "@type": "ListItem", "position": 2, "name": "产品",
"item": "https://www.example.com/products/" },
{ "@type": "ListItem", "position": 3, "name": "Widget",
"item": "https://www.example.com/products/widget" }
]
}
</script>
robots、sitemap 与 noindex
robots.txt、sitemap 与 noindex 分工明确:staging、账户页与重复参数 URL 默认不索引,并在变更后验证 Search Console 类工具。
robots.txt 配置示例:
# /robots.txt — 生产环境
User-agent: *
Allow: /
# 不允许索引账户内页、重复参数、API 路径
Disallow: /account/
Disallow: /api/
Disallow: /*?ref= # 追踪参数
Disallow: /*?sort= # 仅影响排序的参数页
Disallow: /search?q= # 空搜索结果页
# 声明 sitemap 位置
Sitemap: https://www.example.com/sitemap.xml
Sitemap: https://www.example.com/sitemap-products.xml
sitemap.xml 生成示例(Node.js 脚本,适用于 Next.js / 静态站点):
// scripts/generate-sitemap.mjs
import { writeFileSync } from 'fs';
import { getAllProducts, getAllArticles } from './lib/api.mjs';
const BASE_URL = 'https://www.example.com';
async function generateSitemap() {
const products = await getAllProducts();
const articles = await getAllArticles();
const staticPages = ['/', '/about', '/contact', '/products'];
const urls = [
// 静态页面
...staticPages.map(path => ({
loc: `${BASE_URL}${path}`,
changefreq: 'weekly',
priority: path === '/' ? '1.0' : '0.8',
lastmod: new Date().toISOString().split('T')[0],
})),
// 动态产品页
...products.map(p => ({
loc: `${BASE_URL}/products/${p.slug}`,
changefreq: 'daily',
priority: '0.9',
lastmod: p.updatedAt,
})),
// 文章页
...articles.map(a => ({
loc: `${BASE_URL}/articles/${a.slug}`,
changefreq: 'monthly',
priority: '0.7',
lastmod: a.updatedAt,
})),
];
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map(u => ` <url>
<loc>${u.loc}</loc>
<lastmod>${u.lastmod}</lastmod>
<changefreq>${u.changefreq}</changefreq>
<priority>${u.priority}</priority>
</url>`).join('\n')}
</urlset>`;
writeFileSync('./public/sitemap.xml', xml);
console.log(`Generated sitemap with ${urls.length} URLs`);
}
generateSitemap();
SKILL 片段
---
name: seo-metadata-cn
description: 维护页面 SEO 元数据、canonical、结构化数据与抓取策略
tags: [seo, metadata, structured-data, sitemap]
---
# 基础元数据
1. title 55-65字符,每页唯一,格式:关键词 - 价值主张 | 品牌名
2. meta description 150-160字符,包含核心关键词,避免全站重复
3. canonical 与最终用户 URL 一致,消除参数/分页产生的重复索引
# Open Graph 与 Twitter Card
4. og:url 与 canonical 完全一致
5. og:image 绝对 HTTPS 地址,尺寸 1200×630,配 og:image:alt
6. twitter:card = summary_large_image 时同时提供 twitter:image
7. og:locale 与页面语言对应(zh_CN / en_US)
# 结构化数据(JSON-LD)
8. 类型与实际可见内容对应:Article/Product/BreadcrumbList
9. 不为不可见或合成内容生成 JSON-LD(避免处罚)
10. 每次上线用 Google 富结果测试工具验证
# 多语言与抓取
11. hreflang 成对出现(互相引用),x-default 指向默认语区
12. robots.txt 屏蔽账户内页、API 路径、追踪参数变体
13. staging 环境在 HTTP header 或 meta 加 noindex/nofollow
14. sitemap.xml 仅包含可索引的规范 URL,lastmod 用真实修改时间
# Core Web Vitals 与 CI
15. LCP < 2.5s:优先图片尺寸、preload、CDN
16. CLS < 0.1:图片/广告/字体显式指定宽高,避免布局偏移
17. INP < 200ms:减少长任务,交互处理拆分到空闲帧
18. Lighthouse CI 在 PR 中门禁性能 / SEO / 可访问性分数
元数据清单(seo-page JS)
按渲染形态与开关生成 Agent 可用的 head 检查清单;与上方流水线一致。
输出