无需迁移至 Sanity 即可让您的内容为 AI 做好准备
关于CMS内容AI就绪的叙述
在CMS领域目前流传着这样一个叙述:"如果你想要AI就绪的内容,你需要Sanity的结构化内容方法。"说实话,Sanity的内容湖和他们GROQ驱动的AI集成确实给人印象深刻。但问题是——大多数团队不能只是放弃现有的CMS。你在WordPress中有多年的内容。你的应用数据层在Supabase中。你刚刚在六个月前完成了向Payload CMS的迁移。再经历一次迁移的想法让你胃部不适。
好消息:你不需要切换。你需要用不同的方式思考内容的结构化、存储和暴露方式。在过去的一年里,我帮助团队对现有堆栈进行了改造以支持AI消费,这些模式令人惊讶地一致,无论你运行的是哪个CMS或数据库。让我给你讲解一下。
目录
- 什么是"AI就绪内容"
- 为什么Sanity获得所有关注
- 在WordPress中为AI构建内容
- Payload CMS:你已经很接近了
- Supabase作为AI就绪的内容层
- AI就绪内容的通用原则
- 构建AI抽象层
- 无需完整迁移的向量嵌入
- 真实世界架构模式
- 常见问题
什么是"AI就绪内容"
在我们开始战术性讨论之前,让我们先澄清我们实际在谈论什么。"AI就绪内容"不是营销术语(好吧,它是,但其下面有实质内容)。这意味着你的内容满足三个标准:
- 机器可解析的结构 ——AI模型可以可靠地从你的内容中提取意义,无需猜测上下文
- 丰富的元数据 ——每个内容都携带足够的语义信息,使AI能够理解关系、意图和上下文
- API可访问性 ——内容可通过AI代理、RAG管道和LLM工具调用可以消费的编程接口获得
就这样。注意清单上没有什么:特定的供应商。这些是架构模式,而不是产品功能。
内容智能谱系
将内容AI就绪程度想象为一个谱系:
| 级别 | 描述 | 示例 |
|---|---|---|
| 0 | HTML块 | WordPress帖子,包含内联样式和混合媒体 |
| 1 | 分离的关注点 | 带有结构化数据标记的干净HTML |
| 2 | 字段级结构 | 内容被分解为类型化字段(标题、摘要、正文、作者) |
| 3 | 语义关系 | 包含显式引用、分类法和实体链接的内容 |
| 4 | AI原生 | 包含嵌入、语义注释和机器可读意图的内容 |
Sanity的结构化内容模型默认将你推向第3-4级。但是每个CMS都可以达到第3级,并且通过一些额外的基础设施,可以达到第4级。
为什么Sanity获得所有关注
让我们给应该给予的地方归功。Sanity对结构化内容的方法对于AI用例来说确实设计得很好:
- Portable Text 将富文本存储为JSON AST而不是HTML,使其易于以编程方式解析
- GROQ 查询返回你需要的数据的确切形状,这完美地映射到LLM上下文窗口
- Content Lake 将内容视为具有显式引用的类型化文档图
- 他们在2025年的 AI SDK集成 允许从LLM直接工具调用到内容查询
但这是Sanity传道者没有提到的:这些优势是架构模式,而不是专有魔法。你可以在现有堆栈中实现每一个。这只需要有意的设计。
真正的问题不是"我应该切换到Sanity吗?"而是"我如何在已有的地方应用结构化内容原则?"
在WordPress中为AI构建内容
WordPress在2025年支持互联网的约43%。如果你在运行WordPress,你并不孤单,而且你的选项比你可能想象的要多。
第1步:停止对所有内容使用经典编辑器
Gutenberg块编辑器已经将内容存储为结构化块。每个块都有一个类型、属性和内容。这比大多数人意识到的要接近Sanity的Portable Text。
{
"blockName": "core/paragraph",
"attrs": {},
"innerBlocks": [],
"innerHTML": "<p>这是结构化内容,而不仅仅是HTML。</p>",
"innerContent": ["<p>这是结构化内容,而不仅仅是HTML。</p>"]
}
块数据作为序列化注释存储在 post_content 中,但你可以以编程方式解析它:
$blocks = parse_blocks($post->post_content);
$structured = array_map(function($block) {
return [
'type' => $block['blockName'],
'attributes' => $block['attrs'],
'content' => strip_tags($block['innerHTML']),
];
}, array_filter($blocks, fn($b) => $b['blockName'] !== null));
第2步:投资自定义字段和分类法
Advanced Custom Fields (ACF) 或 Meta Box 为你提供第2-3级内容结构。但你需要有意地对待它。不要只是添加字段——设计一个内容模型。
// 为AI消费注册结构化内容类型
register_post_type('knowledge_article', [
'supports' => ['title', 'custom-fields'],
'show_in_rest' => true, // 对API访问至关重要
]);
// 定义语义字段
acf_add_local_field_group([
'title' => 'AI就绪内容字段',
'fields' => [
['key' => 'summary', 'label' => '摘要', 'type' => 'textarea'],
['key' => 'key_concepts', 'label' => '关键概念', 'type' => 'taxonomy', 'taxonomy' => 'concept'],
['key' => 'content_intent', 'label' => '内容意图', 'type' => 'select', 'choices' => [
'informational' => '信息性',
'transactional' => '事务性',
'navigational' => '导航性',
]],
['key' => 'related_entities', 'label' => '相关实体', 'type' => 'relationship'],
],
]);
第3步:通过REST API暴露一切
WordPress REST API是你与AI的桥梁。确保自定义字段被暴露:
add_action('rest_api_init', function() {
register_rest_field('knowledge_article', 'ai_metadata', [
'get_callback' => function($post) {
return [
'summary' => get_field('summary', $post['id']),
'concepts' => wp_get_post_terms($post['id'], 'concept', ['fields' => 'names']),
'intent' => get_field('content_intent', $post['id']),
'related' => get_field('related_entities', $post['id']),
'structured_blocks' => parse_blocks(get_post_field('post_content', $post['id'])),
];
},
]);
});
如果你以Next.js或Astro前端运行WordPress作为无头CMS(这是我们在Social Animal做的很多事情),这个REST API就成为你的AI的主要接口。
第4步:添加JSON-LD结构化数据
这个经常被忽视用于AI就绪性,但这很重要。Google的AI概览和其他AI爬虫程序消费JSON-LD。Yoast SEO或RankMath之类的工具生成基本架构,但对于真正的AI就绪性,你想输出详细的结构化数据:
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "使你的内容AI就绪",
"abstract": "如何为AI消费构建现有CMS内容",
"about": [
{"@type": "Thing", "name": "内容管理"},
{"@type": "Thing", "name": "人工智能"}
],
"mentions": [
{"@type": "SoftwareApplication", "name": "WordPress"},
{"@type": "SoftwareApplication", "name": "Payload CMS"}
]
}
Payload CMS:你已经很接近了
如果你已经在Payload CMS上,祝贺——你可能已经在第2-3级,而无需做太多额外工作。Payload基于集合的架构,具有类型化字段,本质上是结构化的。
为什么Payload已经是AI友好的
Payload将内容存储为MongoDB或Postgres中的类型化JSON文档。每个字段都有定义的类型。关系是明确的。这正是AI需要的。
// 已经是AI就绪的Payload集合
const Articles: CollectionConfig = {
slug: 'articles',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'summary', type: 'textarea' },
{ name: 'body', type: 'richText' }, // 存储为Slate/Lexical JSON
{ name: 'topics', type: 'relationship', relationTo: 'topics', hasMany: true },
{ name: 'contentType', type: 'select', options: ['guide', 'tutorial', 'reference'] },
],
};
Payload的富文本编辑器(v3.x中的Lexical)将内容存储为JSON AST——就像Sanity的Portable Text一样。你已经有结构化内容。
将AI特定的字段添加到Payload
Payload和完全AI就绪之间的差距主要是关于元数据。将这些字段添加到你的集合:
const aiFields: Field[] = [
{
name: 'aiMetadata',
type: 'group',
fields: [
{ name: 'embedding', type: 'json', admin: { hidden: true } },
{ name: 'extractedEntities', type: 'json', admin: { readOnly: true } },
{ name: 'semanticSummary', type: 'textarea', admin: { readOnly: true } },
{ name: 'contentHash', type: 'text', admin: { hidden: true } },
],
},
];
然后使用Payload的钩子在保存时自动生成嵌入:
const generateEmbeddingHook: CollectionAfterChangeHook = async ({ doc, operation }) => {
if (operation === 'create' || operation === 'update') {
const textContent = extractTextFromLexical(doc.body);
const embedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: `${doc.title}\n${doc.summary}\n${textContent}`,
});
await payload.update({
collection: 'articles',
id: doc.id,
data: {
aiMetadata: {
...doc.aiMetadata,
embedding: embedding.data[0].embedding,
contentHash: hashContent(textContent),
},
},
});
}
};
这本质上是Sanity的AI功能在幕后所做的。你只是自己做。对于使用Next.js在Payload上构建的团队,这个模式自然集成到你现有的部署管道中。
Supabase作为AI就绪的内容层
Supabase很有趣,因为它不是CMS——它是一个数据库平台。但越来越多地,团队将其用作内容后端,特别是通过Supabase的pgvector扩展支持嵌入。
pgvector的优势
Supabase自2023年以来一直支持pgvector,并且它已经成熟得很多。这意味着你可以在同一数据库中存储内容和向量嵌入:
-- 启用扩展
create extension if not exists vector;
-- 创建具有嵌入支持的内容表
create table content (
id uuid default gen_random_uuid() primary key,
title text not null,
body text not null,
metadata jsonb default '{}',
content_type text not null,
embedding vector(1536), -- OpenAI text-embedding-3-small维度
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- 为相似性搜索创建索引
create index on content using ivfflat (embedding vector_cosine_ops)
with (lists = 100);
为AI代理构建内容API
Supabase的自动生成的REST API加上Edge Functions为你提供了你需要的一切:
// AI内容检索的Supabase Edge Function
import { createClient } from '@supabase/supabase-js';
Deno.serve(async (req) => {
const { query, limit = 5 } = await req.json();
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_KEY')!);
// 为查询生成嵌入
const embeddingResponse = await fetch('https://api.openai.com/v1/embeddings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'text-embedding-3-small',
input: query,
}),
});
const { data } = await embeddingResponse.json();
const queryEmbedding = data[0].embedding;
// 使用pgvector进行语义搜索
const { data: results } = await supabase.rpc('match_content', {
query_embedding: queryEmbedding,
match_threshold: 0.7,
match_count: limit,
});
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' },
});
});
相似性匹配的Postgres函数:
create or replace function match_content(
query_embedding vector(1536),
match_threshold float,
match_count int
) returns table (
id uuid,
title text,
body text,
metadata jsonb,
similarity float
) language sql stable as $$
select
content.id,
content.title,
content.body,
content.metadata,
1 - (content.embedding <=> query_embedding) as similarity
from content
where 1 - (content.embedding <=> query_embedding) > match_threshold
order by content.embedding <=> query_embedding
limit match_count;
$$;
这为你提供了一个完全功能的RAG(检索增强生成)后端,而无需任何CMS迁移。你的内容在Supabase中,你的AI可以语义地查询它,而你的Astro或Next.js前端可以通过相同的API消费它。
AI就绪内容的通用原则
无论你的CMS如何,这些原则都适用:
1. 将内容与演示分离
这是你能做的最重要的单一事情。如果你的内容与HTML、CSS类和布局关注点纠缠在一起,AI无法可靠地解析它。将内容存储为数据,在演示层将其呈现为HTML。
2. 对所有内容进行类型化
每个字段都应该有一个明确的类型。不要对结构化数据使用泛型"文本"字段。日期应该存储为日期。引用应该是引用,而不是粘贴到文本字段中的slug字符串。
3. 使关系明确
如果文章A引用产品B,这应该是一个类型化的关系——而不是正文中的提及。AI工具需要遍历你的内容图,他们无法用隐含的链接做到这一点。
4. 添加语义元数据
超越基本的SEO元数据。包括:
- 内容意图(信息性、事务性、导航性)
- 受众细分
- 置信度/新鲜度指标
- 实体注释
- 除基本类别外的主题分类
5. 版本和时间戳一切
AI系统需要知道内容有多新鲜。包括 created_at、updated_at,理想情况下还有 valid_until 或 review_date 字段。RAG管道中的陈旧内容会导致幻觉。
构建AI抽象层
这是我一直回到的模式:不是迁移你的CMS,而是在其之上添加一个AI抽象层。
[WordPress/Payload/Supabase] → [内容同步] → [AI层(pgvector/Pinecone)] → [AI消费者]
AI层:
- 同步内容 通过webhook或轮询从你的CMS
- 规范化 它成为一致的结构,无论来源如何
- 生成嵌入 并将它们与规范化内容一起存储
- 暴露AI优化的API 用于RAG、工具调用和语义搜索
// 简化的内容同步管道
interface NormalizedContent {
id: string;
source: 'wordpress' | 'payload' | 'supabase';
sourceId: string;
title: string;
body: string; // 纯文本,去除标记
structuredBody: object; // JSON AST(如果可用)
metadata: {
type: string;
intent: string;
topics: string[];
entities: string[];
createdAt: string;
updatedAt: string;
};
embedding?: number[];
}
async function syncContent(source: ContentSource): Promise<void> {
const rawContent = await source.fetchAll();
for (const item of rawContent) {
const normalized = source.normalize(item);
const embedding = await generateEmbedding(
`${normalized.title}\n${normalized.body}`
);
await aiLayer.upsert({
...normalized,
embedding,
});
}
}
这种方法有一个巨大的优势:你的编辑者继续使用他们认识的CMS。没有重新培训,没有迁移,没有停机时间。AI层与你现有的堆栈并存。
无需完整迁移的向量嵌入
让我们谈谈2025年的成本和工具,因为这对于真实世界的决定很重要:
| 嵌入提供者 | 模型 | 每百万令牌成本 | 维度 | 注释 |
|---|---|---|---|---|
| OpenAI | text-embedding-3-small | $0.02 | 1536 | 最佳成本/质量比率 |
| OpenAI | text-embedding-3-large | $0.13 | 3072 | 更高的准确性 |
| Cohere | embed-v4 | $0.10 | 1024 | 很好的多语言支持 |
| Voyage AI | voyage-3 | $0.06 | 1024 | 对代码内容有强大支持 |
| 本地(Ollama) | nomic-embed-text | 免费 | 768 | 隐私优先的选项 |
对于典型的内容站点,有5,000篇文章,平均每篇1,500字,你看起来大约7.5M令牌。使用OpenAI的小模型,这是$0.15来嵌入你的整个内容库。即使每周重新嵌入也是微不足道的。
向量存储选项
| 解决方案 | 免费层 | 定价(2025) | 最适合 |
|---|---|---|---|
| Supabase pgvector | 500MB数据库 | 8GB为$25/月 | 已在Supabase上的团队 |
| Pinecone | 5M向量 | $70/月启动器 | 大规模生产RAG |
| Qdrant Cloud | 1GB集群 | $25/月 | 高级过滤需求 |
| Weaviate Cloud | 50k个对象 | $25/月 | 多模态内容 |
| Turbopuffer | 1M向量 | 按查询付费 | 成本敏感项目 |
如果你已经在运行Supabase,pgvector是明显的选择。没有额外的服务,没有额外的计费,没有额外的故障点。
真实世界架构模式
让我分享两个我实际构建过的架构:
模式1:WordPress + Supabase AI层
对于有50k+个WordPress帖子的媒体公司:
- WordPress webhook在帖子保存/更新时触发
- Supabase Edge Function接收webhook
- 通过WP REST API获取内容,规范化和嵌入
- 存储在Supabase中,带pgvector
- Next.js前端上的AI聊天机器人查询Supabase进行语义搜索
- 结果作为GPT-4o的上下文传递用于答案生成
额外基础设施总成本:~Supabase pro层$25/月。
模式2:具有内置AI的Payload CMS
对于Payload v3上的SaaS文档站点:
- Payload钩子在每个文档保存时生成嵌入
- 嵌入存储在Payload使用的同一Postgres数据库中的
vector列中 - 用于语义搜索的自定义Payload端点
- 由同一数据库支持的AI文档助手
- 不需要外部向量存储
额外基础设施总成本:$0超出OpenAI API调用(每月几分钱)。
这两个模式花了大约2-3周来实现,而不是完整CMS迁移的3-6个月。如果你在考虑这种架构,我们有定价层涵盖这些项目类型。
常见问题
我真的需要为AI重构我的内容,还是只是炒作? 这不是炒作,但紧急程度取决于你的用例。如果你在构建AI功能(聊天机器人、语义搜索、个性化),结构化内容是必不可少的。如果你在针对AI驱动的搜索(如Google的AI概览或ChatGPT的浏览)进行优化,结构化数据和干净的内容层次可以测量地改善你的可见性。Authoritas进行的2025年研究发现,具有架构标记的页面在AI生成的答案中出现的可能性高40%。
我应该做的最少是什么来使WordPress内容AI就绪? 三件事:(1)始终使用Gutenberg块而不是粘贴HTML,(2)向每个页面添加JSON-LD结构化数据,以及(3)通过REST API暴露自定义字段。这在几周的集中工作中将你从第0-1级提升到第2-3级。你不需要一夜之间重构整个站点。
Payload CMS能否替代Sanity用于AI驱动的内容? 对于大多数用例,是的。Payload v3具有Lexical富文本存储为结构化JSON,具有类型化字段和关系,并支持带pgvector的Postgres。Sanity提供而Payload没有本机提供的主要内容是具有内置AI功能的托管Content Lake。但是如果你愿意连接自己的嵌入管道(这需要大约一天),Payload为你提供等效的功能。
为现有CMS添加向量嵌入的成本是多少? 令人惊讶地少。对于有10,000篇文章的站点,使用OpenAI的text-embedding-3-small进行初始嵌入生成成本约$0.30。更新内容的持续成本通常低于$5/月。向量存储是更大的成本——根据你的提供者和规模预计$0-70/月。Supabase的免费层可以处理许多中小型站点。
我应该使用单独的向量数据库还是在现有数据库中存储嵌入? 如果你在Postgres上(Payload v3和Supabase都使用),在同一数据库中存储嵌入,使用pgvector。一项较少的服务要管理,一项较少的同步要破坏。专用向量数据库(如Pinecone)在你有数百万文档或需要亚毫秒查询时间时有意义。对于大多数内容站点,pgvector速度足够快——对于少于1M向量的集合,典型查询时间为5-20ms。
我如何保持AI嵌入与内容更改同步?
Webhooks是你的朋友。每个现代CMS都支持它们。创建或更新内容时,触发webhook来触发重新嵌入。在嵌入旁边存储内容哈希,以便你可以跳过未更改的内容。对于WordPress,使用 save_post 操作。对于Payload,使用 afterChange 钩子。对于Supabase,使用数据库触发器或Realtime订阅。
多种语言中的内容——这种方法是否仍然有效? 是的,但仔细选择你的嵌入模型。OpenAI的text-embedding-3模型处理多语言内容效果很好。Cohere的embed-v4专门针对跨语言检索进行了优化。规范化层应该将语言代码存储为元数据,以便你的AI消费者可以相应地过滤。一个重要的注意事项:单独嵌入每个语言版本,而不是连接翻译。
迁移到无头CMS是AI就绪内容的前提吗? 不是前提,但它帮助很多。无头CMS架构自然地将内容与演示分离,这是AI就绪的基础。如果你仍在运行单体WordPress主题,内容烤入模板文件中,同时转向无头(WordPress作为后端,使用Next.js或Astro前端)会同时改善你的AI就绪和前端性能。即使在考虑AI用例之前,这通常也值得投资。如果你想探索这个,联系我们——这字面上是我们每天做的事情。