当前位置: 首页 > news >正文

一键搭建 Coze 智能体对话页面:支持流式输出 + 图片直显,开发效率拉满!

一键搭建 Coze 智能体对话页面:支持流式输出 + 图片直显,开发效率拉满!
如果你正在寻找一套能快速对接 Coze 智能体、实现流畅交互体验的前端方案,这份现成的 HTML 代码绝对值得入手。它不仅完整打通了 Coze API 的调用全流程,还优化了流式输出和图片显示等核心体验,无需复杂配置,开箱即用,让开发者聚焦业务逻辑而非底层交互实现。
核心功能亮点

  1. 完整 Coze 智能体调用流程,零门槛对接
    代码已封装好 Coze API 的核心交互逻辑,从会话创建到消息发送全自动化,无需手动处理接口细节。
    自动生成唯一用户 ID,确保会话连贯性,支持多轮对话记忆。
    内置会话创建接口调用,首次发送消息时自动初始化对话,后续直接复用会话 ID。
    严格遵循 Coze API 协议规范,支持文本类型消息传输,参数配置清晰可修改。
  2. 原生流式输出,对话体验无延迟
    采用 SSE(Server-Sent Events)技术处理流式响应,完美还原 Coze 智能体的实时输出能力。
    接收 Coze API 的流式数据时,逐段拼接响应内容并实时更新到界面,避免全量加载等待。
    智能过滤无效数据类型,仅提取并展示role=assistant且type=answer的核心回复内容,减少冗余。
    搭配加载动画过渡,等待期间视觉反馈友好,提升用户感知流畅度。
  3. 图片直显 + 样式优化,视觉体验升级
    针对 Coze 智能体返回的图片链接,实现自动解析与优化显示,无需额外处理。
    支持 Markdown 格式图片语法解析,提取图片 URL 后自动渲染为图片元素。
    图片添加自适应样式,最大宽度贴合对话气泡,搭配圆角设计和 hover 放大效果,交互更细腻。
    区分用户与 AI 消息样式,色彩对比清晰,聊天气泡设计符合主流 IM 产品体验,降低用户适应成本。
    输入框支持回车发送、Shift + 回车换行,自动调整高度适配长文本输入。
    内置调试日志输出,关键流程(如会话创建、流式数据接收)可通过控制台查看详情,便于问题排查。
    错误处理机制完善,API 配置错误、网络异常等场景均有明确提示,降低运维成本。
    快速上手优势
    零框架依赖:纯 HTML+CSS+JavaScript 实现,无需引入 Vue、React 等框架,部署简单。
    配置极简:仅需替换COZE_API_TOKEN和COZE_BOT_ID两个核心参数,即可对接自己的 Coze 智能体。
    响应式设计:适配桌面端与移动端,聊天容器高度自适应屏幕,在不同设备上均有良好表现。
    无论是用于快速验证 Coze 智能体效果,还是作为正式产品的对话交互模块,这份代码都能大幅节省开发时间。它兼顾了功能完整性与体验流畅度,让你无需从零搭建底层交互,专注于智能体的能力优化与业务场景落地。

<!-- 
解决图片显示问题; -->
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Coze API 对话页面</title><style>/* 保持原有样式不变 */* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Microsoft YaHei", sans-serif;}body {background-color: #f5f7fa;padding: 20px;}.chat-container {max-width: 800px;margin: 0 auto;height: calc(100vh - 120px);background: #fff;border-radius: 12px;box-shadow: 0 2px 10px rgba(0,0,0,0.08);overflow: hidden;display: flex;flex-direction: column;}.chat-history {flex: 1;padding: 20px;overflow-y: auto;}.message {margin-bottom: 16px;max-width: 70%;line-height: 1.5;}.user-message {margin-left: auto;}.ai-message {margin-right: auto;}.message-content {padding: 12px 18px;border-radius: 18px;word-wrap: break-word;}.user-message .message-content {background: #0071e3;color: #fff;border-bottom-right-radius: 4px;}.ai-message .message-content {background: #f2f2f7;color: #1d1d1f;border-bottom-left-radius: 4px;white-space: pre-line;}.ai-message .message-content .ai-image {max-width: 100%;border-radius: 8px;margin: 8px 0;display: block;cursor: pointer;transition: transform 0.2s;}.ai-message .message-content .ai-image:hover {transform: scale(1.02);}.ai-message .message-content .ai-link {color: #0071e3;text-decoration: underline;margin: 0 2px;}.ai-message .message-content .ai-link:hover {color: #0066cc;}.chat-input {padding: 16px;border-top: 1px solid #e5e5ea;display: flex;gap: 10px;}#message-input {flex: 1;padding: 12px 16px;border: 1px solid #e5e5ea;border-radius: 24px;outline: none;font-size: 16px;resize: none;height: 48px;}#send-btn {width: 48px;height: 48px;border-radius: 50%;background: #0071e3;color: #fff;border: none;cursor: pointer;display: flex;align-items: center;justify-content: center;font-size: 18px;}#send-btn:disabled {background: #c7c7cc;cursor: not-allowed;}.loading {display: inline-flex;gap: 4px;align-items: center;justify-content: center;}.loading-dot {width: 8px;height: 8px;border-radius: 50%;background: #86868b;animation: load 1s infinite alternate;}.loading-dot:nth-child(2) {animation-delay: 0.2s;}.loading-dot:nth-child(3) {animation-delay: 0.4s;}@keyframes load {from { opacity: 0.5; transform: scale(0.8); }to { opacity: 1; transform: scale(1.2); }}.error-log {font-size: 12px;color: #ff3b30;margin-top: 4px;max-width: 70%;}</style>
</head>
<body><div class="chat-container"><div class="chat-history" id="chat-history"></div><div class="chat-input"><textarea id="message-input" placeholder="请输入消息..." onkeydown="if(event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); }"></textarea><button id="send-btn" onclick="sendMessage()">→</button></div></div><script>// 1. API配置const COZE_API_TOKEN = "xxxx";const COZE_BOT_ID = "xxx";const USER_ID = "test_user_" + Math.random().toString(36).substr(2, 9);// 2. API端点const API_ENDPOINTS = {createConversation: "https://api.coze.cn/v1/conversation/create",sendMessage: "https://api.coze.cn/v3/chat",retrieveStatus: "https://api.coze.cn/v3/chat/retrieve",getMessages: "https://api.coze.cn/v3/chat/message/list"};// 3. 全局变量let conversationId = "";const chatHistory = document.getElementById("chat-history");const messageInput = document.getElementById("message-input");const sendBtn = document.getElementById("send-btn");let currentAiMessageElement = null;let currentFullContent = "";// 4. 发送消息主函数async function sendMessage() {const userInput = messageInput.value.trim();if (!userInput) return;messageInput.value = "";sendBtn.disabled = true;addMessageToUI("user", userInput);const loadingId = addLoadingToUI();currentAiMessageElement = null;currentFullContent = "";try {if (!COZE_API_TOKEN || COZE_API_TOKEN === "xxx") {throw new Error("请填写有效的API Token");}if (!COZE_BOT_ID || COZE_BOT_ID === "xxx") {throw new Error("请填写有效的Bot ID");}if (!conversationId) {addDebugLog("开始创建会话...");conversationId = await createConversation();addDebugLog(`会话创建成功,ID:${conversationId.substring(0, 8)}...`);}addDebugLog("发送流式消息到Coze API...");await sendChatMessage(conversationId, userInput, loadingId);} catch (error) {removeLoadingFromUI(loadingId);addMessageToUI("ai", `请求出错:${error.message}`);addDebugLog(`错误详情:${error.stack}`);} finally {sendBtn.disabled = false;}}// 5. 创建会话async function createConversation() {try {const response = await fetch(API_ENDPOINTS.createConversation, {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${COZE_API_TOKEN}`},body: JSON.stringify({bot_id: COZE_BOT_ID,user_id: USER_ID,stream: false,auto_save_history: true,additional_messages: []})});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const data = await response.json();if (data.code !== 0 || !data.data?.id) {throw new Error(`创建会话失败(code: ${data.code}):${data.msg || '缺少会话ID'}`);}return data.data.id;} catch (error) {console.error("创建会话失败:", error);throw new Error(`创建会话出错:${error.message}`);}}// 6. 发送聊天消息(核心修改:仅拼接 type: 'answer' 的内容)async function sendChatMessage(conversationId, query, loadingId) {try {const controller = new AbortController();const response = await fetch(API_ENDPOINTS.sendMessage, {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${COZE_API_TOKEN}`,'Accept': 'text/event-stream'},body: JSON.stringify({bot_id: COZE_BOT_ID,user_id: USER_ID,additional_messages: [{"role": "user","content": query,"content_type": "text"}],stream: true,auto_save_history: true,conversation_id: conversationId}),signal: controller.signal});if (!response.ok || !response.body) {throw new Error(`HTTP 错误: ${response.status},不支持SSE流式响应`);}removeLoadingFromUI(loadingId);currentAiMessageElement = addStreamingMessageToUI();const decoder = new TextDecoder('utf-8');const reader = response.body.getReader();const SSE_DELIMITER = '\n\n';let buffer = '';let isStreamEnd = false; // 标记流是否结束while (!isStreamEnd) {const { done, value } = await reader.read();if (done) break;buffer += decoder.decode(value, { stream: true });const sseEvents = buffer.split(SSE_DELIMITER);buffer = sseEvents.pop() || '';for (const event of sseEvents) {if (!event.trim()) continue;const lines = event.split('\n').filter(line => line.trim());let sseData = null;// 1. 解析 data 中的 JSON 数据for (const line of lines) {if(line.startsWith('event:'))if (line.includes('completed')) break;if (line.startsWith('data:')) {const jsonStr = line.substring('data:'.length).trim();if (!jsonStr) continue;try {sseData = JSON.parse(jsonStr);} catch (e) {addDebugLog(`解析SSE data失败:${e.message},原始数据:${jsonStr}`);continue;}}}// 2. 核心过滤:仅处理 role=assistant 且 type=answer 的数据if (sseData && sseData.role === "assistant") {// 仅拼接 type: 'answer' 的内容if (sseData.type === "answer" && sseData.content !== undefined) {currentFullContent += sseData.content; const htmlContent = convertMarkdownToHtml(currentFullContent);updateStreamingMessage(currentAiMessageElement, htmlContent);// addDebugLog(`拼接 type=answer 内容,当前长度:${currentFullContent.length}`);} // 流结束判断(非 answer 类型,仅用于停止流)else if (sseData.type === "follow_up") {// addDebugLog("收到流结束信号(follow_up),停止拼接");isStreamEnd = true;break;}// 其他类型(如 tool_response、verbose 等)直接跳过else {// addDebugLog(`跳过非 answer 类型:${sseData.type}`);continue;}}}}// 流结束后关闭读取器await reader.cancel();} catch (error) {if (error.name !== 'AbortError') {console.error("流式发送消息失败:", error);throw new Error(`流式请求出错:${error.message}`);}}}// 7. Markdown转HTML(保持不变)function convertMarkdownToHtml(markdownText) {if (!markdownText) return '';let html = markdownText;// 处理图片const imageRegex = /!\[(.*?)\]\((https?:\/\/[^\s)]+)\)/g;html = html.replace(imageRegex, (match, altText, imgUrl) => {if (imgUrl.endsWith('.jpeg') || imgUrl.endsWith('.jpg') || imgUrl.endsWith('.png') || imgUrl.endsWith('.gif')) {return `<img src="${imgUrl}" alt="${altText || 'AI生成图片'}" class="ai-image">`;}return match;});// 处理链接const linkRegex = /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g;html = html.replace(linkRegex, (match, linkText, linkUrl) => {return `<a href="${linkUrl}" class="ai-link" target="_blank">${linkText}</a>`;});// 处理换行html = html.replace(/\n/g, '<br>');return html;}// 8. 界面辅助函数(保持不变)function addMessageToUI(role, content) {const messageDiv = document.createElement("div");messageDiv.className = `message ${role}-message`;const contentDiv = document.createElement("div");contentDiv.className = "message-content";contentDiv.textContent = content;messageDiv.appendChild(contentDiv);chatHistory.appendChild(messageDiv);chatHistory.scrollTop = chatHistory.scrollHeight;}function addStreamingMessageToUI() {const messageDiv = document.createElement("div");messageDiv.className = "message ai-message";const contentDiv = document.createElement("div");contentDiv.className = "message-content";contentDiv.innerHTML = "";messageDiv.appendChild(contentDiv);chatHistory.appendChild(messageDiv);chatHistory.scrollTop = chatHistory.scrollHeight;return messageDiv;}function updateStreamingMessage(messageElement, htmlContent) {const contentDiv = messageElement.querySelector(".message-content");if (contentDiv) {contentDiv.innerHTML = htmlContent;chatHistory.scrollTop = chatHistory.scrollHeight;}}function addDebugLog(log) {console.log("[Coze Debug]", log);}function addLoadingToUI() {const loadingId = `loading-${Date.now()}`;const loadingDiv = document.createElement("div");loadingDiv.className = "message ai-message";loadingDiv.id = loadingId;const loadingContent = document.createElement("div");loadingContent.className = "message-content loading";loadingContent.innerHTML = `<span class="loading-dot"></span><span class="loading-dot"></span><span class="loading-dot"></span>`;loadingDiv.appendChild(loadingContent);chatHistory.appendChild(loadingDiv);chatHistory.scrollTop = chatHistory.scrollHeight;return loadingId;}function removeLoadingFromUI(loadingId) {const loadingDiv = document.getElementById(loadingId);if (loadingDiv) loadingDiv.remove();}// 输入框自动调整高度messageInput.addEventListener('input', function() {this.style.height = 'auto';this.style.height = Math.min(this.scrollHeight, 120) + 'px';});</script>
</body>
</html>
http://www.dtcms.com/a/576980.html

相关文章:

  • 十大免费ae模板网站短视频素材下载网站
  • 那里做直播网站网页打不开是什么问题
  • 论文分享 | AirRoom:物体是关键!革新室内房间重识别的新范式
  • 游戏 IPA 如何防修改,面向开发者的多工具实战(IPA 加固/无源码混淆/Ipa Guard CLI)
  • 从0到1做一个“字母拼词”Unity小游戏(含源码/GIF)- 项目的创建及准备
  • 在扣子上搭建测试用例自动编写智能体
  • 2023年第二十届五一数学建模竞赛-A题 无人机定点投放问题-基于抛体运动的无人机定点投放问题研究
  • 影刀RPA一键生成销售日报!AI智能分析,效率提升1000%[特殊字符]
  • Rust开发实战之密码学基础——哈希计算与对称加密实战
  • 技术解析:清洗无人机在高空清洁中的应用与优势
  • Linux LVM NAT 模式部署实践
  • 使用 DVC(Data Version Control)进行数据版本管理
  • 网站建设选择哪种开发语言最好从哪里下载wordpress
  • 微服务之网关(Spring Cloud Gateway)
  • ES脚本语言Painless介绍
  • 基于MATLAB的雨流计数法疲劳计算GUI可视化系统
  • WiFi 协议精读:IEEE 802.11-2012,IEEE Std 802.11w™-2009: Protected Management Frames
  • RabbitMQ-Exporter 监控 TLS 加密的 RabbitMQ 集群
  • 重庆佳宇建设集团网站重庆网站自己推广
  • 品牌营销策划网站wordpress 会员开卡消费
  • iOS修改tabbar的背景图
  • 《uni-app跨平台开发完全指南》- 04 - 页面布局与样式基础
  • 【学习笔记更新中】Deeplearning.AI 大语言模型后训练:微调与强化学习导论
  • SQL之表的时间类内置函数详解
  • 线性代数 - 奇异值分解(SVD Singular Value Decomposition)- 计算顺序 旋转→拉伸→旋转
  • html的input的required
  • 【开题答辩全过程】以 基于Java的医务室病历管理小程序为例,包含答辩的问题和答案
  • 移除 XSLT,以更强的浏览器安全边界迎面而来
  • 回溯剪枝的“减法艺术”:化解超时危机的 “救命稻草”(三)
  • 佛山网站建设设计公司陕西住建执业证书官网