VSCode统一聊天扩展架构:基于Provider模式实现多服务集成

news2026/5/3 10:49:57
1. 项目概述一个统一聊天界面的VSCode扩展如果你和我一样每天大部分时间都泡在Visual Studio Code里那你肯定也经历过这种场景一边在编辑器里写代码一边在浏览器、桌面应用甚至手机上来回切换查看GitHub的Issue、Slack的团队消息、或者某个AI助手的回答。这种割裂感不仅打断了心流还严重影响了效率。smallmain/vscode-unify-chat-provider这个项目正是为了解决这个痛点而生的。它不是一个简单的聊天客户端而是一个统一聊天提供者框架旨在将不同来源的聊天服务如GitHub Discussions、Slack、Discord甚至是自定义的AI助手集成到VSCode的侧边栏中让你无需离开编辑器就能在一个统一的界面里处理所有对话。这个项目的核心价值在于“统一”和“可扩展”。它不绑定任何特定的后端服务而是定义了一套清晰的接口Provider API让开发者可以轻松地为任何支持WebSocket或HTTP长轮询的聊天服务编写适配器。对于最终用户来说安装了这个扩展和相应的提供者插件后就能在熟悉的VSCode环境里获得一个高度定制化、低干扰的聊天体验。无论是跟踪开源项目的讨论还是与团队进行技术沟通亦或是与本地部署的大语言模型交互都能变得前所未有的顺畅。2. 核心架构与设计思路拆解2.1 为什么选择“提供者模式”这个项目最精妙的设计在于采用了“提供者模式”Provider Pattern。这是一种在软件设计中常见的设计模式核心思想是将抽象与实现分离。在vscode-unify-chat-provider中扩展本体主扩展只负责提供统一的用户界面UI、消息渲染、输入框、通知等通用功能。而具体的聊天服务连接、消息收发、用户认证等逻辑则完全交给独立的“提供者”扩展来实现。这种设计带来了几个显著优势主扩展高度稳定主扩展的代码一旦稳定几乎不需要为支持新的聊天服务而修改。这降低了核心代码的复杂度和维护成本。生态易于扩展任何开发者都可以遵循公开的Provider API为自己喜欢的服务哪怕是公司内部自研的IM工具编写提供者扩展。这能快速形成一个丰富的插件生态。用户按需组合用户只需要安装主扩展再按需安装自己用得到的提供者扩展即可。不会因为集成了大量用不到的服务而导致扩展臃肿、启动变慢。职责清晰便于调试当某个聊天服务出现连接问题时可以很明确地定位到是哪个提供者扩展的问题排查起来更加方便。2.2 技术栈选型与VSCode扩展API的深度利用项目基于TypeScript开发这几乎是现代VSCode扩展开发的事实标准。TypeScript的静态类型检查能极大提升代码的可靠性和开发体验尤其是在定义复杂的Provider API接口时。它深度依赖VSCode扩展API提供的几个关键能力TreeView API用于在侧边栏创建频道/会话列表树。这比单纯用WebView实现列表要高效和原生得多可以自动获得折叠、搜索、图标支持等功能。Webview API用于渲染主要的聊天消息面板。Webview是一个内嵌的浏览器实例可以加载HTML/CSS/JS为渲染富文本消息、代码高亮、表情符号等提供了无限可能。项目需要精心设计Webview与扩展主进程之间的通信机制。StatusBar API可能在状态栏显示连接状态、未读消息数等摘要信息。Storage API用于安全地存储用户认证令牌Token、个人配置等敏感数据。此外项目必然涉及大量的异步操作和事件驱动编程。聊天消息是实时推送的用户操作发送、切换频道需要即时响应。因此对EventEmitter、Promise、async/await的娴熟运用是基础。2.3 数据流与状态管理设计在一个典型的聊天扩展中数据流管理是关键挑战。我们需要考虑消息从哪里来多个提供者可能同时推送消息。消息到哪里去需要正确路由到对应的Webview标签页。状态如何同步当前选中的频道、各频道的未读状态、连接状态等需要在UI各个部分侧边栏树、状态栏、Webview标题保持一致。一个合理的架构是采用“中心化状态管理”结合“事件驱动”的模式。主扩展维护一个核心的ChatManager单例它负责注册和管理所有激活的提供者。接收所有提供者发来的消息和状态变更事件。将消息和事件分派给对应的UI组件如特定的Webview面板。维护全局的应用状态如providers,activeChannelMap,unreadCounts。当用户切换频道时流程可能是这样的用户点击侧边栏树中的一个频道节点。TreeView触发事件ChatManager收到频道ID。ChatManager找到该频道对应的提供者并向其请求该频道的消息历史如果需要。ChatManager将频道信息和消息历史发送给负责渲染该频道的Webview。Webview更新显示并将该频道的未读计数清零。ChatManager更新全局未读状态并通知状态栏和侧边栏树更新显示。3. Provider API 深度解析与实现要点3.1 接口定义契约就是一切Provider API是整个项目的基石它定义了主扩展与提供者扩展之间必须遵守的契约。一个健壮的API接口应该包含以下核心部分// 这是一个简化的示例展示核心接口概念 export interface ChatProvider { // 提供者的唯一标识符如 ‘github‘, ‘slack‘ readonly id: string; // 提供者显示名称 readonly name: string; // 提供者图标VSCode Uri或ThemeIcon readonly icon: vscode.Uri | vscode.ThemeIcon; // 初始化方法通常用于建立连接、验证Token initialize(): Promisevoid; // 销毁方法用于清理连接、释放资源 dispose(): void; // 获取频道列表返回TreeItem数组用于构建侧边栏树 getChannels(): Promisevscode.TreeItem[]; // 获取指定频道的消息历史 getChannelHistory(channelId: string, limit?: number): PromiseChatMessage[]; // 发送消息到指定频道 sendMessage(channelId: string, content: string): Promisevoid; // 事件当收到新消息时触发 onDidReceiveMessage: vscode.EventChatMessage; // 事件当频道列表发生变化时触发如加入新频道 onDidChangeChannels: vscode.Eventvoid; // 事件当连接状态变化时触发 onDidChangeConnectionStatus: vscode.EventConnectionStatus; } export interface ChatMessage { id: string; channelId: string; author: { id: string; name: string; avatar?: string }; content: string; // 可能是Markdown或纯文本 timestamp: Date; // 可能的附加属性回复引用、反应Reactions、代码片段等 attachments?: Attachment[]; }实现要点错误处理每个异步方法initialize,sendMessage都必须有完善的错误处理并通过vscode.window.showErrorMessage或更优雅的方式如状态栏提示通知用户。网络超时、认证失效、消息发送失败是常见场景。事件节流onDidChangeChannels这类事件可能被频繁触发例如一个活跃的群组。提供者内部需要对原始事件进行节流throttle或防抖debounce避免主扩展被海量事件淹没。认证信息存储务必使用vscode.SecretsAPIcontext.secrets来存储OAuth Token、API Key等敏感信息。切勿存储在普通配置或文件里。3.2 实现一个真实的提供者以GitHub Discussions为例让我们以实现一个GitHub Discussions提供者为蓝本看看具体要怎么做。第一步建立连接与认证GitHub API使用OAuth App或Personal Access Token (PAT)进行认证。在提供者扩展的激活函数中我们需要引导用户完成认证流程。检查context.secrets中是否已有存储的Token。如果没有则打开一个Webview或使用vscode.window.withProgress弹窗引导用户前往GitHub进行OAuth授权。回调URL可以指向一个本地HTTP服务器需在扩展中临时启动或者使用vscode.env.asExternalUri处理更复杂的深度链接。获取到Token后立即用context.secrets.store(‘github-token‘, token)安全存储。实操心得处理OAuth回调在桌面应用中进行OAuth回调是个小难点。一个经过验证的稳定方案是在扩展中启动一个极简的本地HTTP服务器例如使用http模块监听localhost:54321。将OAuth App的回调地址设置为http://localhost:54321/callback。用户授权后GitHub会重定向到此地址并附带code参数。我们的服务器捕获到这个code然后交换为access_token最后在服务器返回的HTML页面中用postMessage通知扩展的Webview完成闭环。记得处理好服务器端口冲突和超时清理。第二步获取频道Discussions列表GitHub Discussions的API端点形如GET /repos/{owner}/{repo}/discussions。我们需要让用户配置他们关心的仓库可以是一个输入框支持owner/repo格式或多个。为每个仓库下的每个discussion生成一个频道节点。TreeItem的label可以设置为discussion标题description可以显示仓库名tooltip可以显示部分内容预览。考虑到仓库和discussion可能很多首次加载和后续刷新需要做好分页和缓存。可以将获取到的列表缓存在内存中并监听onDidChangeChannels事件在后台定时轮询或使用GitHub的Webhook更复杂来更新。第三步收发消息拉取历史消息调用GET /repos/{owner}/{repo}/discussions/{discussion_number}/comments获取评论列表。需要将GitHub的评论数据模型包含用户信息、Markdown正文、创建时间、反应等转换为我们内部的ChatMessage模型。发送消息调用POST /repos/{owner}/{repo}/discussions/{discussion_number}/comments。这里要注意内容安全避免发送失败。发送前可以做一些本地预览或校验。接收实时消息GitHub API没有官方的实时推送如WebSocket。因此我们需要采用长轮询Long Polling或定时轮询方案。对于Discussions这种非即时聊天场景定时轮询例如每30秒或60秒是一个简单可靠的方案。我们可以为每个活跃的频道即用户打开查看的频道设置一个独立的定时器更频繁地如每10秒检查新评论。当检测到新消息时触发onDidReceiveMessage事件。第四步优化与用户体验消息增量更新轮询时使用since参数只拉取上次轮询时间之后的新评论减少网络流量和数据处理开销。处理速率限制GitHub API有严格的速率限制。必须在代码中检查响应头中的X-RateLimit-Remaining并在接近限制时降低轮询频率或提示用户。富文本渲染GitHub评论是Markdown格式。在Webview中渲染时可以集成一个安全的Markdown解析器如marked并确保代码片段有语法高亮可使用highlight.js。3.3 另一个范式WebSocket实时提供者如自定义AI助手对于支持WebSocket的服务提供者的实现范式完全不同。以连接一个本地运行的LLM大语言模型WebSocket服务为例class LocalAIProvider implements ChatProvider { private ws: WebSocket | null null; private messageQueue: string[] []; // 重发队列 private isConnected false; async initialize() { const config vscode.workspace.getConfiguration(‘unify-chat.local-ai‘); const wsUrl config.get(‘serverUrl‘, ‘ws://localhost:8080/chat‘); return new Promisevoid((resolve, reject) { this.ws new WebSocket(wsUrl); this.ws.onopen () { this.isConnected true; // 连接成功后发送队列中积压的消息 this.flushMessageQueue(); this._onDidChangeConnectionStatus.fire(ConnectionStatus.Connected); resolve(); }; this.ws.onmessage (event) { // 解析AI返回的消息触发事件 const aiMessage: ChatMessage this.parseAIMessage(event.data); this._onDidReceiveMessage.fire(aiMessage); }; this.ws.onerror (err) { this.isConnected false; this._onDidChangeConnectionStatus.fire(ConnectionStatus.Error); reject(err); }; this.ws.onclose () { this.isConnected false; this._onDidChangeConnectionStatus.fire(ConnectionStatus.Disconnected); // 可以尝试自动重连 this.scheduleReconnect(); }; }); } async sendMessage(channelId: string, content: string) { const payload JSON.stringify({ type: ‘user_message‘, content }); if (this.isConnected this.ws) { this.ws.send(payload); } else { // 未连接时将消息加入队列等待连接恢复后发送 this.messageQueue.push(payload); vscode.window.showWarningMessage(‘已离线消息已保存将在连接恢复后发送。‘); } } private flushMessageQueue() { while (this.messageQueue.length 0 this.ws this.isConnected) { const msg this.messageQueue.shift(); this.ws.send(msg!); } } }实现要点连接稳定性必须处理WebSocket的断开和自动重连。重连逻辑应包含指数退避策略避免频繁重连轰炸服务器。消息可靠性在离线或发送失败时需要将消息加入本地队列并在连接恢复后重新发送。这对于确保用户体验至关重要。协议设计与自定义服务通信时需要设计一套简单的应用层协议定义消息类型如user_messageai_responseerror、数据格式等。4. 主扩展核心模块实现详解4.1 侧边栏树视图TreeView的构建与管理主扩展需要创建一个TreeDataProvider来管理所有提供者的频道。这里的关键是聚合数据。export class UnifiedChatTreeDataProvider implements vscode.TreeDataProviderChatTreeItem { // 这个事件触发时VSCode会刷新整个树 private _onDidChangeTreeData: vscode.EventEmitterChatTreeItem | undefined | null | void new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.EventChatTreeItem | undefined | null | void this._onDidChangeTreeData.event; // 核心获取树的子元素 async getChildren(element?: ChatTreeItem): PromiseChatTreeItem[] { if (!element) { // 根节点返回所有已激活的提供者作为第一级 const providers ChatManager.getInstance().getAllProviders(); return providers.map(p new ProviderTreeItem(p)); } else if (element instanceof ProviderTreeItem) { // 点击了某个提供者返回该提供者的频道列表 const channels await element.provider.getChannels(); return channels.map(c new ChannelTreeItem(c, element.provider)); } // 频道节点没有子节点 return []; } getTreeItem(element: ChatTreeItem): vscode.TreeItem { // 将我们的数据模型转换为VSCode的TreeItem return element.getVsTreeItem(); } }ChatTreeItem抽象类这是我们的自定义树项基类它封装了显示逻辑图标、标签、点击命令。abstract class ChatTreeItem { abstract getVsTreeItem(): vscode.TreeItem; } class ProviderTreeItem extends ChatTreeItem { constructor(public readonly provider: ChatProvider) { super(); } getVsTreeItem(): vscode.TreeItem { const item new vscode.TreeItem(this.provider.name, vscode.TreeItemCollapsibleState.Collapsed); item.iconPath this.provider.icon; item.contextValue ‘provider‘; // 用于上下文菜单 item.tooltip Provider: ${this.provider.id}; return item; } } class ChannelTreeItem extends ChatTreeItem { constructor(private channelData: any, private provider: ChatProvider) { super(); } getVsTreeItem(): vscode.TreeItem { const item new vscode.TreeItem(this.channelData.title, vscode.TreeItemCollapsibleState.None); item.iconPath new vscode.ThemeIcon(‘comment-discussion‘); item.command { // 点击频道时执行的命令 command: ‘unify-chat.openChannel‘, title: ‘Open Channel‘, arguments: [this.provider.id, this.channelData.id] }; // 可以在这里添加未读计数徽章 // item.description (${unreadCount}); return item; } }刷新机制当任何一个提供者触发onDidChangeChannels事件时主扩展的ChatManager需要捕获它并调用treeDataProvider._onDidChangeTreeData.fire()来通知树视图刷新。为了性能可以只刷新对应的提供者节点而不是整棵树。4.2 Webview面板聊天界面的渲染中枢Webview是聊天内容的主舞台。它的生命周期管理、通信和性能优化是重中之重。创建与注册我们通常为每个打开的频道创建一个独立的Webview面板。使用vscode.window.createWebviewPanel创建并用一个全局的Map来管理它们键可以是${providerId}:${channelId}。const panel vscode.window.createWebviewPanel( ‘unifyChatView‘, // 视图类型 Chat: ${channelName}, // 面板标题 vscode.ViewColumn.Beside, // 显示在编辑器侧边 { enableScripts: true, // 必须启用JS retainContextWhenHidden: true, // 重要隐藏时保持状态避免切换时重载 localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, ‘media‘)] // 允许加载本地资源 } );通信协议Webview前端与扩展后端通过postMessage通信。必须设计一个清晰的协议。// 扩展后端发送消息到Webview panel.webview.postMessage({ type: ‘messages‘, payload: { history: messageList, channel: channelInfo } }); // Webview前端发送消息到扩展 window.addEventListener(‘message‘, event { const message event.data; if (message.command ‘sendMessage‘) { // 调用vscode API将消息发送回扩展 vscodeApi.postMessage(message); } }); // 在扩展中接收Webview的消息 panel.webview.onDidReceiveMessage(async (message) { switch (message.command) { case ‘sendMessage‘: await chatManager.sendMessage(activeProviderId, activeChannelId, message.text); break; case ‘loadMore‘: const moreMessages await provider.getChannelHistory(channelId, message.limit); panel.webview.postMessage({ type: ‘moreMessages‘, payload: moreMessages }); break; } });前端实现Webview的HTML/JS/CSS可以做得非常复杂。一个基本的结构包括消息列表容器一个滚动区域用于渲染消息气泡。需要处理滚动行为新消息到来时自动滚动到底部但用户向上查看历史时则保持位置。消息输入区一个textarea或支持提及、表情符号的富文本编辑器。可以考虑集成一个轻量级的编辑器如CodeMirror或Monaco Editor的简化版以获得更好的代码输入体验。样式与主题适配CSS需要适配VSCode的深色/浅色主题。可以通过在Webview中注入CSS变量或者从扩展端获取当前主题色并传递过去。注意事项Webview安全性Webview可以运行任意JavaScript这是一个巨大的安全风险。务必使用Content Security Policy (CSP)严格限制资源来源。在HTML的meta标签中设置default-src ‘self‘;等策略。对从聊天服务接收到的消息内容进行净化Sanitize防止XSS攻击。特别是当消息内容包含HTML或Markdown时使用安全的库如DOMPurify进行处理后再插入DOM。谨慎处理从Webview接收到的命令做好参数验证和错误处理。4.3 消息存储、同步与状态管理对于聊天应用消息的本地存储和同步策略直接影响性能和用户体验。存储策略内存缓存当前活跃会话的消息应缓存在内存中以实现快速访问。可以使用类似MapchannelKey, ChatMessage[]的结构。持久化存储对于已查看的历史消息可以序列化后存储到VSCode的全局存储context.globalState或工作区存储中。但注意globalState有大小限制约16MB不适合存储大量消息。一个折中方案是只存储最近N条如每个频道最近500条消息更早的历史通过提供者的API重新拉取。索引化数据库如果追求更强大的本地历史搜索功能可以考虑集成一个轻量级嵌入式数据库如SQLite通过node-sqlite3或dexie.jsIndexedDB的包装。但这会显著增加扩展的复杂度和体积。同步策略推模式对于WebSocket提供者消息是实时推送的同步是自动的。拉模式对于轮询的提供者如GitHub需要管理好轮询间隔。一个优化策略是“差异化轮询”用户正在查看的频道轮询间隔短如10秒非活跃频道间隔长如60秒甚至暂停轮询。未读计数同步这是一个状态同步的典型例子。当收到新消息时如果该消息的频道不是当前激活的Webview则需要增加该频道的未读计数。这个计数需要同步更新到侧边栏树节点TreeItem.description和状态栏。这要求ChatManager维护一个全局的MapchannelKey, unreadCount并在任何计数变更时通知所有相关的UI组件。5. 扩展打包、发布与生态建设5.1 工程化与依赖管理一个成熟的VSCode扩展项目需要良好的工程化实践。构建流程使用esbuild或webpack进行打包将TypeScript代码编译、压缩并打包成单个JS文件以提升加载速度。尤其要注意处理Webview的前端资源HTML, CSS, JS。依赖隔离提供者扩展很可能依赖特定的第三方库如octokit/rest用于GitHubslack/web-api用于Slack。这些依赖必须打包进提供者扩展本身的输出中避免与主扩展或其他提供者扩展发生冲突。这意味着每个提供者扩展都是一个独立的、可以单独发布和安装的VSIX包。配置管理提供者的配置如服务器地址、API密钥名称应通过package.json的contributes.configuration部分声明让用户可以在VSCode的设置界面中方便地修改。5.2 调试与测试策略调试主扩展使用VSCode的扩展调试配置可以方便地启动一个扩展开发宿主来调试。Webview调试Webview内的前端代码比较特殊。可以在创建WebviewPanel时启用devTools选项仅限开发版或者利用Chrome DevTools的远程调试功能通过chrome://inspect。提供者扩展由于提供者扩展是动态加载的调试起来可能更麻烦。一种方法是在主扩展的测试环境中模拟加载提供者。测试单元测试对核心工具函数、数据转换逻辑、状态管理函数等使用Jest或Mocha进行单元测试。集成测试使用vscode/test-electron可以编写端到端的集成测试模拟用户点击按钮、输入文本等操作。提供者Mock为Provider API编写一个Mock实现用于测试主扩展的UI和逻辑而无需连接真实的聊天服务。5.3 发布到VSCode Marketplace与生态推广准备发布确保package.json中的publisher、name、version、description、categories、keywords等字段填写准确。精心编写README.md包含清晰的截图、功能说明和安装使用指南。使用VSIX运行vsce package命令生成.vsix文件。可以先手动安装这个文件进行最终测试。发布通过vsce publish命令发布到Marketplace。对于提供者扩展建议命名上体现从属关系例如smallmain.unify-chat-github。生态建设提供完善的开发文档在项目Wiki或主扩展的README中详细说明Provider API的每一个接口、数据类型和预期行为。提供一个“创建你的第一个提供者”的步步指南。提供模板项目创建一个提供者扩展的Yeoman生成器或GitHub模板仓库让开发者能一键初始化项目结构。展示优秀案例在项目主页展示社区贡献的流行提供者扩展如Slack、Discord、Telegram吸引更多用户和开发者。建立沟通渠道创建一个GitHub Discussions区或Discord服务器让用户和开发者可以交流问题、分享经验。6. 常见问题、性能优化与避坑指南6.1 连接与认证问题排查问题1提供者扩展激活失败提示“无法加载模块”。排查检查提供者扩展的package.json中activationEvents设置是否正确。确保主扩展已安装并激活。检查控制台输出Developer: Toggle Developer Tools查看是否有具体的错误堆栈。解决提供者扩展的激活事件通常应设置为onView:unify-chat-sidebar当聊天侧边栏视图被显示时或*在启动时激活但不推荐。确保扩展依赖extensionDependencies中正确列出了主扩展的ID。问题2OAuth认证流程卡住无法获取Token。排查检查OAuth App的回调URL配置是否与扩展中启动的本地服务器地址完全一致包括端口。检查是否有防火墙或安全软件阻止了本地回环地址通信。解决在扩展代码中增加更详细的日志输出OAuth流程的每一步。提供一个“手动输入Token”的备选方案提升用户体验。问题3WebSocket连接频繁断开。排查检查网络是否稳定。检查服务端WebSocket实现是否有心跳机制。检查扩展中WebSocket的onclose事件查看关闭代码和原因。解决实现健壮的重连逻辑包含指数退避如1秒2秒4秒8秒…直到最大60秒和最大重试次数限制。在状态栏显示连接状态让用户知晓。6.2 性能优化要点Webview性能虚拟列表如果一个频道的历史消息非常多上万条在Webview中一次性渲染所有DOM元素会导致严重卡顿。必须实现虚拟列表只渲染可视区域及其附近的消息。图片懒加载消息中的头像、附件图片等应使用loading“lazy“属性或Intersection Observer API实现懒加载。避免频繁的postMessageWebview与扩展主进程的通信是异步且有一定开销的。避免为每条消息都发送一次postMessage可以考虑对短时间内的消息进行批量发送。内存管理及时释放资源当Webview面板被关闭时确保取消所有的事件监听器清除定时器释放对DOM元素和大数据结构的引用。清理缓存对内存中的消息缓存设置上限或采用LRU最近最少使用策略进行淘汰。树视图性能当频道数量巨大时getChildren方法可能成为瓶颈。确保提供者的getChannels方法是高效的并考虑实现分页加载或增量加载到树视图中。6.3 用户体验打磨细节消息通知当收到新消息且聊天窗口不在焦点时应该给出通知。可以使用vscode.window.showInformationMessage但可能打扰更好的方式是在状态栏显示一个闪烁的图标或改变未读计数颜色并在活动栏的聊天视图图标上添加徽章计数。输入体验支持Markdown预览在输入框下方提供一个切换按钮可以预览Markdown渲染效果。代码块快捷输入提供快捷键如CtrlShiftC在光标处插入代码块标记。提及自动补全在输入时弹出当前频道或组织的成员列表供选择。搜索功能在侧边栏顶部增加一个搜索框可以跨频道搜索消息内容。这需要依赖本地消息存储或调用提供者服务的搜索API。多窗口支持确保扩展在VSCode的多个窗口实例中能正常工作状态互不干扰。开发这样一个深度集成、功能丰富的VSCode扩展是一项系统工程从架构设计到细节打磨处处都是挑战。但当你看到所有分散的对话最终汇聚在你最熟悉的编码环境中那种流畅无割裂的体验会让所有的努力都变得值得。smallmain/vscode-unify-chat-provider项目提供了一个极其优雅的框架剩下的就是社区和我们一起用一个个具体的提供者扩展去连接整个数字世界的对话。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2578020.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;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…