扣子Coze实现ChatSDK的会话隔离(纯前端,萌新必看)

项目背景

使用coze提供的代码在网页插入智能体后,发现不同用户之间没有实现会话隔离(可以互相看到对话记录)。

虽然官方文档里也给了解决方案 ,但写的很粗略,对低代码用户非常不友好,而且示例代码给的还是python的,岂不是说要再部署个后端才能实现。

本文提供一个前端实现用户隔离的方案。

实现原理

先来看官方提供的代码:

<script src="https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js"></script> <script> new CozeWebSDK.WebChatClient({ //创建一个智能体界面 config: { bot_id: '**********', // 智能体ID }, componentProps: { title: 'Coze', }, auth: { type: 'token', token: 'pat_********', // 访问令牌 onRefreshToken: function () { return 'pat_********' // 备用访问令牌,可以不填 } } }); </script>

由于coze是根据令牌来区分对话记录,而我们又把令牌写死在代码里,导致所有用户使用的是同一个令牌,看到的就是相同的对话记录。

也就是说,只要给不同用户发不同的令牌,就可以实现用户隔离了。

我们可以使用coze提供的OAuth应用来为每个用户自动申请新令牌,完整流程见下图。

流程对比

前期准备

需要准备以下四个东西:

  • 智能体 ID
  • OAuth ID
  • OAuth 公钥
  • OAuth 私钥

智能体

创建并发布智能体,智能体ID可以在地址栏里找到。

点开智能体,ID在地址栏里

OAuth

创建OAuth应用,类型为服务类应用,保存应用ID。

创建OAuth应用,类型为服务类应用

权限勾选 "Bot管理"、"会话管理"、"文件"、"消息"

设置权限

点击创建Key,保存生成的公钥和私钥,私钥文件用记事本打开即可

创建密钥

完整代码

js代码:

// 加载JWT生成库并初始化Coze智能体,替换你的配置后,加到网页的js里 (function() { // 配置参数(替换为你的实际信息) const COZE_CONFIG = { botId: '1145141145141', //智能体 ID appId: '*************',//OAuth ID publicKey: '******************************************************', //OAuth 公钥 privateKey:`-----BEGIN PRIVATE KEY----- ****************************************************** ****************************************************** -----END PRIVATE KEY-----` //OAuth 私钥,全复制进来 }; // 加载JWT库 function loadJwtLibrary() { return new Promise((resolve, reject) => { // 检查是否加载成功 if (window.KJUR && window.KJUR.jws && window.KJUR.jws.JWS) { resolve(); return; } const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/8.0.20/jsrsasign-all-min.js'; script.type = 'text/javascript'; script.crossOrigin = 'anonymous'; // 处理跨域问题 // 加载成功回调 script.onload = () => { // 验证库是否正确加载 if (window.KJUR && window.KJUR.jws && window.KJUR.jws.JWS) { resolve(); } else { reject(new Error('JWT库加载但未正确初始化')); } }; // 加载失败回调 script.onerror = () => { reject(new Error(`JWT库加载失败,请检查链接: ${script.src}`)); }; document.head.appendChild(script); }); } // 生成符合Coze要求的JWT function generateCozeJwt(userUid) { const header = { alg: 'RS256', typ: 'JWT', kid: COZE_CONFIG.publicKey }; const currentTime = Math.floor(Date.now() / 1000); const payload = { iss: COZE_CONFIG.appId, aud: "api.coze.cn", jti: Math.random().toString(36).substr(2, 32) + Date.now(), iat: currentTime, exp: currentTime + 3600, session_name: userUid }; const formattedPublicKey = COZE_CONFIG.privateKey; return window.KJUR.jws.JWS.sign( header.alg, JSON.stringify(header), JSON.stringify(payload), formattedPublicKey ); } // 获得Token并创建Coze智能体界面 async function getAccessToken(jwt) { try { const response = await fetch("https://api.coze.cn/api/permission/oauth2/token", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${jwt}` }, body: JSON.stringify({ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', duration_seconds: 900 }) }); if (!response.ok) { throw new Error(`获取token失败,HTTP状态码: ${response.status}`); } const data = await response.json(); new CozeWebSDK.WebChatClient({ config: { bot_id: COZE_CONFIG.botId }, componentProps: { title: 'Coze AI助手' }, auth: { type: 'token', token: data.access_token, onRefreshToken: () => data.access_token } }); } catch (error) { throw new Error(`获取Access Token失败: ${error.message}`); } } // 获取用户唯一标识 function getUserUid() { // 从本地存储获取登录用户ID const loggedUserId = localStorage.getItem('website_user_id'); if (loggedUserId) return loggedUserId; // 生成临时访客ID let visitorId = localStorage.getItem('coze_visitor_id'); if (!visitorId) { visitorId = `visitor_${Date.now()}_${Math.random().toString(36).slice(-6)}`; localStorage.setItem('coze_visitor_id', visitorId); } return visitorId; } // 初始化Coze聊天 function initCozeChat() { if (!window.CozeWebSDK || !window.CozeWebSDK.WebChatClient) { console.error('Coze SDK未加载'); return; } const userUid = getUserUid(); const jwtToken = generateCozeJwt(userUid); getAccessToken(jwtToken); } // 加载Coze SDK function loadCozeSdk() { const script = document.createElement('script'); script.src = "https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js"; script.onload = initCozeChat; script.onerror = () => console.error(`Coze SDK加载失败: ${COZE_CONFIG.sdkSrc}`); document.head.appendChild(script); } // 初始化流程 async function init() { try { await loadJwtLibrary(); loadCozeSdk(); } catch (error) { console.error('初始化失败:', error.message); } } // 页面加载完成后初始化 if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();

把测试网页也贴一下,创建一个txt文件,把代码复制进去,再重命名为index.html,双击打开就可以了。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Coze 智能体测试页面</title> </head> <body> <div> <div> <h1>Coze 智能体测试页面</h1> </div> </div> <script> //在此插入上述代码 </script> </body> </html>

成功的话应该会看到这样的界面,并且实现了用户对话隔离:

成功加载

安全问题

由于直接把公钥和私钥写在了js里,肯定有安全问题,但毕竟是全靠前端实现,不知道有啥解决方案( •̀ ω •́ )✧。

Read more

Instruct vs Thinking模式怎么选?Qwen3-VL-WEBUI提供最佳实践路径

Instruct vs Thinking模式怎么选?Qwen3-VL-WEBUI提供最佳实践路径 在多模态大模型逐步渗透到智能办公、自动化测试、教育辅助和内容生成等关键场景的今天,用户对AI能力的要求早已超越“能看图说话”的初级阶段。真正决定体验上限的是:面对不同复杂度任务时,模型能否做出最优响应策略? 阿里通义实验室推出的 Qwen3-VL 系列模型,通过内置 Instruct 与 Thinking 两种推理模式,首次将“快反应”与“深思考”系统化地集成于同一技术框架下。而基于该模型构建的镜像 Qwen3-VL-WEBUI,不仅实现了开箱即用的部署体验,更提供了清晰的工程化路径,帮助开发者精准匹配应用场景。 本文将结合 Qwen3-VL-WEBUI 镜像的实际能力,深入剖析 Instruct 与 Thinking 模式的本质差异、适用边界及协同机制,并给出可落地的选型建议与优化方案。 1. 技术背景:为何需要双模式设计? 传统多模态模型往往采用单一架构处理所有输入——无论问题是“这张图里有什么?”还是“请分析视频中人物行为背后的动机”,都走相同的推理流程。

Kylin(麒麟)V10系统安装WebLogic 12C

Kylin(麒麟)V10系统安装WebLogic 12C

目录 前言 一、JDK环境 二、安装WebLogic 1. 下载安装包 2. 开始安装 前言 先说下服务器的情况:我的环境是国产化环境,所以和之前的X86架构有些区别之处。 CPU是华为鲲鹏(Kunpeng)ARM64(aarch64)指令集架构,所以操作系统是:Kylin Linux Advanced Server V10 (ARM64) 。 由此我们在安装其他软件的时候也要注意这一点了,需要下载安装ARM64(aarch64)指令集架构的软件了,不然会会报指令集不符的相关错误提示。 一、JDK环境 Kylin V10系统默认安装匹配的是OpenJDK。 这里我安装WebLogic 12C时使用的是Oracle JDK。当然OpenJDK应该也是可以的。 JDK要求:WebLogic 12.2.1.4 需要 JDK 8(1.8.

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑

前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑 * 前端老哥必看:window.print只打半截?一招搞定HTML实际高度打印不踩坑 * 别整那些虚的,咱们直接开唠 * 这玩意儿到底是个啥妖魔鬼怪 * 浏览器打印机制那点不为人知的秘密 * CSS里的print媒体查询,是救星还是坑货? * 深挖底层逻辑,把打印机按在地上摩擦 * height: auto失效?布局塌陷的锅谁来背 * 强制分页符的正确打开方式 * 动态内容高度计算,别让JS骗了打印机 * 隐藏的overflow: hidden和fixed定位 * 这招好用是好用,但也有翻车的时候 * 优点当然是爽啊 * 缺点也得认,有些坑真的躲不掉 * 实战场景大乱斗 * 电商后台订单详情打印 * 财务报表长表格打印 * 简历生成器实战 * 电子发票和物流面单 * 遇到报错别慌,老司机的排查套路 * 打印出来是空白?

高效OCR识别新选择|DeepSeek-OCR-WEBUI本地部署指南

高效OCR识别新选择|DeepSeek-OCR-WEBUI本地部署指南 1. 为什么你需要一个本地OCR系统? 你有没有遇到过这样的情况:手头有一堆扫描件、发票、合同或者老照片,想要提取里面的文字,却发现复制粘贴根本不管用?传统OCR工具要么识别不准,要么不支持复杂排版,更别说手写体或模糊图像了。这时候,你就需要一个真正“聪明”的OCR系统。 而今天要介绍的 DeepSeek-OCR-WEBUI,正是这样一个能看懂图、识得字、还能说清楚内容的智能OCR解决方案。它基于国产自研的大模型技术,不仅中文识别精准,还自带可视化界面,部署后直接通过网页操作,像用手机App一样简单。 更重要的是——它是可以完全私有化部署的。你的数据不会上传到任何云端,所有处理都在本地完成,安全又高效。无论是企业文档自动化,还是个人资料数字化,都是理想选择。 2. DeepSeek-OCR-WEBUI 是什么? 2.1 核心能力一览 DeepSeek-OCR-WEBUI 并不是一个简单的文字识别工具,而是一套完整的图像理解与文本提取系统。它的背后是 DeepSeek 团队开源的高性能 OCR 大模