llms.txt とは?

llms.txt は LLM のコンテキストウィンドウが小さくて、ウェブサイト全体を処理できないという制限に対応するため提案された、Markdown 形式のテキストファイルです。

2024/9/3 に Answer.AI の Jeremy Howard 氏が https://llmstxt.org/ で提案しました。robots.txt のように RFC で定義されているわけではありません。

H1 セクションでのウェブサイト名、短い要約、H2 セクションでのリンクリストなど、ある程度フォーマットが決まっています。
例: https://llmstxt.org/llms.txt

ウェブサイト情報全てが含まれた llms-full.txt というものもあります。
例: https://developers.cloudflare.com/llms-full.txt

llms.txt のディレクトリサイトもあります。

設置した llms.txt と llms-full.txt

両方を設置してみました。

Hugo への設定方法

必要な作業は、3 つのファイルを変更または新規作成するだけです。

  • config.toml(変更)
  • layouts/index.llms.txt(新規作成)
  • layouts/index.llmsfull.txt(新規作成)

config.toml

config.toml に以下を追加しました。

llmsllmsfull の output formats を定義しつつ、outputs.home1 のデフォルト値 ["html", "rss"] に追加しています。

[outputs]
  home = ["html", "rss", "llms", "llmsfull"]

[outputFormats.llms]
  baseName = "llms"
  isPlainText = true
  mediaType = "text/plain"
  rel = "alternate"
  root = true

[outputFormats.llmsfull]
  baseName = "llms-full"
  isPlainText = true
  mediaType = "text/plain"
  rel = "alternate"
  root = true

layouts/index.llms.txt

llms.txt のテンプレートとして、layouts/index.llms.txt を作成しました。

# {{ .Site.Title }}

> {{ .Site.Params.Description }}


## Articles
{{ $yearMonthGroups := slice -}}
{{ range where (where (sort (.Site.GetPage "/posts/").Pages "Date" "desc") "Draft" "ne" true) "Sitemap.Disable" "ne" true -}}
{{ $yearMonth := .Date.Format "2006/01" -}}
{{ if not (in $yearMonthGroups $yearMonth) }}
* {{ $yearMonth -}}
{{ $yearMonthGroups = $yearMonthGroups | append $yearMonth -}}
{{ end }}
    * [{{ .Title }}]({{ .Permalink }})
{{- end }}


## Others

{{- $baseURL := .Site.BaseURL | strings.TrimSuffix "/" }}

* [Device]({{ $baseURL }}/device/): 所有デバイス
* [History]({{ $baseURL }}/history/): このサイトの歴史
* [About]({{ $baseURL }}/about/): 自己紹介
* [Privacy Policy]({{ $baseURL }}/privacy/): 当サイトの広告、アフィリエイト、プライバシーポリシー

Hugo のサイト構成は様々だと思いますので、あくまで一例です。本ブログはブログ記事とそれ以外の記事があり、それ以外の記事が増減することは少ないので Others はハードコードしました。

layouts/index.llmsfull.txt

llms-full.txt のテンプレートとして、layouts/index.llmsfull.txt を作成しました。

# {{ .Site.Title }}

> {{ .Site.Params.Description }}


{{/* Articles */}}
{{ range where (where (sort (.Site.GetPage "/posts/").Pages "Date" "desc") "Draft" "ne" true) "Sitemap.Disable" "ne" true }}
--------------------------------------------------------------------------------
title: "{{ .Title }}"
date: "{{ .Date.Format "2006-01-02" }}"
--------------------------------------------------------------------------------
{{ replaceRE "{{<\\s*comment\\s*>}}(.|\n)*?{{<\\s*/comment\\s*>}}" "" .RawContent -}}
{{ end -}}


{{/* Others */}}
{{ range slice "device.md" "history.md" "about.md" "privacy.md" -}}
{{ with site.GetPage . -}}
--------------------------------------------------------------------------------
title: "{{ .Title }}"
lastmod: "{{ .Date.Format "2006-01-02" }}"
--------------------------------------------------------------------------------
{{ replaceRE "{{<\\s*comment\\s*>}}(.|\n)*?{{<\\s*/comment\\s*>}}" "" .RawContent -}}
{{ end -}}
{{ end -}}

--- を含む記事があったので、区切り文字は長めにしました。

また、以下のようなコメントがある記事があったので、llms-full.txt に出力されないようにしました。

{{<comment>}}
これは Hugo のコメントです。HTML のコメントとしても出力されません。
{{</comment>}}

💡 コード中の &lt;&gt; は、それぞれ <> に置換してください。

nginx への設定

llms.txt と llms-full.txt が文字化けしてしまったので、server ディレクティブに以下を追加しました。

location ~ "^/llms(-full)?\.txt" {
    root /usr/share/nginx/html;
    charset UTF-8;
}

まとめ

Hugo で作られた本ブログに llms.txt と llms-full.txt を設置しました。

需要はさておき、自己満足したので良しとしました。

余談ですが、最初に Hugo Discourse で見つけた方法 では、layouts/robots.txtresources.ExecuteAsTemplate をトリガーとして llms.txt を生成していました。しかし、この方法は採用を見送りました。現在 robots.txt に llms.txt を記載することは一般的ではないためです。

さらに余談ですが、本ブログは昔、ChangeLog メモ形式のファイル1つを chalow というツールで HTML に変換して作っていたサイトでした。今回 llms-full.txt を作ってその時を思い出しました。また戻ってきたなと。