普通视图

发现新文章,点击刷新页面。
昨天以前首页

Cursor 2.0 支持模型并发,我用国产 RWKV 模型实现了一模一样的效果 🤩🤩🤩

作者 Moment
2025年11月5日 19:21

最近 Cursor 发布 2.0 版本,其中一个比较亮点的功能就是它可以同时指挥 8 个 Agent 执行任务,最后选择你觉得最好的那个答案。

而在底层模型层面,来自中国本土团队的 RWKV 项目也带来了更具突破性的成果:RWKV7-G0a3 13.3B ——当前全球最强的开源纯 RNN 大语言模型。

这一版本以 RWKV6-world-v2.1 14B 为基础,继续训练了 2 万亿 tokens(并融合了 35B 来自 DeepSeek v3.1 的高质量语料),在保持完全 RNN 架构、无注意力机制(No Attention)、无微调、无刷榜的前提下,取得了与主流 Transformer 模型相媲美甚至更优的表现。

20251104141030

在多项权威基准测试中(包括 MMLU、MMLU-Pro、GSM8K、MATH500、CEval 等),RWKV7-G0a3 在语言理解、逻辑推理与数学推演等任务上均实现显著提升。其中,MMLU-Pro 测评显示模型在多学科综合知识上的掌握更加扎实;GSM8K 与 MATH500 结果表明,其在中高难度数学与逻辑问题上的推理能力已达到同规模模型的领先水平。与此同时,RWKV7-G0a3 继续保持了 RWKV 系列一贯的高推理效率与低显存占用优势,展现出纯 RNN 架构在大模型时代下的强大潜力。

Uncheatable Eval 使用最新的论文、新闻、代码与小说等实时数据进行评测,通过“压缩率”(即 Compression is Intelligence)指标,衡量模型在真实语料下的语言建模能力与泛化水平。

20251104141244

MMLU 系列用于测评语言模型在多学科知识与认知推理方面的能力,其中 MMLU Pro 为进阶版本,包含更复杂的问题设计与更严苛的评测标准。

20251104141616

欲获取更多详细信息,请访问该模型的 官方公众号文章 阅读。

这意味着:

在以 Transformer (deep learning architecture) 架构主导的大模型时代,RWKV 所代表的“纯 RNN ”路线再度崛起:以更低的计算与显存成本、更自然的时序记忆机制,走出一条与主流 LLM 截然不同的进化路径。

RWKV 命名规则中,G0a3 标识了训练数据在版本与质量上的升级(例如:质量层级为 G# > G#a2 > G#,数据规模层级为 G1 > G0),即便参数量相同,G0a3 系列在泛化能力上也具备潜在优势。综合来看,RWKV7-G0a3 13.3B 的发布,不仅刷新了 RNN 模型性能的新高度,也象征着 RWKV 系列在“摆脱 Transformer 架构垄断”路径上迈出了一步。

模型下载

下载 RWKV7-G0a3 13.3B 模型(.pth 格式):

下载 .gguf 格式: modelscope.cn/models/shou…

下载 Ollama 格式: ollama.com/mollysama

如何使用 RWKV 模型(本地部署)

可以使用 RWKV RunnerAi00rwkv pip 等推理工具在本地部署 RWKV 模型。

RWKV 模型同时兼容主流推理框架,如 llama.cppOllama

目前最快的 RWKV 推理工具是 Albatross

由于 RWKV7-G0a3 13.3B 属于新模型,建议优先使用 RWKV Runner 以确保结果稳定与准确。

更多关于部署与推理的使用教程,可参考 RWKV 官网 - 模型部署和推理教程

前端如何实现模型并发的效果

首先,我们要知道模型并发的效果,那我们要知道连接了发起了一个请求之后,它是怎么回复的:

20251105181748

我们现在对的网络请求已经进行了截取,这是其中的一些数据,我们将一下核心的数据写到 json 文件里面让他能够更好的展示:

20251105181910

首先我们知道这是一个流式返回,但是一次流式返回了包含了的内容非常多,这里就是我们并发的关键了,这里的 index 代表并发的下标,而 delta.content 是具体的内容,这样我们知道了 SSE 实现并发的原理了,实际上就是调用 SSE,后端在一次 SSE 的返回中返回同一个问题的不同的结果并通过下标来区分。

我们已经把 SSE 返回机制摸清楚了。下面就轻松地走一遍“边生成边展示”的整个流程:从流式到达、到何时更新、再到怎么把半成品 HTML 安全地渲染出来,最后配上 UI 的滚动与分批加载。读完你就能一眼看懂这套实时渲染是怎么跑起来的。

先说结论:这件事其实就五步,顺次串起来就好了——流式接收、增量累积与触发、HTML 提取与补全、UI 局部更新、以及 iframe 的分批渲染。下面逐段拆开讲。

一、流式数据接收(ai.ts:195–251)

// 使用 ReadableStream 读取流式数据
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let partial = ""; // 处理不完整的行

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value, { stream: true });
  partial += chunk;

  // 逐行解析 SSE 格式:data: {...}
  const lines = partial.split("\n");
  partial = lines.pop() || ""; // 保留不完整的行

  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const json: StreamChunk = JSON.parse(data);
    // 处理每个 chunk...
  }
}

这里的代码的核心要点如下:

  • 逐行处理:SSE 数据按行到达,split('\n') 拆开,半截 JSON 用 partial 暂存。
  • 字符安全:TextDecoder(..., { stream: true }) 负责拼接多字节字符,避免中文或 emoji 被截断。
  • 过滤噪声:只解析 data: 开头的有效行,忽略心跳、空行、注释。
  • 流式收尾:遇到 [DONE] 仅结束对应流 index,其余继续处理。

二、增量累积与智能触发(ai.ts:224–244)

// 为每个 index 累积内容
contentBuffers[index] += delta;

// 判断是否应该触发渲染
const lastLength = lastRenderedLength.get(index) || 0;
const shouldRender = this.shouldTriggerRender(
  contentBuffers[index],
  lastLength
);

if (shouldRender && onProgress) {
  const htmlCode = this.extractHTMLCode(contentBuffers[index]);
  onProgress(index, contentBuffers[index], htmlCode);
  lastRenderedLength.set(index, contentBuffers[index].length);
}

这里的代码的核心要点如下:

  • 多流并行:每个 index 各自独立累积,互不干扰。
  • 智能触发:通过与 lastRenderedLength 比较控制频率,避免“来一点就刷”。
  • 精准更新:只触发对应 index 的渲染,避免全局重排。
  • 兜底刷新:流结束后进行最终更新,确保结果完整。

三、触发策略(ai.ts:62–101)

private static shouldTriggerRender(
  newContent: string,
  oldLength: number,
): boolean {
  // 1. 首次渲染:内容超过 20 字符
  if (oldLength === 0 && newLength > 20) {
    return true;
  }

  // 2. 关键闭合标签出现(语义区块完成)
  const keyClosingTags = [
    '</header>', '</section>', '</main>',
    '</article>', '</footer>', '</nav>',
    '</aside>', '</div>', '</body>', '</html>'
  ];

  const addedContent = newContent.substring(oldLength);
  for (const tag of keyClosingTags) {
    if (addedContent.includes(tag)) {
      return true; // 区块完成,立即渲染
    }
  }

  // 3. 内容增长超过 200 字符(防止长时间不更新)
  if (newLength - oldLength > 200) {
    return true;
  }

  return false;
}

这里的代码的核心要点如下:

  • 首帧提速:内容首次超过 20 字符立即渲染,减少“首屏空白”。
  • 语义闭合优先:检测新增片段中的关键闭合标签(如 </section></div>),保证块级内容完整展示。
  • 超长兜底:即使未闭合,增量超 200 字符也强制刷新。
  • 性能友好:仅比较“新增部分”,无需重复扫描旧文本;参数可根据模型节奏与设备性能调节。

四、HTML 提取与自动补全(ai.ts:19–59, 104–145)

private static extractHTMLCode(content: string): string {
  // 方式1: 完整的 ```html 代码块
  const codeBlockMatch = content.match(/```html\s*([\s\S]*?)```/);
  if (codeBlockMatch) return codeBlockMatch[1].trim();

  // 方式2: 未完成的代码块(流式渲染)
  const incompleteMatch = content.match(/```html\s*([\s\S]*?)$/);
  if (incompleteMatch) {
    return this.autoCompleteHTML(incompleteMatch[1].trim());
  }

  // 方式3: 直接以 <!DOCTYPE 或 <html 开头
  if (trimmed.startsWith('<!DOCTYPE') || trimmed.startsWith('<html')) {
    return this.autoCompleteHTML(trimmed);
  }

  return '';
}

private static autoCompleteHTML(html: string): string {
  // 移除最后不完整的标签(如 "<div cla")
  if (lastOpenBracket > lastCloseBracket) {
    result = html.substring(0, lastOpenBracket);
  }

  // 自动闭合 script、body、html 标签
  // 确保浏览器可以渲染未完成的 HTML
  return result;
}

这里的代码的核心要点如下:

  • 多格式兼容:完整块、未闭合块、裸 HTML 均可识别。
  • 容错补齐:遇到半截标签(如 <div cla)自动裁剪,再补上 </script></body></html> 等关键闭合。
  • 最小修正:仅做“可渲染”层面的修复,保持生成内容原貌。
  • 安全回退:提不出 HTML 时返回空字符串,避免将解释性文字误渲染。

五、UI 实时更新(ChatPage.tsx:240–253)

await AIService.generateMultipleResponses(
  userPrompt,
  totalCount,
  (index, content, htmlCode) => {
    // 实时更新对应 index 的结果
    setResults((prev) =>
      prev.map((result, i) =>
        i === index
          ? {
              ...result,
              content, // 原始 Markdown 内容
              htmlCode, // 提取的 HTML 代码
              isLoading: false,
            }
          : result
      )
    );
  }
);

这里的代码的核心要点如下:

  • 局部更新:仅更新目标项,prev.map 保证不可变数据结构,减少重渲染。
  • 双轨推进:content 用于 Markdown 文本,htmlCode 用于预览展示。
  • 快速反馈:首批数据到达即撤骨架屏,让用户感知“正在生成”。
  • 状态持久:结果存入 sessionStorage,刷新或返回依旧保留上下文。

六、iframe 分批渲染优化(ChatPage.tsx:109–175)

// 找出已准备好但还未渲染的索引
const readyIndexes = results
  .filter(({ result }) => !result.isLoading && result.htmlCode)
  .map(({ index }) => index)
  .sort((a, b) => a - b);

// 第一次渲染:一次性全部加载(用户体验优先)
if (!hasRenderedOnce.current) {
  setIframeRenderQueue(new Set(readyIndexes));
  hasRenderedOnce.current = true;
  return;
}

// 后续渲染:分批加载(每批 8 个,间隔 300ms)
// 避免一次性创建太多 iframe 导致卡顿
const processBatch = () => {
  const toAdd = stillNotInQueue.slice(0, batchSize); // 8 个
  toAdd.forEach((index) => newQueue.add(index));

  if (stillNotInQueue.length > batchSize) {
    setTimeout(processBatch, 300); // 继续下一批
  }
};

这里的代码的核心要点如下:

  • 首批全放:初次渲染不延迟,保证响应速度。
  • 后续分批:按批次(默认 8 个/300ms)渐进挂载,防止主线程卡顿。
  • 动态调度:每轮重新计算“未入队项”,保证不遗漏。
  • 轻量 DOM:仅渲染必要 iframe,滚动与交互更顺滑;参数可按性能灵活调整。

小结

通过语义闭合与字数阈值控制更新频率让画面稳定流畅,HTML 半成品自动补齐避免黑屏,iframe 分批挂载减轻主线程压力并配合 requestAnimationFrame 提升滚动顺滑度,状态由 sessionStorage 兜底并以日志辅助调参;整体逻辑是流式接收边累积、攒到关键点就渲染一帧、UI 精准更新该动的那格,调顺节奏即可实现实时渲染的又快又稳。

效果展示

前面我们说了这么多代码相关的,接下来我们可以把我们的项目运行起来看一下最终运行的效果:

20251105190158

为了让 UI 的效果显示得更好,建议使用 33%缩放的屏幕效果。

20251105190242

在输入框输入我们要问的问题,点击发送,你会看到这样的效果:

20251105190307

这会你能实时看到 24 个页面实时渲染的效果:

20251105190430

这样我们就借助 RWKV7-G0a3 13.3B 模型实现了一个跟 Cursor2.0 版本一模一样的效果了。

总结

RWKV7-G0a3 13.3B 是由中国团队推出的最新一代纯 RNN 大语言模型,在无 Attention 架构下实现了与主流 Transformer 模型相媲美的性能,并在多项基准测试中表现优异。它以更低显存占用和高推理效率展示了 RNN 架构的强大潜力。而前端并发实现中,通过 SSE 流式返回不同 index 的内容,实现了同时生成多个模型响应的并行效果。结合智能触发渲染与分批 iframe 更新,最终达成了类似 Cursor 2.0 的多 Agent 实时对比体验。

前端仓库地址

后端仓库地址

快到 2026 年了:为什么我们还在争论 CSS 和 Tailwind?

作者 Moment
2025年11月5日 09:04

最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777

老实说,我对 Tailwind CSS 的看法有些复杂。它像是一个总是表现完美的朋友,让我觉得自己有些不够好。 我一直是纯 CSS 的拥护者,直到最近,我才意识到,Tailwind 也有其独特的优点。 然而,虽然我不喜欢 Tailwind 的一些方面,但它无疑为开发带来了更多的选择,让我反思自己做决定的方式。

问题

大家争论的,不是 "哪个更好",而是“哪个让你觉得更少痛苦”。 对我来说,Tailwind 有时带来的是压力。比如:

<button
  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-300 ease-in-out transform hover:-translate-y-1"
>
  Click me
</button>

它让我想:“这已经不再是简单的 HTML,而是样式类的拼凑。” 而纯 CSS 则让我感到平静、整洁:

.button {
  background-color: #3b82f6;
  color: white;
  font-weight: bold;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease-in-out;
}
.button:hover {
  background-color: #2563eb;
  box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
  transform: translateY(-4px);
}

纯 CSS 让我觉得自己在“写代码”,而不是“编排类名”。

背景说明

为什么要写这篇文章呢?因为到了 2026 年,CSS 和 Tailwind 的争论已经不再那么重要。

  • Tailwind 发布了  v4,速度和性能都大大提升。
  • 纯 CSS 也在复兴,容器查询(container queries)、CSS 嵌套(nesting)和 Cascade Layers 这些新特性令人振奋。
  • 还有像 Panda CSS、UnoCSS 等新兴工具在不断尝试解决同样的问题。 这让选择变得更加复杂,也让开发变得更加“累”。

Tailwind 的优缺点

优点:

  1. 减少命名烦恼:你不再需要为类命名。只需使用 Tailwind 提供的类名,省去了命名的麻烦。
  2. 设计一致性:使用 Tailwind,你的设计系统自然一致,避免了颜色和间距不统一的麻烦。
  3. 编辑器自动补全:Tailwind 的 IntelliSense 使得开发更加高效,输入类名时有智能提示。
  4. 响应式设计更简单:通过简单的类名就能实现响应式设计,比传统的媒体查询更简洁。

缺点:

  1. HTML 看起来乱七八糟:多个类名叠加在一起,让 HTML 看起来复杂且难以维护。
  2. 构建步骤繁琐:你需要一个构建工具链来处理 Tailwind,这对某些项目来说可能显得过于复杂。
  3. 调试困难:开发者工具中显示的类名多而杂,调试时很难快速找到问题所在。
  4. 不够可重用:Tailwind 的类名并不具备良好的可重用性,你可能会不断复制粘贴类,而不是通过自定义组件来实现复用。

纯 CSS 的优缺点

优点:

  1. 更干净的代码结构:HTML 和 CSS 分离,代码简洁易懂。
  2. 无构建步骤:只需简单的 <link> 标签引入样式表,轻松部署。
  3. 现代特性强大:2025 年的 CSS 已经非常强大,容器查询和 CSS 嵌套让你可以更加灵活地进行响应式设计。
  4. 自定义属性:通过 CSS 变量,你可以轻松实现全站的样式管理,改一个变量,所有样式立即生效。

缺点:

  1. 命名仍然困难:即使有 BEM 或 SMACSS 等方法,命名仍然是一项挑战。
  2. 保持一致性需要更多约束:没有像 Tailwind 那样的规则,纯 CSS 需要更多的自律来保持一致性。
  3. 生态碎片化:不同团队和开发者采用不同的方式来组织 CSS,缺少统一标准。
  4. 没有编辑器自动补全:不像 Tailwind,纯 CSS 需要手动编写所有的类名和样式。

到 2026 年,你该用哪个?

Tailwind 适合:

  • 使用 React、Vue 或 Svelte 等组件化框架的开发者
  • 需要快速开发并保证一致性的团队
  • 不介意添加构建步骤并依赖工具链的人

纯 CSS 适合:

  • 小型项目或静态页面
  • 喜欢简洁代码、分离 HTML 和 CSS 的开发者
  • 想要完全掌控样式并避免复杂构建步骤的人

两者结合:

  • 你可以在简单的页面中使用纯 CSS,在复杂的项目中使用 Tailwind 或两者结合,以此来平衡灵活性与效率。

真正值得关注的 2026 年趋势

  • 容器查询:响应式设计不再依赖视口尺寸,而是根据容器的尺寸进行调整。
  • CSS 嵌套原生支持:你可以直接在 CSS 中使用嵌套,避免了依赖预处理器。
  • Cascade Layers:这让你能更好地管理 CSS 优先级,避免使用 !important 来解决冲突。
  • View Transitions API:它让页面过渡更平滑,无需依赖 JavaScript。

这些新特性将极大改善我们的开发体验,无论是使用纯 CSS 还是借助 Tailwind。

结尾

不管是 Tailwind 还是纯 CSS,都有它们的优缺点。关键是要根据项目需求和个人偏好做出选择。 至于我:我喜欢纯 CSS,因为它更干净,HTML 更直观。但是如果项目需求更适合 Tailwind,那我也会使用它。 2026 年的开发趋势,将让我们有更多选择,让我们能够用最适合的工具解决问题,而不是纠结于某种工具是否“最好”。

Soul 发布超强端侧语音模型,没错,就是你想的那个 Soul 😍😍😍

作者 Moment
2025年11月3日 09:01

最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777

一、是什么?

SoulX‑Podcast 是由 Soul AI Lab 与 Northwestern Polytechnical University 等联合推出的语音合成模型,专门针对“播客/对话”这种多说话人、多轮、多情境的语音内容而设计。 核心目标包括:

  • 支持 多说话人、多轮对话 的语音合成,而不只是传统的一个人朗读。
  • 支持 长时段的生成(例如整期播客的长度,而非几句)。
  • 支持 多语言/方言(普通话、英语、粤语、四川话、河南话)以及 副语言特征(如笑声、叹气、喘息等)。
  • 支持 零样本语音克隆(zero‑shot voice cloning):只用很少目标说话人数据也能生成其风格。

简言之:如果你想自动“生成播客”、“做多人访谈音频”或“创建有地域口音或方言特征”的语音内容,这个模型就是为此类场景量身定制的。

20251103084659

二、主要亮点(为什么很“酷”)

以下是这个模型几个让人印象深刻的地方:

  • 对话场景优化:传统 TTS 模型多数优化的是单个说话人说一段话,SoulX‑Podcast 则把“说话人切换”“对话节奏”“多轮往返”放进模型设计里。

  • 长时间稳定输出:模型官方测试中能够输出超过 90  分钟的连续对话,并且说话人音色、音质维持稳定。

  • 方言 + 跨方言支持:除了标准普通话与英语,还支持四川话、河南话、粤语等方言,并且支持用一种语言的提示生成另一种方言语音(跨方言提示)。

  • 副语言控制:你可以在文本里加标签如“<|laughter|>”笑、“<|sigh|>”叹气,让生成语音听起来更“有人味”。模型在这方面识别准确率约  0.82。

  • 开源 + 学术支持:代码托管在 GitHub,模型放在 Hugging Face,可用于研究/教育用途。

20251103085301

这里官方提供了一些 Demo,可以去体验一下。

三、能用在哪些场景?

结合其特点,以下是一些很实际的应用场景:

  • 自动化播客/访谈:假如你想制作带多个角色、对话式的音频,可以用这个模型生成主持人+嘉宾对话。
  • 虚拟主播或角色配音:给虚拟角色配一个有感情、有方言、有特色的声音。
  • 方言语音产品:为特定地域群体提供方言语音服务,比如粤语播报、川话讲解。
  • 语音克隆与定制化声音:你可以用少量样本,让系统生成你喜欢的声音风格用于朗读、音频书、角色对白等。
  • 教育/语言学研究:对话语音、方言语音、非语言符号(如笑、喘)这些过去难以合成的内容,现在变得可用起来了。

四、简单上手指南

如果想快速体验模型,按照以下流程即可:

  1. 克隆仓库

    git clone https://github.com/Soul‑AILab/SoulX‑Podcast.git
    cd SoulX‑Podcast
    
  2. 安装环境(推荐用  Conda)

    conda create -n soulxpodcast -y python=3.11
    conda activate soulxpodcast
    pip install -r requirements.txt
    

    如果在国内,可使用 PyPI 镜像:

    pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com
    
  3. 下载预训练模型权重

    • 基础模型:SoulX‑Podcast‑1.7B
    • 方言模型:SoulX‑Podcast‑1.7B‑dialect
    pip install -U huggingface_hub
    huggingface-cli download --resume-download Soul‑AILab/SoulX‑Podcast‑1.7B --local-dir pretrained_models/SoulX‑Podcast‑1.7B
    huggingface-cli download --resume-download Soul‑AILab/SoulX‑Podcast‑1.7B‑dialect --local-dir pretrained_models/SoulX‑Podcast‑1.7B‑dialect
    

    或使用 Python 脚本:

    from huggingface_hub import snapshot_download
    snapshot_download("Soul‑AILab/SoulX‑Podcast‑1.7B", local_dir="pretrained_models/SoulX‑Podcast‑1.7B")
    snapshot_download("Soul‑AILab/SoulX‑Podcast‑1.7B‑dialect", local_dir="pretrained_models/SoulX‑Podcast‑1.7B‑dialect")
    
  4. 运行示例生成音频

    bash example/infer_dialogue.sh
    

    该脚本将生成一个对话音频,可用于快速体验模型效果。

  5. (可选)启动 Web UI 如果希望通过图形界面操作:

    python3 webui.py --model_path pretrained_models/SoulX‑Podcast‑1.7B
    

    若使用方言模型,则替换 --model_pathpretrained_models/SoulX‑Podcast‑1.7B‑dialect

  6. 脚本与输出检查建议

    • 准备你的对话文本:设定说话人标签、可插入笑声/叹气等副语言标记,比如 <|laughter|><|sigh|>
    • 若想用某个方言(如粤语、四川话等),使用方言模型,脚本中可加入方言提示。
    • 生成后务必听一听:说话人音色是否一致、说话切换是否自然、对话节奏是否流畅。如有不满意,可调整脚本或标签再试。

这样,你只需要按照 1️⃣ → 6️⃣ 步骤执行,就可以快速上手 SoulX‑Podcast 模型,生成你想要的播客/对话音频。

五、使用时需注意/挑战点

尽管这个模型功能强大,但也有一些你需要留意的地方:

  • 硬件要求:长篇生成 + 多说话人 +方言 +副语言标签,算力需求可能比较高。

  • 生成质量还是有边界:虽然效果优秀,但在极端方言、非常复杂对话情境下,误差可能比普通话更大。论文中方言生成在某些评测上误差还略高。

  • 伦理与合法性:模型支持“零样本语音克隆”,所以存在被滥用的风险(假冒声音、冒充等)。项目方已明确表示不得用于未经授权的语音克隆、诈骗、冒充。

  • 实时或交互式还有限:这个模型目前更适于“预先生成播客”“录制场景”,如果你要做实时语音对话或 live streaming,可能还需要额外的工程适配。

  • 方言提示需要设计:若要生成方言语音,提示文本里可能需要加入“方言典型句子”来帮助模型切换方言。论文中称为 “Dialect‑Guided Prompting (DGP)” 方法。

六、总结

如果你正在寻找一个可以生成“多角色对话”“整期播客”“带方言+带笑+带感情”的语音合成解决方案,SoulX‑Podcast 无疑是目前非常值得尝试的一个选择。只不过要用好它,还需要一定的准备(脚本、提示、算力、合法性意识)。

Electron 发布 39 版本 ,这更新速度也变态了吧❓︎❓︎❓︎

作者 Moment
2025年10月31日 09:11

最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777

Electron 39.0.0 于 2025 年 10 月 27 日发布。此版本带来了对 Chromium、Node.js 和 V8 引擎的更新,提升了性能和稳定性,同时也引入了一些新的功能和改进。以下是此版本的详细变化。

栈升级

  • Chromium: 更新到 142.0.7444.52,这意味着 Electron 在此版本中升级了其底层浏览器引擎。该版本修复了多个性能和安全漏洞,同时引入了一些新的 Web 标准。

  • Node.js: 更新到 22.20.0,这个版本包含了许多重要的 Node.js 修复和改进,包括性能优化和一些新的 API。

  • V8: 更新到 14.2,V8 引擎的升级提升了 JavaScript 执行的效率,使得应用程序的响应速度更快,内存占用更低。

破坏性更改

在这一版本中,有几个 API 和行为发生了变化,这可能会导致与以前版本的兼容性问题。

  • OffscreenSharedTexture:此 API 的签名进行了更新,新的版本提供了一个统一的 handle,用于持有原生句柄。这意味着开发者需要调整代码,以便正确使用这个新的接口。

  • window.open:该方法的行为得到修复,确保它创建的弹出窗口始终是可调整大小的。原本可能出现的不一致性问题已经被解决,确保符合标准规范。

新特性

  • Offscreen 渲染支持 RGBAF16:Electron 现在支持以 RGBAF16 格式输出图像数据。这意味着应用程序可以更好地支持高动态范围(HDR)图像,提供更高质量的图像渲染。

  • process.getSystemMemoryInfo() 增强:在 macOS 上,getSystemMemoryInfo 方法新增了 fileBackedpurgeable 字段,这让开发者能够获得更多关于系统内存的信息,包括哪些内存是文件映射的、哪些可以被清除以释放空间。

  • systemPreferences.getAccentColor:在 Linux 上,Electron 新增了一个方法 systemPreferences.getAccentColor,它返回操作系统的强调色。这对于需要与操作系统主题颜色匹配的应用程序很有用。

  • 托盘图标 guid 选项:在 macOS 上,Tray 构造函数现在支持一个新的 guid 选项。这个选项允许托盘图标在应用程序重新启动后保持相同的位置和状态,使得用户体验更加一致。

  • WebFrameMain API 增强:Electron 新增了 webFrameMain.fromFrameToken(processId, frameToken) 方法,开发者可以通过此方法从帧令牌获取 WebFrameMain 实例,这对于需要直接操作特定帧的应用程序非常有用。

  • 可访问性支持:Electron 引入了更细粒度的可访问性支持,包括为开发者提供更多的 API 来提高对残障人士的支持。这使得应用程序能更好地满足可访问性需求,提供更加友好的用户体验。

  • app.getRecentDocuments() 支持:在 Windows 和 macOS 上,Electron 现在支持 app.getRecentDocuments() 方法。通过这个方法,开发者可以获取到最近访问的文档列表,方便实现类似于“最近使用文件”的功能。

  • USB 设备 API 更新:Electron 新增了对 USBDevice.configurations 的支持。开发者现在可以获取到连接到设备的 USB 配置信息,这对于需要与 USB 设备交互的应用程序非常有用。

  • 文件系统 API 更新:在应用程序中持久化文件系统权限状态变得更加简单。Electron 允许在给定的会话内持久化文件系统授权状态,避免用户每次打开应用时重新授权。

  • 动态导入(ESM)支持:在非上下文隔离的预加载脚本中,Electron 现在支持动态导入 ECMAScript 模块(ESM)。这使得开发者能够在 Electron 中更灵活地使用 JavaScript 模块化。

修复

  • 系统配色问题修复:修复了 systemPreferences.getAccentColor 返回的颜色反转的问题,确保返回的颜色值符合预期。

  • 开发者工具:修复了在 Wayland 上调用 webContents.openDevTools({ mode: 'detach' }) 时可能导致的崩溃问题。Wayland 是 Linux 上的一种显示协议,这个修复对于在该平台上开发的 Electron 应用程序至关重要。

  • 会话管理问题:修复了访问 webContents.session 时可能导致崩溃的问题。这个修复增强了应用程序在多会话环境下的稳定性。

  • 窗口管理:修复了在调用 window.close() 后,执行某些操作可能导致崩溃的问题。这个修复提高了窗口管理的可靠性。

  • 命令行参数问题修复:修复了通过命令行参数传递特性参数时,可能导致的崩溃问题,确保应用程序能够稳定运行。

  • 文件对话框问题修复:修复了在 Windows 上调用 dialog.showOpenDialog 时,如果传入的扩展名过滤器数组为空,可能导致的崩溃问题。

其他更改

  • 资源定位:内部的资源定位机制发生了变化,现在 Electron 使用 DIR_ASSETS 来定位资产和资源。此外,app.getPath 方法现在支持返回一个新的 "assets" 键,用于获取应用程序资源路径。

  • 文档更新:官方文档得到了更新和补充,相关的 API 和功能进行了详细说明,开发者可以参考最新的文档了解更多实现细节。

总结

Electron 39.0.0 版本主要带来了对 Chromium、Node.js 和 V8 的升级,提升了性能和稳定性。此外,新增了对 Offscreen 渲染、系统主题色、USB 设备支持等的支持,也修复了多个与窗口管理、文件对话框等相关的 bug。对于开发者来说,这些更新和改进能够带来更高效、稳定的开发体验。

记录一次修改 PNPM 版本,部署 NextJs 服务时导致服务器崩溃的问题 😡😡😡

作者 Moment
2025年10月30日 08:17

最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777

最近在使用 NextJs 开发 DocFlow 协同编辑这个项目,运行了很久都没有问题,最近终于在部署方面出现了问题了,早上的时候发现 ssh 连不上了,一开始并没有太在意,但后来打开京东云控制台,好家伙真的好家伙:

20251029202122

真的小母牛坐飞机牛逼上天了!原来是 CPU 爆了,怪不得连接不上了,当然网站也 502 了:

20251029202311

那是什么原因呢,接下来就要开始慢慢排查了,既然 vscode 连不上,那我们就在官方提供的 ssh 来连接:

20251029202432

输入命令 htop,罪魁祸首马上出现:

20251029202653

原来是 next-server 和 node(运行在 /home/DocFlow-Server/dist/main.js)占用了基本全部的 CPU,那这个原因就很清楚了,那么接下来就可以排查了这个问题的原因了。

首先,可以明确的一点是,之前一直都是同样的方式部署的,没有出现问题,但是为什么今天就一直出现这个问题呢,那肯定是在运行的进程出现了问题。

我项目使用的是 pm2 部署的,那打个日志看看咯:

20251029203101

问题找到了,原来是 pm2 一直在重启,把内存全部占用了,具体是什么原因的已经不好查了,有可能是安装了一些依赖,但是我的 Github Action 是有一些问题没有处理的:

name: Deploy Next.js to JD Cloud

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4.2.0

      - name: Deploy to server via SSH
        uses: appleboy/ssh-action@v1.2.1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          port: ${{ secrets.SERVER_PORT }}
          password: ${{ secrets.SERVER_PASSWORD }}
          script: |
            # 加载环境配置
            source ~/.bashrc 2>/dev/null || true
            source ~/.profile 2>/dev/null || true

            # 加载 NVM 环境
            export NVM_DIR="$HOME/.nvm"
            [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

            # 设置 PATH
            export PATH="$HOME/.nvm/versions/node/*/bin:/usr/local/bin:/usr/bin:/bin:$HOME/.npm-global/bin:$PATH"

            # 设置工作目录
            PROJECT_DIR="/home/DocFlow"
            mkdir -p $PROJECT_DIR
            cd $PROJECT_DIR

            # 克隆或更新代码
            if [ ! -d ".git" ]; then
              echo "Cloning repository..."
              rm -rf ./*
              git clone git@github.com:xun082/DocFlow.git .
            else
              echo "Updating repository..."
              git fetch origin main
              git reset --hard origin/main
            fi

            # 创建 .npmrc 文件 (如果不存在)
            if [ ! -f ".npmrc" ]; then
              echo "Creating .npmrc file..."
              echo "@tiptap-pro:registry=https://registry.tiptap.dev/" > .npmrc
              echo "//registry.tiptap.dev/:_authToken=${{ secrets.TIPTAP_AUTH_TOKEN }}" >> .npmrc
            else
              echo ".npmrc file already exists, skipping creation."
            fi

            # 安装全局依赖 (只在需要时安装)
            command -v pnpm >/dev/null 2>&1 || npm install -g pnpm@9.4.0
            command -v pm2 >/dev/null 2>&1 || npm install -g pm2

            # 安装项目依赖
            pnpm install

            # 构建项目
            pnpm run build

            # 重启应用
            pm2 restart DocFlow 2>/dev/null || pm2 start "pnpm start" --name DocFlow

我这个部署脚本是基本没问题的,至少能跑,现在查看这两天的 commit,想起来有一个这样的操作:

20251029204359

那基本就可以确定是这个问题了。

其他部署方案

PM2 是一种流行的进程管理工具,适用于 Node.js 应用,尤其在小型项目和单机部署时很有效。然而,PM2 的部署流程存在一些局限性:

  1. 依赖管理和版本控制:PM2 需要手动确保服务器上安装正确的 Node.js 和 pnpm 版本。每次代码更新后,你还需要在服务器上重新 构建项目(执行 pnpm run build),这可能会导致生产环境与开发环境之间的不一致,并增加了维护工作。

  2. 服务中断问题:每次代码更新时,PM2 需要 重启应用 来使新版本生效。如果没有正确配置或管理,应用在重启过程中可能会停机一段时间,这对于高可用性要求较高的生产环境来说是个问题。

与 PM2 相比,Docker 自动化部署 通过容器化解决了这些问题,提供了更简洁、更一致的部署流程,特别适用于云端和多环境部署。主要优势包括:

  1. 无需依赖管理和环境配置:Docker 将应用及其所有依赖、环境变量、构建过程封装在 Docker 镜像 中。这样,服务器上无需安装特定版本的 Node.js 或 pnpm,避免了手动配置和版本不一致的问题。你只需要在 构建镜像时 配置好所有的依赖和环境,部署时无需再进行任何安装或构建操作。

  2. 代码问题不会影响运行:如果代码有问题,Docker 使得应用 不会受到当前部署的影响。因为 Docker 镜像已经包含了所有必要的依赖,部署失败时,原先的镜像仍然可以继续运行,确保生产环境不受影响。而在 PM2 部署 时,更新代码后需要重启应用,如果代码有问题,应用会直接停机,导致服务中断。

  3. 无缝的 CI/CD 集成:Docker 完美集成 CI/CD 流程。例如,使用 GitHub Actions,你可以自动构建 Docker 镜像并推送到仓库。服务器只需要 拉取最新镜像,并启动新的容器。如果部署失败,不会影响当前运行的容器,你可以迅速恢复服务,且无需手动修复。相反,PM2 需要重新启动应用,且服务停机时需要手动修复代码。

  4. 快速回滚:由于 Docker 镜像的版本化机制,你可以轻松回滚到之前的稳定版本。即便遇到部署失败的情况,只需要拉取旧版本的镜像,应用立即恢复,不需要手动操作,这为应用提供了更高的可靠性。

总结

PM2 部署中,如果代码有问题,必须手动修复并重启应用,这可能会导致 服务中断,并影响用户体验。而且每次更新都需要在服务器上重新构建项目,这可能导致环境不一致,增加了部署的复杂性。

Docker 部署则通过 容器化 确保应用和环境的一致性,部署失败时不会影响生产环境的运行,原有容器仍可继续工作,且通过 CI/CD 流程可以自动恢复和快速回滚,不需要人工干预。即使代码存在问题,原有的容器仍可保持正常服务,确保应用的高可用性。

因此,对于生产环境,Docker 提供了比 PM2 更稳定、高效、自动化的部署方式,尤其适合大规模、高可用的应用部署。

❌
❌