基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-10-27 10:12

用边缘转换让网站直接向 Agent 提供 Markdown

dev.to · AI#Agent#边缘计算#内容协商
Editor brief · 编辑速览

教程用约 160 行无依赖代码,基于 Cloudflare Worker 的 HTMLRewriter 将站点 HTML 按需转换为 Markdown。转换仅针对明确请求 Markdown 的成功 GET HTML 响应,其余资源保持原有处理。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

同一个网址,浏览器拿到 HTML,AI agent 请求时拿到 Markdown,而且不用再维护一份页面副本。作者在 Cloudflare Worker 上用约 160 行、零依赖的转换代码,实现了这件事。

值得看的不是 HTML 转 Markdown 有多新鲜,而是这套方案如何划清转换范围、保留元数据、处理缓存,以及承认自身局限。对于已有静态落地页、想给 agent 提供更干净内容的团队,它提供了一个可以参考的实现思路。

先确认客户端要什么,再决定是否转换

Agent 读取网页,通常要下载 HTML,再从导航、脚本、样式中提取正文。有些 agent 已经会发送 Accept: text/markdown,服务器可以据此返回 Markdown。

作者没有另建 Markdown 站点,而是在静态资源返回途中转换。入口只接受同时满足四个条件的响应:GET 请求、客户端明确接受 Markdown、资源请求成功、内容类型为 HTML。

下面这段代码负责把转换范围限制在目标页面内:

export async function serveMarkdownForAgents(request, assets) {
  const response = await assets.fetch(request);
  if (request.method !== 'GET' || !wantsMarkdown(request) || !response.ok
      || !response.headers.get('content-type')?.toLowerCase().startsWith('text/html')) {
    return response;
  }
  // ...convert
}

关键是最后一行判断:即使请求接受 Markdown,图片、CSS、sitemap 等非 HTML 资源仍然原样返回。

wantsMarkdown 也不能简单写成 includes('text/markdown')。作者按逗号拆开 Accept 中的媒体类型,再按分号读取参数,只接受精确匹配的 text/markdown。没有 q 时视为接受;存在 q 时,数值必须满足 value > 0 && value <= 1。

例如 Accept: text/html, text/markdown;q=0 明确排除了 Markdown,字符串搜索却会误判。作者提到,这个场景以及「请求 Markdown 时非 HTML 资源保持不变」都有测试。

不过,这段逻辑判断的是「是否接受 Markdown」,没有比较 HTML 和 Markdown 的相对优先级。若要做完整的内容协商,还需要补充规则。

160 行能处理正文,靠的是页面足够规整

转换使用 Cloudflare 的 HTMLRewriter。它是流式解析器,按选择器触发元素和文本回调,不会先建立完整 DOM 树。转换器因此要自己保存少量状态:输出字符串数组、列表计数栈,以及表示当前是否处于忽略区域的 skippedDepth。

HTML 经 Worker 转换为 Markdown 的内容流程

作者跳过 head、nav、footer、script、style、noscript、svg、button、template,也跳过带 hidden、aria-hidden="true"、role="navigation" 或 skip-link 类名的内容。

进入这些区域时增加深度,遇到结束标签再减少;只有深度为零,正文文本才进入输出。这里有个容易漏掉的边界:img、br 等空元素没有结束标签,不能等待它们的 onEndTag。否则计数无法归零,后面的正文会一起消失。

常规映射比较直接:标题转成 # 前缀,段落和章节之间插入空行,strong、em 对应 **、*,pre 转为围栏代码块。列表栈让 ol 输出 1.、2.,让 ul 输出 -。

链接和图片则需要筛选。相对链接 /privacy 会转成绝对 URL,协议只允许 http、https、mailto、tel,javascript: 链接被丢弃。图片必须同时有 alt 文本和可解析的 src 才会输出,避免把装饰图塞进正文。

文本还会解码 &amp;、&nbsp; 和数字实体,折叠空白,最后清理换行附近的空格,把连续空行限制为一个。

正文之外,独立处理器收集 <title>、meta description、canonical URL,写入开头的 front matter。application/ld+json 的文本则放到末尾的 ## Structured data 下,让 FAQ、产品信息等结构化数据也能被 agent 读到。

但这不等于浏览器渲染结果。Worker 只看到静态 HTML,页面加载后由 JavaScript 补出的内容不会出现。

正文转换完,响应头也必须一起改

同一个 URL 返回两种正文时,缓存策略也是功能的一部分。

作者把内容类型改成 Markdown,同时清除与原 HTML 内容绑定的响应头:

headers.set('content-type', 'text/markdown; charset=utf-8');
// 若尚未存在 Vary: Accept 或 Vary: *,则添加 Vary: Accept
for (const name of ['content-length', 'content-encoding', 'content-md5',
                    'etag', 'content-range', 'accept-ranges', 'digest']) headers.delete(name);

要特别看 etag 的删除:正文已经改变,继续沿用 HTML 的 ETag,可能让条件请求得到指向错误表示的 304。

Vary: Accept 则告诉共享缓存,响应内容还取决于请求的 Accept。否则缓存只认 URL,就可能把 agent 请求得到的 Markdown 发给随后访问的浏览器。上面的注释描述了需要补充的逻辑,并没有展示具体实现。

转换整体包在 try/catch 中,发生异常就返回原 HTML。作者的取舍很明确:agent 收到 HTML 尚能继续处理,转换失败导致 500 则直接打断访问。

原文提供的验证命令是 curl -s -H "Accept: text/markdown" https://talkaside.com/ | head -20,同一 URL 在普通浏览器中仍返回正常页面。不过,文章没有给出延迟、CPU 开销或 token 节省量的对照数据,不能据此推算性能收益。

我的判断

我认可这套方案把 HTML 保留为唯一内容源,再按请求派生 Markdown。对于结构稳定的营销页,它省去了人工同步两份内容的麻烦,也把缓存和降级处理纳入了实现。

但「约 160 行够用」有明确前提:作者的页面主要由标题、段落、列表和 FAQ 构成,转换器也专门适配了该站点的语义 HTML。这是有边界的工程经验,不能直接当成通用转换方案。

原文自己承认,表格只输出竖线分隔的行,缺少 |---| 表头分隔行,因此不属于严格有效的 Markdown 表格。富组件和复杂表格更适合交给成熟的 HTML-to-Markdown 库处理,代价是增加打包依赖。文章没有给出两者的质量、体积或耗时对比。

还要检查代码块保真度:原文同时提到 pre 围栏和空白折叠,却没有展示如何保护代码里的缩进与换行。迁移到技术文档站之前,这一点需要验证。

能拿来做什么

  • 如果你的静态落地页已经通过 Cloudflare Worker 提供资源,可以借鉴这套请求判断,只转换成功的 HTML GET,并验证 q=0 和非 HTML 资源的行为。
  • 给同一 URL 增加 Markdown 表示时,把 Vary: Accept、旧 ETag 和内容相关响应头一起检查,分别验证浏览器请求与 agent 请求。
  • 前端页面如果依赖客户端请求补正文,先确认静态 HTML 实际包含哪些内容。这个转换器无法补齐浏览器执行 JavaScript 后才出现的信息。
  • 技术文档站应先用嵌套列表、代码块和表格做样例验证,再决定采用定制转换器还是成熟库,重点检查信息有没有丢失、格式有没有改变。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
模型输出通过 JSON 校验仍可能执行错事
下一篇
音频转写接口的上传与供应商解耦实践

资讯速览

dev.to · AI · 2026-10-27 10:12

AI 评分 8.0 / 10

相关标签

#Agent#边缘计算#内容协商

继续阅读

  • GPT-6.1 Terra 主打低成本视觉智能体
  • 模型输出通过 JSON 校验仍可能执行错事
  • 音频转写接口的上传与供应商解耦实践
  • Ollama 在 Mac 上支持本地流式语音
  • GPT-6.1 Terra 如何取舍速度与能力