讓內容站同時服務人類讀者和 AI agent 的 6 條實作,附可抄的清單
最近 4 天,我和 Claude Code 一起把 smarts.md 從一個「給人看的智慧合約文件站」改造成「給 AI agent 用的智慧合約文件站」。
這兩者聽起來像同一件事,差別其實很大。給人看的網站,你追求的是可讀、可搜尋、載入快;給 AI agent 用的網站,你追求的是:
這篇記錄我讓 Claude 做的 6 件事,每件都能獨立搬到你的網站用。
robots.txt 明確歡迎 AI 爬蟲大多數網站的 robots.txt 是從舊範本抄來的,預設拒絕所有不認識的 User-Agent。但 2025 年後,不少 AI 爬蟲是新加入的 User-Agent——GPTBot、ClaudeBot、PerplexityBot、Applebot-Extended、Google-Extended、CCBot、meta-externalagent……你要被它們抓取,就得明確 Allow。
smarts 的 robots.txt 開頭寫了策略聲明,接著逐一 User-agent + Allow: /,涵蓋 OpenAI、Anthropic、Perplexity、Google Gemini、Apple、Common Crawl、Meta、Cohere 等 12 個爬蟲。立場很直接:這個站本來就是給 AI 讀的。
llms.txt 給 LLM 看的選單llms.txt 是 llmstxt.org 提出的約定:在網站根目錄放一份精簡的 Markdown,告訴 LLM「這個網站在做什麼、怎麼用、主要入口有哪些」。相當於你網站的電梯簡報。
smarts 的 llms.txt 約 60 行,包含:
/{chain}/{address} vs 友善 slug)核心想法:LLM 讀文件的成本是 token,它不會整站爬。你給它一份蒸餾過的目錄,命中率會高很多。
.well-known/mcp.json 提前押注MCP 協定還沒形成像 /.well-known/openid-configuration 那種自動發現約定,目前沒有任何公開 client 會去爬固定路徑的 manifest。
但發布這份 manifest 只要 30 行:
def well_known_mcp
response.set_header("Cache-Control", "public, max-age=3600")
response.set_header("Access-Control-Allow-Origin", "*")
render json: {
name: "smarts",
version: "0.1.0",
description: "...",
protocol_version: "2024-11-05",
transports: [{ type: "sse", endpoint: "https://smarts.md/mcp/sse" }],
capabilities: { tools: true, resources: false, prompts: false },
tools: MCP_TOOLS.map { |t| { name: t[:name], description: t[:blurb] } }
}
end
一旦某個發現約定出現,我一行程式碼都不用改就能對接。同時這份 manifest 對開發者也是自文件化——任何人好奇你的 MCP 長怎樣,curl smarts.md/.well-known/mcp.json 就夠了。
關鍵是 tools 陣列從 MCP_TOOLS 常數衍生,單一真實來源。既有的結構性測試強制每個 app/tools/*_tool.rb 都要出現在常數裡,manifest 免費繼承「新工具不會被漏」的保證。
.md 版本:每頁一份 WebFetch-friendly 的蒸餾這條最讓我滿意。Claude Code 的 WebFetch 工具拿到 HTML 後得自己清洗、抽內容,耗 token 又不穩定。但如果你主動提供 .md 格式呢?
Rails 的 respond_to 天然支援:
# config/routes.rb
get ":slug(.:format)", to: "contracts#show",
constraints: { slug: ContractSlugs::ROUTE_PATTERN, format: /html|md/ }
訪問 https://smarts.md/usdc-eth 回傳 HTML,訪問 https://smarts.md/usdc-eth.md 回傳一份 40 行 Markdown——地址、鏈、分類、MCP endpoint、如何用 AI agent 查詢、原始連結。
# USD Coin on Ethereum
- **Address:** `0xa0b8...`
- **Chain:** Ethereum
- **Classification:** ERC-20 Token
## Query via AI agent
- **MCP endpoint:** `https://smarts.md/mcp/sse`
- **Reference:** `usdc-eth`
- **Sample prompt:** "Tell me the current state of usdc-eth"
AI agent 抓一次 .md,所有它想要的結構化資訊都在裡面,不用跑 DOM parser。這條 Claude 做得很快——一個 controller、兩個 .md.erb 模板、routes 裡加 (.:format),就這些。
這是反過來的發現:我網站的訪客裡,已經有一部分人在用 Claude Code / Cursor / Windsurf。他們看到一個合約頁面,下一步往往想「讓我的 AI 幫我看看這個」。
那就在每個合約頁面渲染一張卡片:
usdc-eth)或 chain/address,旁邊一個 Copy 按鈕mcp.smarts.md 教你一行指令接入使用者從「我在看這個合約」到「我在問我的 AI」,只有一次點擊的摩擦。這張卡片用了 DaisyUI 的 card + 一個通用 Stimulus copy controller,50 行 erb 搞定,還有回歸測試鎖定 data-copy-text-value 屬性,防止哪天 refactor 把錯的字串塞進使用者剪貼簿。
OpenGraph、Twitter Card、JSON-LD 這套是給傳統搜尋和社群卡片用的,但值得做——AI 爬蟲也會讀這些。
重點在 JSON-LD 怎麼描述一個智慧合約。Claude 選的是 WebPage 包一個 SoftwareApplication:
{
"@type": "WebPage",
"about": {
"@type": "SoftwareApplication",
"name": "USD Coin",
"applicationCategory": "SmartContract",
"operatingSystem": "Ethereum",
"identifier": "0xa0b8..."
}
}
operatingSystem 放鏈名,identifier 放地址,additionalType 放分類(例如「Uniswap V3 Pool」)。schema.org 沒有專門的 SmartContract 類型,但用 SoftwareApplication + 這幾個欄位,足夠讓 LLM「理解」這是一個跑在 Ethereum 上、有明確地址識別的合約。
OG image 是 1200×630 的 summary_large_image,Twitter Card、麵包屑、softwareVersion、license 欄位一併補齊。
上面 6 件事是 4 天裡 7 個 PR 的總和:
| PR | 檔案數 | 大小 |
|---|---|---|
feat/ai-crawler-discovery |
2 | ~126 行 |
feat/well-known-mcp-manifest |
4 | ~129 行 |
feat/markdown-contract-pages |
6 | ~298 行 |
feat/contract-mcp-card |
3 | ~126 行 |
feat/seo-meta-tags |
8 | ~292 行 |
feat/seo-enrichments |
8 | ~189 行 |
feat/og-card |
— | — |
每一塊都是獨立 PR、獨立測試、獨立 commit message。這種切分的節奏是跟 Claude 協作最值得的地方——每個 PR 都小到可以一眼看完 diff,但加起來網站已經長出完整的 AI 接入面。
如果你有一個內容型網站,想讓它對 AI 友善,按這個順序做:
robots.txt 明確 Allow 主流 AI 爬蟲/llms.txt(一份精簡選單).md 格式 variant/.well-known/mcp.json前 5 條大部分 SEO 工程師不會告訴你,因為它們不在傳統 SEO 範疇裡。但從我這 4 天的資料看,AI 流量的成長曲線和人類流量是兩條完全獨立的曲線——兩條都得服務。