# 扣子智能体 API 调用前后端部署实战:基于对象存储与云函数的 Serverless 方案
# 一、 问题的提出
在有关智能体的研究中 我们需要将制作的智能体给学生使用 但是必须考虑以下问题:
- 终端限制:校园环境下的计算机机房通常受限于系统还原机制或安全策略,不便于安装各类客户端 App。因此,基于浏览器的 Web 网页成为学生访问智能体的最佳载体。
- 数据采集需求:研究核心在于分析学生与 AI 的对话行为。我们需要通过 API 调用,结合扣子(Coze)的数据提取功能,实时获取学生身份信息(班级、学号)及完整的对话上下文,以供后续的教育数据挖掘。
- 并发与性能考量:教学场景具有典型的高并发特征。上课期间,50-100 名学生会在短时间内同时发起请求,这对系统的稳定性提出了要求。
# 二、 技术栈的选择:Serverless 架构的优势
在方案规划初期,我们对比了传统云服务器与 Serverless 架构,最终选择了 ** 对象存储(COS/OSS)+ 云函数(SCF/FC)** 的组合,其核心动机如下:
- 极低成本与免运维:传统服务器需配置环境且闲置成本高;Serverless 方案按量计费,无须管理操作系统底层,极大地降低了研究经费支出。
- 规避备案周期:国内云服务器若绑定域名访问,通常需要漫长的工信部备案流程。利用对象存储的静态托管功能及云函数的 API 网关,可以快速搭建实验环境,符合研究的时间节点。
- 弹性扩展能力:云函数原生支持高并发触发,能轻松应对百人级别的瞬间请求峰值。
- 伪动态网页(Pseudo-dynamic Web):前端采用静态 HTML 托管在对象存储中,通过 JavaScript 调用后端云函数。这种架构既拥有静态网页的加载速度,又具备动态交互的逻辑能力。
# 三、 技术实现:从架构雏形到深度隔离
# 3.1 系统架构设计:走向彻底的 Serverless
为了实现 “开箱即用” 且 “免运维”,我们确立了前后端彻底解耦的 Serverless(无服务器)架构。
- 前端:采用对象存储(COS)。将编写好的 HTML、CSS 和 JS 文件作为静态资源直接托管。这种 “扁平化” 的存储结构天然自带 CDN 属性,能够利用边缘计算节点实现秒级加载,彻底消除了服务器带宽瓶颈。
- 后端:采用云函数(SCF)。作为前端与扣子(Coze)服务器之间的桥梁,云函数仅在学生点击 “开始实验” 时触发运行。它负责处理所有高风险的加密计算和令牌申请,确保敏感信息(如私钥)永远不会暴露在客户端网络中。
# 3.2 初步最小化测试(MVP):个人密钥
在架构搭建初期,为了快速跑通前后端的数据交互链路,构建了一个最小化可行性产品(MVP),并使用 ** 个人访问令牌(PAT - Personal Access Token)** 进行了初步测试。
-
测试过程与链路打通:为了快速验证 Serverless 架构的连通性,我们首先构建了一个最小化测试(MVP)链路。
- 前端构造与请求发起:我们在前端快速搭建了包含 “班级” 和 “学号” 输入框的基础交互页面。在逻辑层,我们利用原生的
fetchAPI 构造了一个标准的 HTTP POST 请求,将学生填写的表单数据封装为 JSON 载荷(Payload),定向推送到腾讯云函数暴露的 API 网关接口。 - 后端代理与请求转发:在接收端,云函数作为中间层解析并提取了前端传入的参数。为了在测试期避开复杂的签名校验以追求快速跑通,我们在云函数代码中直接硬编码了开发者的个人访问令牌(PAT)。云函数在 HTTP 标头中挂载该 PAT 后,直接作为代理向扣子(Coze)的底层 API 发起调用。
- 状态反馈与数据闭环:云函数在获取到扣子服务器的流式或结构化响应后,将其原封不动地作为 Response 返回给前端。前端接收到反馈参数并进行解析,最终将对话结果渲染至 UI 界面,成功完成了
“前端数据采集 云端中转 扣子响应 页面渲染” 的完整数据闭环。
- 前端构造与请求发起:我们在前端快速搭建了包含 “班级” 和 “学号” 输入框的基础交互页面。在逻辑层,我们利用原生的
-
测试结果与暴露的问题:
虽然单用户测试表现良好,但在模拟多名学生同时在线的压力测试中,系统遭遇了的会话串台。- 权限越界:PAT 拥有开发者账户的全局权限,一旦被恶意抓包,整个智能体后台将面临风险。
- 上下文污染:由于 PAT 无法区分调用来源,扣子服务器会将所有学生的请求视为 “同一个开发者” 在发言。导致学生 A 的界面上出现了学生 B 的回答,需要进一步调整
阶段性结论:MVP 测试证明了 Serverless 架构的可行与高连通性,但也彻底否定了 PAT 在多用场景下的可用性。我们必须重构鉴权链路,引入基于 OAuth 2.0 的 JWT(JSON Web Token)动态加密体系。
# 3.3 鉴权与加密部署 对话隔离的实现
根据扣子(Coze)官方文档介绍,将智能体接入自有 Web 环境时,我们需要采用 OAuth 2.0 的 JWT(JSON Web Token)鉴权来实现用户的分发以及对话的隔离。为了防止 RSA 私钥暴露在前端网页中被恶意抓包,我们将加密签名的主战场转移到了后端的云函数(SCF)。
具体的实现思路与部署如下:
# 1. 技术选型与实现思路
要生成符合扣子官方规范的 JWT 令牌,我们需要在后端进行 RS256(非对称加密)签名。
- 核心依赖库:我们选用了 Python 的
cryptography库。这是目前处理 PEM 格式密钥和数字签名最成熟的工业级库,能够完美支撑 RSA 算法的底层需求。 - 整体思路:前端仅负责传递学生的基本信息(班级、学号);云函数接收信息后,在本地计算并拼装出 JWT 的 Header 和 Payload,利用私钥对其进行数字签名;随后,云函数带着这个签名向扣子服务器 “换票”,拿到有时效的 Access Token 后再下发给前端。
# 2. JWT 加密的核心代码逻辑
在云函数内部,我们纯手工构建了 JWT 的签发引擎,核心步骤分为三块:
- 组装 Header:声明加密算法,即
{"alg": "RS256", "typ": "JWT"}。 - 组装 Payload:这是最关键的一步。我们填入应用的 Client ID(
iss),通过时间戳设置 2 小时的过期时间(exp),并生成毫秒级时间戳(jti)防止重放攻击。更为核心的是,我们在这一步将前端传来的学生信息经过 MD5 加密后,作为session_name注入到 Payload 中,为后续的对话隔离打下基础。 - 执行签名:将 Header 和 Payload 分别进行 URL 安全的 Base64 编码,中间用
.连接。调用cryptography库的padding.PKCS1v15()和hashes.SHA256(),利用我们在云端保存的 RSA 私钥对这段字符串进行签名,最终拼接成完整的 JWT 字符串。
# 3. 云端部署与接口化
代码逻辑写好后,我们将整个工程打包部署到了腾讯云函数(SCF)。
- 在云函数控制台,我们为其配置了 API 网关触发器,生成了一个可供公网访问的 HTTPS 接口(URL)。
- 由于这套逻辑完全是事件驱动的(Serverless),只有当学生请求访问时,云端容器才会被唤醒并执行上述加密流程,极大节省了服务器常驻成本。
# 4. 前端对应与跨域打通
后端接口就绪后,前端页面的对接逻辑就变得非常轻量化了。
- 请求换票:学生点击 “开始实验” 后,前端
startChat函数利用内置的fetchAPI,向我们的云函数网关发起 POST 请求,请求体中携带${班级}_${学号}_${课次}。 - SDK 初始化:云函数返回
czs_开头的最终 Token 后,前端直接将该 Token 传入CozeWebSDK.WebChatClient的auth配置项中。前端彻底摆脱了复杂的密钥计算,只负责 “进场”。
# 四、 坑点排查与系统优化
在整个部署过程中,我们并不是一帆风顺的。尤其是 JWT 鉴权更是花费了整整两个小时去排查各种问题。因此做这个其实也不是那么的简单,也记录下来供避坑使用。
# 4.1 核心坑点复盘
# 1. 云函数底层环境冲突
- 问题描述:在腾讯云函数(SCF)中部署签名库
cryptography时,系统报错提示找不到底层 C 语言的GLIBC库。 - 原因分析:云函数默认的 Python 3.9 环境较新,其预编译的二进制包与云端容器的老旧底层库版本不匹配。
- 解决手段:手动将云函数的运行环境降级为 Python 3.7,并在本地打包时锁定安装
cryptography==3.4.8版本,从而确保了加密环境的稳定性。
# 2. 私钥格式解析失败
- 问题描述:最初尝试将 RSA 私钥存放在云函数的环境变量中,但代码读取时始终报错
Could not deserialize key data。 - 原因分析:环境变量在保存长文本时会破坏 PEM 格式严格的换行符结构,导致 RSA 算法无法识别私钥。
- 解决手段:放弃环境变量传参,改在 Python 脚本顶部使用
"""(三引号) 将私钥直接以多行字符串的形式硬编码在代码里。
# 3. 浏览器缓存导致的 “串台”
- 问题描述:即便后端实现了会话隔离,部分学生在更换学号登录后,依然能看到上一个人的聊天记录。
- 原因分析:扣子(Coze)的 Web SDK 会在浏览器的
IndexedDB数据库中缓存旧的conversation_id。 - 解决手段:在前端执行 “物理清场”。在点击开始实验时,通过代码强制执行
localStorage.clear()并删除IndexedDB中的coze_sdk_db数据库,确保环境纯净。
# 4. 跨域请求(CORS)拦截
- 问题描述:前端网页调用云函数接口时,浏览器报跨域资源共享错误。
- 原因分析:前端托管在对象存储(COS),后端在云函数(SCF),两者域名不一致触发了浏览器的安全策略。
- 解决手段:在云函数代码中手动增加了针对
OPTIONS预检请求 的放行逻辑,并在响应头中加入Access-Control-Allow-Origin: *。
# 4.2 优化
在解决掉生存问题后,为了适配真实的课堂教学,我们又在前端做了几项针对性的优化。
# 1. 交互:从输入到点选
- 优化策略:将最初的文本输入框全部替换为受控下拉列表。
- 实施细节:预设好 1-10 班、1-99 号以及具体的实验课次。
- 效果:初中生不再需要打字输入班级学号,不仅降低了认知负荷,更从源头上避免了因学生输入格式不统一(如 “1 班” 和 “一班”)导致的后端 MD5 匹配失败。
# 2. 心理反馈优化:动效与视觉引导
- 优化策略:引入毛玻璃视觉风格,并增加状态流转动画。
- 实施细节:利用
backdrop-filter: blur(40px)打造卡片质感;在点击登录后,通过卡片消失与对话框平滑展开的过渡效果,掩盖后端申请 Token 的等待时间。 - 效果:给了学生明确的 “系统正在准备独立房间” 的暗示,增强了实验的沉浸感。