
WordPress 站点接入 AI 聊天后,交给模型和向量索引的文本从哪里取,决定了会员内容会不会从回答里漏给未登录的访客。AI Chat for Amazon Bedrock 在固定提交 628d350 的 includes/class-ai-chat-bedrock-content.php 里,把这件事收拢到一个出口:先判断整篇文章是否公开,再删掉带可见性规则的区块,然后以未登录身份渲染正文,只使用渲染出来的结果。本文依据这份源码和仓库附带的合成数据测试,讲这几道处理各管什么,noindex 页面为什么另算,以及站点仍要自己补上的部分。
插件的整体功能见 AI Agents & Chat for Amazon Bedrock:本站在用的 WordPress 对话与 MCP 插件;同一插件在订单数据上的另一条边界见 只出现“包裹”“物流”这类词不够:WooCommerce 聊天助手如何判断一句话在问订单。下文结论来自阅读源码和测试文件([O],项目自己的实现与断言)。本文没有运行测试,也没有在真实站点上验证。
为什么去掉标签还不够
文件头注释先交代了起因。会员和可见性插件(注释列出 Block Visibility、Paid Memberships Pro、MemberPress、Restrict Content 等)是在页面渲染时隐藏某些段落的,数据库里存的 post_content 仍然包含访客看不到的文字。直接对存储的标记去掉标签再建索引,会员段落就进了 embedding,也进了回答访客问题时引用的参考材料。注释写明,插件交给模型或向量索引的所有内容都从这个类取得:以无人登录的状态让文章经过 the_content,只用它产生的结果。
对外的入口是 public_text():返回“未登录访客看到的纯文本”,标题在前,保留段落分隔,供后续切分成段落。
整篇判定:是否公开,是否可用于回答
is_public() 要求文章状态为 publish、没有设置访问密码,并且文章类型可公开查看(is_post_type_viewable())。不满足的文章,public_text() 返回空字符串。站点可以通过过滤器 ai_chat_bedrock_is_public_post 把已发布的文章判为不公开,注释举的例子是会员插件按请求逐次保护的会员专用文章类型:整篇只给特定受众的内容,应在这一层排除。
聊天和智能体另有一道 is_answerable():在公开的前提下,SEO 插件标了 noindex 的页面默认也不用于回答。注释的理由是,站点不让搜索引擎收录的页面通常有原因,例如附带表单赠送下载的感谢页、活动落地页、只给某一位客户的页面;聊天本质上是在搜索站点,所以同样把它们排除在外,除非设置项 include_noindex 打开。代码读取 Yoast SEO、Rank Math、SEOPress 对文章及其文章类型默认值的设置,以及 All in One SEO 对单篇文章的设置;“设置 → 阅读”里的“建议搜索引擎不索引本站点”不计入,因为那通常用来隐藏预发布站点。其他 SEO 插件可通过 ai_chat_bedrock_post_is_noindex 接入。
要分清两件事:noindex 只是编辑上对搜索引擎的偏好,不是访问控制。noindex 页面本身仍然公开,任何人拿到链接都能打开,代码注释也写明朗读不受这道判定影响(访客已经在这个页面上)。index_version() 把提取逻辑版本(当前 VERSION = 3)和是否包含 noindex 拼成索引版本号,任一项变化都会让所有文章重新处理。
区块与渲染:有规则就删,再以访客身份渲染
只以访客身份渲染还不够。注释给了一个实际遇到的例子:Block Visibility 只在前台请求中加载它的过滤器,而设置页建索引和聊天回答都走 admin-ajax,结果会员区块对所有人渲染,被建进了索引。所以 without_restricted_blocks() 在渲染前就删掉所有带可见性规则的区块,不管是什么请求,也不去推算规则对访客是否生效。只对访客显示的区块(例如“登录后阅读更多”)也一并删掉。注释的权衡是:丢掉一句登录提示没有代价,而猜错哪条规则会隐藏区块就可能泄露内容。
测试文件 tests/content.php 用合成区块覆盖了 blockVisibility 属性的几种写法。下表按其中的断言整理:
| 区块设置(测试夹具) | 是否进入文本 |
|---|---|
启用的 controlSets,规则为仅登录用户可见 | 否,嵌套在公开分组里的同类区块也删除 |
启用的 controlSets,规则为仅未登录访客可见 | 否 |
hideBlock: true | 否 |
1.x 写法:visibilityByRole 指定角色 | 否 |
启用的 scheduling | 否 |
插件不认识的设置(夹具里叫 somethingNew) | 否,未知设置一律视为规则 |
规则集未启用或为空、visibilityByRole: all、hideBlock: false、未启用的 scheduling | 是,这些设置不限制任何人 |
删除嵌套区块时,代码同时去掉父区块 innerContent 里对应的占位,父区块自己的标记保留;没有可删内容时原样返回。其他插件的受限区块可通过 ai_chat_bedrock_block_is_restricted 声明,测试用 className 为 paywall 的段落演示了这一点。
随后 render_as_guest() 把当前用户临时设为 0、设置全局 $post,让删减后的正文经过 the_content;渲染期间用输出缓冲丢弃任何过滤器直接 echo 的内容,并在 finally 里恢复原用户和原全局文章。一个静态标志防止过滤器在渲染中再次请求文本造成递归。最后 to_text() 去掉 script、style、noscript、template、form、svg、iframe 整段(注释指出 wp_strip_all_tags 会保留表单标签文字),再经过过滤器 ai_chat_bedrock_indexable_text。该过滤器的文档写明:只能用来删得更多,不能加入访客读不到的文字。
测试与朗读:同一条边界的两个检查点
测试文件开头写明运行方式。下面的克隆与检出步骤是本文补充的,源码没有写明所需的 PHP 版本:
git clone https://github.com/noteflowai/ai-chat-for-amazon-bedrock.git
cd ai-chat-for-amazon-bedrock
git checkout 628d350366fb120343dd7506f259c64267ae8025
php tests/content.php
全部断言通过时输出 OK: guest-visible content checks passed;有失败时向标准错误输出 FAILED 和失败列表,退出码为 1。测试用桩函数代替 WordPress,包括区块解析器,并注册一个模拟可见性插件:登录用户看到完整内容,访客看不到 members-only 段落。核心断言是会员段落里的合成折扣码 TIGER42 不出现在访客文本里,前后的公开段落保留,渲染结束后登录用户(ID 7)和全局文章恢复。草稿、带密码的文章和不可查看的文章类型都没有公开文本。区块夹具模拟的是一个公开说明加会员分组的课程页,类似本站 课程 里“登录后继续阅读”的讲义结构。所给测试文件里没有覆盖 noindex 判定的断言。
朗读功能在 includes/class-ai-chat-bedrock-speech.php。文件头注释说明它默认关闭,分两部分:聊天回答的“收听”按钮只朗读站点签过名的回答文本;文章的“收听这篇文章”只把未登录访客能读到的文本发给 Amazon Polly,因此会员内容不会发出,生成的音频保存在上传目录中,直到文章改动。同一文件里,默认只有 post 类型提供文章朗读(过滤器 ai_chat_bedrock_speech_post_types),设置项可以要求登录后才能收听。所给的朗读源码摘录不完整,本文没有核对它调用文本出口的具体代码,以上依据的是注释。
局限 / 注意
- 内置的区块级识别只认 Block Visibility 的属性。用短代码、整页规则或其他区块属性限制内容的会员插件,要么依赖“以访客身份渲染”时它们的过滤器确实在当前请求中加载,要么需要站点自己写
ai_chat_bedrock_block_is_restricted或ai_chat_bedrock_is_public_post过滤器。Block Visibility 的例子说明前一种前提并不总是成立。接入后应以未登录身份在真实站点上回读:问一个答案只在会员区块里的问题,确认回答和引用的段落里都没有它。 - 测试是用桩函数和合成数据的单元测试,证明的是这段代码在这些夹具上的行为,不证明线上站点安全,也不证明与每个会员插件兼容。
- “只删不加”是过滤器文档里的约定,代码不会阻止过滤器加入文字;站点自己写的过滤器要自己负责。
- 文本缓存的键只由文章 ID、修改时间和
VERSION组成;没有持久对象缓存时只在当前请求内有效,有持久对象缓存时最长保留一小时。在会员插件设置里改规则而不修改文章,不会让这份缓存失效;向量索引何时随之更新,不在本文所读的文件里。 - noindex 排除只影响聊天和智能体的取材,不是访问控制;要保护的内容应放在会员规则或非公开文章里,而不是靠 noindex。
- 网站上的 Polly 朗读和本站课程视频的 AI 配音是两套独立的东西。开启朗读还涉及 AWS 权限和按量计费,本文不讨论这些配置,也不构成修改 IAM 或计费设置的依据。



