跳至正文
Acecore
目录
安全渲染 AI 聊天回答中的 Markdown 链接

如果 AI 聊天返回 详见[服务列表]( /services/ ),链接可能不会被渲染,原始 Markdown 会直接留在界面上。

Acecore 的咨询 AI 聊天也遇到过这个问题,并在 修正 Markdown 链接渲染的 PR 中调整了渲染器。

本文从这个小修正出发,整理如何把 AI 回答安全地转换为 DOM。

AI 回答不是可信 HTML

首先,AI 输出应作为文本处理,而不是 HTML。

聊天 UI 中确实需要链接、粗体和列表。但如果把回答直接放进 innerHTML,就等于让浏览器解释模型输出的任意字符串。

你不需要实现完整 Markdown。你需要的是一个小型渲染器:只检测聊天支持的少量表达,并只创建安全的 DOM 节点。

问题不只是空白

这次直接出现问题的是这样的链接:

[服务列表](/services/)

人能理解,但如果正则把 URL 定义为“不含空白的字符串”,它就不会匹配。

严格版本类似这样:

;/\[([^\]]+)\]\(([^)\s]+)\)/

[^)\s]+ 不允许空白,所以 ( /services/ ) 不会作为链接被解析。修正方式是在括号内部允许前后空白,然后再规范化 URL。

;/\[([^\]]+)\]\(\s*([^)]+?)\s*\)/

但不能只放宽正则就结束。后面必须进行规范化和安全校验。

href 要先 trim 再校验

顺序应固定:

  1. 从 Markdown 中取出 label 和 raw href
  2. 对 raw href 执行 trim()
  3. 用允许列表校验 trim 后的 href
  4. 只有允许时才创建 a 标签
const href = String(rawHref || '').trim()

if (label && isSafeMarkdownHref(href)) {
  const link = document.createElement('a')
  link.href = href
  link.rel = 'noopener noreferrer'

  if (/^https?:\/\//i.test(href)) {
    link.target = '_blank'
  }

  link.textContent = label
  parent.appendChild(link)
}

校验的值和实际放进 DOM 的值必须一致。否则安全校验会变弱。

允许列表要按产品决定

AI 可以展示哪些 URL,应由每个站点自己决定。

Acecore 的咨询 AI 大致允许以下范围:

类型示例判断
内部路径/services/允许
同一originhttps://acecore.net/...允许
官方LINEhttps://lin.ee/...目的明确,因此允许
mailtomailto:[email protected]只允许固定地址
teltel:05088902788只允许固定号码
其他外部任意 URL原则上不链接化

实现可以这样写:

function isSafeMarkdownHref(href) {
  if (href.startsWith('/')) return true

  try {
    const url = new URL(href, window.location.origin)
    if (url.origin === window.location.origin) return true
    if (url.hostname === 'acecore.net') return true
    if (url.hostname === 'lin.ee') return true
  } catch {
    return false
  }

  return href === 'mailto:[email protected]' || href === 'tel:05088902788'
}

这个函数应按产品调整。招聘站可能允许招聘媒体,电商可能允许支付或配送追踪域名,SaaS 可能允许文档和状态页。

不允许的链接退回文本

链接未通过校验时如何处理,也是一项设计。

对于咨询 AI,保留原始 Markdown 文本通常比删除更好。用户能保留上下文,开发者也能看到模型试图输出什么。

渲染器不仅负责创建安全链接,也负责在无法安全创建链接时安全失败。

提前准备测试用例

这种渲染器如果只测正常路径,很容易漏掉问题。

至少确认以下用例:

输入期待结果
[服务列表](/services/)生成内部链接
[服务列表]( /services/ )trim 后生成内部链接
[LINE]( https://lin.ee/example )生成允许的外部链接
[危险](javascript:alert(1))不链接化
[外部](https://example.com/)域名不允许时不链接化
[损坏](/services/作为文本显示
`code` 和 [link]( /contact/ )code 和 link 都正确渲染

PR #99 中确认了 [服务列表]( /services/ )[服务列表](/services/)[LINE]( https://lin.ee/DjIrdqj ) 会解析到预期 URL。

默认不要实现完整 Markdown

AI 聊天所需的 Markdown 子集可以很小:

  • 段落
  • 列表
  • 粗体
  • 行内代码
  • 链接

表格、图片、原始 HTML、脚注、深层标题结构会迅速扩大渲染器的责任。聊天 UI 只需要可读的引导。

如果之后使用成熟 Markdown 库,也仍需单独决定是否允许 HTML、如何限制 URL、外部链接添加哪些属性。

总结

AI 聊天的 Markdown 链接渲染看起来只是一个小 UI 修正,但本质上是在决定信任 AI 输出的边界。

重点如下:

  • AI 回答作为文本处理,而不是 HTML
  • 只把必要的 Markdown 子集转换成 DOM
  • 允许 Markdown 链接 URL 前后空白
  • href trim 后再做安全校验
  • 只允许内部 URL 和必要的外部域名
  • 不允许的链接保留为文本
  • 测试损坏 Markdown 和危险 URL

AI 越多参与网站导线,链接渲染就越重要。便利的 Markdown 支持和严格的链接控制,应在同一实现中一起设计。

AI 回答的链接渲染流程

Text

首先把模型回答当作纯文本处理。

Parse

只解析聊天中实际需要的 Markdown 表达。

Validate

trim href,并只允许站内 URL 或认可的域名。

Render

使用 DOM API 创建安全元素,而不是使用 innerHTML。

Markdown 链接渲染中应拆开的判断

粗略渲染

  • 直接把 AI 回答放进 innerHTML
  • 一开始就试图实现完整 Markdown 规范
  • URL 前后有空白时无法链接化
  • 把外部 URL 和 javascript: URL 当成同一类处理

小而安全的渲染

  • 以文本接收回答,只把需要的表达转换成 DOM
  • 只支持聊天中使用的 Markdown 子集
  • URL trim 后再校验
  • 不允许的 URL 保留为普通文本
导入检查
  • 已完成: 不要把 AI 回答当成 HTML 信任
  • 已完成: 允许 Markdown 链接 URL 前后的空白
  • 已完成: href 一定要 trim 后再校验
  • 已完成: 只允许内部路径、当前 origin 和必要的外部域名
  • 已完成: 为外部链接明确设置 target 和 rel
  • 已完成: 不允许的链接保留为文本
  • 已完成: 不只测试正常路径,也测试危险 URL 和损坏的 Markdown
常见问题
使用 markdown-it 或 marked 就足够了吗?
即使使用库,也仍然需要设计 HTML 输出的处理方式、允许哪些链接目标、是否添加 target 和 rel,以及如何拒绝危险 URL。聊天用途下,小型自定义渲染器也可能足够。
允许 URL 前后空白会不会变危险?
风险不在空白本身,而在 trim 后允许什么。对 trim 后的 href 做允许列表校验,就能同时容忍模型的格式波动并保持安全。
不允许的 URL 应该删除吗?
多数情况下保留为文本更便于调试,也能保留回答上下文。如果运营规则要求隐藏可疑字符串,也可以删除整个链接。

评论

正在加载评论...

不能发布链接、邮箱地址或宣传内容。

G

Gui

Acecore 代表。从业务课题梳理到设计、导入和上线后的改进,统筹推进业务系统、Web、数据库/基础设施、质量保证以及 AI 应用。 以 C#/.NET 的实际开发能力为基础,同时理解 PHP/JavaScript、SQL Server/PostgreSQL/MySQL、Linux/Windows Server,将需求整理、技术选型、质量标准和基于 GitHub 的开发运营作为整体流程来设计。 将生成式 AI 用于开发、验证和信息整理等业务流程,作为帮助小团队更快、更可靠交付成果的实务基础。

业务课题梳理技术选型系统设计C#/.NET数据库/基础设施设计GitHub开发运营生成式AIAI工作流设计质量设计现场联动

想了解更多关于我们的服务?

我们提供系统开发、网站设计、平面设计、IT教育等全方位支持。

相关文章

搜索文章