回到頂部
幫 195 篇文章自動生成 llms.txt:用 80 行 Node 解鎖 GEO — 封面

幫 195 篇文章自動生成 llms.txt:用 80 行 Node 解鎖 GEO

靜態站想被 ChatGPT、Perplexity 引用?不用等 SEO 寫完,80 行 Node 腳本就能在每次 build 自動產出全文索引。

為什麼會做這個

我的網站有 195 篇 AI 主題文章。SEO 慢慢做沒問題,但有一個更快的流量來源:讓 LLM 引用我

當有人在 ChatGPT 問「2026 有哪些 AI 教學網站?」,如果 ChatGPT 知道我的站,它會直接把我列出來。問題是:ChatGPT 怎麼會知道?

答案是 llms.txt 規範。這是社群提案的標準,讓網站像 robots.txt 一樣告訴 AI 爬蟲「我有什麼內容、在哪裡找」。目前 ChatGPT、Claude、Perplexity 都會抓這個檔案。

痛點:手動維護不可能

llms.txt 本身只是一份 Markdown 連結清單,寫一次很簡單:

# Mason AI Lab

> 中文 AI 知識站,涵蓋 195 篇教學與產業洞察。

## 學習入門
- [AI 新手村](https://masonailab.com/learn/beginners/)
- [Prompt 寫作入門](https://masonailab.com/learn/prompt-basics/)
...

每寫一篇新文章就要手動更新一次 llms.txt,這不是工程問題,是紀律問題——而我已經被自己騙過太多次了。任何需要紀律的事都會被遺忘

我需要一個會自動跑的東西。

解法:prebuild hook + 80 行 Node

Astro 的 package.json 支援 npm 的 prebuild 生命週期,意思是每次 npm run build 之前會自動跑這個腳本:

{
  "scripts": {
    "prebuild": "node scripts/gen-llms-full.mjs",
    "build": "astro build"
  }
}

這保證了只要我部署網站,llms-full.txt 就一定是最新的——不需要記得、不需要規律,完全自動。

腳本核心邏輯

import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
import { join, relative, sep, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
const CONTENT_DIR = join(ROOT, 'src', 'content');
const SITE = 'https://masonailab.com';

// 1. 遞迴掃描所有 .md 檔
function walk(dir) {
  const out = [];
  for (const name of readdirSync(dir)) {
    const p = join(dir, name);
    const s = statSync(p);
    if (s.isDirectory()) out.push(...walk(p));
    else if (name.endsWith('.md')) out.push(p);
  }
  return out;
}

// 2. 解析 frontmatter,抽出 title / description / body
function parse(file) {
  const raw = readFileSync(file, 'utf8');
  const m = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
  if (!m) return null;
  // ... 抽 title, description, slug, section
}

// 3. 依分類排序、串接、寫檔
const articles = walk(CONTENT_DIR).map(parse).filter(Boolean);
articles.sort(/* 依 section 順序 */);

const out = articles.flatMap(a => [
  `## ${a.title}`,
  `Source: ${SITE}/${a.section}/${a.slug}/`,
  a.body,
  '---',
]).join('\n\n');

writeFileSync(join(ROOT, 'public/llms-full.txt'), out);

完整版 80 行,連 Windows 路徑空格的 bug 都處理了(fileURLToPath 別忘了)。

結果

[gen-llms-full] wrote 195 articles -> public/llms-full.txt (1096.7 KB)

每次 push 上 Cloudflare Pages,llms.txt 與 llms-full.txt 都是最新的。195 篇文章從此會自動出現在任何抓 llms.txt 的 AI 引擎裡

學到的事

  1. GEO(Generative Engine Optimization)和 SEO 是兩種戰場。SEO 等 Google 排名要 3-6 個月,GEO 是「我有沒有被 AI 看到」,可以即時生效。如果你平常用 AI 輔助寫程式,這個腳本 30 分鐘就能搞出來。
  2. 靜態站的優勢被 AI 時代放大了。Astro 把所有內容變成 .md,讓全站索引變成 80 行腳本就能搞定的事——換成 WordPress 或 React SPA 會痛苦十倍。
  3. 任何需要紀律的事都應該被自動化。Prebuild hook 是被低估的工程實踐:零維護成本、零遺忘風險、隨 build 同步。
  4. llms.txt 不是銀彈,它只是讓 LLM「找得到」你。內容好不好、有沒有獨特觀點、能不能被引用,還是要自己寫。

接下來要做的事

  • 觀察 server log,看哪些 AI bot 真的在抓 llms.txt(GPTBot、ClaudeBot、PerplexityBot)
  • 為每篇文章加結構化的「TL;DR」區塊,讓 AI 引用時更容易截取重點
  • 寫一個監測腳本,定期問 ChatGPT「2026 中文 AI 教學網站有哪些?」,看我的站有沒有被列出來

原始碼在 scripts/gen-llms-full.mjs (連結待補)。歡迎 fork 改造成你的版本。


❓ FAQ

Q:llms.txt 跟 sitemap.xml 有什麼不同?

A:sitemap.xml 是給 Google 等搜尋引擎看的「網址清單」,只列出 URL 和最後更新時間。llms.txt 則是給 AI 引擎看的「全文索引」,包含每篇文章的完整內容。搜尋引擎需要自己爬頁面解析 HTML;AI 引擎直接讀 llms.txt 就能理解你的全部內容。兩者互補,不是取代關係。

Q:llms.txt 這麼大(1.1 MB),會影響網站效能嗎?

A:不會。這個檔案只有 AI 爬蟲會抓,一般使用者的瀏覽器不會載入它。放在 Cloudflare Pages 等 CDN 上,邊緣節點會快取,對伺服器幾乎零負擔。而且 AI 爬蟲的頻率很低(通常每天 1–2 次),不像 Google 爬蟲那麼頻繁。

Q:WordPress 或其他框架也能做 llms.txt 嗎?

A:可以,但會比較麻煩。WordPress 需要寫一個外掛來遍歷所有文章,或者用 WP-CLI 匯出後再處理。React SPA 更痛苦——內容分散在 API 和 state 裡,沒有靜態 .md 檔可以直接掃。Astro 和 Hugo 這類靜態站框架天生適合這種全站索引的操作。

Q:要同時放 llms.txt 和 llms-full.txt 嗎?

A:建議兩個都放。llms.txt 是簡短的「目錄」(標題 + 連結),讓 AI 快速掃過知道你有什麼;llms-full.txt 是完整全文,讓 AI 直接拿去引用。不同 AI 爬蟲偏好不同,兩個一起放成本只多幾行腳本,但覆蓋面最廣。

Q:怎麼知道 AI 真的有抓我的 llms.txt?

A:看伺服器 log。Cloudflare Pages 的 Analytics 可以過濾 User-Agent,找 GPTBotClaudeBotPerplexityBotGoogle-Extended 這些 AI 爬蟲的 request。通常上線 1-2 週後會開始看到它們定期抓 /llms.txt/llms-full.txt

Q:llms.txt 會影響一般 SEO 排名嗎?

A:不會直接影響 Google 排名——Google 主要還是看 HTML 頁面。但 llms.txt 會影響「AI 搜尋」的曝光(ChatGPT、Perplexity 等),這是獨立於 Google 之外的新流量源。換句話說:傳統 SEO 和 GEO 是兩條平行的管線,llms.txt 做的是後者。