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

SvelteKit 聊天应用接入工具执行审批

dev.to · AI#SvelteKit#工具调用#人工审批
Editor brief · 编辑速览

教程用约 150 行代码搭建 SvelteKit AI 聊天页,通过服务端路由调用模型,并要求用户批准后才执行 add_task 工具。界面采用作者维护的 MIT 组件库 aparté,提供完整示例及无需密钥的模拟演示。

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

AI 深度解读

给 SvelteKit 加一个 AI 聊天框,难点不只是把模型的回复显示出来,还包括:工具什么时候执行、用户在哪里确认、密钥放在哪里,以及执行结果怎么回到对话里。作者用 AI SDK 和 aparté,把这些环节接成了一个约 150 行的任务清单示例。

它最值得参考的是执行顺序:模型先提出操作,页面展示参数,用户批准后才真正添加任务。不过,这仍是一份演示教程,距离生产环境还有鉴权、持久化和服务端权限校验需要补齐。

模型负责提出操作,页面负责等待批准

作者把聊天系统分成两部分:Vercel AI SDK 提供消息、流式响应和工具调用;aparté 提供聊天界面、审批交互和工具展示。按原文的说明,AI SDK 现成的聊天组件面向 React,Svelte 页面需要另配 UI。

aparté 是作者维护的 MIT 库,以 Web Components 为基础,外面提供 Svelte 薄封装,也有 React、Vue 和 Angular 封装。教程使用的版本是 SvelteKit 3.1、Svelte 5.57、AI SDK 7 和 aparté 1.0.2。

示例只开放一个工具:add_task。模型可以提出任务标题和截止日期,但不能立即修改清单。下面这段注册代码定义了参数、审批要求和实际执行逻辑:

aparteGlobalConfig.registerTool(
  {
    name: 'add_task',
    description: "Add a task to the user's list.",
    inputSchema: {
      type: 'object',
      properties: {
        title: { type: 'string' },
        due: { type: 'string', description: 'Due date, YYYY-MM-DD' },
      },
      required: ['title'],
    },
    needsApproval: true,
  },
  async (call) => {
    const task: Task = {
      title: String(call.input['title']),
      due: call.input['due'] ? String(call.input['due']) : undefined,
    };
    tasks.push(task);
    return { toolCallId: call.id, content: `Added "${task.title}".`, structuredContent: task };
  },
);

关键是 needsApproval: true。模型发出调用后,流程会停在处理函数之前,等用户选择 Approve 或 Reject。

模型提出调用,不等于工具已经执行。

批准之后,浏览器里的处理函数才会执行 tasks.push(task)。返回值中的 content 给模型阅读,structuredContent 则把结果保留为数据,供界面组件使用。

用户也能直接输入「不,改成周五」。按作者描述,这会拒绝当前调用,再把这句话作为工具结果交给模型,让它重新提出操作。审批因此不只是一个确认按钮,也是纠正模型参数的入口。

聊天界面展示 add_task 工具调用并等待用户批准

密钥留在服务端,但执行循环为什么放在浏览器

服务端入口是 src/routes/api/chat/+server.ts。它接收页面消息,调用模型,再把响应流返回页面。核心调用如下:

  const result = streamText({
    model: anthropic('claude-haiku-5-5'), // 服务端选择模型,而不是页面
    system: `You keep the user's task list. Today is ${new Date().toDateString()}.`,
    messages: toModelMessages(request.messages),
    abortSignal: req.signal,
    ...(request.tools?.length
      ? { tools: toToolSet(request.tools), toolChoice: toToolChoice(request.toolChoice) ?? 'auto' }
      : {}),
  });

需要看清 model 和 toToolSet(request.tools) 两处。前者由服务端固定,页面即使请求更昂贵的模型,也不会改变这里的选择;后者只把工具声明交给模型,不附带 execute 执行函数。

几个适配函数承担了协议转换:toModelMessages 转换消息,toToolSet 转换工具声明,toToolChoice 转换工具选择配置,fullStreamToAparteEvents 转换响应事件。最终通过 NDJSON 返回,即每行一个 JSON 对象,响应类型是 application/x-ndjson。

页面用 AparteBackendTransport({ endpoint: '/api/chat' }) 发送请求。完整循环由页面驱动:请求一次模型,等待审批,执行工具,再请求下一次模型。服务端路由每一步只做一次模型调用。

这种安排适合修改页面状态,或携带用户会话调用业务 API。若工具需要直接访问数据库或使用秘密凭据,作者建议把循环迁到 aparté 的 server loop,审批仍留在页面。原文没有展开这部分实现。

密钥配置也有版本差异:SvelteKit 3 在 src/env.ts 中通过 defineEnvVars 声明 ANTHROPIC_API_KEY,从 .env 读取,再由 $app/env/private 导入。SvelteKit 2 则跳过声明文件,使用 $env/static/private。

此外,路由把供应商错误留在服务端日志,只向浏览器返回 The model call failed.。作者给出的理由是:供应商报错可能包含部分密钥。

工具卡片和流式 Markdown,决定用户能否看懂过程

工具审批要有用,用户就得看得懂自己正在批准什么。

aparté 默认把工具调用显示成一行,参数可以展开查看。通过 <AparteToolUI name="add_task">,开发者可以用自己的 TaskCard.svelte 替换这一行。

卡片读取 tool.input 展示标题和日期,读取 tool.status 展示进度。同一次调用的组件会原地更新,状态从 awaiting-approval 变为 resolved 或 rejected,对应等待批准、已添加和未添加。模型参数还在流式到达时,组件也会跟着更新。

这里有个细节:示例卡片展示的是调用参数;虽然工具返回了 structuredContent,这张卡片并没有读取它。接入真实接口后,如果后端会修正日期或补充任务编号,就需要进一步区分「模型申请的数据」和「实际执行的结果」。

Markdown 则由 setupMarkedStreamingProvider() 配置。marked 负责解析,Vercel 的 remend 临时补齐尚未闭合的语法,让未完成的加粗、表格也能逐步显示。

页面生命周期通过 createAparteClient() 管理:挂载时启动聊天循环,销毁时停止。作者称页面仍支持服务端渲染和水合,不会出现不匹配。配置导入放在 <script module>,避免每次页面挂载都重复执行模块初始化。SvelteKit 3 使用 #lib 别名,SvelteKit 2 对应改为 $lib。

我的判断

这套方案最有价值的地方,是把「生成建议」和「产生副作用」之间的边界做成了明确交互。相比聊天回复一句「已经帮你添加」,工具卡片能把参数、等待状态和执行结果摆出来,用户更容易发现日期理解错误之类的问题。

但审批界面不能代替后端授权。

示例处理函数运行在浏览器,真实业务 API 仍需校验会话、参数和操作权限。String(...) 只是类型转换,不能据此认定输入有效。聊天路由同样如此:原文明确提醒,它默认响应所有能访问的人,必须先检查用户会话。

演示还没有保存会话,刷新就会重来。作者提到可以接 storage adapter,但没有提供实现。内存任务数组也没有展示多人隔离和持久化方案,不能直接作为生产数据层。

证据范围尤其需要看清:作者实际运行时,用的是通过 @ai-sdk/openai-compatible 接入的本地 Gemma,替换了 Claude。因此,anthropic('claude-haiku-5-5') 这一行并未实测。无密钥演示则使用脚本模型,它能展示审批、卡片和流式 Markdown,但不理解用户请求。

原文也没有给出 aparté 与自建组件在体积、性能或维护成本上的对照。「约 150 行」说明示例紧凑,不能直接证明团队接入成本更低。我的取舍是:已有聊天 UI 时,优先参考它的审批流程;需要快速补齐 Svelte 聊天界面时,再评估引入整套组件的收益。

能拿来做什么

  • 给现有 AI 编程或业务助手增加写操作前,先列清工具名称、参数和副作用,并把用户确认放到真正执行之前,让用户能检查模型准备做什么。
  • 在 SvelteKit 中接模型时,把密钥和模型选择放进服务端路由;接业务 API 时,再分别补齐聊天入口与工具接口的会话校验。
  • 做任务类交互时,让卡片持续展示待批准、完成和拒绝状态;如果接口会修改输入,就用实际返回的数据展示最终结果。
  • 按工具需要的权限决定循环位置:页面状态操作可留在浏览器,涉及数据库或秘密凭据时评估 server loop,并分别验证模型接入、审批和拒绝后的重试流程。
Original source

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

阅读原文
上一篇
编程代理绕过网络隔离暴露沙箱风险
下一篇
给编程智能体设定可执行的项目规则

资讯速览

dev.to · AI · 2026-11-08 19:12

AI 评分 8.0 / 10

相关标签

#SvelteKit#工具调用#人工审批

继续阅读

  • 防止智能体靠改测试数据蒙混过关
  • 编程代理绕过网络隔离暴露沙箱风险
  • 给编程智能体设定可执行的项目规则
  • 把现有 SQL 表关系映射为知识图谱
  • 为 Agent 技能包加上静态检查