Sink Workers AI 功能详解:为短链接生成智能 Slug 与社交预览元数据

Sink Workers AI 功能详解:为短链接生成智能 Slug 与社交预览元数据 Sink Workers AI 功能详解为短链接生成智能 Slug 与社交预览元数据【免费下载链接】Sink⚡ A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.项目地址: https://gitcode.com/GitHub_Trending/si/Sink本文基于 Sink 官方文档 docs/features/ai.md 展开介绍 Sink 如何把 Cloudflare Workers AI 作为可选能力接入短链接服务通过/api/link/ai与/api/link/og-ai两个端点由模型读取目标页面内容后生成短代码slug建议和 OpenGraph 标题/描述。读完本文你将掌握 Workers AI 绑定的开启方式、三个相关配置项模型与提示词的默认值和约束以及端点在“页面抓取失败、模型调用失败、响应解析失败”三类异常下的降级路径并能对照源码验证每个行为。功能定位完全可选不绑定也不影响核心链路Sink 是一个 100% 运行在 Cloudflare 上的短链接服务Simple / Speedy / Secure Link Shortener with Analytics。Workers AI 在 Sink 中只是增强项绑定了AI时创建/编辑链接的界面可以用 AI 推荐短代码和社交预览文案没有绑定时链接创建、跳转、分析等全部核心功能照常工作只是 AI 端点会返回501AI not enabled。配置参考文档 中也将AIbinding 明确标注为 Optional“Workers AI suggestions”而必需的绑定只有 D1DB、KVKV。开启方式将 Workers AI 绑定为 AI开启的唯一前提是把 Workers AI 以固定名称AI绑定到 Worker。仓库中的 wrangler.jsonc 展示了本地/部署共用的绑定声明ai: { binding: AI, remote: true }绑定建立后运行时代码通过event.context.cloudflare.env.AI访问该能力。如果未绑定两个 AI 端点在入口处即抛出 501// server/api/link/ai.get.ts const { AI } cloudflare.env if (!AI) { throw createError({ status: 501, statusText: AI not enabled }) }server/api/link/og-ai.get.ts 中有完全相同的检查逻辑。模型与提示词则通过环境变量覆盖默认值定义在 nuxt.config.ts 的runtimeConfig中变量默认值用途NUXT_AI_MODELcf/qwen/qwen3-30b-a3b-fp8Workers AI 使用的模型NUXT_AI_PROMPT内置 slug 提示词短代码生成提示词自定义时必须保留{slugRegex}占位符NUXT_AI_OG_PROMPT内置 OG 提示词社交预览OpenGraph提示词内置的aiPrompt全文为You are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information should be derived from the URL and page content (if provided). Do not make any assumptions beyond the given information. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {slug: example-slug}{slugRegex}占位符在请求时会用应用配置里的真实正则替换。这个正则在 app/app.config.ts 中定义slugRegex: /^[a-z0-9](?:-[a-z0-9])*$/i,也就是说模型被要求产出的 slug 必须符合“小写字母数字段 连字符”的格式——这正是 Sink 全站校验短代码用的同一套正则见 server/middleware/1.redirect.ts 的跳转校验保证 AI 建议出来的 slug 与平台可接受的 slug 天然一致。若自定义NUXT_AI_PROMPT时删掉该占位符模型就无法感知格式约束建议的 slug 更可能被后续规范化逻辑改写或不符合预期这就是文档强调“必须保留{slugRegex}”的原因。端点一/api/link/ai —— 短代码建议接口定义在 server/api/link/ai.get.tsOpenAPI 元数据声明它“Generate a slug using AI based on the URL”需要 Bearer 认证即站点登录 token必填 query 参数为url。请求与参数GET /api/link/ai?url目标链接url由 zod 的z.url()校验缺失或非法均返回400测试用例见 tests/api/link.spec.ts。内部流程抓取页面内容调用fetchPageMarkdown(event, url, AI)尝试读取目标页详见下一节成功则拼出URL: ...\n\nPage content: ...作为用户消息失败则只用裸 URL。构造 few-shot 对话系统提示词为替换过{slugRegex}的aiPrompt随后内置 4 组示例对话cloudflare→{slug: cloudflare}、nuxt→{slug: nuxt}、sink.cool→{slug: sink-cool}等最后追加当前目标。完整构造见 server/api/link/ai.get.ts。调用模型AI.run(aiModel, { messages, chat_template_kwargs: { enable_thinking: false, thinking: false } })通过chat_template_kwargs显式关闭思考模式以降低延迟与输出噪声。解析并规范化parseAiResponse解析出 JSON 对象后取slug字段再经normalizeSlug处理默认小写除非设置了NUXT_CASE_SENSITIVE逻辑在 server/utils/link-store.ts最终返回{ slug }。降级行为只要模型调用抛出异常端点不会报错给前端而是返回基于 URL 的简单建议取 URL 路径的最后一段取不到则取 hostname把非[A-Z0-9-]字符替换为-、截断到 50 字符、去掉首尾连字符空则回退为link。测试returns a fallback slug when Workers AI fails验证了当AI.run与AI.toMarkdown同时失败时/api/link/ai?url.../fallback-slug仍返回 200 且slug为fallback-slug见 tests/api/link.spec.ts。端点二/api/link/og-ai —— 社交预览标题与描述接口定义在 server/api/link/og-ai.get.ts用于生成 OpenGraph 卡片的title与description。请求与参数GET /api/link/og-ai?url目标链接[locale语言标签]url必填zodz.url()校验缺失/非法返回 400locale可选指定生成文案的偏好语言。locale的处理比较讲究resolveMetadataLocale 会先用Intl.getCanonicalLocales把传入值规范化为合法语言标签解析失败或未传时回退到当前请求自身的重定向语言resolveRedirectLocale(event)而不是凭空猜测。规范化后的语言会被追加到系统提示词末尾${aiOgPrompt}\nGenerate the title and description in the language matching this locale: ${locale}.系统提示词aiOgPrompt默认值要求模型把页面总结为 OpenGraph 预览的标题与描述并固定输出{title: ..., description: ...}的 JSON 结构对话中同样携带两组 few-shot 示例Cloudflare 与 Nuxt 的官方简介文案。字段级降级与 slug 端点的“整体回退”不同og-ai 是按字段独立回退的模型调用失败时走fallbackMetadata(url)title 取去掉www.前缀的 hostnamedescription 为Short link for ${url}URL 解析失败则用通用文案调用成功但某个字段缺失或为空时也只回退该字段例如const title String(result.title ?? ).trim() || fallback.title const description String(result.description ?? ).trim() || fallback.description测试用例验证了 AI 全挂时title为example.com、description非空tests/api/link.spec.ts。页面抓取fetchPageMarkdown 的边界与防护两个端点共用 server/utils/markdown.ts 中的fetchPageMarkdown来获取目标页内容它的实现体现了在 Serverless 环境下“读任意外部页面”的安全边界协议白名单只处理http:/https:其他协议直接返回null不抓取页面只用裸 URL 提问5 秒超时AbortControllersetTimeout控制整个抓取256 KB 字节上限流式读取 body到达上限即reader.cancel()从源码注释看目的是“bound memory and upstream transfer”避免超大页面撑爆 Worker 内存内容类型分流若响应本身是text/markdown直接使用若是text/html则交给 Workers AI 的AI.toMarkdown()转成 Markdown转换失败仅告警并降级为“无页面内容”4096 字符截断进入提示词的页面正文最多 4096 字符MAX_MARKDOWN_LENGTH语言透传把当前请求的Accept-Language头转发给目标站优先取回对应语言的内容。任何一步失败非 2xx、超时、转换失败都被捕获并告警最终返回null——这正好衔接上文两个端点的“裸 URL 提问”路径保证 AI 功能在目标页不可读时依然可用。响应解析parseAiResponse 的容错设计Workers AI 不同模型的返回结构并不统一有的直接给response字符串有的套在choices[0].message.content里且模型偶尔会用 Markdown 代码块包裹 JSON。server/utils/ai.ts 中的parseAiResponse统一处理了这些情况兼容response与choices[0].message.content两种取值位置stripCodeFence剥掉形如json ... 的外层代码围栏仅当首行是或json时用destr安全解析 JSON只有结果是“对象”排除数组与null时才返回否则返回空对象。返回空对象时两个端点分别按前述策略回退slug 走 URL 派生og-ai 走 hostname 派生保证接口永远返回可用的结果。前端调用点链接编辑器中的两处集成AI 能力最终服务于仪表盘的链接编辑器基础表单中“AI 生成 slug”app/components/dashboard/links/editor/Form.vue 调用/api/link/ai?url...成功后把result.slug写入表单的slug字段高级设置中的社交预览app/components/dashboard/links/editor/Advanced.vue 调用/api/link/og-ai把返回的title、description填入链接的元数据字段。两个调用点都只做“填充表单”不自动保存——与文档中 “Always review before saving”保存前请人工复核的要求一致。数据隐私注意事项文档用 warning 级别强调页面内容与目标 URL 可能会被发送到 Cloudflare Workers AI。具体来说目标页正文截断到 4096 字符与完整 URL 会出现在发给模型的messages中og-ai 还会附带 locale。如果你的实例可能处理敏感链接内部系统、未公开页面、含隐私参数的链接需要先评估数据敏感度与相关政策再决定是否开启AI绑定不开启对短链核心功能没有任何影响。小结Sink 的 Workers AI 集成可以概括为一条“能算则算、算不了就兜底”的链路绑定AI→ 抓取页面5s 超时 / 256KB / 4096 字符的三重边界→ few-shot 提示词请求结构化 JSON → 容错解析 → 失败时按字段或整体回退到 URL 派生结果。三个环境变量NUXT_AI_MODEL、NUXT_AI_PROMPT、NUXT_AI_OG_PROMPT覆盖默认行为其中自定义 slug 提示词必须保留{slugRegex}占位符以维持格式约束。相关实现与测试可分别在 server/api/link/ai.get.ts、server/api/link/og-ai.get.ts、server/utils/markdown.ts、server/utils/ai.ts 和 tests/api/link.spec.ts 中查证。【免费下载链接】Sink⚡ A Simple / Speedy / Secure Link Shortener with Analytics, 100% run on Cloudflare.项目地址: https://gitcode.com/GitHub_Trending/si/Sink创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考