無需遷移至 Sanity 即可讓您的內容做好 AI 準備
讓你的內容為 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 步:停止將經典編輯器用於所有內容
古騰堡區塊編輯器已經將內容儲存為結構化區塊。每個區塊都有一個類型、屬性和內容。這比大多數人意識到的更接近 Sanity 的 Portable Text。
{
"blockName": "core/paragraph",
"attrs": {},
"innerBlocks": [],
"innerHTML": "<p>This is structured content, not just HTML.</p>",
"innerContent": ["<p>This is structured content, not just 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 步:投資自訂欄位和分類法
進階自訂欄位(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-Ready Content Fields',
'fields' => [
['key' => 'summary', 'label' => 'Summary', 'type' => 'textarea'],
['key' => 'key_concepts', 'label' => 'Key Concepts', 'type' => 'taxonomy', 'taxonomy' => 'concept'],
['key' => 'content_intent', 'label' => 'Content Intent', 'type' => 'select', 'choices' => [
'informational' => 'Informational',
'transactional' => 'Transactional',
'navigational' => 'Navigational',
]],
['key' => 'related_entities', 'label' => 'Related Entities', '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'])),
];
},
]);
});
如果你將 WordPress 作為無頭 CMS 執行,並搭配 Next.js 或 Astro 前端(這是我們在 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": "Make Your Content AI-Ready",
"abstract": "How to structure existing CMS content for AI consumption",
"about": [
{"@type": "Thing", "name": "Content Management"},
{"@type": "Thing", "name": "Artificial Intelligence"}
],
"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 一樣。你已經有結構化內容。
向 Payload 新增 AI 特定欄位
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 功能在底層所做的。你只是自己做。對於在 Payload 上使用 Next.js 構建的團隊,此模式自然地整合到你現有的部署管道中。
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 加上邊界函數為你提供所需的一切:
// Supabase 邊界函數用於 AI 內容檢索
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. 為所有內容分類
每個欄位都應該有一個明確的類型。不要將泛型「文本」欄位用於結構化資料。日期應儲存為日期。參考應是參考,而不是粘貼到文本欄位中的段落。
3. 使關係明確
如果文章 A 參考產品 B,這應該是一個型別化的關係 -- 而不是正文中的提及。AI 工具需要遍歷你的內容圖,他們無法用隱含的連結做到這一點。
4. 新增語義中繼資料
超越基本 SEO 中繼資料。包括:
- 內容意圖(資訊性、交易性、導航性)
- 受眾段
- 置信度/新鮮度指標
- 實體註釋
- 超越基本類別的主題分類
5. 對所有內容進行版本控制和時間戳記
AI 系統需要知道內容有多新鮮。包括 created_at、updated_at,理想情況下還要 valid_until 或 review_date 欄位。RAG 管道中的陳舊內容會導致幻覺。
構建 AI 抽象層
我一直回到的模式是:不要遷移你的 CMS,而是在其上面新增一個 AI 抽象層。
[WordPress/Payload/Supabase] → [Content Sync] → [AI Layer (pgvector/Pinecone)] → [AI Consumers]
AI 層:
- 同步內容 從你的 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 年的成本和工具,因為這對於真實世界的決定很重要:
| 嵌入提供者 | 模型 | 每 1M 個令牌的成本 | 維度 | 注意 |
|---|---|---|---|---|
| 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 | 強大的程式碼內容 |
| Local (Ollama) | nomic-embed-text | Free | 768 | 隱私優先選項 |
對於一個典型的內容網站,有 5,000 篇文章,平均 1,500 字,你大約看著 7.5M 個令牌。使用 OpenAI 的小模型,這是 $0.15 來嵌入你的整個內容庫。即使每週重新嵌入也是微不足道的。
向量儲存選項
| 解決方案 | 免費層 | 定價(2025) | 最適合 |
|---|---|---|---|
| Supabase pgvector | 500MB 資料庫 | $25/mo 用於 8GB | 已經在 Supabase 上的團隊 |
| Pinecone | 5M 向量 | $70/mo 入門 | 規模上的生產 RAG |
| Qdrant Cloud | 1GB 叢集 | $25/mo | 進階篩選需求 |
| Weaviate Cloud | 50k 物件 | $25/mo | 多模式內容 |
| Turbopuffer | 1M 向量 | 按查詢付費 | 對成本敏感的專案 |
如果你已經執行 Supabase,pgvector 是明顯的選擇。沒有額外的服務,沒有額外的帳單,沒有額外的故障點。
真實世界架構模式
讓我分享兩個我實際上構建過的架構:
模式 1:WordPress + Supabase AI 層
對於有 50k+ WordPress 貼文的媒體公司:
- WordPress 網鉤在文章保存/更新時觸發
- Supabase 邊界函數接收網鉤
- 通過 WP REST API 獲取內容、標準化和嵌入
- 使用 pgvector 儲存在 Supabase 中
- Next.js 前端 上的 AI 聊天機器人查詢 Supabase 進行語義搜尋
- 結果作為 GPT-4o 的背景傳遞以用於回答生成
總額外基礎設施成本:~$25/月用於 Supabase 專業層。
模式 2:Payload CMS 內置 AI
對於 Payload v3 上的 SaaS 文件網站:
- Payload 勾點在每次文件保存時產生嵌入
- 嵌入儲存在 Payload 使用的相同 Postgres 資料庫中的
vector列中 - 用於語義搜尋的自訂 Payload 端點
- 由相同資料庫驅動的 AI 文件助手
- 除了 OpenAI API 呼叫之外,無需額外的基礎設施
總額外基礎設施成本:$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 功能的託管內容湖。但是如果你願意連接你自己的嵌入管道(這大約需要一天),Payload 給你相當的能力。
向現有 CMS 新增向量嵌入的成本是多少? 令人驚訝地少。對於有 10,000 篇文章的網站,使用 OpenAI 的 text-embedding-3-small 的初始嵌入生成成本約 $0.30。更新內容的持續成本通常每月不到 $5。向量儲存是更大的成本 -- 根據你的提供者和規模,預計 $0-70/月。Supabase 的免費層可以處理許多中小型網站。
我應該使用單獨的向量資料庫還是在現有資料庫中儲存嵌入? 如果你在 Postgres 上(Payload v3 和 Supabase 都使用),使用 pgvector 在同一資料庫中儲存嵌入。要管理的服務更少,一次同步要破壞的東西更少。Pinecone 等專用向量資料庫在你有數百萬個文件或需要亞毫秒查詢時間時有意義。對於大多數內容網站,pgvector 的速度綽綽有餘 -- 對於 1M 向量以下的集合,典型查詢時間是 5-20 毫秒。
我如何讓 AI 嵌入與內容更改保持同步?
網鉤是你的朋友。每個現代 CMS 都支援他們。當內容被建立或更新時,觸發一個網鉤來觸發重新嵌入。在嵌入旁邊儲存一個內容雜湊,以便你可以跳過未更改的內容。對於 WordPress,使用 save_post 動作。對於 Payload,使用 afterChange 勾點。對於 Supabase,使用資料庫觸發器或實時時訂閱。
關於多語言內容呢 -- 這種方法仍然有效嗎? 是的,但要謹慎選擇你的嵌入模型。OpenAI 的 text-embedding-3 模型處理多語言內容很好。Cohere 的 embed-v4 特別針對跨語言檢索進行了優化。標準化層應該將語言程式碼儲存為中繼資料,以便你的 AI 消費者可以相應地篩選。一個重要的注意事項:單獨嵌入每個語言版本,而不是連接翻譯。
遷移到無頭 CMS 是 AI 就緒內容的先決條件嗎? 不是先決條件,但它非常有幫助。無頭 CMS 架構自然地將內容與演示分開,這是 AI 就緒性的基礎。如果你仍然執行將內容烘焙到範本檔案中的單體 WordPress 主題,同時進行無頭化(WordPress 作為後端,帶有 Next.js 或 Astro 前端)同時改善你的 AI 就緒性和你的前端效能。甚至在考慮 AI 用例之前,它通常值得投資。如果你想探索這個,聯繫我們 -- 這正是我們每天做的事。