Wan2.1-umt5与Node.js全栈开发:打造实时AI聊天应用

news2026/3/13 21:43:55
Wan2.1-umt5与Node.js全栈开发打造实时AI聊天应用你是不是也想过自己动手做一个像模像样的AI聊天应用不是那种简单的问答机器人而是能记住对话历史、能实时响应、体验流畅的现代Web应用。听起来有点复杂别担心今天我们就用Node.js和Wan2.1-umt5大模型从零开始搭建一个。这个项目会覆盖一个完整应用的前后端用Node.js搭建高性能后端服务用Vue或React构建交互友好的前端界面再用WebSocket打通两者实现真正的实时对话。我们会重点解决几个实际开发中绕不开的问题怎么管理成千上万的用户连接不让服务器崩溃怎么让AI记住你们刚才聊了啥以及怎么让AI的回答像水流一样一个字一个字“流”出来而不是干等半天才蹦出一大段跟着做下来你不仅能得到一个可用的聊天应用更能掌握一套处理实时AI交互的实战思路。1. 项目蓝图与环境搭建在写第一行代码之前我们得先把“厨房”收拾好。这里没有复杂的理论就是一些实实在在的准备工作。1.1 你需要准备什么首先确保你的电脑上已经安装了Node.js。这是我们的基石。打开终端Windows上是命令提示符或PowerShellMac或Linux上是Terminal输入以下命令检查node --version npm --version如果能看到版本号比如v18.x.x和9.x.x说明已经安装好了。如果没有去Node.js官网下载最新的LTS长期支持版本安装过程就像安装普通软件一样简单。接下来我们需要一个代码编辑器。Visual Studio Code (VS Code) 是大多数开发者的选择它轻量、免费而且有海量插件。当然你用WebStorm、Sublime Text甚至记事本都行顺手最重要。最后也是这个项目的核心——访问Wan2.1-umt5模型的能力。你需要确保你有一个可以调用该模型API的途径比如通过某个云服务平台提供的服务并获取到相应的API密钥通常是一个长字符串。请妥善保管这个密钥我们后面会用到。1.2 创建你的项目骨架我们不从完全空白开始那样太费时间。让我们创建一个结构清晰的项目文件夹。在你的工作目录下新建一个文件夹名字就叫ai-chat-app。打开终端进入这个文件夹cd /path/to/your/ai-chat-app。在这个文件夹里我们再创建两个子文件夹server和client。顾名思义server放后端代码client放前端代码。现在你的项目结构看起来应该是这样ai-chat-app/ ├── server/ (后端Node.js Express/Fastify) └── client/ (前端Vue 或 React)我们先来搭建后端。进入server文件夹初始化一个新的Node.js项目cd server npm init -y这个命令会生成一个package.json文件它记录了项目的依赖和脚本。接下来安装我们需要的核心“食材”npm install express socket.io dotenv axiosexpress: 一个极简的Node.js Web框架帮我们快速搭建HTTP服务器。socket.io: 实现WebSocket通信的利器让前后端能实时双向对话。dotenv: 管理环境变量比如我们的API密钥这样就不会把敏感信息硬编码在代码里。axios: 一个好用的HTTP客户端用来向后端这里指Wan2.1-umt5的API服务发送请求。前端部分我们稍后再处理。现在后端的基础环境就准备好了。2. 构建实时通信的后端引擎后端是整个应用的大脑它要处理连接、管理对话、调用AI再把结果实时推回去。我们一步步来。2.1 搭建基础的HTTP与WebSocket服务器在server文件夹下创建一个名为index.js的文件这是我们的主入口文件。// server/index.js require(dotenv).config(); // 加载环境变量 const express require(express); const http require(http); const socketIo require(socket.io); const axios require(axios); const app express(); const server http.createServer(app); const io socketIo(server, { cors: { origin: http://localhost:3000, // 允许前端地址连接根据你的前端端口调整 methods: [GET, POST] } }); // 一个简单的内存存储用于关联用户Socket和对话历史生产环境请用Redis等数据库 const userSessions new Map(); // 处理WebSocket连接 io.on(connection, (socket) { console.log(用户已连接: ${socket.id}); // 为新用户初始化一个会话 userSessions.set(socket.id, { history: [] // 对话历史初始为空数组 }); // 监听前端发来的聊天消息 socket.on(send_message, async (data) { const { message } data; const userSession userSessions.get(socket.id); if (!userSession) { socket.emit(error, 会话不存在或已过期); return; } // 1. 将用户消息加入历史 userSession.history.push({ role: user, content: message }); // 2. 调用AI模型API这里需要替换为实际的Wan2.1-umt5 API调用 try { // 模拟一个简单的AI回复实际开发中替换为真实的API调用 const aiResponse await callAIModel(userSession.history); // 3. 将AI回复加入历史 userSession.history.push({ role: assistant, content: aiResponse }); // 4. 将AI回复实时发送回前端 socket.emit(receive_message, { message: aiResponse }); } catch (error) { console.error(调用AI API失败:, error); socket.emit(error, AI服务暂时不可用请稍后再试。); } }); // 用户断开连接时清理资源 socket.on(disconnect, () { console.log(用户已断开: ${socket.id}); userSessions.delete(socket.id); }); }); // 模拟的AI调用函数实际需替换 async function callAIModel(history) { // 这里应构造符合Wan2.1-umt5 API要求的请求体 // 例如将history格式化为模型需要的prompt const prompt history.map(h ${h.role}: ${h.content}).join(\n); // 实际调用示例需根据具体API文档调整 // const response await axios.post(YOUR_MODEL_API_ENDPOINT, { // model: wan2.1-umt5, // messages: history, // 或根据API要求格式化 // stream: true // 如果支持流式输出 // }, { // headers: { Authorization: Bearer ${process.env.API_KEY} } // }); // return response.data.choices[0].message.content; // 模拟返回 return 这是AI对历史对话的回复。历史记录有 ${history.length} 条。你刚说“${history[history.length-1].content}”; } const PORT process.env.PORT || 5000; server.listen(PORT, () { console.log(后端服务器运行在 http://localhost:${PORT}); });同时在server目录下创建一个.env文件用于存放你的API密钥记得把它加入.gitignore避免上传到公开仓库API_KEYyour_actual_api_key_here PORT5000现在运行node index.js你的后端服务器就启动起来了它正在监听5000端口并准备好了WebSocket连接。2.2 实现对话上下文记忆上面的代码已经有了一个简单的内存存储 (userSessionsMap)。但这里有个问题内存存储会在服务器重启后丢失所有数据而且当用户量很大时内存会吃不消。更健壮的做法是使用外部数据库比如Redis。Redis是内存数据库速度极快非常适合存储会话这种临时、需要快速存取的数据。我们来升级一下安装Redis客户端npm install ioredis。修改index.js引入Redis// 在文件顶部引入 const Redis require(ioredis); const redis new Redis(); // 默认连接本地的6379端口 // 替换原来的 userSessions Map 逻辑 io.on(connection, async (socket) { console.log(用户已连接: ${socket.id}); const sessionKey session:${socket.id}; // 从Redis读取或初始化会话历史 let history []; try { const storedHistory await redis.get(sessionKey); if (storedHistory) { history JSON.parse(storedHistory); } } catch (err) { console.error(读取Redis会话失败:, err); } // 监听消息事件 socket.on(send_message, async (data) { const { message } data; // 更新历史 history.push({ role: user, content: message }); // 调用AI... const aiResponse await callAIModel(history); history.push({ role: assistant, content: aiResponse }); // 将更新后的历史存回Redis并设置过期时间例如1小时 try { await redis.setex(sessionKey, 3600, JSON.stringify(history)); } catch (err) { console.error(保存会话到Redis失败:, err); } socket.emit(receive_message, { message: aiResponse }); }); socket.on(disconnect, () { console.log(用户已断开: ${socket.id}); // Redis数据已持久化这里可以不做额外清理或根据业务逻辑清理 }); });这样即使用户刷新页面或短时间重连他们的对话历史也能被保留。2.3 集成真正的Wan2.1-umt5 API与流式响应模拟回复没意思我们来接入真正的AI能力。关键是实现“流式响应”让AI思考一个字就吐出一个字用户体验会好很多。首先你需要查阅你所使用的Wan2.1-umt5 API服务商的文档确认其是否支持流式输出通常通过设置stream: true参数以及返回的数据格式。我们修改callAIModel函数假设API支持Server-Sent Events (SSE) 或类似的流式返回async function callAIModel(history, socket) { // 根据API要求格式化历史消息 const messages history.map(h ({ role: h.role, content: h.content })); try { const response await axios({ method: post, url: YOUR_STREAMING_API_ENDPOINT, // 替换为你的流式API地址 data: { model: wan2.1-umt5, messages: messages, stream: true // 关键参数开启流式 }, headers: { Authorization: Bearer ${process.env.API_KEY}, Content-Type: application/json }, responseType: stream // 告诉axios我们期待一个流 }); let fullResponse ; const stream response.data; // 监听数据流 stream.on(data, (chunk) { // 处理流式数据块这里需要根据API返回的实际格式解析 // 例如常见格式是 data: {...}\n\n const lines chunk.toString().split(\n).filter(line line.trim() ! ); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); // 去掉 data: 前缀 if (data [DONE]) { return; // 流结束 } try { const parsed JSON.parse(data); const content parsed.choices[0]?.delta?.content || ; // 根据API响应结构调整 if (content) { fullResponse content; // 关键步骤将每个片段实时推送给前端 socket.emit(receive_message_chunk, { chunk: content }); } } catch (e) { console.error(解析流数据失败:, e); } } } }); stream.on(end, () { // 流结束时可以发送一个完成信号给前端 socket.emit(receive_message_end); console.log(流式响应结束。); return fullResponse; // 返回完整的响应内容用于存入历史 }); stream.on(error, (err) { console.error(流请求错误:, err); socket.emit(error, AI响应流中断); }); } catch (error) { console.error(调用AI API失败:, error); throw new Error(AI服务请求失败); } }同时在前端发送消息的事件处理中我们需要调用这个新的callAIModel并传入socket对象以便在函数内部向该连接发送数据块。这样后端就能一边接收AI模型吐出的文字流一边像传纸条一样一片片地实时推送给前端了。3. 打造灵动的前端交互界面后端准备好了现在来做一个能看能用的界面。我们以React为例因为它的生态和 hooks 非常适合管理实时状态。当然你用Vue 3的Composition API也能达到类似效果。3.1 使用Create React App快速初始化在项目根目录的client文件夹下我们快速搭建一个React应用cd ../client npx create-react-app .安装我们需要的额外依赖用于WebSocket通信的socket.io-client和一个简单的样式库比如mui/material或antd让界面好看点这里我们选择轻量的socket.io-client和原生CSS。npm install socket.io-client3.2 构建聊天室组件我们创建一个主要的聊天组件ChatRoom.js// client/src/ChatRoom.js import React, { useState, useEffect, useRef } from react; import io from socket.io-client; import ./ChatRoom.css; // 一些基础样式 function ChatRoom() { const [messages, setMessages] useState([]); // 所有消息 const [inputMessage, setInputMessage] useState(); // 输入框内容 const [isConnected, setIsConnected] useState(false); // 连接状态 const [isLoading, setIsLoading] useState(false); // AI是否正在回复 const messagesEndRef useRef(null); // 用于自动滚动到底部 const socketRef useRef(); // 保存socket实例 // 初始化WebSocket连接 useEffect(() { // 连接到后端服务器假设后端运行在5000端口 const socket io(http://localhost:5000); socketRef.current socket; socket.on(connect, () { console.log(已连接到服务器); setIsConnected(true); }); socket.on(disconnect, () { console.log(与服务器断开连接); setIsConnected(false); }); // 监听完整的AI回复非流式时使用 socket.on(receive_message, (data) { addMessage(assistant, data.message, false); setIsLoading(false); }); // 监听流式回复的每一个片段 socket.on(receive_message_chunk, (data) { addMessage(assistant, data.chunk, true); // true 表示是片段追加 }); // 监听流式回复结束 socket.on(receive_message_end, () { setIsLoading(false); }); socket.on(error, (errorMsg) { console.error(服务器错误:, errorMsg); addMessage(system, 错误: ${errorMsg}, false); setIsLoading(false); }); // 组件卸载时断开连接 return () { socket.disconnect(); }; }, []); // 添加消息到列表isChunk为true时追加到最后一条消息 const addMessage (sender, text, isChunk false) { setMessages(prev { if (isChunk prev.length 0 prev[prev.length - 1].sender assistant) { // 如果是AI的流式片段追加到最后一条消息 const lastMessage prev[prev.length - 1]; const updatedLastMessage { ...lastMessage, text: lastMessage.text text }; return [...prev.slice(0, -1), updatedLastMessage]; } else { // 否则添加为新消息 return [...prev, { sender, text, id: Date.now() }]; } }); }; // 发送消息 const sendMessage () { if (!inputMessage.trim() || isLoading) return; const msg inputMessage.trim(); // 先将用户消息显示在界面上 addMessage(user, msg, false); setInputMessage(); setIsLoading(true); // 通过WebSocket发送消息到后端 socketRef.current.emit(send_message, { message: msg }); }; // 处理回车键发送 const handleKeyPress (e) { if (e.key Enter !e.shiftKey) { e.preventDefault(); sendMessage(); } }; // 消息列表更新后自动滚动到底部 useEffect(() { messagesEndRef.current?.scrollIntoView({ behavior: smooth }); }, [messages]); return ( div classNamechat-container div classNamechat-header h2AI聊天室/h2 div className{connection-status ${isConnected ? connected : disconnected}} ● {isConnected ? 已连接 : 连接中...} /div /div div classNamemessages-container {messages.map((msg) ( div key{msg.id} className{message-bubble ${msg.sender user ? user-message : ai-message}} div classNamemessage-sender{msg.sender user ? 你 : AI助手}/div div classNamemessage-text{msg.text}/div /div ))} {isLoading ( div classNamemessage-bubble ai-message div classNamemessage-senderAI助手/div div classNamemessage-text typing-indicator正在思考.../div /div )} div ref{messagesEndRef} / {/* 用于自动滚动的锚点 */} /div div classNameinput-area textarea classNamemessage-input value{inputMessage} onChange{(e) setInputMessage(e.target.value)} onKeyDown{handleKeyPress} placeholder输入消息... (按Enter发送ShiftEnter换行) disabled{isLoading || !isConnected} rows{3} / button classNamesend-button onClick{sendMessage} disabled{isLoading || !isConnected || !inputMessage.trim()} 发送 /button /div /div ); } export default ChatRoom;再添加一点基础样式 (ChatRoom.css) 让界面看起来舒服些/* client/src/ChatRoom.css */ .chat-container { display: flex; flex-direction: column; height: 90vh; max-width: 800px; margin: 20px auto; border: 1px solid #e0e0e0; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.1); font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; } .chat-header { background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center; } .connection-status { font-size: 0.9em; padding: 4px 12px; border-radius: 20px; background-color: rgba(255,255,255,0.2); } .connection-status.connected { color: #aaffaa; } .connection-status.disconnected { color: #ffaaaa; } .messages-container { flex: 1; padding: 20px; overflow-y: auto; background-color: #f9f9f9; } .message-bubble { margin-bottom: 16px; max-width: 80%; padding: 12px 16px; border-radius: 18px; line-height: 1.4; word-wrap: break-word; } .user-message { background-color: #007bff; color: white; align-self: flex-end; margin-left: auto; border-bottom-right-radius: 4px; } .ai-message { background-color: #e9ecef; color: #333; align-self: flex-start; border-bottom-left-radius: 4px; } .message-sender { font-size: 0.8em; font-weight: bold; margin-bottom: 4px; opacity: 0.8; } .typing-indicator::after { content: ...; animation: dots 1.5s steps(4, end) infinite; } keyframes dots { 0%, 20% { content: .; } 40% { content: ..; } 60%, 100% { content: ...; } } .input-area { display: flex; border-top: 1px solid #e0e0e0; padding: 15px; background-color: white; } .message-input { flex: 1; padding: 12px; border: 1px solid #ddd; border-radius: 8px; resize: none; font-size: 1em; margin-right: 12px; } .message-input:focus { outline: none; border-color: #6a11cb; } .send-button { padding: 12px 24px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; } .send-button:hover:not(:disabled) { opacity: 0.9; } .send-button:disabled { background: #cccccc; cursor: not-allowed; }最后在App.js中引入这个组件// client/src/App.js import React from react; import ChatRoom from ./ChatRoom; import ./App.css; function App() { return ( div classNameApp ChatRoom / /div ); } export default App;现在在client目录下运行npm start你的React应用就会在http://localhost:3000启动。确保后端也在运行 (node server/index.js)然后你就可以在浏览器里打开应用开始和AI聊天了你会看到消息实时出现如果后端集成了流式APIAI的回答还会一个字一个字地“打”出来。4. 应对真实世界的挑战一个玩具应用和能上线的应用之间差的就是对这些“麻烦事”的处理。4.1 高并发下的连接与状态管理当用户多起来内存存储和简单的连接管理就不够用了。我们之前提到了用Redis存储会话。对于连接管理socket.io本身在集群环境下需要适配。我们需要一个适配器让多个后端实例能共享连接状态。常用的有socket.io/redis-adapter。安装适配器在server目录下运行npm install socket.io/redis-adapter。修改后端index.js// 在文件顶部引入 const { createAdapter } require(socket.io/redis-adapter); const { createClient } require(redis); // 在创建 io 实例后连接Redis适配器 const pubClient createClient({ url: redis://localhost:6379 }); const subClient pubClient.duplicate(); Promise.all([pubClient.connect(), subClient.connect()]).then(() { io.adapter(createAdapter(pubClient, subClient)); console.log(Redis适配器已启用); }); // 确保服务器关闭时也关闭Redis连接 server.on(close, () { pubClient.quit(); subClient.quit(); });这样即使你通过负载均衡部署了多个后端服务器实例用户的连接和广播消息也能被正确处理。4.2 提升流式传输的稳定性和用户体验流式传输很酷但网络不稳定怎么办前端需要更健壮的处理。超时与重连在后端调用AI API时设置合理的超时并向前端发送明确的错误或结束信号。前端加载状态我们已经在代码中使用了isLoading状态来禁用输入框和按钮并显示“正在思考...”的提示。错误处理前端监听了error事件可以给用户友好的提示而不是控制台一片红。中断生成可以增加一个“停止生成”按钮。当用户点击时前端发送一个cancel_generation事件到后端后端需要有能力中断正在进行的AI API请求这取决于AI服务商是否支持并清理资源。4.3 安全与生产环境考量在真正部署前这几件事必须做环境变量我们已经用.env文件管理了API密钥。确保在生产环境中也正确设置并且.env文件绝不上传至代码仓库。CORS配置我们之前在socket.io初始化时配置了CORS。在生产中你需要将origin替换为你前端应用的真实域名。输入验证与清理永远不要信任前端传来的数据。在后端处理send_message事件时应该对message内容进行验证如长度、字符类型和清理防止注入攻击。API速率限制为了防止滥用可以在后端对每个用户或每个IP地址调用AI API的频率进行限制。可以使用express-rate-limit等中间件。日志与监控记录重要的操作和错误方便排查问题。5. 总结与展望走完这一趟我们从零搭建了一个具备实时对话、上下文记忆和流式响应能力的AI聊天应用。核心其实就三块用Node.js和Express处理逻辑用Socket.io建立实时桥梁再用React构建交互界面。过程中我们解决了会话记忆用Redis、流式传输解析AI API的数据流这些关键问题。实际用起来你会发现流式输出的体验比等待完整响应好太多用户能立刻感受到AI在“思考”和“回应”。Redis的引入也让应用变得更健壮能记住对话也能支撑更多用户。当然这只是一个起点。你可以在这个基础上做很多扩展比如增加多房间或私聊功能让用户能创建不同的聊天主题加入文件上传让AI能分析图片或文档或者做一个更漂亮的前端支持Markdown渲染、代码高亮让AI的回复看起来更专业。开发这类应用最大的乐趣在于看着想法一点点变成可交互的产品。希望这个项目能给你一个扎实的起点。接下来不妨试着接入一个真正的、功能更强大的Wan2.1-umt5 API服务或者优化一下UI把它变成你自己的作品。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2408929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…