0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Astro で llms.txt を多言語生成する最小実装(Content Collections 起点)

0
Posted at

llms.txt は、サイトルートに置く LLM 向けの Markdown 形式の目次です。sitemap.xml が URL の機械可読リストなのに対し、llms.txt は「このサイトは何で、どこを読めばいいか」を要約付きリンクで並べます。

Astro なら、Content Collections を情報源にして API ルートから動的生成できます。記事一覧を手で書かずに済むので、記事を足してもメンテが要りません。この記事では、英日 2 言語のサイトで /llms.txt/ja/llms.txt/llms-full.txt の 3 本を 1 つのレンダラから出す最小構成を紹介します。

先に断っておくと、llms.txt が AI 検索の流入にどれだけ効くかは、まだ慣習も計測も固まっていません。ここは実装の話だけに絞ります。

最小の生成ルート

Astro の file-based API ルートで、src/pages/.txt.ts を置けばテキストを返せます。GET ハンドラから text/plainResponse を返すだけです。

// src/pages/llms.txt.ts
import type { APIContext } from "astro";
import { renderLlmsTxt } from "../lib/llmsTxt";

export async function GET(_context: APIContext) {
  const body = await renderLlmsTxt({ docLang: "en" });
  return new Response(body, {
    status: 200,
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
      "Cache-Control": "public, max-age=3600",
    },
  });
}

.txt.ts という拡張子がポイントで、ビルド時に /llms.txt の URL に出力されます。中身を組み立てるロジックは src/lib/llmsTxt.ts に切り出して、ルート側は薄い入口にしておきます。こうすると言語別エンドポイントでロジックを再利用できます。

Content Collections から組み立てる

記事リストは getCollection で取得して、その場で並べます。手書きのリストにすると記事を足すたびに更新を忘れて、本文と llms.txt がずれます。

// src/lib/llmsTxt.ts(抜粋)
export async function renderLlmsTxt(opts: LlmsTxtOptions): Promise<string> {
  const blog = await getCollection("blog", ({ data }) => !data.draft);
  blog.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
  // ...セクションを組み立てて join("\n") で返す
}

({ data }) => !data.draftdraft: true を落とすのを忘れないでください。これを抜くと、書きかけの下書きが llms.txt に載って、未公開の URL を LLM に案内してしまいます。sitemap や RSS と同じ除外条件をここでも揃えます。

多言語は 2 軸で分ける

多言語サイトの肝はここです。レンダラに 2 つの軸を持たせます。

  • filterLang: どの言語の記事を載せるか
  • docLang: 見出しや説明文をどの言語で書くか

この 2 軸を分けると、同じレンダラから 3 本のエンドポイントを出せます。

// src/pages/llms.txt.ts        → 英語見出し・全言語の記事
renderLlmsTxt({ docLang: "en" });

// src/pages/ja/llms.txt.ts     → 日本語見出し・日本語の記事だけ
renderLlmsTxt({ filterLang: "ja", docLang: "ja" });

英語版 /llms.txtfilterLang を指定しません。英語版を「サイト全体の入口」と位置づけて、両言語の記事を拾える状態にしています。日本語版 /ja/llms.txtfilterLang: "ja" で日本語面に閉じます。

代表記事だけは docLang で絞る

1 つだけ設計判断があります。英語版は記事自体は両言語を拾えますが、「代表記事(Featured)」のセクションだけは docLang の言語に絞ります。

const featuredSource = filteredBlog.filter(
  (p) => entryLangLocal(p.id) === opts.docLang,
);

翻訳ペアの両方を代表枠に並べると、同じ内容が 2 言語で 2 枠を占めて、限られた枠の中のユニークなシグナルが半分になります。枠が有限なリスト(代表記事)では言語を絞り、容量制約のゆるい全文ダンプ(llms-full.txt)では両言語を載せる、という住み分けです。

ハマりどころ

  • draft 除外を共通レンダラに置く: 3 本のエンドポイントが同じ除外条件を通るように、getCollection のフィルタをレンダラ側に集約します。1 本だけ素通しにすると下書きが漏れます
  • filterLangdocLang を混同しない: 「載せる記事の言語」と「文言の言語」は別軸です。英語版で filterLang を未指定にしているのは意図的な設計で、後から filter を足してバグ修正したつもりにならないように

まとめ

llms.txt は Content Collections を情報源に Astro の API ルートで生成すれば、手動メンテが要りません。多言語は filterLang と docLang の 2 軸に分けると、1 つのレンダラから 3 本出せます。

llms-full.txt(全文インデックス)での言語の相互参照、robots.txt との役割分担、利用条件セクションの書き方は Aulvem 本家にまとめました → llms.txt / llms-full.txt を Astro で多言語生成する — Aulvem の GEO 実装

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?