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

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

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

PHP 站点可用约 40 行的 llms.php 从数据库或 CMS 读取栏目与文章并拼接成 Markdown,再通过 Nginx/Apache 重写将 /llms.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。这几条补上,这套东西才算真的可以放着不管了。