Article·  

介绍 Nuxt Agent

我们在 nuxt.com 上的专属 AI 代理,基于官方文档和 Nuxt 生态构建。我们使用 AI SDK、我们的 MCP 服务器和 Nuxt UI 组件在内部实现了它。
Hugo Richard

Hugo Richard

@hugorcd

Sébastien Chopin

Sébastien Chopin

@Atinux

首先,我们要感谢 Kapa.ai,过去几年里他们通过赞助为我们的 AI Chat 小部件提供支持,并且仍在通过我们的 Discord 服务器 继续支持我们的社区。:br 我们决定构建自己的产品,以展示 Nuxt 所能实现的能力,并提供与框架及其生态系统更深度的集成。

Nuxt Agent 现已在 nuxt.com 上进入 Beta 版。我们亲自构建了它,将其接入网站,并连接到了我们的文档、模块目录、博客和部署指南。

在 nuxt.com 任意位置按 ⌘I(或 Ctrl+I)即可打开 agent,或者直接前往 /chat 体验全屏模式。

从文档小部件到真正的 agent

过去几年里,Kapa AI 作为文档问答小部件很好地服务了我们。它会搜索文档并总结答案,仅此而已。但 Nuxt 不只是文档。这里还有模块、模板、部署提供商、更新日志、GitHub issues、playground,以及贯穿整个站点的真实导航。

我们希望拥有一个能够处理这一切的 agent,并且使用与 nuxt.com 其他部分相同的设计语言,以及我们已经在运行的相同内容流水线(Nuxt Content)。所以我们基于去年 11 月发布的 Nuxt MCP 服务器 构建了自己的实现。

以下是该 agent 目前能做的事情:

  • 基于权威来源回答:通过 MCP 工具,而不是检索到的文本片段,基于官方 Nuxt 文档和生态数据作答。
  • 渲染富 UI:模块、模板、博客文章、托管提供商和 playground 链接会以可点击卡片的形式返回,而不是普通链接。
  • 流式输出一切:在运行过程中持续输出,包括工具调用进度。
  • 形成闭环:反馈、投票和问题报告会流入我们的内部工具,这样我们就能持续改进它。

认识这个 agent

与它交互的三种方式

你可以通过三种方式访问 agent:

  • 大屏幕上固定在右侧的 侧边栏,在小屏幕上则会滑出覆盖。可通过页头或 ⌘I 切换。
  • /docs/blog 页面底部的 提问栏,这样你无需离开当前阅读页面就能发问。
  • 位于 /chat全屏聊天,适合更长时间的会话。

它知道你当前在哪个页面

当你一边阅读文档一边问“我该如何为我的应用自定义这个?”时,agent 会自动将该页面作为上下文带入。页脚里一个小小的“Agent 正在使用此页面”指示器会明确显示这一点,而且你可以随时关闭它。

富答案,而不只是文本

回答返回的不只是文本。询问某个模块时,你会得到一个带有从 api.nuxt.com 实时拉取元数据的模块卡片。询问 starter 模板时,你会得到一排可点击的模板卡片。询问部署时,你会得到链接到正确指南的提供商卡片。需要复现 bug?agent 还能根据对话本身生成一个 StackBlitz playground 链接。

试着问:"Show me official starter templates" —— 你会得到完整列表(nuxt-ui-dashboardnuxt-ui-saasnuxt-ui-landingnuxt-ui-chatnuxt-ui-docsnuxt-ui-portfolio),这些卡片可一键打开。

内置反馈

每条助手消息都有一个赞/踩按钮。如果你想分享更多内容(缺失内容、错误答案、想法),Report issue 操作会打开一个简短表单,并在我们这边创建一条附带对话内容的 Linear 工单,这样我们就拥有跟进所需的一切信息。

agent 也可以自己打开这个表单。如果你要求“提交反馈”或“报告问题”,或者对话开始显得有些沮丧,agent 会调用 report_issue 工具,并在内联中打开同样的表单。无需到处找按钮。

对话会被保存,并可在刷新后继续,因此你可以离开后再回来接着上次的位置继续。

agent 实际能做什么

agent 的底层依据来自 Nuxt MCP 服务器,也就是 Cursor、Claude Desktop 和 ChatGPT 连接的同一个服务器。这意味着 Nuxt Agent 和你本地的 AI 助手共享相同的结构化数据:官方文档、模块目录、博客、部署指南,以及 Nuxt 仓库的更新日志。

在这些底层依据之上,agent 还拥有一组原生工具,会在聊天中以 UI 形式呈现:模块和模板卡片、托管提供商、博客文章、StackBlitz playground 链接,以及跨 nuxtnuxt-modulesnuxt-content 的 GitHub issue 搜索。每当你粘贴错误信息时,agent 会优先调用该 issue 搜索。

Web 也可用,通过 Anthropic 的原生 web_search,但仅作为模型无法合理知道内容时的后备方案:比如刚发布的 Vue 版本、新近公开的 RFC、生态系统的最新新闻。它不是通用搜索引擎,系统提示词对此也有明确说明。对于应当从文档或通过 MCP 暴露的其他 Nuxt 内容中回答的问题,我们从不使用 web_search

内部实现

技术栈

一个位于 server/api/agent.post.ts 的 Nitro 处理器驱动全部流程。客户端使用指向 /api/agent@ai-sdk/vue Chat 实例。服务端则使用 AI SDK v6 的 streamText 调用 claude-sonnet-4.6,工具由我们自己的 MCP 服务器(/mcp,同源)和少量原生工具(show_*open_playgroundreport_issue)合并而成。聊天状态存放在 Drizzle ORM 中,而 evlog 会为 token、成本和工具调用提供结构化遥测包装。

使用 AI SDK v6 的 UIMessage 流式传输

整个流水线运行在 AI SDK v6UIMessage 流式模型之上。服务端代码如下:

server/api/agent.post.ts
const stream = createUIMessageStream({
  execute: async ({ writer }) => {
    const result = streamText({
      model: ai.wrap(MODEL),
      maxOutputTokens: 4000,
      stopWhen: stopWhenResponseComplete,
      system: systemPrompt,
      messages: await convertToModelMessages(messages),
      tools: {
        ...mcpTools as ToolSet,
        web_search: anthropic.tools.webSearch_20250305(),
        search_github_issues: createSearchGitHubIssuesTool(event),
        show_module: showModuleTool,
        show_template: createShowTemplateTool(event),
        show_blog_post: createShowBlogPostTool(event),
        show_hosting: createShowHostingTool(event),
        open_playground: openPlaygroundTool,
        report_issue: reportIssueTool
      },
      experimental_telemetry: {
        isEnabled: true,
        integrations: [createEvlogIntegration(ai)]
      }
    })

    writer.merge(result.toUIMessageStream({
      sendSources: true,
      originalMessages: messages,
      onFinish: ({ messages: finalizedMessages }) => {
        event.waitUntil(saveChat(finalizedMessages))
      }
    }))
  }
})

有两个细节值得指出。stopWhen: stopWhenResponseComplete 是一个自定义谓词,只要模型生成了文本且不再有其他工具调用,就会立刻结束循环,并且最多限制在 10 步内。这避免了经典的“模型在工具上无限循环”的故障模式。还有 event.waitUntil(saveChat(...)),它将持久化操作放到响应生命周期之外,因此流会立即为用户结束,而聊天记录会在后台完成 upsert。

一个 MCP 服务器,两个消费者

agent 和外部 AI 助手使用的是同一个 MCP 服务器。这是我们做出的最重要的架构选择。路由处理器会打开一个指向自身 /mcp 端点的 HTTP MCP 客户端:

server/api/agent.post.ts
const httpClient = await createMCPClient({
  transport: { type: 'http', url: `${getRequestURL(event).origin}${MCP_PATH}` }
})
const mcpTools = await httpClient.tools()

这些工具随后会与原生 UI 工具合并成一个传递给 streamText 的单一 tools 对象。好处在于:我们添加到 MCP 服务器中的任何工具,都会立即对 Nuxt Agent 以及所有指向它的外部助手可用,无需额外接线。我们在去年 11 月还为 MCP 服务器本身的工作方式写了一篇 单独的文章

持久化、成本和速率限制

聊天记录保存在一张 agent_chats 表中,并以客户端每次请求都会发送的 x-chat-id 头作为键。Drizzle 的 onConflictDoUpdate 会在整个会话生命周期内累加 token 使用量、估算成本、耗时和请求次数。这让我们免费获得每个聊天的分析数据。

每个请求在流式输出开始前还会先经过一个小型的 consumeAgentRateLimit 辅助函数。当前限制是每个 IP 指纹每天 20 条消息,这对于真实使用来说已经足够,同时也足以在某些逻辑开始循环时防止成本失控。

简洁而有力的系统提示词

agent 的很多质量都来自提示词。几个规则承担了大部分作用:当用户粘贴错误时优先使用 search_github_issues;当答案应以卡片形式展示时,优先使用 show_module 而不是 get_module;除非问题确实超出了模型能知道的范围,否则绝不调用 web_search;并且绝不只通过一次工具调用就结束一个回合。正是这些规则一起减少了工具噪音和幻觉,使 agent 能始终聚焦于手头任务。

接下来是什么

该 agent 正在以 Beta 版启动。短期内,我们专注于基础能力:更好的回答质量、跨轮次更丰富的记忆,以及更清晰的来源引用。

从更长远来看,我们希望 nuxt.com 感觉更像一个应用,而不是一个静态网站。下一步是用户账号。每个登录用户都会拥有自己的会话、会被保存且可在不同设备间继续的聊天记录,而 Nuxt Agent 将成为这个更像应用的 nuxt.com 的第一个真正构建模块。

我们很希望你能帮助塑造它接下来的发展方向。如果 agent 出了问题,或者遗漏了你希望看到的内容,请使用聊天中的 Report issue 按钮。它会在我们这边创建一张工单,并附上完整的对话记录,我们会逐一阅读。

现在就试试 Nuxt Agent:按 ⌘I 打开它,在任意 docs 页面使用 ask bar,或者前往 /chat 体验全屏模式。

nuxt.com 的完整源代码都在 GitHub 上,包括 agent、MCP server,以及上面提到的所有工具。agent handler 位于 server/api/agent.post.ts,原生工具位于 server/utils/tools/,UI 组件位于 app/components/agent/。你可以将其中任何内容作为自己应用的灵感。如果你想构建自己的 MCP server,Nuxt MCP Toolkit 能在几分钟内帮你完成。