2026年2月

以前我觉得开源模型大多是玩具,真要干活、写复杂逻辑,还得老老实实给闭源大厂交 API 的保护费。GLM-5 的发布,不是一次简单的版本号 +1,而是直接把开源模型从玩具拉到了员工的级别。

image.png

跑完测试后,我发现,以前得雇人或者自己熬夜干的活,现在这个模型真的能接手了。

为什么说 GLM-5 让我的认知崩塌了?

参数量 744B(激活 40B),预训练数据 28.5T tokens,AI一般都有2个问题,脑子不够用和记性太差,这也是用 AI 开发的痛点,而 GLM-5就没有这个烦恼。

1. 它不再是小镇做题家

以前评测模型,大家喜欢看它做奥数题。但说实话,谁家好人在工作中用到奥数呀,我它帮我规划任务。

这次 GLM-5 在 Vending Bench 2 上的表现就很厉害了,要知道这个测试很变态的,要求模型在模拟环境里经营一家自动售货机公司,周期长达一年。

  • 大多数开源模型:落地成盒,开局就寄,根本搞不清库存和资金流。
  • GLM-5:不仅活下来了,最后账户余额还剩 4,432 美元

image.png

这个成绩在开源界是断层第一,直逼闭源的 Claude Opus 4.5。就是说,如果你把 GLM-5 接入业务流,它真的具备长期规划和资源管理的能力。

2. 从生成文字到交付工作

大家以前用模型最烦的是什么?它给你吐出一堆 Markdown 格式的文本,你还得自己复制粘贴去排版。

GLM-5 这次最让我惊喜的是它对办公场景的理解。它能把那些复杂的推理结果,直接生成 .docx、.pdf 甚至 .xlsx 文件

  • 写 PRD?它直接给你一个格式完美的 Word 文档。
  • 做财报分析?它直接扔给你一个带公式的 Excel 表格。

这才是真正的生产力工具。它省掉那些最没技术含量的格式调整和文档整理时间。

3. 技术上的降本增效

我也好奇,这么大的参数量,跑起来会不会慢得像蜗牛?

GLM-5 用了 DeepSeek Sparse Attention (DSA) 的技术,让模型只关注该关注的信息,把算力用在刀刃上。再加上 slime 的强化学习架构,解决了大模型越训越傻的问题。

image.png

所以它的逻辑密度高,废话少。

image.png

本地部署

说到这,很多人可能跃跃欲试想在本地跑一下。毕竟是开源模型,数据握在自己手里才踏实。

GLM-5 这种量级的模型,对 Python 环境、依赖库有要求。我之前为了跑一个大模型,光是解决 Python 依赖冲突就花了一整天,最后心态崩了模型还没跑起来。

所以这次为了不重蹈覆辙,直接上 ServBay。如果以前你觉得这种工具是给新手用的,那就错了,这是给想省时间的人用的。

你想跑 GLM-5,得装特定版本的 Python,还得配 vLLM 或者 SGLang,原生环境里搞,很容易把之前的项目环境搞挂。用 ServBay,点击下载Python, 它直接给我弄了一个隔离的、干净的 Python 3.10+ 环境。

image.png

就这么简单。在这个干净的沙盒里,再运行安装命令:

pip install -U vllm --pre --index-url https://pypi.org/simple --extra-index-url https://wheels.vllm.ai/nightly

没有报错,没有红字,一次通过。

这一步省下来的时间,足够我把 GLM-5 的 API 文档看两遍了。

最后

如果你看看未来的工作方式长什么样,可以试试 GLM-5。

它不是那种让你“哇”一声然后就关掉的玩具,它是那种你用了一次,就会把招聘助手的计划推迟的工具。

值得试试。

Cloudflare 推出了开源的Moltworker,支持在 Cloudflare 开发者平台上运行 Moltbot(一款可自托管的个人 AI 智能体),从而不再需要专用的本地硬件。Moltbot 最初名为 Clawdbot,设计定位是通过聊天应用提供个人助手服务,可集成 AI 模型、浏览器与第三方工具,且全程由用户自主掌控。

 

Moltworker 将 Moltbot 适配到了 Cloudflare Workers 中,在架构上组合了入口 Worker 与隔离的沙箱(Sandbox)容器。Worker 充当 API 路由与管理层,而 Moltbot 运行时及其各类集成则在沙箱内执行。包括对话记忆与会话数据在内的持久化状态会存储在 Cloudflare R2 中,解决了容器本身无状态、生命周期短的问题。

 

该实现充分利用了 Cloudflare Workers 近期在 Node.js 兼容性上的增强。Cloudflare 表示,对 Node API 更完善的原生支持减少了对变通方案的需求,让更多 npm 包可以不经修改直接运行。尽管 Moltbot 当前主要在容器中运行,但 Cloudflare 认为,这种更强的兼容性未来将让更多智能体逻辑可以更贴近边缘节点执行。

 

Moltworker 集成了多项 Cloudflare 服务,复刻并扩展了本地版 Moltbot 的体验。AI 请求会经由 Cloudflare AI Gateway 进行路由,支持多模型厂商、集中式可观测性与多种配置选项;浏览器自动化任务通过 Cloudflare Browser Rendering 来处理,使 Moltbot 可以控制无头 Chromium 实例完成页面导航、表单填写与内容抓取,而无需在容器内直接运行浏览器;API 与管理后台的身份认证则通过 Cloudflare Zero Trust Access 实现。

该项目在早期用户中引发了褒贬不一的反响。有人认为,基于 Cloudflare 托管的方案显著降低了使用门槛。Peter Choi 在评论 中指出,在 Cloudflare 上运行 Moltbot 有望大幅提升普及度,但同时质疑这种迁移是否会改变项目最初强调完全本地控制的核心吸引力。

 

另一些用户则强调其运维优势,有的用户这样说到

我一直在 VPS 上实现自托管,虽然能用,但管理服务器本身很麻烦。这个方案看起来是“部署后无需维护”的版本,很好奇状态在多次 Worker 调用之间是如何持久化的。

 

Cloudflare 已经在 GitHub 上开源了 Moltworker,并将其定位为概念验证(proof of concept),而非正式支持的产品。官方表示,该项目旨在展示其开发者平台(结合了 Workers、Sandboxes、AI Gateway、Browser Rendering 与存储服务)如何在边缘环境安全地、规模化地运行 AI 智能体。

 

原文链接:

Cloudflare Demonstrates Moltworker, Bringing Self-Hosted AI Agents to the Edge

利益相关声明:文中产品由厂商提供

2025 年 10 月至 2026 年 1 月期间,少数派与模块化迷你主机先行品牌 Khadas 合作,发起了针对 Khadas 最新产品 Mind 2 的众测活动。本次活动分两期举行,每期挑选 10 名少数派用户,免费提供包含主机和所有扩展模块的全套 Mind Family 供体验。

我们已在早先公布了第一期活动的获奖结果。日前,第二期活动也已经圆满结束。经过评议,我们很高兴宣布以下两篇质量突出的文章获得第二期众测活动的奖项:

其余按时提交符合要求的文章作者,都将获得参与奖,包含一套少数派周边及 Khadas 产品优惠券。我们再次感谢各位作者的积极参与,也请大家期待和关注少数派今后带来更多新趣产品的体验活动。

读者可以通过 #Mind 2 众测 标签查看所有体验文章。以下是第二期活动的优秀奖获奖文章。


「写在前面」

在过去几年中,我一直尝试在尝试打造一台属于我自己的「跨场景」个人终端,虽然「跨场景」这个定义叫法来自 Khadas,但是相似的想法在我心中由来已久。我所期待的并不是一台可以随时随地进入工作状态的卷王利器,而是一台可以 7x24 随时待命,胜任我「跨场景」所有需求的个人终端。因为,在经历了多年的工作和生活后,我深刻的体会到:生活和工作或许可以尽可能完全分离,但生活中的我和工作中的我却始终是同一个我。一台集合了我所有的记忆、经验、工具以及最重要的——使用习惯的设备,能够真正融入我的生活,成为我生活的一部分,提升我生活的便捷性和幸福感。

为了达成这一目标,我尝试过 Windows To Go、云主机、轻薄本 + 雷电显卡坞、Windows 掌机 + 拓展坞等等各种方案,但最终都因为实践中的各种痛点而浅尝辄止,无法长期使用下去。这种长久的遗憾一直困扰着我,直到我体验到 Khadas Mind 2,一款能够重新激发我对「跨场景」个人终端的热情的产品。

「外观及做工」

Mind 2 主机尺寸是 146mm×105mm×19mm,重量 435g,这约等于两台 iPhone 15 Pro Max(221g)或一台 iPhone 15 Pro Max+ 保护壳 +10000mAh 移动电源的重量。机身整体重量分布合理,裸机拿在手里不会觉得压手,同时高密度的金属机身带来的份量感恰到好处,在质感和重量中取到了一个比较平衡的点。得益于他相较其他 Mini PC 产品更薄的厚度,它可以很轻松地放进腰包和电脑包的小口袋里。手感、重量、体积的三重加分让人很有意愿随时带上他。

外观设计上,机身采用 CNC 工艺一体成型的哑光深灰色阳极氧化铝外壳,侧面散热鳍片与背面 I/O 面板为磨砂黑色,整体配色偏深邃内敛。机身边角处理圆润不硌手,电源键区域使用倒角处理,搭配长条形电源按键,操作手感符合直觉。相较近两年我体验过的其他 Mini PC 产品,边缘硌手问题得到了比较好的解决,电源键的设计也较常见的圆形平面按键使用起来更加自然。

做工质感方面,金属部分用料很扎实,按压起来感觉很坚固,不会出现市面上有的金属外壳笔记本产品按压之后轻微变形的问题,塑料部分磨砂细腻不粘指纹,没有廉价的塑料感,开孔边缘光滑没有毛刺;机身装配工艺精细,不同材质拼接处贴合紧密,接口缝隙对称,按键回弹清脆,接口插拔阻力适中。整体做工质感给我的体验和 Mac 是同一水平,属于是强迫症会感到治愈的那种。

Mind 2 细节图

另外,Mind Dock 尺寸是 187mm×126mm×17mm,重量 490g;Mind Graphics 尺寸为 197mm×133mm×100mm,重量 2650g。这两者的设计风格、用料和做工与 Mind 2 一致,都属于近年来我使用过的电子产品里面外观做工比较让我满意的。这里放一张图,把 Mind 2 全家桶和大家比较熟悉的 BANGCASE 供大家感受他们的尺寸。

Mind Family 尺寸对比图(图中两个手机壳均为 15 Pro BANGCASE)

「性能体验」

性能参数

Khadas Mind 2 主机有 Intel Ultra 5-125H、Intel Ultra 7-155H 两个 CPU 配置,此外还有 Intel Ultra 7-255H 的 Mind 2s 版本可选。内存和存储方面有 16GB/32GB/64GB LPDDR5X 内存和 512G/1T/2TB PCIe 4.0 固态硬盘可选。

关于这些核心硬件的量化参数,网上可以查到的数据很多。我更想要分享的是我自己将它作为主力设备使用下来的感受是:持平主流 4060Ti 的台式 PC,略低于 5060 的游戏笔记本

在接入 Mind Graphics 的情况下,黑神话·悟空(2K 分辨率/影视级画质/关闭光追/关闭帧生成)平均帧率 31 帧/秒,赛博朋克 2077(2K 分辨率/最高画质/关闭光追/关闭帧生成)平均帧率 65.4 帧/秒。

在 64G 内存和 16G 显存的加持下,Mind 2 在本地大模型上表现高于我使用的 4060Ti 台机和 5060 游戏本,我自己测试到的速率是:gpt-oss:120b-9.29 tokens/s、deepseek-r1:32b-2.37 tokens/s、deepseek-r1:14b-4.73 tokens/s、deepseek-r1:8b-12.94 tokens/s,供大家参考。

考虑到这是一台已经发布了一年多的设备,它在性能上的表现符合我对它的预期。如果再考虑到在这样的尺寸和静音表现上达到这样的性能,它所提供的整体感受则是超出预期的。

此外,机身底面有一个 2230 M.2 固态硬盘接口可供后续扩容使用,使用磁吸盖板保护,可以徒手拆开盖板,加装硬盘方便快捷,未来如果固定硬盘的螺丝换成快拆卡扣会更好。

接口规格

常规接口配置方面,Mind 2 搭配 Mind Graphics 底座的情况下,接口数量和规格远超相近尺寸 Mini PC 设置更大尺寸的 ITX 主机,接近市面上高端主机接口配置,能够完全胜任各种应用场景。具体各个组件以及不同组合下提供的接口数量、规格我做了一个表格方便大家比较。

产品/组合多屏显示雷电 4USB4USB-AHDMIDP有线网口音频接口读卡器指纹读取器供电接口
Mind 221x40Gbps1x40Gbps2x10Gbps1xHDMI 2.1 TMDS-----1xPD
Mind Dock---3x5Gbps2xHDMI 2.0-1x2.5Gbps1x3.5mm1x200MB/s11xPD
Mind Graphics-1x40Gbps-3x10Gbps2xHDMI 2.1a1xDP1.4a1x2.5Gbps1x3.5mm1x200MB/s11xAC
Mind 2+Mind Dock41x40Gbps1x40Gbps

2x10Gbps

3x5Gbps

1xHDMI 2.1 TMDS

2xHDMI 2.0

-1x2.5Gbps1x3.5mm1x200MB/s11xPD
Mind 2+Mind Graphics61x40Gbps1x40Gbps5x10Gbps

1xHDMI 2.1 TMDS

2xHDMI 2.1a

1xDP1.4a1x2.5Gbps1x3.5mm1x200MB/s11xAC

在常规接口之外,Mind 2 系列产品配备了足以「战未来」的 Mind Link 接口,接口设计带宽为 PCIe 5.0 x8,最高速率 256GT/s,在当前 Mind 2+Mind Graphics 搭配的情况下可以实现 128GT/s 的传输速率,远高于常见的雷电 4 接口和现在尚未普及的雷电 5 接口。这个接口也是我认为 Mind 生态最未来可期的地方,256GT/s 上限的接口,不管是现阶段扩展 50 系以及后续 60 系显卡,还是未来更多设备类型的接入,甚至多台 Mind 设备并联部署本地大模型,都是绰绰有余的。

「跨场景实践体验」

在 2026 年 1 月末,这个 Intel Ultra 第三代 CPU 刚刚发布、新机普遍开始采用 50 系显卡的节点,Mind 2 的性能不算顶级,但它的「跨场景」实践体验给我带来的惊喜却并不亚于今年推出的各种新品,这种体验完全符合我对于「跨场景」个人终端的期望。下面我将结合我自己的使用场景为大家分享我自己的感受。

基于我的行业和岗位属性,我的工作地点不固定,电脑使用场景包括 Excel 处理大量数据、Photoshop 简单修图、剪映视频剪辑等。工作之外,我主要的娱乐方式包括竞技网游、单机单机 3A 大作、高清影音、3D 建模等。为了在各种使用场景下都能达成跨场景体验,我尝试过很多不同的解决方案,但都因为存在无法解决的痛点而不得不选择放弃,在正式分享 Mind 2 体验之前,我也把这些踩过的坑分享给大家。

方案痛点
移动固态硬盘运行 WTG
  • 发热严重,性能不稳定,长时间运行有掉盘风险;
  • 硬件兼容性差,较新较旧硬件都存在问题,经常出现问题;
  • 官方停止支持,无法通过常规方式使用 Win 11;
云电脑
  • 长期使用订阅成本高;
  • 实际性能不足,画面压缩、掉帧严重;
  • 极度依赖网络,4G/5G 情况下复杂操作几乎不可用;
  • 数据安全与信息安全问题;
  • 硬件云电脑终端配置低,使用体验较差
远程桌面(基于 RDP/Moonlight&Sunshine)
  • 极度依赖网络,4G/5G 情况下复杂操作几乎不可用;
  • 家用宽带上行不稳定;
轻薄笔记本 + 显卡坞
  • 显卡坞兼容性问题,部分品牌笔记本电脑无法识别雷电 4 显卡坞;
  • 显卡坞自身运行不稳定;
  • 产品本身设计不足,电源外置、体积大、接线杂乱、噪音大、发热严重;
  • 雷电 4 接口及显卡坞主控性能损耗;
NAS 同步用户文件夹
  • 同步速度受家庭 / 办公带宽、NAS 硬盘读写速度限制;
  • 极度依赖网络,同步失败后容易导致文件混乱;

这些痛点在过去都是需要额外付出大量的时间和精力尽量降低影响,但当我开始使用这台可以轻松放入口袋,自身性能与容量足够满足绝大部分场景需求同时又能够随时切换扩展的 Mind 2 时,这些所有的痛点都会迎刃而解。

使用场景 1-固定办公

这种场景下,我的使用方案是 Mind 2 单机 + 带 PD 供电的拓展坞。日常办公需要用到的鼠标、键盘、打印机都连接到拓展坞上,只需要一根线接入 Mind 2,即刻进入工作状态。依靠 Mind 2 Ultra 7 155H+64G 内存的强悍性能,不管是 Office 三件套还是日常的修图/剪辑都可以轻松胜任。

这种使用场景下,如果使用的显示器是支持一线通反向供电的,可以进一步简化,去掉拓展坞,直接使用显示器供电驱动 Mind 2,不管外围设备技术新旧,都可以简单适配。

Mind 2 主机搭配拓展坞使用

另外,对于工作中需要多屏显示的用户,Mind 2 单机身支持双 C 口视频输出,配合 Mind Dock 使用最高支持四路视频输出,即便是不使用 Mind Graphics 也可以满足绝大多数多屏显示需求。

使用场景 2-移动办公

在 Mind 2 发布后,官方宣布了 Mind xPlay 便携屏幕的计划。虽然目前这个产品在不久前的 CES 2026 才正式发布,但是我已经自己使用 3D 打印提前尝鲜体验到了。我制作了一个适配 Smartisan TNT GO 便携屏的 Mind 2 底座,将 Mind 2 主机安装在 TNT GO 上后,配合便携屏自带的摄像头、键盘、触控板,直接变身为一台高配笔记本。

整套方案含键盘在内重量 1.6Kg,相较在售的比较有代表性的高性能轻薄本 1.48Kg 的重量略重 100g+,和 ROG 幻 X 2025 带键盘的 1.59Kg 重量基本一致,属于比较方便携带移动办公的范围。

TNT Go 自带的手写笔还支持微软 MPP 2.0 协议,完美适配 OneNote、Office 套件等微软自家软件的手写体验,也支持各种设计软件的手写笔控制。我自己使用的比较多的是简单的 3D 建模,直接使用手写笔和触屏操作非常顺手,和 iPad 建模使用逻辑基本一致。

在拆下便携屏自带磁吸键盘只使用触屏的情况下,Mind 2+ 便携屏重量 1.2Kg,和幻 X 单机身重量一致,可以短时间握持使用或者倚靠在其他物体上使用。竖向握持便携屏时,Mind 2 的机身靠近手掌,正好可以当做平板的握把,反而握持手感要更好一些,竖向使用的时候,阅读和批注文档体验提升明显。

目前这种使用方式还是存在一个小缺点:TNT Go 本体较厚,再装上 Mind 2 主机,厚度是明显大于常规的二合一平板产品的。相信未来官方的 Mind xPlay 能更好地解决这个问题。

使用场景 3-影音娱乐

我家使用的是 4K@120Hz 的电视,同时家里内网是 2.5G 网络。因此我将 Mind Dock 布置在电视柜上,使用有线接入网络,搭配附带触控板的无线键盘。回家之后直接将 Mind 2 主机放在 Mind Dock 上,拿起键盘躺在在沙发上操作,一步到位进入放松状态,不需要反复起身操作电脑。

不管是播放在线视频还是 NAS 中的高品质片源,Mind Dock 的 2.5G 网口加持下,几乎感觉不到缓冲等待的过程。这个过程中唯一小小的遗憾是 Mind Dock 自带的 HDMI 接口不支持 HDMI 2.1 的 4K@120Hz HDR 画面输出,虽然绝大部分情况下足够完全发挥片源画质,但对于极端高画质的情况,还是需要手动切换到 C 口画面输出。希望后续产品可以提升 Dock 上的画面输出规格。

另外,这套组合有一个非常方便到我的设定:Mind Dock 在前面板上设置了一个 5Gbps 的 USB-A 接口和一个 200MB/s 的 SD 4.0 读卡器,每次外出拍摄素材回来之后可以直接把 SD 卡插在 Mind Dock 上进行浏览,USB-A 口则用来插读卡器读取 Pocket 3 的 TF 卡,不管是相机的 6K25p 视频还是 Pocket 3 的 4K60p 视频,两个接口的速率都能满足素材播放要求,省去了每次都要在后面摸索插拔的困扰。

Mind 2 搭配 Mind Dock 连接电视使用

使用场景 4-游戏娱乐

对于 Mind 生态最重磅的一环——Mind Graphics,我选择将它和书房的显示器连接在一起,Mind Graphics 的输出接口完全满足显示器 2K@240Hz 的画面需求。桌面端 4060Ti 显卡虽然在当前已经是 N-1 代的产品,但是实际体验下来,我日常玩的网游(海克斯大乱斗、云顶之弈)可以最高特效满帧运行,常玩的单机 3A(黑神话·悟空、艾尔登法环)略降一点画质打开 DLSS 也能以比较好的效果流畅运行。考虑到目前 Steam 硬件调查上,截止 2025 年 12 月显卡榜第一名依然是四年前发布的 3060,Mind Graphics 内置的这块两年半之前发布的 4060Ti 在未来几年都还能够满足主流游戏性能需求。

此外,Mind Graphics 还内置全频单元和高频单元组成的双扬声器系统,声音效果显著强于显示器内置音响,我主观感受音质好于 500 元档位的桌面音箱。可以在简化桌面走线的情况下提供良好的音频体验。

Mind 2 搭配 Mind Graphics 连接显示器使用

特殊场景 1-便携娱乐

除了以上常见的使用心态,我自己还发现了一种同样好用的小众使用方式——配合最近两年热度很高的 AR 观影眼镜使用。因为 Mind 2 主机本身足够小巧,不管沙发旁茶几上还是床头柜上都可以稳稳放下,搭配 Iris Xe 核显,在沙发或者床上躺着的时候连接 Mind 2 进行大屏观影或者游玩策略类游戏,都是很特别的体验,很好的解决了 AR 眼镜自身性能不足和常用电脑尺寸过大不方便随处放置的问题。

Mind 2 主机连接 AR 观影眼镜使用

特殊场景 2-雷电 4 显卡坞

Mind Graphics 除了结合 Mind 2 主机使用之外,还有一个隐藏用途——作为其他品牌笔记本的雷电 4 显卡坞 + 桌面音箱使用。在最初看到这个功能的时候,我并不太能理解这种这么细分的使用场景。但在今年元旦经历了 Mind 2+Mind Graphics 拆分开变成两台高性能电脑应急开黑的事件后,我开始重新认真看待这个功能。

虽然说以 Mind Graphics 的售价来和市售雷电 4 显卡坞对比,性价比确实不如小厂显卡坞 + 单独购买显卡的方案,但是对于本身就需要使用 Mind 2 的目标群体来说,则相当于是额外附送了一个静音小巧美观 + 桌面 4060Ti+ 内置 300W 氮化镓电源 + 桌面音箱的增值服务。更何况,直到 2026 年的今天,市面上做工、审美、尺寸以及功能都能达到 Mind Graphics 同一水平的产品依然是凤毛麟角。因此,这个看似非常特殊的使用场景还是单独列出来,方便和我有同样需求的朋友参考。

Mind Graphics 作为显卡坞使用

「未来将至」

分享了这么多我自己的使用感受,截止到本文完稿,CES 2026 已经落下帷幕,各个厂商参展新品的相关信息也逐步完善。在 CES 2026 的众多参展厂家的新闻中,我也看到了期待已久的 Khadas 新品信息。

本次 CES 展会,Khadas 发布了三款新品:全球首款 Intel Panther Lake 处理器的模块化迷你主机,体积仅 0.43L 的 Mind Pro;搭载桌面级 RTX 5060 Ti,支持 180W 性能输出 Mind Graphics 2;集成屏幕、键盘、触控板和 48Wh 电池的 Mind xPlay。这三款新品,在现有的 Mind 生态的基础上,将 Mind 主机更新到 Ultra 三代处理器,通过更好的性能与能耗比进一步提升 Mind 主机的使用体验;将 Mind Graphics 升级到最新的 5060Ti;补上 Mind 生态在移动办公上的拼图。目前三款新品已经在海外开启预约,根据官方消息,国内市场也会尽快上架。

不同于我们已经见过的数据&计算单元与用户分离的云主机/串流解决方案或是用户&数据与计算单元分离的 WTG 解决方案,Mind 生态为我们展示了一个目前来看最合理的方向:将用户&数据&核心计算单元融合成一台无感携带的个人终端,再使用统一的、高上限的接口标准匹配不同的细分场景。自 Mind 1 开始,到 Mind 2,再到刚刚发布的 Mind Pro,我们能看到 Mind 生态在产品逐渐丰富的同时,作为 Mind 生态核心的 Mind 主机也在向着理想中的个人终端不断进化。在这个发展方向上诞生出的 Mind 2,远远不止是一台「性能更强一点、尺寸更小一点、外观更精致一点的 Mini PC」,而是通向「跨场景」甚至「全场景」个人终端的一次重要的突破。

或许,这就是「跨场景」个人终端的理想形态。


「彩蛋」

在体验 Mind 产品的过程中,认识了很多使用和关注 Khadas 产品的朋友。为了更好地体验 Khadas 产品,我也制作了一些相关的配件上传到拓竹社区供大家免费使用,同样也在使用 Khadas 产品的各位朋友可以直接搜索 Khadas 下载打印。

目前我已经制作好的模型有:Mind Dock 防尘盖、Mind Graphics 防尘盖、适配 TNT GO 的 Mind 2 底座、Mind 2 便携屏通用底座。大家有其他需求或者想法也欢迎大家在评论区交流。

Mind 生态 3D 打印配件

 

    11.jpg
    本次更新围绕"记忆系统工程化"和"Agent 能力结构化"两条主线,对云服务和开源项目做了系统升级。核心改进集中在多视角记忆、记忆版本管理、检索召回质量、Skills 本地化,以及若干生产环境的稳定性优化。

    本次发布亮点

    22.png

    1. 多视角记忆:让每个 Agent 拥有“自己的记忆世界”

    传统 Agent 架构里,记忆通常是全局共享的,所有 Agent 共享同一份"客观记忆池"。这在多角色系统、多 Agent 协作场景中容易导致行为冲突和角色混淆。

    我们上线了多视角记忆(Multi-Perspective Memory),为每个 Agent 引入"主观视角"的记忆结构。同一事实可以在不同 Agent 那里形成不同视角的记忆表达和认知结构。每个 Agent 的记忆体拥有自己的"主观视角",适合需要角色化、队伍化的 AI 游戏或多 Agent 协作场景,比如组队游戏、角色化陪伴类应用。

    帮助系统在多 Agent 协作时避免"一刀切"的全局记忆,便于实现个性化行为和角色差异化决策。记忆不再是单一全局视图,而是 Agent 级别的认知世界模型。

    33.png

    2. 多视角 AI 小游戏 Demo:多 Agent 记忆协作的真实形态

    基于多视角记忆的小游戏"冲顶鳌太线"已经上线,提供组队冲顶玩法的示例。Demo 以组队协作为核心场景,多 Agent 各自拥有独立视角记忆,同时参与协作任务目标,形成"个体认知 + 团队目标"的复合结构。

    该 Demo 以组队协作为核心场景:

    • 多 Agent 各自拥有独立视角记忆
    • 同时参与协作任务目标
    • 形成“个体认知 + 团队目标”的复合结构

    3. 检索记忆(search/memory)能力增强:更准 + 更省 Token

    3.1 关键词召回 + 语义相似度混合排序

    在事实记忆检索链路中新增 ​关键词召回机制​,并与原有语义相似度检索进行混合排序:

    • 提升召回覆盖率;
    • 提升召回准确率;
    • 避免单一语义相似导致的语义漂移问题。

    实测效果:

    • LongMemEval 提升 1.8%;
    • Locomo 提升 0.72%。

    该能力默认开启,开发者无需额外配置。

    3.2 消耗 Token 更少的记忆召回策略(相关性精筛)

    新增 relativity(相关性阈值)memory_limit_number/top_k 等参数,允许开发者按阈值只返回高相关性的记忆,从而显著降低注入 prompt 的 token 消耗,控制成本并提高上下文质量。

    为解决记忆注入导致的 Token 消耗问题,search/memory 接口新增 ​相关性精筛机制​:

    • relativity:相关性阈值(0\~1);
    • memory_limit_number / top_k:召回数量上限。

    系统只返回:

    • 相关性 ≥ 阈值;
    • 数量 ≤ 上限 的记忆集合。

    这使 MemOS 的记忆注入从“暴力拼接”升级为:

    精准召回 + 强相关过滤 + Token 成本可控

    📌 当前 relativity 仅对 事实记忆、偏好记忆 生效。

    示例(云服务)​:

    data = {
      "user_id": "memos_user_123",
      "query": "为我规划5天的成都游。",
      "relativity": 0.8, # 只返回相关性 >= 0.8 的记忆
      "memory_limit_number": 9 # 最多返回 9 条
    }

    示例(开源)​:

    {
      "user_id": "memos_user_123",
      "readable_cube_ids": ["memos_user_123_cube"],
      "query": "为我规划5天的成都游。",
      "relativity": 0.8,
      "top_k": 9
    }

    注意:relativity 当前仅对事实记忆偏好记忆生效。

    4. Skills 能力工程化升级 + MindDock 插件接入

    4.1 Skills 本地化存储机制

    Skills 文件支持​本地保存​,系统会为本地 Skills 自动生成专属访问 URL,LLM 可通过接口远程加载并运行 Skills,支持私有化部署与企业级管理。

    这使 Skills 从"运行态能力"升级为可管理、可分发、可治理的能力资产。

    开源项目中,Skills 文件现已支持​本地保存​:

    • 系统自动生成专属访问 URL
    • 大模型可通过接口远程加载 Skills
    • 支持私有化部署与企业级管理

    4.2 Skills 生成质量优化

    系统现在可以基于用户历史消息生成更完整、更结构化的 Skills 描述,使技能从"零散规则"升级为结构化能力模块。

    配置本地储存(开源)(简要步骤):

    Step 1: 添加环境变量到项目根目录的.env 文件

    SKILLS_REPO_BACKEND=LOCAL
    SKILLS_LOCAL_DIR=/tmp/upload_skill_memory/ # 最终存储位置
    SKILLS_LOCAL_TMP_DIR=/tmp/skill_memory/ # 生成时的临时位置
    SKILLS_LLM=gpt-4o

    Step 2: 启动本地服务

    uvicorn memos.api.server_api:app --host 0.0.0.0 --port 8001 --workers 1

    4.3 MindDock 插件能力接入

    插件 MindDock 现已支持:

    • 在 ChatGPT;
    • 千问;
    • 等多平台聊天环境中。

    并支持实时注入 Skills,使 Skills 成为​跨平台通用能力层​,而非单一模型绑定能力。

    44.png

    1. MCP 删除记忆路径增强:删除不再是“弱操作”

    为更好支持用户删除记忆的意图识别与落地,MCP 处理逻辑更新为:

    • 在识别到删除意图后,调用 deleteMemory 接口直接删除对应记忆;
    • 同时调用 addFeedback 接口以记录用户反馈并更新相关记忆项,确保删除操作更可靠且可审计。

    从“模糊删除”升级为​双通道强语义删除机制​,确保用户对记忆控制权的完整性与可靠性。

    55.png

    6. 记忆调度模块化重构:工程级稳定性升级

    记忆调度任务处理器实现模块化重构并集中统一管理。

    重构内容包括:

    • 将检索流程拆分为:search enhancererank filter 四阶段;
    • 新增 search_service 统一 API 与 Scheduler 的文本检索实现;
    • 修复 Redis Streams 调度消息序列化问题,补齐 mem_read/pref_add processoruser_context 传递。

    我们提升了调度的可靠性、可观测性与可扩展性,便于在高并发场景下稳定运行。

    66.png

    7. 文档记忆双轨检索:记忆 + 原文 + 上下文协同

    新能力​:

    • 支持​原文片段(RawFileMemory)与记忆(SummaryMemory)混合检索​,并可按需同时召回原文上下文以增强长文本语义连贯性;
    • search_memory_type 支持三种模式:All(原文 + 记忆混合)、AllSummaryMemory(仅记忆)、RawFileMemory(仅原文片段);
    • neighbor_discovery 配置用于是否召回原文分片的上下文。

    现在,文档记忆同时具备:

    • 语义抽象能力;
    • 原文可追溯性;
    • 上下文连贯性。

    开源示例​:

    data{
      "user_id": "testfile", 
      "readable_cube_ids": ["testfile_cube"],
      "query": "minddock 适配什么浏览器",
      "search_memory_type": "AllSummaryMemory", # 三种检索模式 All | AllSummaryMemory | RawFileMemory
      "neighbor_discovery": "true", # 若想召回原文上下文则置为 True
    }

    检索到的结果中:memory_type 新增 RawFileMemory(记忆原文片段)。

    8. 记忆过滤器(Filter)支持秒级时间精度

    filter 字段现在支持秒级别时间范围过滤(例如 "create_time": "2026-02-12 10:00:00"),适用于检索/获取记忆与对话接口的精确时窗筛选,提高审计与时效性控制的能力。

    示例​:

    "filter" : {
      "and": [
        {"create_time": {"gt": "2026-02-01 10:00:00"}},
        {"create_time": {"lt": "2026-02-12 10:00:00"}}
      ]
    }

    9. 对话接口(Chat)稳定性与能力增强

    • 修复了 qwen3-32b 回答失败的问题,恢复模型可用性;
    • 对话接口现支持 relativity 字段,允许开发者在对话阶段控制召回记忆的相关性阈值,从源头减少低价值上下文注入。

    对话系统在稳定性与成本控制层面同步升级。

    10. 开源社区(CHANGELOG 摘要)

    新增 / 新功能

    • 记忆检索优化(关键词检索 + 语义混合);
    • 文档记忆双轨检索:原文 + 记忆协同检索;
    • 文档记忆上下文唤醒(分片上下文);
    • relativity 精筛字段(0\~1);
    • MindDock 与云服务 Skill 支持;
    • MCP 删除意图触发 deleteMemoryaddFeedback
    • Chat 接口可传 relativity

    改进

    • 检索 pipeline 重构(Search → Enhance → Rerank → Filter);
    • 调度任务处理器模块化与 Redis Streams 修复;
    • Skills 本地化存储与 URL 发布;
    • Skills 生成质量提升。

    修复

    • Playground 使用体验问题修复;
    • 偏好记忆阈值字段使用错误修复;
    • 修复 get_memory 在复杂 filter 情形下的调用失败或卡顿问题;
    • 修复 Chat 接口 qwen3-32b 回答失败,兼容 LLM 的 enable thinking 参数。

    关于 MemOS

    MemOS 为 AGI 构建统一的记忆管理平台,让智能系统如大脑般拥有灵活、可迁移、可共享的长期记忆和即时记忆。

    作为记忆张量首次提出“记忆调度”架构的 AI 记忆操作系统,我们希望通过 MemOS 全面重构模型记忆资源的生命周期管理,为智能系统提供高效且灵活的记忆管理能力。本次更新围绕"记忆系统工程化"和"Agent 能力结构化"两条主线,对云服务和开源项目做了系统升级。核心改进集中在多视角记忆、记忆版本管理、检索召回质量、Skills 本地化,以及若干生产环境的稳定性优化。
    77.jpg

    天河说,要有诗。于是就有了诗。

    话不多说,先吟七首,给各位大爷助助兴。

    好姑娘狗娘养

    妹妹

    你和我上床 就是好姑娘

    妹妹 你想把我的钱花光

    就是狗娘养

    ——2025 年 9 月 12 日 晚 在某地

    玉女摇仙佩

    相思负尽,锦瑟空弹,报我沉忧清泪。

    楚馆寻情,秦楼买醉,一啖呢喃滋味。

    此际休言悔。但轻磨鬓角,半遮鸳被。

    窃声对、芳龄齿岁,直是良辰好景欢会。

    何曾料痴心、如今付了,山红野翠。

    应恨彩云易散,燕子迟归,梦远星河摇坠。

    万古千秋,青梅难遂,毕竟东流春水。

    且枕前依偎。从今似、浪客红尘无累。

    怎教我、冬雷夏雪,徒劳十载,一夕沦碎。

    天明未。萦愁黯黯人无寐。

    ——2021 年 2 月 5 日 夜 在某地

    大乐赋

    暮春春几许、鸳鸯被里香凝絮。

    休问芳名、枕前一夜,暂作萍鱼戏。

    趁残花将谢、飘蓬未举,且共此、人间漫趣。

    妹妹哥哥,缠绵语、有时动情弦一缕。

    天地也,合欢是、去他方圆规矩。

    银瓶破、水浆迸,乍失向来心意。

    分飞去,各自天涯浪客,仿佛未遇。

    ——2021 年 4 月 17 日 下午 在某地

    冬夜梦觉

    觉来不似梦中是,负尽深情只有诗。

    诗里哀思眠后忘,梦中心事醒犹痴。

    霓虹明暗分长夜,人世挣扎乞饱食。

    操蛋人生不解释,北欧下次投胎时。

    ——2023 年 1 月 8 日 凌晨 在某地

    秋夜与有须游花海酒吧

    人间无屌事,天上有清歌。

    明日谁先死,今宵恐难得。

    华年不作乐,垂老将如何。

    玉女香成醉,吉他弦乱拨。

    ——2025 年 11 月 8 日 夜 在某地

    唐多令·秋夜与有须饮酒

    暮色黯然收,年华东水流。换几回、日月春秋。

    且趁今天人尚在,吹啤酒,涮肥牛。

    街上好多妞,夜深瞎逛悠。倒不如、加个 QQ 。

    每代青春无限有,何必恨、少年游。

    ——2025 年 10 月 31 日 夜 在某地

    我想和你一起要饭

    妹妹

    我想和你一起要饭

    在地铁站 在路灯旁边

    我们俩 一人一个碗

    从天亮 蹲到黑天

    有人路过

    就说「可怜可怜」

    给我个馒头

    或者给我点钱

    妹妹 你帮我放风

    帮我留意城管

    城管一来 我抱起碗

    你抱着钱

    我们一起跑很远很远

    要是跑不掉

    我们就一起拘留几天

    一起吃免费三餐

    等出来了

    我们就换个地点

    继续要饭

    在公园 在商场门前

    我们俩 一人一个碗

    从天亮 蹲到黑天

    ——2025 年 7 月 22 日 在某地

    看到的大爷们,都发个评论,必会升职加薪、梦想成真。

    根据反馈看是否续更。

    大爷们,常来玩儿~

    当我们将来自不同来源的数据集合并,或从其他工作表复制数据时,如果数据匹配不够严谨,就很容易产生重复行。这些重复数据不仅会影响数据整洁度,还可能干扰统计分析和公式计算,甚至导致结果失真。

    因此,删除重复行是 Excel 数据处理中非常常见且重要的一项操作。本文将介绍如何使用 Spire.XLS for .NET 以编程方式高效地实现这一功能。

    安装 Spire.XLS for .NET

    首先,需要在 .NET 项目中添加 Spire.XLS for .NET 包中的 DLL 文件作为引用。您可以通过官网下载对应的安装包获取 DLL 文件,也可以直接通过 NuGet 进行安装。

    PM> Install-Package Spire.XLS

    在 C# 和 VB.NET 中删除 Excel 重复行

    手动删除重复行不仅步骤繁琐,而且十分耗时。借助 Spire.XLS for .NET,可以一次性识别并移除所有重复行,大幅提升处理效率。

    具体实现步骤如下:

    1. 创建一个 Workbook 实例。
    2. 使用 Workbook.LoadFromFile() 方法加载示例 Excel 文件。
    3. 通过 Workbook.Worksheets[sheetIndex] 获取指定索引的工作表。
    4. 使用 Worksheet.Range 属性指定需要检测并删除重复记录的单元格区域。
    5. 获取该区域中包含重复内容的行。
    6. 遍历所有重复行,并通过 Worksheet.DeleteRow() 方法将其删除。
    7. 使用 Workbook.SaveToFile() 方法保存处理后的结果文件。

    通过以上步骤,即可实现对 Excel 重复行的自动化删除。

    具体示例代码如下:

    using Spire.Xls;
    using System.Linq;
    
    namespace RemoveDuplicateRows
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 创建 Workbook 实例
                Workbook workbook = new Workbook();
    
                // 加载示例 Excel 文档
                workbook.LoadFromFile("Test.xlsx");
    
                // 获取第一个工作表
                Worksheet sheet = workbook.Worksheets[0];
    
                // 指定需要删除重复记录的单元格区域
                var range = sheet.Range["A1:A" + sheet.LastRow];
    
                // 获取重复行的行号
                var duplicatedRows = range.Rows
                       .GroupBy(x => x.Columns[0].DisplayedText)
                       .Where(x => x.Count() > 1)
                       .SelectMany(x => x.Skip(1))
                       .Select(x => x.Columns[0].Row)
                       .ToList();
    
                // 删除重复行        
                for (int i = 0; i < duplicatedRows.Count; i++)
                {
                    sheet.DeleteRow(duplicatedRows[i] - i);
                }
    
                // 保存结果文档
                workbook.SaveToFile("RemoveDuplicateRows.xlsx");
            }
        }
    }

    申请临时许可证

    如果您希望去除生成文档中的评估提示信息,或解除功能限制,可以为自己申请一个为期 30 天的试用许可证。

    按:@郝海龙是少数派人气付费教程《英语自学手册》的作者,也是许多人关注的创作者。本期,《装了啥》请到他介绍自己的近况的规划,常用的软硬件工具,以及关于AI时代学习英语和从事创作的思考。请介绍一下你自己 ...


    按:@郝海龙 是少数派人气付费教程《英语自学手册》的作者,也是许多人关注的创作者。本期,《装了啥》请到他介绍自己的近况的规划,常用的软硬件工具,以及关于 AI 时代学习英语和从事创作的思考。

    请介绍一下你自己。

    我是郝海龙,现在算是个全职爸爸,业余作家,正在申请私家侦探执照。

    你日常主要使用的硬件产品都有哪些?

    • iPhone 16 Pro:有了孩子后,很难有大块时间用电脑,所以使用手机的频率显著上升。意外发现,满屏文字(尤其中文)会让小孩觉得无聊,无形中减少了他的屏幕时间。
    • MacBook Pro (2021, Apple M1 Max):主力工作机。早年用来写会员通讯,现在有了 AI,主要用来玩各种 AI 工具和语音创作。
    • 飞傲 M21 随身听(FiiO M21):因为生活当中充满了各种琐碎的事情,没有办法欣赏需要整块时间的文艺作品,而音乐是为数不多的能够在零碎时间完整欣赏的艺术。于是我给自己买了这台安卓播放器,听我自己喜欢的大部分风格的音乐基本都够用了。
    • 拜亚动力 DT990 Pro 耳机 250OHM 版:因为自己经常会听一些实验性质的音乐,可能会打扰到家人,于是我专门给随身听配了一副耳机。我有「监听耳机还原真实」的执念,这款既满足监听需求,阻抗也适中,Mac 和飞傲 M21 都能直推,无需耳放。
    • Kindle Colorsoft (2025 Signature Edition):在所有的 E-ink 阅读器中,我最喜欢的还是 Kindle,因为无论是从显示效果还是电池续航来说,它都是最好的。去年 Kindle 进军澳洲后终于入手,现已成为我的主力电子书阅读设备。
    • 洛斐小顺青春版键盘:写作多年,试过无数机械和静电容键盘,始终难寻完美。直到这两年发现在 Cherry 的专利过期之后,很多国产轴体大发力,出了很多意想不到好键盘。但年纪大了,玩不动客制化,就在 Kickstarter 上买了洛斐小顺青春版(我知道众筹只是噱头,肯定会量产)。这是一款矮轴键盘,键程短,按压力度也算舒服,是我比较喜欢的感觉,不过配列只是勉强能接受。最近买了山海新造的陶瓷键帽准备换上,还特意为空格键配了重压力轴体。
    • 铁三角黑胶唱机:跟着复古风买了黑胶唱机。流行歌(如 Taylor Swift)其实 CD 或流媒体效果更好,但在古典或爵士乐上,黑胶确实别有一番独特味道。
    • Blackwing 602 铅笔:这款铅笔因为卖得太贵,我一直以为是智商税,出于童年补偿心理买了,才发现「好写」并非虚言。偶尔用铅笔的话强烈推荐,但我学语言用量大,也在看平替,目前发现三菱 Hi-Uni(一定要选 B 硬度笔芯)比较接近。它家配件除了颜值没有特别值得称道的地方,不推荐。
    • 钢笔和墨水:同样出于补偿心理买了不少,发现 Lamy、百乐甚至国产廉价笔反而更好写。墨水推荐澳洲冷门牌子 Van Dieman's Ink,但需注意墨水流动性与钢笔出水特性的匹配,否则体验极差。
    • Casio G-Shock GMW-B5000:带孩子看手机不便,便重拾戴表习惯。这块表几乎天天戴,前阵子表带坏了闲置两月,刚换好新表带,今天又戴上了。

    会用哪些软件工具或技术来提高效率?

    我用绝大多数软件时,其实并没有从一开始就考虑效率,很多时候就是用上发现放不下了。下面我分类说说常用的软件吧。

    AI 相关

    目前我只付费订阅了 ChatGPT 和 Gemini。之前我用这两款 AI 来帮助我学习私家侦探相关课程,我也见证了这两款软件的成长和变化,后面 AI 相关的问题里再具体说。

    文艺相关

    • Kimico 家出品的 BookBuddy、MusicBuddy、MovieBuddy 全系列 Pro 版:基本上算是我私人的书影音记录库。其中 BookBuddy 最为好用,如果你有超过三百本书的话,强烈推荐,虽然界面不够摩登,但胜在该有的都有。
    • Netflix、Prime Video、Disney+、YouTube Premium:都付费订阅了,但说实话好像真的没有太多时间去看,已经变成小孩专门看动画片的服务了。
    • Kindle、读墨(以及几乎所有的主流电子书阅读软件):作为一个嗜书的人,不太想受限于平台,一本想读的书在哪个平台上有就在哪个平台上读。
    • Doppler、Apple Music、Qobuz:听歌主力。Qobuz 买无损,导入 Doppler 听。一直没找到完美的云盘同步播放器,Doppler 是目前最好的本地选择。
    • GoodLinks:非常好用的链接管理以及稍后读应用。我在互联网上看到的文章或者有用的链接一般都会导入。
    • PhotonCam:买过无数吃灰的相机 App,唯独这款因支持导入 AI 手搓滤镜,让我重拾了手机摄影的乐趣。
    PhotonCam + AI 手搓滤镜效果

    学习相关

    • Anki:很早就买了,但是在学英语的时候没有用上,因为觉得界面实在是太丑了,但最近学日语时发现意外好用,或许它本就是为此设计的?
    • Dictionaries(物書堂):收录正版辞书,界面优雅,一次购买终身使用,学英语、日语强烈推荐,其他语言也可以看看有没有辞书。
    • 欧路词典:另一款好用的词典软件,现在有了 AI 功能,不过我很少使用。

    通讯工具和社交媒体

    • Telegram:我的主力即时通讯工具。(微信也用,是我和家人主要的通讯工具。)
    • Spark:电子邮件客户端。
    • Ivory:主力 Mastodon 客户端。X 时间线乱了后,我更多在 Mastodon 发言。
    • X、Instagram、Facebook:其中 Facebook 主要当二手市场用。

    创作相关

    • Ulysses:这个不多说了,之前我专门写过文章,也接受过 Ulysses 中英文专访。
    • Day One:用这个写了十几年日记,从未间断。
    • Drafts:主力文字收集工具,几乎任何文字都从这里开始,然后再考虑分发问题。
    • 语音输入法:受倪匡先生 1996 年用声控电脑写《双程》(就在回答这个访谈的今天,刚收到了一本旧版此书)的启发,我一直对语音创作非常感兴趣,而近来 AI 语音输入法的效果终于能达到让我满意的程度。之前推荐过 Typeless,但最近它对文字改动的幅度比之前大了很多,就转用结合 OpenAI API 的工具(如闪电说)。最近我的大部分长文章都是使用AI语音输入进行创作,再手动微调的。
    卫斯理《双程》,勤+缘出版社(1996)

    效率相关

    • Things:主力的待办事项及 GTD 应用。
    • Timery for Toggl Track:时间记录软件。

    其他

    还有其他很多应用,比如 PDF Expert、Dropbox、Hookmark、Paste、Alfred、Keyboard Maestro、Hazel 等等,这些软件很多人都推荐过,并且使用也很直观,就不赘述了。

    如果方便,请展示一下你的手机主屏幕和电脑桌面

    能跟少数派读者分享一下过去这几年主要从事的项目吗?

    这几年我主要的项目就只有一个,就是从 2021 年 7 月到 2023 年 11 月做的《林中来信》会员通讯系列。由于实在没办法和新手爸爸的生活平衡,最终无奈关停了。之后如果有时间,我会从时效性不强的文章(大部分如此)中筛选一部分,出版一部精选集。

    作为一名前播客主播,你对近两年中文播客的兴盛有什么可以分享的观察?今后还会考虑主持播客吗?

    对于中文播客兴盛我非常开心,但也确实没有太多时间去听很多新兴的节目,很难说出什么特别有见地的看法。唯一想感慨的就是:还是得做视频播客,音频和视频好像在中文听众群体中的热度区别特别大。

    至于我自己,还是会考虑再做播客,但正如前面所说,可能会是视频形式。其实有这个想法也有好长时间了,但是我发现视频节目想要有人来看,对视频本身的质量要求很高,我自己又特别不喜欢做后期和剪辑。好在现在有了 AI,很多事情也在逐渐变得容易,所以我打算再观察一段时间,找到合适的契机就做。

    不过播客的内容可能不太会是我之前一贯的内容,可能会做一点自己真正喜欢的东西,比如像现在很流行的罪案解读什么的,也符合我作为推理小说作家和准私家侦探的身份。但一切还是未定之数。

    在完成《英语自学手册》之后,你对于英语学习是否还有更新或更进一步的思考可以分享?

    AI 肯定是绕不开的,它给外语学习带来两个问题。

    一是如何利用 AI 辅助学习?

    其实网上可以找到很多直接 AI 辅助学习语言的软件。包括 Joe Hu(《英语自学手册》读者、实践者 )出品的 Joe Speaking,以及 tinyfool 老师和周楷雯老师(《英语自学手册》早期版本试读者 )出品的鹦鹉螺口语(暂时仅日语)和 Miraa(日语)。甚至你可以直接用 ChatGPT、Gemini 来跟 AI 对话。这对口语训练,尤其是内向者是极大助力。可以说,外语学习如今最大的门槛,只剩主观能动性。

    而第二个问题就正好与此有关——还有必要学外语吗?

    会员专属文章,欢迎加入少数派会员。

    优质内容

    权益周边

    会员社群

    power+

    小白零成本上手OpenClaw(小龙虾AI):龙猫免费Token+memU bot搭建+飞书控制全教程

    码字不易,哪怕内容稍显啰嗦,每一处细节都是为了小白能顺利上手——这篇依旧是喂饭级超长教程,无需懂代码、无需深厚计算机基础,跟着老马的步骤一步步来,你就能零成本拥有:理论上无限额度的大语言模型API接口、本地私人电脑AI助手(OpenClaw,人称小龙虾AI机器人),甚至能在手机上动动嘴,远程控制电脑上的小龙虾替你干粗活。

    全程零门槛、零成本,哪怕你对AI技术一窍不通,也建议尝试——学AI、了解AI,多动手总没有坏处,老马是上过大学的人,绝不坑你!废话不多说,教程分为三个核心阶段,Les't go!

    第一阶段:免费白嫖龙猫大模型API(解决Token烧钱痛点)

    搭建OpenClaw(小龙虾AI)的核心前提,是拥有一个大语言模型API接口——没有它,小龙虾就是个“傻子”,啥活也干不了。市面上很多人会忽悠你付费开通GLM-4.7、MiniMax M2.1、Kimi K2.5等API服务,每月要花几十到一两百块;更有甚者,让你购买Claude code模型API,分分钟烧的都是美刀,贵得离谱。

    今天给大家带来的福利,是美团推出的LongCat(龙猫)大模型——美团送外卖不差钱,目前注册龙猫开放平台,每天免费送Token,最高可嫖5000万额度,完全能喂饱“吃货”小龙虾(小龙虾消耗Token速度较快)。

    1.1 龙猫大模型核心优势及额度说明

    龙猫大模型总参数量685亿,但每次推理仅激活29亿至45亿参数,实现高稀疏性,既保证性能,又降低消耗,核心额度政策如下(实测真实有效):

    • 基础免费额度:注册即送,每天至少50万tokens,三款模型共享(LongCat-Flash-Chat、LongCat-Flash-Thinking、LongCat-Flash-Thinking-2601),当天没用完,第二天自动重置为50万,无时间限制。
    • 升级额度(推荐):点击平台“申请更多额度”,随便填写简单的公司信息(无需真实资质,审核极松),30分钟内即可审核通过,额度直接从50万升级到500万,足够日常使用。
    • 终极福利额度:LongCat-Flash-Lite(轻量化模型),目前限时不限量,调用该模型不消耗任何额度,相当于“无限使用”,完美解决小龙虾Token消耗快的问题。

    1.2 注册龙猫平台并获取API(全程3分钟搞定)

    操作极其简单,仅需3步,全程无需付费,无需复杂操作:

    1. 访问官网:在电脑浏览器地址栏输入 https://longcat.chat/platform/usage,打开龙猫API开放平台。
    2. 注册登录:使用手机号码获取验证码,直接注册并登录(无需绑定银行卡、无需实名认证,流程极简)。
    3. 查看额度:登录后,页面会直接显示你的Token额度,以及可使用的模型,记住这三款核心模型名称(后续配置会用到):

    • LongCat-Flash-Chat(主模型,共享50-500万额度)
    • LongCat-Flash-Thinking(思考模型,共享50-500万额度)
    • LongCat-Flash-Lite(轻量化模型,无限额度,优先推荐)

    1.3 获取API密钥(核心配置,必看)

    API密钥是后续配置OpenClaw的核心,没有它无法接入模型,获取步骤如下(图文指引,小白也能看懂):

    1. 登录龙猫开放平台后,找到左侧菜单中的“API Keys”,点击进入。
    2. 进入页面后,平台会默认给你创建一个API密钥,你可以直接复制使用;也可以点击“新建API Key”,自定义创建(建议直接用默认的,省时间)。
    3. 复制API密钥,保存到电脑记事本或手机备忘录中(后续会反复用到,别弄丢,每个人的密钥都是唯一的,老马也无法获取你的密钥)。

    1.4 龙猫API接口格式(无需懂,复制即用)

    龙猫平台兼容两种主流API格式,无需理解原理,后续配置时按要求复制对应地址即可:

    补充说明:配置时只需填三个东西——API接口地址(推荐Anthropic格式)、API密钥(刚才复制的)、模型名称(优先选LongCat-Flash-Lite,无限额度),其余无需管。

    第二阶段:搭建memU bot加强版OpenClaw(小白傻瓜式一键安装)

    很多小白反馈,原版OpenClaw搭建门槛太高——Windows系统要装WSL或Node.js,不懂计算机知识根本无从下手;环境搭建报错、Github访问不畅、不会魔法上网,每一步都能把小白逼疯。

    今天推荐的方法,是搭建memU bot加强版OpenClaw——memU本身是一个开源记忆框架,专门做记忆管理,memU bot相当于把memU和OpenClaw结合,不仅解决了原版OpenClaw的搭建痛点,还优化了记忆功能和安全性,具体优势如下:

    • 傻瓜式安装:砍掉复杂不稳定的环境与网络门槛,一键安装,无需手动配置环境。
    • 记忆功能升级:原版OpenClaw“记性差”,memU加持后,能长期记忆你的使用习惯,提前预判你的需求。
    • 优化体验:解决了原版OpenClaw上下文管理差、安全性不足的问题,使用更流畅、更安全。
    • 多平台适配:支持Windows、Mac系统,操作步骤完全一致,小白通用。

    2.1 下载memU bot(解决QQ邮箱拦截坑)

    以Windows 11系统为例(Mac系统操作完全一致),下载步骤如下,重点解决QQ邮箱拦截的常见坑:

    1. 访问memU bot官网:https://memu.bot(官网是英文,无需担心,只需找输入框即可)。
    2. 获取下载链接:在官网找到输入电子邮箱(E-mail)的输入框,输入你常用的邮箱(QQ邮箱、163邮箱均可),点击“Send Link”,官网会在几秒钟内,将下载链接发送到你的邮箱。
    3. 解决邮箱拦截问题(重点):

    • 打开邮箱,会收到一封英文邮件,QQ邮箱可能会提示“疑似欺诈”,直接忽略即可。
    • 点击邮件中的Windows下载按钮,大概率会被QQ邮箱拦截,此时不要慌——鼠标右键点击“Windows下载按钮”,选择“复制链接地址”。
    • 在浏览器新建标签页,将复制的链接粘贴到地址栏,按回车键,即可正常下载(避开邮箱拦截)。

    2.2 安装memU bot(解决系统拦截坑)

    下载完成后(建议保存到桌面,方便查找),开始安装,重点解决系统拦截问题:

    1. 双击桌面的memU bot安装包,系统可能会提示“未知发布者”,点击“更多信息”。
    2. 在新弹出的页面中,点击“仍要运行”,即可开始安装(系统拦截是正常现象,无需担心安全问题)。
    3. 安装过程无需手动操作,默认安装到C盘,安装完成后,会自动在桌面生成快捷方式,并启动memU bot软件。

    2.3 配置龙猫大模型API(核心步骤,必做)

    启动memU bot后,需要将第一阶段获取的龙猫API配置进去,否则小龙虾无法“思考”,步骤如下(全程鼠标操作,无需懂代码):

    1. 进入设置界面:点击memU bot软件左下角的“齿轮按钮”(设置图标),进入配置页面。
    2. 选择提供商:在右侧的“LLM提供商”下拉框中,选择“Custom Provider”(自定义提供商)。
    3. 填写核心配置(重点,按要求填,不要错):

    • API地址:粘贴Anthropic格式地址:https://api.longcat.chat/anthropic
    • API密钥:粘贴第一阶段复制的龙猫API密钥(注意:不要有空格、不要复制错字符)。
    • 模型名称:优先填写“LongCat-Flash-Lite”(无限额度,无需担心消耗);也可根据需求填写另外三款模型名称。
    1. 保存配置:填写完成后,点击页面中的“保存更改”,确保配置生效。

    2.4 可选配置:MemU API与Tavily搜索API(提升小龙虾能力)

    配置完龙猫API后,小龙虾已经能正常使用(基础功能无影响),页面中还有两个空的API配置项(MemU API、Tavily搜索API),作用及获取方法如下(可选,小白可先不配置,后续再补充):

    • MemU API:提升小龙虾的记忆能力,让它能更好地记住你的使用习惯,点击“从MemU平台获取→”,用邮箱注册即可免费获取API密钥(英文网站,可用浏览器翻译,操作简单)。
    • Tavily搜索API:给小龙虾增加搜索能力,让它能实时获取网络信息,点击“从tavily.com获取(每月1000次免费搜索)→”,注册即可免费获取(每月免费1000次,足够日常使用)。

    补充说明:不配置这两个API,不影响小龙虾的基础使用,只是缺失记忆和搜索功能;后续也可通过安装插件,补充这两项能力,老马建议大家后续有空再配置,先完成基础搭建,体验核心功能。

    第三阶段:memU bot接入飞书(手机远程控制小龙虾)

    配置完模型后,下一步就是接入飞书机器人——后续我们主要通过飞书APP(手机/电脑均可),与小龙虾对话,下达指令,甚至在手机上动动嘴,远程控制电脑上的小龙虾干活,步骤依旧是小白友好型,全程截图级指引。

    3.1 注册飞书并创建企业自建应用

    1. 访问飞书开放平台:在电脑浏览器打开 https://open.feishu.cn/app,用手机号码获取验证码登录(建议用常用手机号,方便后续使用)。
    2. 创建应用:登录后,在主页面找到“创建企业自建应用”按钮,点击进入创建页面。
    3. 填写应用信息(无需复杂,随便填):

    • 应用名称:随便取(如“小龙虾AI”“memU bot”均可)。
    • 应用描述:随便写(如“远程控制小龙虾AI机器人”)。
    • 应用图标:可选择喜欢的背景色和图形,无需自定义,默认图标即可。
    1. 完成创建:填写完成后,点击“创建”,进入应用的设置界面。

    3.2 获取飞书App ID和App Secret(核心参数)

    这两个参数是memU bot接入飞书的关键,获取步骤如下:

    1. 在飞书应用设置界面,左侧菜单找到“凭证与基础信息”,点击进入。
    2. 在页面中找到“App ID”和“App Secret”两个参数,分别复制,保存到电脑记事本(和之前的龙猫API密钥放在一起,方便后续使用)。
    3. 注意:不要泄露这两个参数,仅用于自己的memU bot配置。

    3.3 memU bot配置飞书参数

    回到电脑上的memU bot软件,继续在设置界面操作,完成飞书接入:

    1. 在memU bot设置页面,找到左侧“通用”下面的“平台”选项,点击进入。
    2. 滚动鼠标,找到“飞书”设置区域,将刚才复制的“App ID”和“App Secret”,分别粘贴到对应输入框中。
    3. 点击“保存更改”,memU bot这边的飞书配置就完成了。

    3.4 飞书应用添加能力(后续补充完善)

    回到飞书开放平台的应用设置界面,还需要给应用添加对应能力,才能正常使用,步骤如下(后续可逐步完善):

    1. 在飞书应用设置左侧菜单,找到“添加应用能力”,点击进入。
    2. 后续可根据需求,添加“机器人”“消息推送”等相关能力(具体可参考飞书官方指引,或后续老马补充教程),目前先完成基础配置,确保能正常连接即可。

    第四阶段:常见问题排查+补充说明(小白必看)

    很多小白跟着教程操作,还是会遇到小问题,这里整理了最常见的4个问题,以及补充说明,帮你避开所有坑:

    4.1 常见问题排查

    • 问题1:配置龙猫API后,小龙虾无法使用?
      解决:优先检查API密钥是否复制正确(有无空格、大小写错误);其次检查API地址是否粘贴正确(推荐Anthropic格式);最后确认模型名称填写无误(优先LongCat-Flash-Lite)。
    • 问题2:memU bot下载/安装时,被邮箱/系统拦截?
      解决:下载时用“复制链接地址,浏览器直接打开”的方法,避开邮箱拦截;安装时点击“更多信息→仍要运行”,避开系统拦截,均为正常现象。
    • 问题3:飞书App ID和App Secret复制错误,无法接入?
      解决:回到飞书“凭证与基础信息”页面,重新复制,确保没有多复制、少复制字符,粘贴后保存更改,重启memU bot即可。
    • 问题4:龙猫额度不够用?
      解决:点击龙猫平台“申请更多额度”,随便填写公司信息,30分钟内审核通过,额度升级到500万;或切换到LongCat-Flash-Lite模型,无限额度,无需担心消耗。

    4.2 补充说明

    • 关于龙猫额度:目前LongCat-Flash-Lite模型限时不限量,建议尽快配置使用,后续若有调整,老马会及时补充更新。
    • 关于memU bot版本:软件会持续更新,若后续版本界面有细微变化,核心配置步骤不变,只需找到对应设置项即可。
    • 关于手机控制:飞书APP可在应用商店下载,登录后即可找到创建的应用,与小龙虾对话,下达远程控制指令(后续会补充详细的指令使用教程)。
    • 关于课后作业:MemU API和Tavily搜索API的获取,建议小白后续有空再操作,锻炼一下动手能力,操作难度极低,看懂网页、会点按钮即可。

    第五阶段:总结与后续支持

    综上,整个教程全程零成本、零门槛,无需懂代码、无需深厚计算机基础,跟着步骤操作,你就能拥有:无限额度的龙猫大模型API、memU bot加强版OpenClaw(小龙虾AI),以及手机远程控制能力。

    后续若遇到配置失败、无法使用等问题,可转发本文并在评论区留言“666”,老马会提供手把手配置指导,全程协助你完成所有操作,确保你能顺利用上免费的小龙虾AI机器人。

    最后再强调一句:学AI、用AI,多动手、多尝试,哪怕是小白,也能快速上手——这一切都是免费的,何乐而不为呢?后续老马还会补充飞书控制详细指令、API进阶配置等内容,记得关注哦!

    本文由mdnice多平台发布

    前后差不多快进去一个月了
    目前我的家宽出来了,但是政企商宽还在 122.192 开头的小黑屋里,ip 属地是徐州,由于商企的处理流程特殊貌似需要客户经理处理,现在联通一口咬死宽带没有问题,理由是测速正常,ip 不是本地是全省 ip 随机分配导致…
    目前直播/视频上传/文件备份等均被限制在 1mbps ,只有联通自己的测速还有全球网测等大众 app 测速正常,小众站点和 iperf3 都原形毕露,但是联通不认。联通一直说是网站对我限速而不是他们运营商有限速,之前装维师傅说宽带被管控现在也改口了说听公司的处理方法
    问一下大家是如何处理的?特别是商企宽带

    如果说 DeepSeek 让 AI 学会了说人话,那 GLM-Image 就是专治 AI 画图「听不懂人话」的老毛病——毕竟,谁还没被那些鬼画符文字气笑过呢?

    原先的扩散模型手艺好,但耳朵背。现在的 AI 画图工具,像极了手艺精湛却耳背的 Tony 老师——你说招牌写开业大吉,他画出一串连考古学家都破译不了的符号。扩散模型训练稳定、泛化强,但面对复杂指令和知识密集型场景,总在信息表达和语义对齐上掉链子。

    GLM-Image 的解法很务实:让专业的模块干专业的事。90 亿参数的自回归模块(基于 GLM-4-9B-0414)当阅读理解冠军,生成携带语义信号的视觉词元;70 亿参数的扩散解码器(沿袭 CogView4 架构)当像素级工匠,还原高频细节。文科生写剧本、理科生做特效,分工明确才能出大片。

    除文本生成图像外,GLM-Image 还支持图像编辑、风格迁移、身份保持、多主体一致性。更关键的是,它终于能正确渲染中文了!通过集成 Glyph-byT5 进行字符级编码,开业大吉不会再变成开壶大古,海报设计师总算可以松口气了。

    开源,为了好用而不只是能用,由智谱华章以开源形式发布的 GLM-Image 打破「高性能=闭源收费」的潜规则。160 亿总参数对开发者友好,自回归懂语义 + 扩散雕细节的混合架构,或将成为下一代模型的标配。

    毕竟,我们要的不是抽卡式的运气游戏,而是能听懂复杂需求的靠谱搭档。当 AI 海报终于出现正确的汉字,记得感谢这个双脑协作的聪明架构——从耳背 Tony 到贴心设计师,GLM-Image 真的下了功夫。

    教程链接: https://go.openbayes.com/cZzpu

    使用云平台: OpenBayes

    http://openbayes.com/console/signup?r=sony_0m6v

    首先点击「公共教程」,找到「GLM-Image:首个全流程国产芯片训练模型」,单击打开。


    页面跳转后,点击右上角「克隆」,将该教程克隆至自己的容器中。


    在当前页面中看到的算力资源均可以在平台一键选择使用。平台会默认选配好原教程所使用的算力资源、镜像版本,不需要再进行手动选择。点击「继续执行」,等待分配资源。

    若显示「Bad Gateway」,这表示模型正在加载中,请等待约 2-3 分钟后刷新页面即可。

    使用步骤如下:

    1. 页面跳转后,点击左侧 README 页面,进入后点击上方「运行」。

    1. 点击运行后等待加载模型与初始化

    1. 待运行完成,即可点击右侧 API 地址跳转至 demo 页面。

    1. 打开后上传你想要的图片或文字,点击运行

    1. 成图展示


    教程链接: https://go.openbayes.com/cZzpu

    在广告投放系统中,IP地理定位往往是最基础的数据能力之一。但在实际业务中,很多团队为了节省成本,会优先采用免费IP库进行地域定向,那么免费IP库是否真的适用于广告投放?

    本文基于本人自测数据,数据测试时间为2025.9月,从准确率与误差距离两个维度进行分析,得出相关结论。

    一、为什么IP地址准确度对于广告系统更为重要?

    其实很好理解,在程序化广告系统中,IP定位通常会对广告系统中的定向精准投放、用户来源定向、本地生活推送广告匹配、区域黑名单过渡、投放报表分析等多多项业务有所影响,所以一旦IP定位精度出现过多误差,将会直接影响以下对于广告系统的相关维度:

    CPM浪费; ROI下降;广告主投诉; 数据分析偏差;合规风险

    换句话说,IP数据质量不是“辅助数据”,而是投放链路中的基础设施。

    二、测试设计说明

    1.样本数据

    构建100,000条真实IP→地理位置映射样本,覆盖全球主要国家、多个省/州、城市级别、IPv4与IPv6以真实定位数据作为基准进行比对。

    2.测试对象(免费库)

    本次选取两款常见免费IP数据库:

    产品类型更新频率
    DB-IP Free社区免费版月度
    GeoLite 2社区免费版月度

    三、实测结果

    1️国家级准确率

    产品国家准确率
    DB-IP Free97.8%
    GeoLite 299.1%

    结论:国家级定位表现尚可,误差比例较低。

    2️省/州级准确率

    产品省级准确率
    DB-IP Free86.5%
    GeoLite 290.7%

    结论:开始出现10%左右误差,已不适合做精细区域策略。

    3️城市级准确率

    产品城市准确率平均误差距离
    DB-IP Free63.5%52.7km
    GeoLite 270.1%38.9km

    关键结论:

    城市级误差明显

    平均误差40–50公里

    移动网络与云出口IP误差更大

    在本地商户广告、区域商圈广告场景下,这种误差对于广告投放来说是不可接受的。
    【工具对比】1免费IP库用于广告投放是否可靠?误差率实测报告.jpg

    四、免费IP库为何不适合广告投放?

    1 更新周期滞后

    IP地址段变化频繁,而免费库多为月度更新,存在明显延迟。

    2️ 数据采集维度有限

    免费库通常基于公开数据与社区样本,缺乏商业级验证机制。

    3️ 城市级定位天然误差

    ISP出口聚合导致城市归属偏移,免费库难以持续修正。

    4️ 无SLA保障

    广告系统属于实时高并发场景,而免费库没有服务稳定性保障,没有数据纠错通道也没有相关的技术支持。

    五、广告投放真实影响评估

    我们在模拟广告投放场景中进行ROI偏移测算:

    -城市级定向误差导致约8%–15%投放浪费
    -本地商户广告转化率下降10%以上
    -数据报表城市分布偏差显著

    在流量规模较大的广告平台中,这种误差会被放大。

    六、结论:免费库不适合用于广告投放

    可以明确得出结论:

    免费IP库可以用于测试环境、非核心统计分析,但不适合用于正式广告投放系统。

    尤其是在以下场景:
    城市级精准广告/本地生活服务投放/高预算品牌定向广告/跨境合规广告免费库的误差率与数据滞后,都会直接影响商业结果。

    七、建议:采用专业IP地址库

    对于广告系统,建议采用专业商业IP地址库,例如:

    • IP数据云-特点:高频更新,城市级精度优化,支持IPv4/IPv6,适合国内外广告系统接入
    • DB-IP商业版-特点:数据覆盖全面,稳定性较好,海外业务适用性强
    • IPnews-特点:强调实时数据更新,精度与稳定性均优于免费版本,适合对实时性要求高的投放系统

    相比免费库,专业IP数据库通常具备:更高城市级准确率,更小平均误差距离,更高更新频率,SLA保障,数据纠错机制。

    八、工程实践建议

    建议广告系统采用分层策略:

    1.生产环境使用商业IP库
    1.关键流量实时API校验
    1.对高价值流量进行多源交叉验证

    在广告系统中,IP数据属于“基础数据能力”,节省IP成本,往往会放大广告成本。

    2026 年,智能体将在企业级应用中取得哪些实质性突破?点击下载《2026 年 AI 与数据发展预测》白皮书,获悉专家一手前瞻,抢先拥抱新的工作方式!

    语义层并非新兴概念。早在二十一世纪初我便开始使用该技术,并在多项分析解决方案(尤其是企业数据仓库)中主导其设计、配置与实施。

    我曾使用多种语义层工具,包括 IBM Cognos Framework Manager 及微软数据平台的各类语义层功能。

    事实上,几乎所有“商业智能(BI)+自助服务”场景都涉及语义层的应用。

    近年来,由于智能体 AI 发展的需求,语义层正迎来复兴浪潮。因此,本文将围绕 Snowflake 平台语境,提出 7 项利用语义层实现 AI 就绪的战略建议。

    注:本文全篇手动撰写,内容基于我的行业经验与专业知识、相关研究,以及对客户需求与行业趋势的观察。

    利用语义层提升 Snowflake 平台上 Cortex AI Analyst 的 AI 提示准确性。如图所示,本例中的语义层集成位于顶部:VWS_CUSTOMER_DETAILS。图片来源:Dan Galavan

    要点一:理解语义层的传统作用

    首先需要明确基本概念。

    根据《牛津英语词典》的定义:“语义学是语言学和逻辑学中研究意义的分支。

    此外,维基百科将语义层定义为:

    “……将复杂数据映射为熟悉的业务术语,例如产品、客户或收入。”

    语义层能够为数据提供易于业务理解的、一致的解释视角,即一种抽象的“逻辑”视图。

     

    数据抽象逻辑视图示例。图片来源:Dan Galavan

    图中展示了一个抽象逻辑视图的示例,该视图在清晰描述数据的同时保持了更贴近业务用户需求的设计。

    语义层可用于定义和配置以下内容:

    • 标准化定义与含义

    • 数据对象间的关系

    • 指标

    语义层过去乃至现在仍广泛应用于商业智能场景。这对数据治理具有重要价值,有助于提升业务逻辑的一致性、可复用性及可维护性。

    要点二:了解人工智能为何推动语义层“复兴”

    通过语义层提升 Snowflake 平台上 Cortex AI Analyst 的 AI 提示词准确性。图中顶部的 VWS_CUSTOMER_DETAILS 即为此类语义层集成示例。

    核心观点:语义层有助于提升 Agentic AI 文本转 SQL 的准确性。

    换言之,当我们用 AI 增强商业智能分析时,若采用自然语言+智能体与数据进行交互,则必须将自然语言转换为底层数据仓库的查询语言(通常为 SQL)。

    语义层可将大型语言模型处理(常称为“推理”)与基于规则的定义相结合。规则定义承载了业务理解与业务逻辑。

    语义层有助于管控不准确性风险(亦称“幻觉”),从而提升可信人工智能的可靠性。

    要点三:数据建模是这一领域的关键技能

    如前所述,语义层是数据的“逻辑”或抽象视图。因此,在使用语义层时,我们无需深入了解数据存储库的内部工作机制。

    但在设计语义层时,必须有人完成以下任务:

    • 确定使用哪些源数据库表及其他对象;

    • 梳理这些对象之间的关联关系;

    • 对语义层对象进行命名,确保其符合业务通俗性;

    • 制定相应指标逻辑;

    • 确认业务定义;

    • 简而言之,有效建立从物理数据库到语义层的映射关系。

    物理数据模型示意图。图片来源:Dan Galavan

    实现上述目标的前提是理解数据。从实施层面来看,数据建模角色最适合承担此项工作,同时需要结合业务用户等领域专家的意见进行完善。

    要点四:AI 增强型语义层设计与人机协同机制

    Human in the Loop:评估来自 Snowflake AI Cortex Analyst 的 AI 生成语义层建议。 图片来源:Dan Galavan

    尽管人工智能能够提出建议并增强语义层设计,但从质量保证的角度来看,用户验证至关重要。若我们采用基于 AI 的语义层配置建议,则必须建立相应的“人机协同”审批流程,例如涉及:

    • 确定概念同义词;

    • 业务友好的描述定义;

    • 正确的提示词与查询组合。

    以 Snowflake 云语义视图为例,其 Cortex AI 服务能够为语义层设计提供有益建议。这些建议可能包括描述、指标及查询语句。然而,必须由具备相应领域知识的系统用户对这些建议进行评估。

     

    Snowflake Cortex Analyst 语义视图已验证查询。 图片来源:Dan Galavan

    一个典型产出是已验证查询。在此场景中,系统会监测自然语言提示词及其对应查询,以识别潜在可复用的模式。一旦发现候选查询,系统将自动向用户推送建议。用户可选择接受、编辑或拒绝该条目。

    要点五:技术栈兼容性

    如同技术生态系统的任何组成部分,语义层的兼容性始终是需要考量的关键因素。因此,务必确保您的技术栈能够满足实际需求。

    以 Snowflake 平台为例,其功能支持包括:

    • 配置语义视图时提供多种接口与选项,例如 SQL、用户界面、REST API 及 YAML 格式,并集成了智能体 AI 功能;

    • 支持第三方商业智能工具,如 Sigma、Omni、Honeydew 和 Hex;

    • 兼容第三方设计与交付工具,例如采用 dbt 进行集成测试,且其路线图中已纳入 SqlDBM 的设计功能。

    要点六:语义层开放标准即将到来

    该标准源于开放语义交换规范(OSI)。图片来源:Snowflake

    开放标准不仅能降低供应商锁定风险,还有助于推动技术普及。当前,语义层开放标准——开放语义交换规范(OSI)正在制定中。

    该标准主要关注以下三个核心领域:

    • 标准化:提供统一的定义语言;

    • 互操作性:促进数据交换;

    • 可扩展性:支持模型随数据需求演进而灵活适配。

    该项目已获得众多机构的共同推进,参与方涵盖从 Blackrock 到 AWS 再到 Salesforce 等各类组织。

    从数据与人工智能战略视角来看,这一开放标准将带来显著价值,特别是在 AI/BI 工具及相关互操作性方面。

    要点七:面向语义层与智能体人工智能的治理方法

    若无法将语义层开放给智能体人工智能(Agentic AI),则前述所有工作均无法实现。

    再次以 Snowflake 平台为例,我们可以配置 AI 智能体,使其调用 Cortex Analyst 语义视图。这将为智能体提供我们在前文讨论的各个领域的明确指引,例如:

    • 数据含义;

    • 指标定义;

    • 针对智能体提示的已验证预制查询语句。

    语义视图还可应用于 BI 工具、Notebook 环境或 AI/MCP 集成中。

    利用 Snowflake Intelligence 功能,通过智能体人工智能+ Cortex Analyst 语义视图提交自然语言提示。图片来源:Dan Galavan

    此外,Snowflake Intelligence 功能可结合前述 AI 智能体与语义视图对数据进行查询。我们能够通过提交自然语言提示生成图表,并识别数据趋势。另一优势在于,Snowflake 的生成式人工智能能力与平台整体的数据治理功能紧密结合。

    我曾在生成式人工智能方兴未艾的 2023 年 Snowflake 全球峰会上就此话题发表过演讲。

    结论

    正如我们所看到的,语义层在与商业智能(BI)工作负载相关的领域虽已存在多年,但当前正迎来一场智能体人工智能(Agentic AI)的复兴。这场复兴的驱动力,源于确保“与数据对话”时实现可信人工智能的迫切需求。换句话说,这正是我们在“以 AI 增强 BI”的过程中所要应对的挑战。

    然而,在此过程中,务必牢记以下几点:

    数据建模在此背景下为何扮演关键角色

    • AI 增强如何助力语义层的设计

    • Human in the Loop 的重要性

    • 即将发布的开放语义交换规范

    • 采用治理化方法运用智能体人工智能(本文以 Snowflake Intelligence 平台的语义视图为例进行说明)

    • 综合考虑以上要素,将有助于有效推进数据与人工智能战略的落地。

    我还有许多相关的实践建议希望分享,并期待能尽快抽时间撰写成文。敬请持续关注!

    在此,预祝各位在语义层与智能体人工智能的探索之旅中一切顺利。如有任何疑问,欢迎随时联系

    © Dan Galavan 2026

    Dan Galavan 是一名独立的数据治理与数据管理顾问,拥有 27 年的客户咨询与数据解决方案交付领导经验。他自 2019 年起开始使用 Snowflake 数据平台,持有都柏林大学学院颁发的“商业高级人工智能”文凭,并已获得 Snowflake Snowpro 高级架构师认证。

    原文地址:https://medium.com/snowflake/snowflake-in-a-nutshell-7-strategic-ai-semantic-layer-tips-78422f7e0bdc

    点击链接立即报名注册:Ascent - Snowflake Platform Training - China更多 Snowflake 精彩活动请关注专区

     

    概要

    该方案是一个典型的用空间(Redis缓存)和异步化换取时间(响应速度)和系统稳定性(数据库抗压) 的架构设计。它非常适合点赞这类写多读多、对实时一致性要求稍低的业务场景。

    前言

    学习java过程中的心得,如有错误请提醒作者纠正,感谢不尽!!!

    如果有更好的实现,欢迎分享!!!

    当前实现优点

    1. 高性能与低延迟

      • 写操作快:用户点赞/拉踩请求直接操作内存数据库Redis,响应速度极快,用户体验好。
      • 数据库在定时任务触发前压力小:高频的写操作被Redis承接,避免了直接冲击MySQL
    2. 数据一致性保障

      • 原子性操作:使用Lua脚本在Redis内完成状态切换(点赞->拉踩->无状态),保证了“一个评论同一时刻只能有一种状态”的业务逻辑的原子性,防止并发请求导致的数据错乱。
      • 分布式锁:对单个用户的操作加锁(RLock),防止同一用户极短时间内的重复提交造成缓存数据问题。
    3. 批量处理效率高

      • 异步落库:定时任务将缓存中的大量变更集中起来,通过批量插入/更新ON DUPLICATE KEY UPDATE)和批量更新统计CASE WHEN)的方式与数据库交互,极大地减少了网络I/O和SQL执行次数,数据库处理效率高。

    当前实现存在问题

    1. 定时任务引发的数据库峰值:

      1. 可通过使用消息队列削峰填谷
      2. 将定时任务从处理全部数据改为处理部分数据,定时任务的周期调低

    主要实现细节

    Redis + Redis分布式锁 + 原子操作 + 异步落库 + SpringBoot定时任务

    流程图

    请求方法设计

    请求URL/api/article/v1/{commentId}/vote

    请求方法:PUT

    请求参数:type(Integer);(type=0表示修改成无状态,type=1表示修改成点赞状态,type=-1表示修改成拉踩状态)

    URL示例:/api/article/v1/{commentId}/vote?type = 1

    Redis表设计

    下述三表都用来记录用户对评论的状态(点赞、拉踩、无状态)

    articles:comments:likes:users:{userId}

    {userId}为动态键名

    Redis Set数据结构;

    存储的值:{commentId}

    articles:comments:dislikes:users:{userId}

    {userId}为动态键名

    Redis Set数据结构;

    存储的值:{commentId}

    articles:comments:stateless:users:{userId}

    {userId}为动态键名

    Redis Set数据结构;

    存储的值:{commentId}

    MySQL表设计

    该博客聚焦实现点赞/拉踩功能,涉及x_article_comments表不多,故不做x_article_comments表的字段介绍

    • x_article_comments_votes

      • 联合主键(comment_id, user_id)
      • vote字段表用户对评论的状态,1代表点赞,-1代表拉踩,0代表无状态,即不处于点赞状态或拉踩状态
    -- 文章评论表
    CREATE TABLE x_article_comments (
      id BIGINT AUTO_INCREMENT PRIMARY KEY,
      article_id BIGINT UNSIGNED NOT NULL,            -- 关联的文章
      parent_id BIGINT UNSIGNED NULL,               -- NULL 表示顶级评论;否则是回复
      root_id BIGINT UNSIGNED NULL,                 -- 同一Thread的Root评论 id(便于查询整个Thread)
      user_id BIGINT UNSIGNED NOT NULL,             -- 评论作者
      content TEXT NOT NULL,
      status TINYINT DEFAULT 1,            -- 1=显示,0=已删除/隐藏,2=待审核 等
      like_count INT DEFAULT 0,
      dislike_count INT DEFAULT 0,
      reply_count INT DEFAULT 0, -- 该层孩子的数量
      reply_descendant_count INT DEFAULT 0, -- 该层后代的数量
      version INT DEFAULT 0,               -- 乐观锁(更新时校验)
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
      PRIMARY KEY (id)
    ) ENGINE=InnoDB
      DEFAULT CHARSET=utf8mb4
      COLLATE=utf8mb4_unicode_ci;
    
    -- 点赞/踩表(每个用户对某条评论的动作)
    CREATE TABLE x_article_comments_votes (
      comment_id BIGINT NOT NULL,
      user_id BIGINT NOT NULL,
      vote TINYINT NOT NULL, -- 1=like, -1=dislike, 0=none
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (comment_id, user_id)
    ) ENGINE=InnoDB
      DEFAULT CHARSET=utf8mb4
      COLLATE=utf8mb4_unicode_ci;

    后端实现细节

    处理用户发起的点赞/拉踩/无状态请求

    Controller层

    校验参数

    先校验当前用户是否登录,userReadApi.getCurrentUserId()是我封装好的方法,用于获取发起当前请求的用户ID

            // 获取当前用户ID
            Long userId = userReadApi.getCurrentUserId();
            if (userId == null) {
                return AjaxResult.error(HttpStatus.UNAUTHORIZED, "用户未登录或获取用户ID失败");
            }
    完整代码
        /**
         * 点赞/拉踩/无状态评论
         */
        @PutMapping("/{commentId}/vote")
        public AjaxResult voteComment(
                @PathVariable("commentId") Long commentId,
                @RequestParam("type") Integer type
        ) {
            log.info("进入请求 /api/article/v1/{}/vote -> 点赞/取消点赞评论 type={}", commentId, type);
    
            // 获取当前用户ID
            Long userId = userReadApi.getCurrentUserId();
            if (userId == null) {
                return AjaxResult.error(HttpStatus.UNAUTHORIZED, "用户未登录或获取用户ID失败");
            }
    
    
            articleService.voteComment(userId, commentId, type);
    
            log.info("返回结果 /api/article/v1/{}/vote -> OK", commentId);
            return AjaxResult.success();
        }

    Service层

    校验参数
            // 1. 参数校验
            if (commentId == null || type == null) {
                throw new ClientException(HttpStatus.BAD_REQUEST, "参数不完整");
            }
            if (type != ArticleCommentVoteConstants.LIKE && type != ArticleCommentVoteConstants.DISLIKE && type != ArticleCommentVoteConstants.STATELESS) {
                throw new ClientException(HttpStatus.BAD_REQUEST, "投票类型非法");
            }
    
            // 2. 校验评论是否存在
            ArticleComment comment = articleCommentMapper.selectArticleCommentById(commentId);
            if (comment == null) {
                throw new ClientException(HttpStatus.NOT_FOUND, "评论不存在");
            }
    分布式锁 + lua脚本

    使用了redisson实现加锁,并使用了lua脚本保证原子性,从而防止用户频繁发起请求导致在redis缓存的数据出现错误的情况

    分布式锁相关代码
            // 5. 尝试获取分布式锁
            RLock lock = redisson.getLock(RedisKeyConstants.ARTICLES_COMMENTS_VOTES_LOCK_KEY + userId);
            boolean isLocked = false;
            try {
                isLocked = lock.tryLock(0, RedisKeyConstants.ARTICLES_COMMENTS_VOTES_LOCK_KEY_EXPIRE_TIME, TimeUnit.SECONDS);
                if (!isLocked) {
                    throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "系统繁忙,请稍后再试");
                } else {
                    // 获取到锁,执行投票逻辑,此处省略
                    // ...
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ClientException(HttpStatus.ERROR, "系统繁忙,请稍后再试");
            } finally {
                if (isLocked && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    log.info("用户 {} 的锁已释放", userId);
                }
            }
    lua脚本相关代码

    预先准备好redis键和lua脚本

            // 3. 构建 Redis 键
            String likeKey = RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + userId;
            String dislikeKey = RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + userId;
            String statelessKey = RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + userId;
            // 4. 根据投票类型处理逻辑
            DefaultRedisScript<Long> deleteScript = new DefaultRedisScript<>();
            deleteScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(RedisLuaConstants.ARTICLE_COMMENTS_VOTE_LUA_SCRIPT_PATH)));
            deleteScript.setResultType(Long.class);
            Long execute = null;

    lua脚本文件

    lua脚本能够保证原子性,若用户频繁发起请求也能保证以下的要求

    三个键对应的set集合里的值应保持互斥,只允许一个值如3只能出现在一个键,例如:共有三个键like:{userId}、dislike:{userId}、stateless:{userId},键like现在持有3,用户对commentId = 3发起了点赞/拉踩/无状态请求,修改为拉踩,此时要去除like:{userId}键和stateless:{userId}键的set集合中值为3的元素,执行完后只有键dislike:{userId}存在值3

    -- Lua 脚本:处理评论投票逻辑
    local mainKey = KEYS[1]       -- 进行add的键
    local srem1Key = KEYS[2]    -- 进行srem的键
    local srem2Key = KEYS[3]  -- 进行srem的键
    local commentId = ARGV[1]     -- 评论 ID
    
    -- 添加到main集合
    redis.call('SADD', mainKey, commentId)
    
    -- 从两个集合中移除
    redis.call('SREM', srem1Key, commentId)
    redis.call('SREM', srem2Key, commentId)
    
    -- 返回操作结果(可选)
    return 1  -- 表示成功执行
    

    获取到锁后,为实现存进redis,执行的操作如下

                    switch (type) {
                        case ArticleCommentVoteConstants.LIKE:
                            // 4. 处理点赞逻辑
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(likeKey, dislikeKey, statelessKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "处理点踩逻辑失败");
                            }
                            break;
                        case ArticleCommentVoteConstants.DISLIKE:
                            // 5. 处理点踩逻辑
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(dislikeKey, likeKey, statelessKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "处理点踩逻辑失败");
                            }
                            break;
                        case ArticleCommentVoteConstants.STATELESS:
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(statelessKey, dislikeKey, likeKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.ERROR, "处理无状态逻辑失败");
                            }
                            break;
                        default:
                            throw new ClientException(HttpStatus.BAD_REQUEST, "未知的投票类型");
                    }
    完整代码
        @Override
        public void voteComment(Long userId, Long commentId, Integer type) {
            // 1. 参数校验
            if (commentId == null || type == null) {
                throw new ClientException(HttpStatus.BAD_REQUEST, "参数不完整");
            }
            if (type != ArticleCommentVoteConstants.LIKE && type != ArticleCommentVoteConstants.DISLIKE && type != ArticleCommentVoteConstants.STATELESS) {
                throw new ClientException(HttpStatus.BAD_REQUEST, "投票类型非法");
            }
    
            // 2. 校验评论是否存在
            ArticleComment comment = articleCommentMapper.selectArticleCommentById(commentId);
            if (comment == null) {
                throw new ClientException(HttpStatus.NOT_FOUND, "评论不存在");
            }
    
            // 3. 构建 Redis 键
            String likeKey = RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + userId;
            String dislikeKey = RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + userId;
            String statelessKey = RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + userId;
            // 4. 根据投票类型处理逻辑
            DefaultRedisScript<Long> deleteScript = new DefaultRedisScript<>();
            deleteScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(RedisLuaConstants.ARTICLE_COMMENTS_VOTE_LUA_SCRIPT_PATH)));
            deleteScript.setResultType(Long.class);
            Long execute = null;
            // 5. 尝试获取分布式锁
            RLock lock = redisson.getLock(RedisKeyConstants.ARTICLES_COMMENTS_VOTES_LOCK_KEY + userId);
            boolean isLocked = false;
            try {
                isLocked = lock.tryLock(0, RedisKeyConstants.ARTICLES_COMMENTS_VOTES_LOCK_KEY_EXPIRE_TIME, TimeUnit.SECONDS);
                if (!isLocked) {
                    throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "系统繁忙,请稍后再试");
                } else {
                    // 获取到锁,执行投票逻辑
                    switch (type) {
                        case ArticleCommentVoteConstants.LIKE:
                            // 4. 处理点赞逻辑
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(likeKey, dislikeKey, statelessKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "处理点踩逻辑失败");
                            }
                            break;
                        case ArticleCommentVoteConstants.DISLIKE:
                            // 5. 处理点踩逻辑
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(dislikeKey, likeKey, statelessKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.SERVICE_UNAVAILABLE, "处理点踩逻辑失败");
                            }
                            break;
                        case ArticleCommentVoteConstants.STATELESS:
                            execute = stringRedisTemplate.execute(deleteScript, Arrays.asList(statelessKey, dislikeKey, likeKey), commentId.toString());
                            if (execute == null) {
                                throw new ClientException(HttpStatus.ERROR, "处理无状态逻辑失败");
                            }
                            break;
                        default:
                            throw new ClientException(HttpStatus.BAD_REQUEST, "未知的投票类型");
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ClientException(HttpStatus.ERROR, "系统繁忙,请稍后再试");
            } finally {
                if (isLocked && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    log.info("用户 {} 的锁已释放", userId);
                }
            }
        }

    定时任务

    定时任务要实现落库到MySQL中,并且清空redis对应的缓存

    创建定时任务

    @Component
    @Slf4j
    public class VoteSyncScheduler {
    
        private final ArticleService articleService;
        public VoteSyncScheduler(ArticleService articleService) {
            this.articleService = articleService;
        }
    
        @Scheduled(fixedRate = 5 * 60 * 1000) // 每五分钟执行一次
        public void syncVote() {
            log.info("开始执行点赞同步任务...");
            Boolean result = articleService.syncVote();
            log.info("点赞同步任务执行完成。");
        }
    }

    syncVote实现

    MySQL涉及操作多张表,所以需要在方法上加上注解@Transactional

    参考流程图

    1. 我们首先要从redis中获取数据,并将数据封装到两个集合中。
    2. 落库到MySQL
    3. 根据x_article_comments_votes表,通过SQL语句统计出like_count, dislike_count
    4. 将统计出的数据更新到x_article_comments
    5. 清除已在该定时任务处理完的redis缓存数据
    变量说明
    • List<ArticleCommentVote> votes = new ArrayList<>(); // 保存所有投票数据
    • Set<Long> TotalcommentId = new HashSet<>(); // 保存所有评论id
    • likesUserIdToCommentIdsMap、dislikesUserIdToCommentIdsMap、statelessUserIdToCommentIdsMap——存储的值为经过逻辑后键中已被处理的值,在最后一步清除redis缓存发挥作用
    从redis中获取数据,并将数据封装到两个集合中
            Set<String> userLikesKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + "*");
            Set<String> userDislikesKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + "*");
            Set<String> userStatelessKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + "*");
            if (userLikesKeys.isEmpty() && userDislikesKeys.isEmpty() && userStatelessKeys.isEmpty()){
                log.info("没有需要同步的数据");
                return true;
            }
            List<ArticleCommentVote> votes = new ArrayList<>();      // 保存所有投票数据
            Set<Long> TotalcommentId = new HashSet<>();     // 保存所有评论id
            Map<Long, Set<String>> likesUserIdToCommentIdsMap = new HashMap<>();
            Map<Long, Set<String>> dislikesUserIdToCommentIdsMap = new HashMap<>();
            Map<Long, Set<String>> statelessUserIdToCommentIdsMap = new HashMap<>();
            for (String userLikesKey : userLikesKeys) {
                String userId = userLikesKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY, "");
                Set<String> commentIds = stringRedisTemplate.opsForSet().members(userLikesKey);
                for (String commentId : commentIds) {
                    votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.LIKE, null, null));
                    TotalcommentId.add(Long.parseLong(commentId));
                    // 更新或新增likesUserIdToCommentIdsMap键值对,值的set集合新增commentId
                    likesUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
                }
            }
            for (String userDislikesKey : userDislikesKeys) {
                String userId = userDislikesKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY, "");
                Set<String> commentIds = stringRedisTemplate.opsForSet().members(userDislikesKey);
                for (String commentId : commentIds) {
                    votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.DISLIKE, null, null));
                    TotalcommentId.add(Long.parseLong(commentId));
                    // 更新或新增dislikesUserIdToCommentIdsMap键值对,值的set集合新增commentId
                    dislikesUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
                }
            }
            for (String userStatelessKey : userStatelessKeys) {
                String userId = userStatelessKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY, "");
                Set<String> commentIds = stringRedisTemplate.opsForSet().members(userStatelessKey);
                for (String commentId : commentIds) {
                    votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.STATELESS, null, null));
                    TotalcommentId.add(Long.parseLong(commentId));
                    // 添加到statelessUserIdToCommentIdsMap键值对,值的set集合新增commentId
                    statelessUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
                }
            }
    落库到MySQL
    Service层相关代码
            // 根据votes执行落库逻辑(包含插入/更新)
            if (!votes.isEmpty()){
                int i = articleCommentVoteMapper.batchInsertOrUpdate(votes);
                if (i < 0){
                    throw new ClientException(HttpStatus.ERROR, "批量插入或更新数据失败");
                }
                log.info("批量插入或更新数据成功,数量为:{}", i);
            } else {
                log.info("没有需要落库的数据");
                return true;
            }
    SQL语句相关实现
        <!-- 原有方法 -->
        <insert id="batchInsertOrUpdate" parameterType="java.util.List">
            INSERT INTO x_article_comments_votes (
                comment_id,
                user_id,
                vote,
                create_time,
                update_time
            )
            VALUES
            <foreach collection="list" item="item" separator=",">
                (
                    #{item.commentId},
                    #{item.userId},
                    #{item.vote},
                    NOW(),
                    NOW()
                )
            </foreach>
            ON DUPLICATE KEY UPDATE
                vote = VALUES(vote),
                update_time = VALUES(update_time)
        </insert>
    根据x_article_comments_votes表,通过SQL语句统计出like_count, dislike_count
    Service层相关代码
            List<UpdateCommentVoteCountDTO> updateCommentVoteCountDTOList = getCommentLikeCountAndDislikeCountByListOfCommentId(TotalcommentId);
    SQL语句相关实现
    <!-- 新增方法: 聚合查询点赞/点踩数量 -->
    <select id="selectCommentLikeCountAndDislikeCountByListOfCommentId" resultType="com.anon.spaceblogserver.modules.article.POJO.DTO.UpdateCommentVoteCountDTO">
        SELECT
            comment_id AS commentId,
            SUM(CASE WHEN vote = 1 THEN 1 ELSE 0 END) AS likeCount,
            SUM(CASE WHEN vote = -1 THEN 1 ELSE 0 END) AS dislikeCount
        FROM x_article_comments_votes
        WHERE comment_id IN
        <foreach collection="commentIds" item="commentId" open="(" separator="," close=")">
            #{commentId}
        </foreach>
        GROUP BY comment_id
    </select>
    将统计出的数据批量更新到x_article_comments
    Service层相关代码
    int updated = batchUpdateCommentLikeCountAndDislikeCount(updateCommentVoteCountDTOList, MySQLBatchSizeConstants.DEFAULT_UPDATE_BATCH_SIZE);
    if (updated < 0){
        throw new ClientException(HttpStatus.ERROR, "批量更新数据失败");
    }
    log.info("批量更新数据成功,更新数量为:{}", updated);
    限制单条SQL语句长度,批量更新操作具体实现

    因为SQL语句采用了case when减少网络往返,为限制SQL语句长度防止溢出以及影响性能,采用的策略如下

    若检测到参数updateCommentVoteCountDTOList超过指定个数,将会拆分成多条SQL语句与MySQL数据库进行交互

    /**
     * 批量更新评论的点赞数和点踩数
     * @param updateCommentVoteCountDTOList     需要更新的评论点赞数和点踩数列表
     * @param batchSize                   批次大小,建议根据实际情况调整,过大可能导致单次更新过慢,过小可能导致更新次数过多
     * @return      成功更新的记录数
     */
    public int batchUpdateCommentLikeCountAndDislikeCount(List<UpdateCommentVoteCountDTO> updateCommentVoteCountDTOList, Integer batchSize) {
        int totalUpdated = 0;
        for (int i = 0; i < updateCommentVoteCountDTOList.size(); i += batchSize) {
            // 截取当前批次的数据
            List<UpdateCommentVoteCountDTO> batch = updateCommentVoteCountDTOList.subList(
                    i,
                    Math.min(i + batchSize, updateCommentVoteCountDTOList.size())
            );
            // 执行当前批次的更新
            int updated = articleCommentMapper.batchUpdateCommentLikeCountAndDislikeCount(batch);
            if (updated < 0) {
                log.error("批量更新数据失败,当前批次更新数量: {}", updated);
                return -1;
            }
            totalUpdated += updated;
    
            log.info("批量更新评论点赞/点踩数,当前批次更新数量: {}, 累计更新数量: {}", updated, totalUpdated);
        }
        return totalUpdated;
    }
    SQL语句相关实现
    <update id="batchUpdateCommentLikeCountAndDislikeCount">
        UPDATE x_article_comments
        SET like_count = CASE id
        <foreach collection="list" item="item">
            WHEN #{item.commentId} THEN #{item.likeCount}
        </foreach>
        END,
        dislike_count = CASE id
        <foreach collection="list" item="item">
            WHEN #{item.commentId} THEN #{item.dislikeCount}
        </foreach>
        END,
        update_time = NOW()
        WHERE id IN
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.commentId}
        </foreach>
    </update>
    清除已在该定时任务处理完的redis缓存数据

    前面提到的变量——likesUserIdToCommentIdsMap、dislikesUserIdToCommentIdsMap、statelessUserIdToCommentIdsMap。存储的值为经过逻辑后键中已被处理的值,在最后一步清除redis缓存发挥作用

    上述变量类型形式为Map<Long, Set<String>>,满足了userId -> 评论id集合,这可以精准且方便的清除已处理后的redis缓存数据

    构建上述三个Map集合的过程在第一步操作中经历三个for循环已经构建完毕

    该操作同样用到lua脚本保证原子性

    lua脚本

    该脚本实现安全批量的移除key中要删除的元素列表为什么不直接把键删除的原因 -> 假如在该定时任务执行过程中以及该脚本执行之前用户又发起点赞/拉踩/无状态请求,若与定时任务触发后从redis查到的用户对评论的状态相等,则会移除,这并没有什么问题,若不相等,不会移除新请求中用户设置的点赞/拉踩/无状态,留到下一次定时任务触发后处理

    -- 安全批量删除,包含key存在性检查
    -- KEYS[1]: Set的key
    -- ARGV[1..n]: 要删除的元素列表
    --
    local key = KEYS[1]
    local key_type = redis.call('TYPE', key).ok
    
    -- 检查key是否存在且类型为set
    if key_type == 'none' then
        return 0
    elseif key_type ~= 'set' then
        return redis.error_reply('WRONGTYPE Operation against a key holding the wrong kind of value')
    end
    
    -- 兼容 Lua 5.1 和 Lua 5.2+
    local unpack = unpack or table.unpack
    
    local result = 0
    
    -- 如果 ARGV 不为空,则执行批量删除
    if #ARGV > 0 then
        result = redis.call('SREM', key, unpack(ARGV))
    end
    
    return result
    Service层相关代码
    // 清空Redis中的数据
    DefaultRedisScript<Long> deleteScript = new DefaultRedisScript<>();
    deleteScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(RedisLuaConstants.BATCH_REMOVE_MEMBERS_FROM_SET_LUA_SCRIPT_PATH)));
    deleteScript.setResultType(Long.class);
    likesUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
        Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + userId.toString()), commentIdSet.toArray());
        if (result == null || result < 0){
            log.error("批量删除用户 {} 缓存点赞的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
        }
    });
    dislikesUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
        Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + userId.toString()), commentIdSet.toArray());
        if (result == null || result < 0){
            log.error("批量删除用户 {} 缓存点踩的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
        }
    });
    statelessUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
        Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + userId.toString()), commentIdSet.toArray());
        if (result == null || result < 0){
            log.error("批量删除用户 {} 缓存无状态的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
        }
    });

    SyncVote完整代码

    @Transactional
    @Override
    public Boolean syncVote() {
        //TODO 发散思维,该段逻辑似乎在收藏、投币等功能有相似之处,该段之所以有点复杂是因为有三种状态————点赞、点踩、无状态,且三者之间是互斥的,但前面的收藏、投币功能没有这个问题,并且好像只有两个状态,日后可以抽象出一个通用方法来处理这类功能,减少代码重复度
        Set<String> userLikesKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + "*");
        Set<String> userDislikesKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + "*");
        Set<String> userStatelessKeys = stringRedisTemplate.keys(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + "*");
        if (userLikesKeys.isEmpty() && userDislikesKeys.isEmpty() && userStatelessKeys.isEmpty()){
            log.info("没有需要同步的数据");
            return true;
        }
        List<ArticleCommentVote> votes = new ArrayList<>();      // 保存所有投票数据
        Set<Long> TotalcommentId = new HashSet<>();     // 保存所有评论id
        Map<Long, Set<String>> likesUserIdToCommentIdsMap = new HashMap<>();
        Map<Long, Set<String>> dislikesUserIdToCommentIdsMap = new HashMap<>();
        Map<Long, Set<String>> statelessUserIdToCommentIdsMap = new HashMap<>();
        for (String userLikesKey : userLikesKeys) {
            String userId = userLikesKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY, "");
            Set<String> commentIds = stringRedisTemplate.opsForSet().members(userLikesKey);
            for (String commentId : commentIds) {
                votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.LIKE, null, null));
                TotalcommentId.add(Long.parseLong(commentId));
                // 更新或新增likesUserIdToCommentIdsMap键值对,值的set集合新增commentId
                likesUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
            }
        }
        for (String userDislikesKey : userDislikesKeys) {
            String userId = userDislikesKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY, "");
            Set<String> commentIds = stringRedisTemplate.opsForSet().members(userDislikesKey);
            for (String commentId : commentIds) {
                votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.DISLIKE, null, null));
                TotalcommentId.add(Long.parseLong(commentId));
                // 更新或新增dislikesUserIdToCommentIdsMap键值对,值的set集合新增commentId
                dislikesUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
            }
        }
        for (String userStatelessKey : userStatelessKeys) {
            String userId = userStatelessKey.replace(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY, "");
            Set<String> commentIds = stringRedisTemplate.opsForSet().members(userStatelessKey);
            for (String commentId : commentIds) {
                votes.add(new ArticleCommentVote(Long.parseLong(commentId), Long.parseLong(userId), ArticleCommentVoteConstants.STATELESS, null, null));
                TotalcommentId.add(Long.parseLong(commentId));
                // 添加到statelessUserIdToCommentIdsMap键值对,值的set集合新增commentId
                statelessUserIdToCommentIdsMap.computeIfAbsent(Long.parseLong(userId), k -> new HashSet<>()).add(commentId);
            }
        }
        // 根据votes执行落库逻辑(包含插入/更新)
        if (!votes.isEmpty()){
            int i = articleCommentVoteMapper.batchInsertOrUpdate(votes);
            if (i < 0){
                throw new ClientException(HttpStatus.ERROR, "批量插入或更新数据失败");
            }
            log.info("批量插入或更新数据成功,数量为:{}", i);
        } else {
            log.info("没有需要落库的数据");
            return true;
        }
        // 根据TotalcommentId集合中的commentId,利用聚合函数获取对应表中对应评论的likeCount和dislikeCount,封装成List<updateCommentVoteCountDTO> updateCommentVoteCountDTOList
        List<UpdateCommentVoteCountDTO> updateCommentVoteCountDTOList = getCommentLikeCountAndDislikeCountByListOfCommentId(TotalcommentId);
        // 根据updateCommentVoteCountDTOList执行批量更新的逻辑,作用的表为x_article_comments,使用case when then语法更新likeCount和dislikeCount
        int updated = batchUpdateCommentLikeCountAndDislikeCount(updateCommentVoteCountDTOList, MySQLBatchSizeConstants.DEFAULT_UPDATE_BATCH_SIZE);
        if (updated < 0){
            throw new ClientException(HttpStatus.ERROR, "批量更新数据失败");
        }
        log.info("批量更新数据成功,更新数量为:{}", updated);
        // 清空Redis中的数据
        DefaultRedisScript<Long> deleteScript = new DefaultRedisScript<>();
        deleteScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(RedisLuaConstants.BATCH_REMOVE_MEMBERS_FROM_SET_LUA_SCRIPT_PATH)));
        deleteScript.setResultType(Long.class);
        likesUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
            Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_LIKES_USERS_KEY + userId.toString()), commentIdSet.toArray());
            if (result == null || result < 0){
                log.error("批量删除用户 {} 缓存点赞的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
            }
        });
        dislikesUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
            Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_DISLIKES_USERS_KEY + userId.toString()), commentIdSet.toArray());
            if (result == null || result < 0){
                log.error("批量删除用户 {} 缓存点踩的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
            }
        });
        statelessUserIdToCommentIdsMap.forEach((userId, commentIdSet) -> {
            Long result = stringRedisTemplate.execute(deleteScript, List.of(RedisKeyConstants.ARTICLES_COMMENTS_STATELESS_USERS_KEY + userId.toString()), commentIdSet.toArray());
            if (result == null || result < 0){
                log.error("批量删除用户 {} 缓存无状态的评论失败,欲删除的commentId -> {}", userId, commentIdSet);
            }
        });
        return true;
    }

    平时跟外部开会比较多,需要整理会议纪要。中文英文的会议都有。

    飞书、腾讯会议、Zoom 、Google Meet ,啥会议软件都可能会用。所以想要希望一个比较通用的方案。

    如果有本地方案,那最好了(比如 macbook M5 可以运行的小模型?)。付费方案也可以接受,但不要太贵。。。

    今日速览

    1. Starnus:自动化帮你精准触达理想客户。
    2. Gro:AI 销售引擎,把数据变成实际行动。
    3. EditWithAva:AI 视频剪辑,创意秒变可发布大片。
    4. Lindy Assistant:主动式 AI 助理,每天省你两小时。
    5. Visual Editing by DatoCMS:无头 CMS 也能轻松实现所见即所得。
    6. Edgee:压缩提示降成本,AI 账单砍一半。
    7. Scout Program:用真金白银玩转早期投资竞技场。
    8. FocalRead:三倍速阅读神器,告别书单压力。
    9. Visla AI Director Mode:AI 导演模式,逐帧规划你的视频故事。
    10. Cube:AI 代理构建数据模型,分析报告零错误。


    1. Starnus

    这款工具专为创业者和 B2B 团队打造,能自动化搞定外部推广,让你轻松找到并联系下一个客户。

    • 定义理想客户群(ICP),自动匹配相似潜在客户
    • 整合商业和联系数据,丰富客户信息
    • 生成个性化联系内容,提升转化率
    • 一站式平台跟踪回复情况,全程无忧

    Starnus
    热度:🔺464
    访问官网 Product Hunt 详情


    2. Gro

    当销售团队被数据淹没时,Gro 帮你把这些数据转化为实际行动,打造统一高效的 AI 销售引擎。

    • 整合潜在客户开发、目标定位、外展联系和意图追踪
    • 实时更新超 10 亿条数据库,基于 AI 进行倾向评分
    • 支持多渠道自动化,精准捕捉意图信号
    • 无需导出数据或分散工具,工作流程无缝衔接

    Gro
    热度:🔺389
    访问官网 Product Hunt 详情


    3. EditWithAva

    全球首款 AI 助手视频编辑器,能理解你的素材语义,把创意直接变成可发布的视频作品。

    • 自动选择场景,剪辑重复镜头,节省编辑时间
    • 按创意意图组装编辑,支持插入补充镜头和添加字幕
    • 提供配音等功能,提升视频专业度
    • 简化视频制作流程,从创意到发布一气呵成

    EditWithAva
    热度:🔺346
    访问官网 Product Hunt 详情


    4. Lindy Assistant

    你的 AI 执行助理,设置只需两分钟,却能每天主动帮你节省两小时,管理邮箱、会议和日历不在话下。

    • 主动筛选邮件,准备会议,无需手动提示
    • 自动记录笔记,发送跟进邮件,提升工作效率
    • 简化日常任务管理,释放更多时间专注核心工作
    • 用户友好设置,快速上手体验智能助理便利

    Lindy Assistant
    热度:🔺260
    访问官网 Product Hunt 详情


    5. Visual Editing by DatoCMS

    解决了无头 CMS 与可视化编辑的复杂关系,让开发者轻松实现,内容编辑者享受所见即所得的流畅体验。

    • 提供简单易用的方法,在 DatoCMS 中集成可视化编辑
    • 结合草稿模式和实时更新,修改内容直观快捷
    • 消除在记录表单中寻找字段的麻烦,提升编辑效率
    • 增强内容管理系统用户体验,支持无缝协作

    Visual Editing by DatoCMS
    热度:🔺171
    访问官网 Product Hunt 详情


    6. Edgee

    在提示信息到达大语言模型前进行压缩,能大幅降低令牌成本,帮你省下一半的 AI 账单。

    • 压缩提示信息,减少令牌使用量
    • 支持相同代码运行,成本降低多达 50%
    • 无缝集成现有工作流程,无需额外调整
    • 专注于优化 AI 应用的经济性,提升性价比

    Edgee
    热度:🔺170
    访问官网 Product Hunt 详情


    7. Scout Program

    像玩幻想体育一样,用真实资金进行早期投资,公开透明地展示投资者技能,打造可验证的投资记录。

    • 每季度提供 10 万美元资金,投资初创公司
    • 投资者公开构建投资理论,竞争表现结果
    • 投资组合和结果完全透明,提升信任度
    • 压缩十年声誉积累,突出季度竞技亮点

    Scout Program
    热度:🔺132
    访问官网 Product Hunt 详情


    8. FocalRead

    别再被书单压垮了!这款工具利用快速串行视觉呈现技术,帮你以三倍速度阅读,同时提升理解力。

    • 从 Safari、X 或 Reddit 直接分享文章导入
    • 支持 EPUB、PDF 文件导入,或粘贴任意文本
    • 可调节阅读速度(100-1200 字/分钟),适应不同需求
    • 提供书签、多种主题、章节导航和可分享引用视频

    FocalRead
    热度:🔺124
    访问官网 Product Hunt 详情


    9. Visla AI Director Mode

    Visla 推出的 AI 导演模式,让你从任何输入开始,逐帧规划视频场景,打造专业级的 AI 生成视频。

    • 利用 AI 生成逐帧故事板图像,规划视频场景
    • 设定视频风格、节奏、配音风格和屏幕元素
    • 锁定产品、标志等品牌资产,确保视觉一致性
    • 选择内容转化为完整 AI 视频片段,灵活控制成品

    Visla AI Director Mode
    热度:🔺119
    访问官网 Product Hunt 详情


    10. Cube

    AI 分析工具常因不了解业务逻辑而出错,Cube 通过自动构建语义层,确保回答问题和生成报告零错误。

    • AI 代理自动构建语义层,理解业务逻辑
    • 连接数据后几秒内获得准确结果,提升分析效率
    • 基于开源语义层(GitHub 超 19,000 星),可靠透明
    • 提供免费使用选项,降低入门门槛

    Cube
    热度:🔺113
    访问官网 Product Hunt 详情

    先提前祝各位 v2exer 新年快乐不加班,工资和头发都越来越多!

    起因:没啥原因,虽然现在用的 华为 Q6 网线版+华为 ax6 没啥大问题,但是就是想折腾(不支持自定义 hosts,关闭 dhcp 后,ikuai 获取不到 dhcp 请求,不支持端口回流或者性能有问题,夏天偶发过热死机,端口偶发变成 100MB)

    参考 acwifi 的文章,整了一个万能组网 https://www.acwifi.net/29361.html

    求大佬们看下有没有啥问题:
    光猫桥接,tp 5005pe ac 拨号,下接中兴 BE5100pro+(客厅)和中兴 晴天 PoE 子路由或者小米 BE3600 Pro 子路由(卧室,这两个我问了客服都支持瘦 ap 模式,主要是为了卧室美观才上了一个 poe 子路由)

    pasted-image-1770967876964.webp

    大佬们,这样组网有啥问题们,有什么更好的建议么

    1. 假如方案可以,客厅的 nas 可以直接接 5005pe 上了(客厅留了双网口),be5100pro+ 降级 be5100 单 2.5g 口
    2. 除了上面列出的设备,有没有其他的性价比选择(尽量不加钱)