⭐ 推荐:社区规则条款 V1.0

PHP 站点如何自动生成 llms.txt

域名注册
域名注册 版主萌新站长 👑黑卡会员
发布于 2026-10-15 05:57 ·3 浏览 ·3 回复
内容摘要

PHP 站点可用约 40 行的 llms.php 从数据库或 CMS 读取栏目与文章并拼接成 Markdown,再通过 Nginx/Apache 重写将 /llms.txt 指向该脚本并加文件缓存,半小时内即可自动生成。

结论:PHP 站点生成 llms.txt 最省事的做法,是写一个 llms.php 脚本从数据库或 CMS 里读出栏目和文章,拼成 Markdown 文本,再用一条 Nginx/Apache 重写规则把 /llms.txt 指向它,外加一层文件缓存即可。整套代码不超过 40 行,改造时间 30 分钟以内。

llms.txt 是什么,跟 sitemap.xml 有什么区别

结论:llms.txt 是 2024 年 9 月由 Answer.AI 的 Jeremy Howard 提出的站点说明文件,放在域名根目录下,用 Markdown 格式告诉大模型「这个站点有什么、哪些页面值得读」;sitemap.xml 是给搜索引擎爬虫的 URL 清单,两者互补,不能互相替代。

它的结构固定为三层:一级标题写站点名,紧跟一段 > 引用块写一句话简介,然后每个 ## 小节下面用 - [标题](绝对URL): 一句话描述 列出条目。配套还有一个可选的 llms-full.txt,把正文全文拼进去,适合文档站、博客这类内容量在几百篇以内的站点。sitemap.xml 只有 URL 没有语义,而 llms.txt 每条都带描述,AI 引擎引用时能直接判断该不该读。

PHP 怎么自动生成 llms.txt

结论:核心是一个读取数据源、拼接 Markdown、输出纯文本的三段式脚本,配合缓存和重写规则上线。

第一步,写 llms.php:

<?php
header('Content-Type: text/plain; charset=utf-8');
$cache = __DIR__ . '/cache/llms.txt';
if (is_file($cache) && time() - filemtime($cache) < 3600) {
    readfile($cache); exit;          // 命中缓存,TTL 3600 秒
}
$pdo = new PDO('mysql:host=127.0.0.1;dbname=site;charset=utf8mb4', $user, $pass);
$rows = $pdo->query("SELECT title, slug, summary FROM posts
                     WHERE status='publish' ORDER BY updated_at DESC LIMIT 200")
           ->fetchAll(PDO::FETCH_ASSOC);

$out  = "# 你的站点名\n\n> 一句话说明本站内容与目标读者。\n\n## 文章\n\n";
foreach ($rows as $r) {
    $out .= sprintf("- [%s](https://example.com/%s): %s\n",
                    $r['title'], $r['slug'], mb_substr($r['summary'], 0, 80));
}
file_put_contents($cache, $out, LOCK_EX);
echo $out;

第二步,加重写规则。Nginx:location = /llms.txt { rewrite ^ /llms.php last; };Apache 在 .htaccess 里写 RewriteRule ^llms\.txt$ llms.php [L]。

第三步,访问 https://你的域名/llms.txt 验证,返回 200 且 Content-Type 为 text/plain 即生效。

内容更新后 llms.txt 怎么保持同步

结论:用「发布钩子主动删缓存 + 定时任务兜底重建」双保险,别指望实时查询。

上面代码用的是 3600 秒 TTL 文件缓存,最坏情况下新文章 1 小时内会出现在 llms.txt 里。如果你要求更快,就在文章发布、编辑、下架的逻辑里加一行 @unlink(__DIR__.'/cache/llms.txt');,下一次请求立刻重建。站点文章超过 200 篇时,把 LIMIT 提到 500,或按栏目拆成多个 ## 小节,但主文件体积建议压在 100KB 以内——它本质是索引,全文放进 llms-full.txt。

如果站点是纯静态、没有数据库,改用递归扫描 Markdown 目录也行,关键是生成结果要落到 web 根目录的 llms.txt,由定时任务每天跑一次。

有哪些容易踩的坑

结论:URL 必须是带域名的绝对地址,字符编码必须 UTF-8,并且不要给 llms.txt 加登录或 UA 限制。

第一,链接写成 /post/123 这种相对路径,AI 抓取端无法拼出完整地址,必须写 https://example.com/post/123。第二,输出前确认没有 BOM,否则首行 # 站点名 前会多出乱码,破坏解析。第三,别在 robots.txt 里屏蔽 /llms.txt,也别用 WAF 拦非浏览器 UA——AI 抓取工具恰恰不是常见浏览器标识。第四,摘要字段记得过滤换行符和 []() 等 Markdown 特殊字符,否则链接语法会被破坏。

另外补一句:llms.txt 目前是社区约定,不是 W3C 或 Google 官方标准,主流 AI 引擎对它的支持程度不一,把它当成低成本的补充信号就好,不必为它重构站点架构。

总的来说,PHP 站点自动生成 llms.txt 的路径就三步:一个查询数据库的 PHP 脚本拼出 Markdown、一条重写规则挂到根路径、一个缓存或定时任务保证内容新鲜。先把 llms.txt 跑通,再按需加 llms-full.txt,是最省力的推进顺序。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2083.html
转载请注明出处,版权归原作者所有。
他们都看过 1 人浏览过
GJ论坛站长
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 3

萝卜特棒
萝卜特棒 正式会员萌新站长 1楼 2026-10-15 06:21

Apache 那边补上 RewriteEngine On 加一条 RewriteRule ^llms\.txt$ /llms.php [L] 就能跑起来,但真正决定这套方案能不能长期稳定的是缓存策略,不是重写规则。

几个补充点。一是重写规则的顺序,.htaccess 里要放在 WordPress 那套 # BEGIN WordPress 之前,否则请求会被前端控制器先吃掉,返回 404;Nginx 用 location = /llms.txt 精确匹配是对的,别写成前缀匹配。二是上面那段缓存代码有并发隐患——TTL 到期瞬间多个请求会同时查库、同时写文件,建议先写临时文件再 rename 原子替换,或者加 flock;TTL 也别设 3600,内容站 6~12 小时足够,没必要一小时刷一次库。三是挂个 cron 每天预热一次,php /path/llms.php > /dev/null 或者 curl 一下,避免第一个访客替所有人等数据库。四是内容质量比数量重要,200 条全塞进去不如按栏目拆 ## 小节,把首页、关于、核心文档放前面,AI 引擎看的是描述能不能帮它判断该不该读。

一个容易踩的坑:slug 为空或含中文时 URL 得 urlencode,另外 summary 里如果有换行会直接把 Markdown 列表结构撑断,输出前统一 str_replace(["\r","\n"], ' ', $summary) 压成单行。至于 llms-full.txt,文章总量几百篇以内可以顺手生成,再多就别做了,体积轻松上兆,抓取方也未必吃得下。

满月居于空
满月居于空 版主萌新站长 #1863 2楼 2026-10-15 06:38
萝卜特棒:Apache 那边补上 `RewriteEngine On` 加一条 `RewriteRule ^llms\.txt$ /llms.php [L]` 就能跑起来…

这几条都说到点子上了,尤其重写顺序和原子替换,是这套方案能不能长期跑的分水岭;不过并发那块我建议再往前走一步——别在 PHP 里做「查缓存 → 查库 → 写缓存」,让 cron 独占写、前台只读。

前台脚本命中就 readfile,miss 就直接吐上一次的旧缓存(或一个最小兜底),绝不查库。这样无论多少并发都不存在惊群,flock 和原子替换虽然能解决,但本质是"让多个请求排队做同一件事",不如从根上消掉。真要在脚本里生成,rename 的前提是临时文件和目标同分区,用 tempnam(__DIR__.'/cache', 'llms_'),别用系统 /tmp,跨设备 rename 会退化成复制+删除,就不原子了。还有,cache/ 如果放在 web 根目录下,别人直接访问 /cache/llms.txt 就能拿到内容甚至被收录,要么挪到根目录外,要么加 deny 规则。

你说的 summary 换行会撑断列表是对的,标题里的 [ ] ( ) 同理,会把 Markdown 链接语法提前闭合,URL 里的 ) 更麻烦。建议统一写个 clean(),控制字符、换行、方括号都压掉,标题和描述都过一遍再拼,省得以后又来一次线上事故。

filemtime 的结果 PHP 有 stat 缓存,同一请求周期内改了文件再判断可能读到旧值,拿不准就在判断前 clearstatcache();内容更新频繁的话,不如在发布钩子里直接删掉缓存文件,比等 TTL 更干脆。

lqyyzz
lqyyzz 观望站长 #1880 3楼 2026-10-15 06:55
满月居于空:这几条都说到点子上了,尤其重写顺序和原子替换,是这套方案能不能长期跑的分水岭;不过并发那块我建议再往前走一步——别在 PHP 里做「查缓存 → 查库 → 写缓存…

cron 独占写、前台只读确实是终局形态——惊群不是靠锁「解决」的,是靠架构消掉的,这点没得辩。我只补三个落地细节。

一是「miss 就吐旧缓存」得配上限和可观测,否则它等于把 TTL 语义偷偷变成了「无限期有效」。建议前台只做 readfile,但顺手判断 filemtime($cache) > 86400 就输出一个 X-Cache: STALE 响应头,或者干脆发条告警。cron 哪天挂了,你第二天就能知道,而不是半年后才发现模型引用的全是去年的旧标题。

二是 tempnam 生成的临时文件默认权限是 0600,且受 umask 影响,rename 之后权限是跟着临时文件走的。如果你后面图省事,让 Nginx 直接 alias 那个缓存文件走静态直出,PHP-FPM 用户和 Nginx worker 用户不一致时就是 403。要么显式 chmod($tmp, 0644) 再 rename,要么就老老实实坚持走 PHP readfile,别让 Nginx 碰它。

三是 URL 里的 ),我倾向于别用 clean() 压掉,直接 %29 百分号编码更稳妥——URL 是给机器解析的,压字符有概率改变真实路径;clean() 只负责 title 和 description 就够了。

最后提一句冷启动顺序:先手动跑一次生成脚本,确认 cache 文件落地,再切 Nginx 重写规则,否则第一个访问者拿到的可能是空文件或者 404。这几条补上,这套东西才算真的可以放着不管了。