看现在到处都在招 Agent 工程师,好多简历内容都是赋能提效。
在前公司也做过很多 AI 应用的 demo 了,比如 text2sql 的智能问数,比如 RAG 以及智能客服之类的。

实际做下来的时候 发现好像 AI 应用最好的产品 还是创意生成类的,比如 cursor 这类的 ide,lovable 、墨刀这样生成原型的 ,这类的通用点就是 允许幻觉存在,或者说幻觉的生成是可以接受的。

企业落地的时候 ,幻觉对于很多领导或者项目来说 ,又是不能接受的东西了。最后做着做着,AI 应用只能在一些无关痛痒的地方落地,有时候锦上添花可能都称不上。

本来想发在 linuxdo 。发不了。因为始皇(此论坛的站长,自称秦始皇)容不下反对声音。质疑几句,轻则禁言,重则封号。那我就只能发到 V2EX ,若污染了各位的 timeline ,我也在此道歉。我就随便谈谈我的想法。

我算是 L 站的活跃用户。之前花了不少时间发帖、回帖、写内容。结果在最近这一轮所谓“为社区好”的政策下,我直接喜提永久封禁。现在回头看,自己的时间、内容和热情,最后都成了始皇拿去招商、圈钱的资本。很多人都知道,L 站起家于薅羊毛,就是个薅羊毛的蝗虫论坛。今年各大厂商开始收紧政策,羊毛越来越少,不少公益站也陆续倒了。始皇估计是眼看这条路不好走了,就开始喊“学 AI ,上 L 站”。交易区之类的地方,也开始对二手羊毛严加管控。那架势,就像突然想洗白了。

问题是,壳可以换,路数没怎么变。之前那个“富可敌国”,本质上就是商家交钱,给始皇买推广资格和头衔。很多人自然会觉得,这是站方在背书。结果商家跑路了,站方一边说自己不承担责任,一边又不愿意正面回应质疑。后来有人去问,有人去追,有人去质疑,最后的结果不过是先被始皇的红卫兵围攻,再被处理,严重一点就是永久封禁。

我觉得最近最恶心的一条政策,就是强制要求“20 字回复”。字数不够不行。凑字数也不行。至于什么算凑字数,什么算有效回复,全看管理员主观判断。规则写得像制度,执行起来还是人治。看你顺眼就过,看你不顺眼就拿规则砸你。更讽刺的是,始皇现在还开始给自己立人设了。自己靠招商赚了多少钱,闭口不提。说得最多的,是自己多努力、多辛苦、多加班。那套话术翻来覆去,核心就一个意思:你们都得感激我。说实话,这种味道越来越重,真把自己当秦始皇了。

不过话说回来,现在各大厂都在收紧羊毛,L 站本身也越来越空心了。羊毛贴少了,技术贴也少了,剩下的大多是灌水、发病和没什么营养的重复讨论。一个社区到了这个阶段,还要靠压制反对声音来维持秩序,那也不值得待了。

很难在外网找到还有一个类似于国内那样的,鼓励互相举报、敢质疑就禁言封禁,然后站点管理者干嘛都得感激感恩,说不好就得被红卫兵围攻,看来大部分人即使翻了墙,心里的辫子也是没剪掉。要是能说一个在墙外把墙内玩的那套玩的最明白的,或许就是 L 站了吧。当然,我也祝 neo 早日称帝,成为他梦想中的秦始皇。

379c4cc423b34b86c24478b07fabfc3e.png

广州是华南地区人工智能产业的重镇,海珠、天河、黄埔、南沙等区均出台了针对生成式AI备案的奖励政策。最高100万元的一次性补贴,对AI企业而言是不小的激励。但补贴申领与备案周期紧密挂钩,想尽快拿到备案号,前期充足的准备必不可少。

生成式人工智能 #大模型备案 #算法备案 #网络安全 #AI产品安全应用

一、广州各区AI备案补贴政策概览

截至目前,广州市已形成覆盖主要城区的AI备案奖励政策体系:

<colgroup><col width="auto"><col width="auto"><col width="auto"></colgroup>
<span id="w-e-text-136"><span><span>区域</span></span></span><span id="w-e-text-138"><span><span>补贴标准</span></span></span><span id="w-e-text-140"><span><span>申领条件</span></span></span>
<span id="w-e-text-143"><span><span>海珠区</span></span></span><span id="w-e-text-145"><span><span>最高100万元</span></span></span><span id="w-e-text-147"><span><span>首次完成国家级备案</span></span></span>
<span id="w-e-text-150"><span><span>天河区</span></span></span><span id="w-e-text-152"><span><span>最高100万元/年</span></span></span><span id="w-e-text-154"><span><span>研发投入的10%,需完成国家级备案</span></span></span>
<span id="w-e-text-157"><span><span>黄埔区</span></span></span><span id="w-e-text-159"><span><span>最高100万元</span></span></span><span id="w-e-text-161"><span><span>需具备示范性应用</span></span></span>
<span id="w-e-text-164"><span><span>南沙区</span></span></span><span id="w-e-text-166"><span><span>最高100万元</span></span></span><span id="w-e-text-168"><span><span>通用或垂直领域大模型</span></span></span>
补贴力度不可谓不大。但政策通常要求企业先完成国家级备案,才能申领补贴。这意味着备案周期直接决定了企业能否搭上政策红利的便车。 ## 二、备案流程与补贴申领的时间节点 要准确把握时间线,首先需要了解备案的完整流程。广东省的备案流程分为四个阶段: 综合来看,从启动到拿号,备案周期通常在4至8个月,复杂情况可能延长至9个月甚至更长。企业需据此倒推时间节点,确保在政策窗口期内完成关键里程碑。 ## 三、备案周期与补贴申领周期对比 各区的补贴申领通常有明确的申报时间窗口,常见形式为每年集中申报一次。这意味着: 时间容错窗口约2~3个月 若备案周期为6个月,而补贴申报为年度集中受理,企业需在上一年度第四季度前完成备案,方能赶上次年补贴申报 举例而言,若某企业在2026年6月才完成备案,而该区补贴申报截止时间为2026年5月,则该企业需等到下一年度才能申领,白白错过一年的政策红利。 时间规划建议 建议企业在年初即启动备案筹备工作,争取在当年第三季度前完成中央网信办复审,以便搭上年底或次年初的补贴申报窗口。具体时间安排需结合各区政策细则动态调整。 ## 四、各区政策差异与申领条件 广州各区的AI备案补贴政策存在明显差异,企业需针对性了解: 海珠区:侧重于通用大模型应用,补贴额度最高,需提供示范性应用案例 天河区:按研发投入比例补贴,适合持续研发的AI企业,需提供研发费用专项审计报告 黄埔区:强调示范性应用,需证明模型已在实际场景中产生效果 南沙区:支持通用与垂直领域两类大模型,门槛相对灵活 此外,部分区的补贴政策可能存在附加条件,如需服务本地企业、需在当地纳税、需提供就业岗位等。建议企业在申领前详细了解本区政策的完整条款。 ## 五、时间窗口与行动建议 综合以上分析,我们建议广州AI企业按以下时间规划推进备案与补贴申领工作: Q1启动:向属地网信办了解备案要求,完成备案意向登记 Q2冲刺:完成备案材料准备,提交省级网信办初审 Q3跟进:配合中央网信办复审,争取早日拿号 Q4申报:完成备案后,立即启动补贴申领材料准备 备案与补贴申领是一场与时间的赛跑。2026年的政策窗口期有限,早启动、早备案、早受益。

你是不是也觉得,做一部AI漫剧需要专业团队、昂贵软件、至少几个月?
直到我亲手用一台普通Windows电脑,从零开始——安装技能插件、配置密钥、上传剧本,到分镜图全部自动生成,整个过程不到30分钟。这不是概念演示,不是PPT画饼。这是一部完整的"有道AI"品牌广告漫剧,20个分镜,角色自动提取,画面自动生成,旁白自动配音。
今天,我把整个过程掰开揉碎讲给你听。
__
01 从一个技能插件说起
做AI漫剧,你首先需要一个"大脑"——一个能理解你意图、替你调用各种AI能力的Agent。
我用的工具叫QClaw,一个腾讯的OPENCLAW的AI Agent平台。它像手机一样,本身什么都能做,但真正让它变强的,是"技能插件"。
我选用的漫剧工具是橙星梦工厂,可以一键安装技能:如下图1:

安装Funai-skill的过程,简单到像装一个App,如下图2:

没有手动配置环境变量,没有pip依赖地狱,没有版本冲突。30秒后,skills/Funai-skill文件夹就静静地躺在工作区里,里面有SKILL.md使用手册、配置模板、API接口文档。
但有一个东西不能自动获取——Token。,获取KEY,如下截图3:

02 那个藏在链接里的密钥
KEY是你在AI漫剧平台的身份凭证。没有它,所有API都是摆设。
获取方式出乎意料地简单:打开 https://ai.fun.tv/#/openclaw ,登录账号,复制KEY到Qclaw.

03 20个分镜的诞生
剧本可以用AI大模型制作,一份20镜头的"有道AI"品牌广告脚本,20个镜头改写成流畅的小说段落:
清晨,未来城市的玻璃幕墙上反射着金色的阳光。在这座被科技包裹的城市里,一位二十八岁的年轻女性从智能床上醒来。床单在她起身后自动抚平,仿佛有一双无形的手在打理着一切。
"又是被AI叫醒的一天。"她心想,走向智能镜面。镜面上立刻浮现出蓝色的界面:早安,今天有三个任务等待处理。
提交剧本后,AI自动拆分和优化分镜,因为它认为某些镜头需要更细致的画面过渡。提交剧本截图

这种"AI帮你补镜头"的能力,是手动分镜做不到的。

04 角色提取:AI比你更懂你写的人
剧本提交后,最让我惊讶的是角色提取。我没有手动创建角色、上传参考图、调整面部参数。AI从剧本中自动识别出主角:一位28岁的年轻女性,设计师,黑色长直发,穿深色休闲连衣裙搭配米色小西装。
30秒后,角色图就生成了。
不是那种"大概差不多"的AI人像——是风格统一、细节到位、可以直接用于后续分镜的正式角色图。
更关键的是,角色自带音色配置:推荐音色编号10048,语速1.2倍。这意味着后续每个分镜的旁白,都会用同一个声音、同一种语速朗读。
一致性,才是AI内容创作最大的难题。不是生成一张好图,而是生成100张风格统一的图。
如下截图

05 分镜图:20张画面的魔法时刻
角色确认后,进入最耗时的步骤——分镜图生成。
20个分镜,每个分镜都需要:

  1. 解析旁白文本,提取画面要素
  2. 匹配角色外观和风格设定
  3. 生成符合16:9画面比例的图像
  4. 保持与前后分镜的视觉连贯性
    我选择的是"现代写实"风格,即梦3.0模型。这个组合生成的画面质感接近电影剧照,不像卡通,也不像照片,恰好处于那个"比现实更美"的甜区。
    等待的过程比想象中快。大约2分钟后,全部20张分镜图生成完毕。每一张都能清晰看到:
    • 场景1:未来都市晨光中的卧室
    • 场景7:树叶轮廓变幻为3D建筑模型
    • 场景14:三屏并置的工作成果展示
    • 场景20:品牌slogan落版——"有道AI•创造无限"
    从文字到画面,AI理解的不是像素,是意图。
    完成制作后导出,如下截图

06 写在最后
从安装技能到20张分镜图全部生成,实际操作时间大约30分钟。但这是第一次。第二次做,15分钟就够了。AI漫剧的门槛已经低到这个程度:一台电脑,一个浏览器,一份剧本。不需要绘画基础,不需要视频剪辑技能,不需要配音设备。你只需要知道:你想讲一个什么故事。剩下的,AI会替你完成。
__
你试过用AI做漫剧吗?最想用AI做什么类型的内容?欢迎在评论区聊聊。
本文为个人体验分享,仅供参考。

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

数据工程师的角色正在经历一场深刻的变革。如今,数据工程师的工作早已超越单纯编写脚本、将数据从 A 点迁移至 B 点的范畴,而是逐步演变为“全栈构建者”。在应对海量数据的同时,他们还需兼顾复杂的 DevOps 工作流与语义建模。随着技能体系的演进,编程范式也明显向声明式编程转变。工程师不再需要耗费大量时间管理脆弱且逐步执行的命令式指令,而是转而定义数据的期望最终状态,由底层平台自主决定如何实现这一目标。

从动态表到语义视图,再到 Cortex 代码,Snowflake 正将传统数据工程工作流中耗时数天的任务缩短至数分钟。其目标已不再是“少花钱多办事”,而是借助工具实现“多快好省”。依托下一代工具,数据工程师无需操心基础设施的配置、异构工具的管理或繁重的手工编码,只需集中精力定义指标与业务需求,为 AI 智能体提供上下文,便能在其湖仓一体数据基础上交付 AI 解决方案。

以下六大特性,将助您的数据工程生产力迈上新台阶。

在 Snowflake 上加速构建:Cortex Code

数据工程师只需在 Cortex Code 中使用简单的提示词,即可构建生产级数据管道。Cortex Code 使各类数据工程师和分析师都能轻松上手 Snowflake 开发。即便是经验最丰富的数据工程师,也能借助它显著提升工作效率——降低任务复杂度,减少构建耗时。数据工程师可以从零开始创建管道,或将现有代码迁移至 Snowflake;提升可观测性、故障排查及调试能力;并将 AI 作为提高生产力的放大器,助力交付端到端的数据管道。

自主式管道:动态表

多年来,管理增量处理一直是一项依赖复杂逻辑和调度机制的重度手工劳动。动态表使数据工程师、平台团队乃至分析师只需提供 SQL 查询语句,Snowflake 即可自动完成增量更新与编排工作。

借助 Snowflake 动态表提升业务部门的数据交付效率

Travelpass 利用动态表摆脱了复杂的手动编码。通过采用声明式方法,该公司简化了数据管道,显著减少了维持实时数据流所需的工程工时——效率提升了 350%。

规模化开发:Snowflake 上的 dbt 项目

dbt 长期以来一直是数据转换领域的行业标准。如今,企业可以在 Snowflake 上原生运行其开源版本。通过直接在 Snowflake 基础设施上运行 dbt 项目,您可以减少管理 dbt 基础设施与编排工作所带来的额外成本。

Snowflake 上的 dbt 项目提供了一体化的开发体验,使版本控制、测试和文档与数据位于同一平台之上。它赋能团队像对待软件代码一样管理数据转换,从而实现从开发到生产环境的平滑、安全且高度可扩展的过渡。

Snowflake 上的 dbt 项目将改变数据工程实践

数据超级英雄 Keith Belanger 与 Jan Láznička 在本期《Behind the Cape》中阐述了 dbt 项目将如何改变您的数据工程实践。

简化编排:Snowflake 任务

任务允许您安排任意 SQL 语句或存储过程按特定时间间隔运行,或响应特定事件触发执行。

通过利用有向无环图(DAG)结构,任务使工程师能够直接在 Snowflake 内部构建复杂的多步骤工作流。这在许多场景下免除了对昂贵第三方编排工具的依赖,使逻辑紧贴数据存放处,并显著降低了架构复杂性。

在 Snowflake 上原生编排数据管道

Snowflake 现场首席技术官 Jeremiah Hansen 阐述了 Snowflake 任务如何与 dbt 项目中的转换配合,以原生方式处理整个数据管道,无需管理外部编排器。

提升数据质量:数据指标函数

缺乏信任的自动化毫无意义,这正是数据指标函数(DMFs)的用武之地。过去,数据质量往往是事后才考虑的问题——通常是一堆匆忙编写的“健全性检查”脚本。DMFs 提供了一种声明式方法来自动衡量数据健康状况(例如数据新鲜度、唯一性或空值计数)。

现在,您无需再为每张表编写自定义验证脚本,而是可以将质量指标定义为表元数据的一部分。这些内建的、用户可启用的可观测性能力意味着:一旦数据未达到您的业务标准,系统便能立即标记问题,从而帮助您在数据异常影响下游应用和用户之前及时捕获。

使用 DMFs 评估数据质量

Augusto Rosa 讲解了如何利用 DMFs 监控数据的状态与完整性,并衡量诸如数据新鲜度、重复值或空值计数等关键指标。

业务逻辑:语义视图

最后,语义视图的兴起正在弥合工程团队与管理层之间长期存在的“定义鸿沟”。过去,业务逻辑分散于各类 BI 工具中,导致对于同一问题(例如“我们的客户流失率是多少?”)常常得出截然不同的答案。

通过将这些逻辑迁移至语义层——特别是通过语义视图——数据工程师能够一次性固化业务定义。无论用户查看的是仪表板、电子表格,还是 AI 驱动的聊天界面,他们都将从同一事实来源中获取数据。这使得数据仓库从单纯的表集合,转变为面向业务的就绪型知识库。

几分钟内完成 AI 驱动的语义建模

了解 eSentire、HiBob、Simon AI 及 VTS 等组织如何利用语义视图的 Autopilot 功能,确保 AI 智能体基于同一套可信的业务指标运行,同时将语义模型的创建时间从数天缩短至数分钟。

这些特性带来的影响,已不仅是团队效率的渐进式提升;它们标志着数据战略正朝着更自动化、更可靠、更贴合业务的方向发生根本性转变。

如需进一步了解 Snowflake 上的数据工程,请下载《数据工程新必备指南》,并注册参加将于 4 月 22 日举办的线上活动“Snowflake Connect:为 AI 就绪型数据构建转换流水线”。同时,您也可以随时点播观看近期的线上动手实验:面向 AI 智能体的自主 SQL 流水线

原文地址:https://www.snowflake.com/en/blog/next-gen-data-engineering-snowflake-features/

 

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

Hjz3jh35bOl7qsATOrS6rXYYXjZV8YGt.webp


这是 4 月 10 日给狐蒂云的负责人发的消息,只是我随便提出的一个需求。因为,我发现狐蒂云发布的通知中基本都是以主机区为通知的,但是购买主机后,如果自己不备注后面就不知道自己买的是哪个区的主机了。

这会影响到后续大家看通知的问题,因为有可能就不知道这个通知是不是在说自己的主机。我以为就没有后续了,今天在用户群内看到有一个新功能上线了。


0GusajSXCPcRcdcLBmavIbEHYQtNU3RY.webp


作为一个使用近一年的用户,我现在还不好说有多么靠谱,但整体看来,他们还是对用户的需要比较看重的。


S8YeB5xDQCSJHgumUPpvT0hQWEmXUwNp.webp


上面这是我自己在用的主机。。

我没有推荐过任何主机商,自己用了一段时间,现在觉得狐蒂云起码可以做到有意识的为用户着想,所以今天我就推荐一下!

如果你想要一个配置还可以,预算有限的情况下,狐蒂云算是一个选择。
这是我的推荐 AFF,可以不用的。
https://www.szhdy.com/aff/CZAFZGVK

备注:如果你的项目非常重要,我的建议还是用大厂的产品,这毫无疑问!

必知必会:大模型训练通信开销计算详解与面试指南

AI-Compass 致力于构建最全面、最实用、最前沿的AI技术学习和实践生态,通过六大核心模块的系统化组织,为不同层次的学习者和开发者提供完整学习路径。

为什么通信开销很重要?

通信开销如同隐匿的丝线,牵动着大模型训练的每一个环节。在分布式训练中:

  • 显存墙:单个计算设备无法容纳大模型训练时所需的显存
  • 通信墙:系统内不同计算设备需要进行密集的数据传输以满足信息同步等要求

符号约定

符号含义
Φ模型参数量(FP16精度下占用2Φ字节)
b批次大小(batch size)
s序列长度(sequence length)
h隐藏层维度(hidden dimension)
NGPU数量

1. 集合通信原语详解

1.1 核心问题

  • 分布式训练中 GPU 之间有哪些基础通信方式?
  • 各通信原语之间有什么组合关系?
  • 不同并行策略分别依赖哪些通信原语?

1.2 原文核心要点

在分布式训练过程中,不同的 GPU 之间可以通过集合通信原语来传递模型参数、梯度等信息。核心原语包括 Broadcast、Scatter、Gather、Reduce、All-Gather、Reduce-Scatter、All-Reduce 和 All-to-All 八种,它们是所有并行策略的通信基石。

1.3 通俗理解

直观类比

想象你在一个快递分拣中心工作,有 4 个分拣员(GPU),每人负责一个区域的包裹:

  • Broadcast(广播):总部发了一张新的分拣规则表,复印 4 份发给每个分拣员——每人拿到的内容完全一样
  • Scatter(分发):一大批包裹到了,总部按目的地拆成 4 堆,每个分拣员只拿到自己负责区域的那堆——每人拿到的内容不同
  • Gather(收集):每个分拣员把自己分好的包裹送回总部汇总——多方汇聚到一处
  • Reduce(规约):4 个分拣员各自统计了自己区域的包裹数量,总部把 4 个数字加起来得到总数——汇总时带了"求和"计算
  • All-Gather(全收集):每个分拣员把自己的分拣清单分享给所有同事——每人最终都有完整的全局清单
  • All-Reduce(全规约):4 个分拣员各自统计包裹数,互相通信后每人都知道总数——边计算边共享
  • All-to-All(全交换):每个分拣员有 4 类包裹,分别发给对应的同事——每人既发又收,完全交换
核心要点
  • 原语是所有并行策略的通信积木,掌握原语就掌握了通信开销计算的基础
  • All-Reduce 是最核心的原语,它可以分解为 Reduce-Scatter + All-Gather
  • 不同原语对应不同的并行场景:数据并行用 All-Reduce,ZeRO 用 Reduce-Scatter + All-Gather,专家并行用 All-to-All
建立了直觉之后,下面我们用具体的示意图和对比表来严格定义这些通信原语。

1.4 八大通信原语

1. Broadcast(广播)

一个发送者,多个接收者

将一个GPU的完整数据复制到所有其他GPU上。

操作前:                    操作后:
┌───┬───┬───┬───┐         ┌───┬───┬───┬───┐
│ A │   │   │   │   →     │ A │ A │ A │ A │
└───┴───┴───┴───┘         └───┴───┴───┴───┘
GPU0 GPU1 GPU2 GPU3       GPU0 GPU1 GPU2 GPU3

应用场景:模型初始化时,将主节点的参数广播到所有工作节点。


2. Scatter(分发)

一个发送者,多个接收者(数据分片)

将一个GPU的数据切片后分发给不同GPU,每个GPU获得不同的部分。

操作前:                    操作后:
┌─────────────┐            ┌───┬───┬───┬───┐
│ A │ B │ C │ D │    →     │ A │ B │ C │ D │
└─────────────┘            └───┴───┴───┴───┘
    GPU0                   GPU0 GPU1 GPU2 GPU3

与Broadcast的区别

  • Broadcast:每个GPU收到完整相同的数据
  • Scatter:每个GPU收到不同的数据片段

3. Gather(收集)

多个发送者,一个接收者

将多个GPU的数据收集到一个GPU上。

操作前:                    操作后:
┌───┬───┬───┬───┐         ┌─────────────┐
│ A │ B │ C │ D │    →    │ A │ B │ C │ D │  (其他GPU为空)
└───┴───┴───┴───┘         └─────────────┘
GPU0 GPU1 GPU2 GPU3            GPU0

4. Reduce(规约)

多个发送者,一个接收者(带计算)

将多个GPU的数据规约运算(如求和SUM、求最大值MAX、求乘积PROD)后发送到一个GPU。

操作前:                    操作后:
┌───┬───┬───┬───┐         ┌───────────────┐
│ A │ B │ C │ D │    →    │ A+B+C+D       │  (其他GPU为空)
└───┴───┴───┴───┘         └───────────────┘
GPU0 GPU1 GPU2 GPU3            GPU0

5. All-Gather(全收集)

多个发送者,多个接收者

= Gather + Broadcast

每个GPU都收集到所有GPU的数据。

操作前:                    操作后:
┌───┬───┬───┬───┐         ┌─────────────┬─────────────┬─────────────┬─────────────┐
│ A │ B │ C │ D │    →    │ A,B,C,D     │ A,B,C,D     │ A,B,C,D     │ A,B,C,D     │
└───┴───┴───┴───┘         └─────────────┴─────────────┴─────────────┴─────────────┘
GPU0 GPU1 GPU2 GPU3       GPU0          GPU1          GPU2          GPU3

6. Reduce-Scatter(规约分发)

多个发送者,多个接收者

在所有GPU上按维度执行相同的Reduce操作,再将结果发散到集群内所有GPU上。

操作前:                              操作后:
GPU0: [A0,A1,A2,A3]                 GPU0: [A0+B0+C0+D0]
GPU1: [B0,B1,B2,B3]       →         GPU1: [A1+B1+C1+D1]
GPU2: [C0,C1,C2,C3]                 GPU2: [A2+B2+C2+D2]
GPU3: [D0,D1,D2,D3]                 GPU3: [A3+B3+C3+D3]

7. All-Reduce(全规约)

多个发送者,多个接收者

= Reduce + Broadcast = Reduce-Scatter + All-Gather

在集群内所有GPU上都执行相同的Reduce操作,并将结果发送到所有GPU上。

操作前:                    操作后:
┌───┬───┬───┬───┐         ┌─────────┬─────────┬─────────┬─────────┐
│ A │ B │ C │ D │    →    │ A+B+C+D │ A+B+C+D │ A+B+C+D │ A+B+C+D │
└───┴───┴───┴───┘         └─────────┴─────────┴─────────┴─────────┘
GPU0 GPU1 GPU2 GPU3       GPU0      GPU1      GPU2      GPU3

8. All-to-All(全交换)

多个发送者,多个接收者(完全交换)

每个GPU将自己的数据发散到所有GPU,同时收集所有GPU的数据。

操作前:                              操作后:
GPU0: [A0,A1,A2,A3]                 GPU0: [A0,B0,C0,D0]
GPU1: [B0,B1,B2,B3]       →         GPU1: [A1,B1,C1,D1]
GPU2: [C0,C1,C2,C3]                 GPU2: [A2,B2,C2,D2]
GPU3: [D0,D1,D2,D3]                 GPU3: [A3,B3,C3,D3]

与All-Gather的区别

  • All-Gather:不同GPU向某一GPU收集到的数据相同
  • All-to-All:不同GPU向某一GPU收集到的数据不同

1.5 通信原语对比表

<!-- trick-image:start idx=1 platform=blog -->
八大集合通信原语快递分拣类比示意图
<!-- trick-image:end idx=1 -->

原语发送方接收方是否计算典型应用
Broadcast1N参数初始化
Scatter1N数据分片
GatherN1结果收集
ReduceN1是(规约)梯度聚合到主节点
All-GatherNNZeRO-3参数恢复
Reduce-ScatterNN是(规约)ZeRO梯度聚合
All-ReduceNN是(规约)数据并行梯度同步
All-to-AllNN专家并行

组合关系

  • All-Reduce = Reduce + Broadcast = Reduce-Scatter + All-Gather
  • All-Gather = Gather + Broadcast

1.6 小结

维度说明
原语总数8 种基础通信原语
核心原语All-Reduce = Reduce-Scatter + All-Gather
通信模式1→N、N→1、N→N 三种基本模式
选型依据是否需要计算(规约)、是否需要全局同步
关键应用DP 用 All-Reduce,ZeRO 用 RS+AG,EP 用 All-to-All

2. 数据并行的通信开销计算

2.1 核心问题

  • 数据并行的工作原理是什么?
  • 朴素 All-Reduce 和 Ring-All-Reduce 的通信量分别是多少?
  • Ring-All-Reduce 为什么更高效?

2.2 原文核心要点

数据并行策略中,每个 GPU 保存完整模型副本,训练数据被拆分。各 GPU 计算梯度后通过 All-Reduce 同步。Ring-All-Reduce 通过环形拓扑将通信量均匀分配到每个 GPU,避免单点瓶颈。单卡通信量为 2×(N-1)×Φ/N ≈ 2Φ。

2.3 通俗理解

直观类比

想象你在一个班级里做小组作业

朴素方式(班长汇总法):30 个同学各做一份答案,全部交给班长。班长一个人汇总 29 份答案,算出最终版本,再抄 29 份发回去。班长累得半死,其他人在旁边等着——这就是单点瓶颈

Ring 方式(传话游戏法):30 个同学围成一个圈,每人把答案分成 30 段。第一轮,每人把第 1 段传给右边的同学并加上自己的;第二轮继续传递第 2 段……经过 29 轮传递,每人手上都有了完整汇总结果。虽然传了很多轮,但每轮每人只处理一小段,没有人特别累

核心要点
  • 朴素 All-Reduce 的总通信量和 Ring-All-Reduce 相近(~2NΦ),但负载分布天差地别
  • Ring-All-Reduce 分为 Reduce-Scatter 和 All-Gather 两阶段,各需 N-1 次通信
  • 实际工程中几乎都用 Ring-All-Reduce,因为它消除了单点瓶颈
建立了直觉之后,下面我们用数学公式来严格定义通信量的计算方法,并给出具体的数值示例。

2.4 技术原理与公式推导

掌握了直观类比后,接下来我们用精确的数学语言来刻画通信量的计算方法。这样在实际工作中就能准确预估不同配置下的通信开销。

工作原理

在数据并行策略中:

  1. 每一个GPU上都保存一份完整的模型副本
  2. 训练数据被拆分成多份,分配给不同的GPU
  3. 每个GPU对所接收的数据进行前向传播和反向传播,计算出一份梯度
  4. 这份梯度被传送给负责梯度收集的GPU做聚合操作(一般是梯度累加)
  5. 聚合完成后,负责梯度计算的GPU会从负责梯度收集的GPU处拉取完整的梯度结果并更新模型参数
两种 All-Reduce 实现方式
方式一:朴素 All-Reduce

假设有N个GPU参与数据并行,其中:

  • 1个GPU负责梯度收集
  • N-1个GPU负责梯度计算

通信开销公式

$$
C_{\text{naive}} = 2 \times N \times \Phi
$$

符号含义示例值
$C_{\text{naive}}$朴素 All-Reduce 系统总通信量112 GB
$N$GPU 数量4
$\Phi$模型参数量(FP16 下占 2Φ 字节)7B

通信过程

  1. 负责梯度收集的GPU承载的通信量:(N-1) × Φ
  2. 每个负责梯度计算的GPU的通信量:Φ
  3. 所有负责梯度计算的GPU通信总量:(N-1) × Φ

问题:当N增大时,整个系统的总通信量约为 2 × N × Φ,且收集梯度的GPU成为瓶颈。

数值计算示例

假设用 4 张 GPU 训练 LLaMA-7B(Φ = 7B):

$$
C_{\text{naive}} = 2 \times 4 \times 7\text{B} \times 2\text{B/param} = 112 \text{ GB}
$$

指标朴素 All-Reduce
GPU0(收集节点)通信量(4-1) × 7B × 2B = 42 GB
GPU1/2/3 各自通信量7B × 2B = 14 GB
系统总通信量112 GB
瓶颈GPU0 承载 42 GB,其他仅 14 GB

方式二:Ring-All-Reduce(环形全规约)

核心思想:将数据切分成N份,通过环形拓扑结构进行传输,避免单点瓶颈。

两个阶段

阶段1:Reduce-Scatter

  • 将数据切分成N份
  • 进行N-1次通信
  • 每次通信量:Φ/N
  • 最终每个GPU拥有一个大小为Φ/N的完整梯度片段

阶段2:All-Gather

  • 进行N-1次通信
  • 每个GPU将自身拥有的大小为Φ/N的完整梯度发送给相邻的GPU
  • 最终所有GPU都拥有完整梯度

可视化流程

为了更直观理解Ring-All-Reduce的工作原理,下图展示了4个GPU环形拓扑下的两阶段数据流动:

flowchart TD
    subgraph RS["阶段1: Reduce-Scatter (3轮)"]
        direction LR
        RS1["轮1: GPU0→GPU1→GPU2→GPU3→GPU0<br/>各GPU聚合1/4数据"]
        RS2["轮2: 继续环形传递并聚合<br/>每GPU完成2/4数据片段"]
        RS3["轮3: 最后一轮传递<br/>每GPU持有完整的1/4梯度"]
        RS1 --> RS2 --> RS3
    end

    subgraph AG["阶段2: All-Gather (3轮)"]
        direction LR
        AG1["轮1: GPU0→GPU1→GPU2→GPU3→GPU0<br/>各GPU收集1/4完整梯度"]
        AG2["轮2: 继续环形传递<br/>每GPU拥有2/4完整梯度"]
        AG3["轮3: 最后一轮传递<br/>所有GPU拥有完整梯度"]
        AG1 --> AG2 --> AG3
    end

    RS --> AG

    style RS fill:#e1f5ff
    style AG fill:#fff4e1

上图说明:

  • Reduce-Scatter阶段: 数据切4份,环形传递3轮,每轮传输Φ/4,每GPU最终持有1/4的完整聚合梯度
  • All-Gather阶段: 将完整梯度片段环形传递3轮,每GPU收集其他GPU的完整片段,最终所有GPU拥有完整梯度
  • 每卡通信量: (3 × Φ/4) × 2 = 1.5Φ × 2 = 3Φ/2 ≈ 2Φ (当N较大)

单个GPU通信量公式

$$
C_{\text{Ring}} = 2 \times \frac{N-1}{N} \times \Phi \approx 2\Phi \quad (\text{当 } N \text{ 较大时})
$$

符号含义示例值
$C_{\text{Ring}}$Ring-All-Reduce 单卡通信量21 GB
$N$GPU 数量4
$\Phi$模型参数量7B
$\frac{\Phi}{N}$每次传输的数据片段大小1.75B

推导过程

$$
C_{\text{Ring}} = \underbrace{(N-1) \times \frac{\Phi}{N}}_{\text{Reduce-Scatter}} + \underbrace{(N-1) \times \frac{\Phi}{N}}_{\text{All-Gather}} = 2 \times \frac{(N-1) \times \Phi}{N}
$$

整个系统通信量

$$
C_{\text{Ring,total}} = N \times C_{\text{Ring}} = 2 \times (N-1) \times \Phi \approx 2N\Phi
$$

数值计算示例

同样 4 张 GPU 训练 LLaMA-7B(Φ = 7B):

$$
C_{\text{Ring}} = 2 \times \frac{4-1}{4} \times 7\text{B} \times 2\text{B/param} = 2 \times \frac{3}{4} \times 14\text{ GB} = 21 \text{ GB}
$$

指标Ring-All-Reduce
每卡每次传输量7B/4 × 2B = 3.5 GB
每卡 Reduce-Scatter3 × 3.5 GB = 10.5 GB
每卡 All-Gather3 × 3.5 GB = 10.5 GB
每卡总通信量21 GB(均匀分布)
系统总通信量4 × 21 GB = 84 GB

2.5 两种方式对比

<!-- trick-image:start idx=2 platform=blog -->
朴素与环形All-Reduce通信模式对比图
<!-- trick-image:end idx=2 -->

对比项朴素All-ReduceRing-All-Reduce
总通信量2 × N × Φ2 × N × Φ
单GPU通信量分布不均衡(收集节点负载重)均衡(每个GPU约2Φ)
瓶颈收集GPU的带宽无单点瓶颈
适用场景小规模集群多机多卡分布式系统
关键结论:Ring-All-Reduce的总通信量与朴素All-Reduce接近,但每个GPU上的通信量更为均衡,从而缓解了通信瓶颈问题。

2.6 小结

维度说明
通信操作All-Reduce(梯度同步)
单卡通信量$2 \times \frac{N-1}{N} \times \Phi \approx 2\Phi$
系统总通信量$2 \times N \times \Phi$
Ring 优势负载均衡,无单点瓶颈
通信时机仅在反向传播完成后

3. 张量并行的通信开销计算

3.1 核心问题

  • 张量并行如何切分模型参数?
  • 前馈网络层和注意力层各需要几次 All-Reduce?
  • 一个完整 Transformer 层的通信量是多少?

3.2 原文核心要点

张量并行将模型参数切分成多个参数块放到不同 GPU 上独立计算,最后聚合结果。Megatron-LM 将 FFN 层的第一个矩阵按列切分、第二个按行切分,前向和反向传播各需一次 All-Reduce。一个完整 Transformer 层的总通信量为 8×b×s×h。

3.3 通俗理解

直观类比

想象你和同事一起做一张超大的拼图(矩阵乘法):

拼图太大了,一个人的桌子放不下。于是你们把拼图纵向切成两半,一人拼左半边,一人拼右半边。拼完各自的部分后,需要把结果拼到一起看全貌——这就是前向传播的 All-Reduce。

发现有几块拼错了(反向传播计算梯度),你们各自修正自己那半边的错误,修好后再汇总一次看哪些地方还需要调整——这就是反向传播的 All-Reduce。

一个 Transformer 层有两大组件(注意力 + FFN),每个组件都要"拼一次 + 修一次",所以一共需要 4 次 All-Reduce,总通信量为 8 × b × s × h

核心要点
  • 张量并行的通信发生在每一层的前向和反向传播中,频率远高于数据并行
  • 通信量与激活值大小 b×s×h 成正比,而非模型参数量 Φ
  • 因此张量并行适合用在机内高带宽互联(NVLink 600 GB/s)的 GPU 之间
建立了直觉之后,下面我们用数学公式来严格推导张量并行的通信量。

3.4 技术原理与公式推导

<!-- trick-image:start idx=3 platform=blog -->
张量并行FFN层拼图类比通信流程图
<!-- trick-image:end idx=3 -->

理解了"拼图"的直觉后,下面我们看看 Megatron-LM 如何在工程上实现这个切分策略,以及通信量的精确计算方法。

Megatron-LM 切分方法

张量并行策略的基本思路:

  1. 将模型的参数切分成多个参数块
  2. 将不同的参数块放到不同的GPU上进行独立计算
  3. 最后将计算的结果进行聚合以完成训练

以Transformer模型为例,主要涉及:

  • 词嵌入层(通信开销较小)
  • 前馈神经网络层(FFN)
  • 多头注意力层
前馈神经网络层的通信开销

前馈神经网络层的计算分为两步:

Y = GELU(XA)    # 第一个线性层 + 激活函数
Z = Dropout(YB)  # 第二个线性层 + Dropout

其中:

  • X是输入数据,维度为(b, s, h)
  • A是第一个全连接层,维度为(h, h'),通常h' = 4h
  • B是第二个全连接层,维度为(h', h)

张量切分方式

  • A沿着列维度切分:A = [A₁, A₂]
  • B沿着行维度切分:B = [B₁, B₂]ᵀ

可视化数据流

下图展示了2-GPU张量并行下FFN层的矩阵切分和通信流程:

flowchart TD
    subgraph Input["输入 X (b,s,h)"]
        X[X 在各GPU复制]
    end

    subgraph GPU1["GPU 1"]
        A1["A₁ 列切分<br/>(h, h'/2)"]
        Y1["Y₁ = GELU(XA₁)<br/>(b,s,h'/2)"]
        B1["B₁ 行切分<br/>(h'/2, h)"]
        Z1["Z₁ = Y₁B₁<br/>(b,s,h)"]
    end

    subgraph GPU2["GPU 2"]
        A2["A₂ 列切分<br/>(h, h'/2)"]
        Y2["Y₂ = GELU(XA₂)<br/>(b,s,h'/2)"]
        B2["B₂ 行切分<br/>(h'/2, h)"]
        Z2["Z₂ = Y₂B₂<br/>(b,s,h)"]
    end

    subgraph AR["All-Reduce"]
        Z_final["Z = Z₁ + Z₂<br/>(b,s,h)"]
    end

    X --> A1
    X --> A2
    A1 --> Y1 --> B1 --> Z1
    A2 --> Y2 --> B2 --> Z2
    Z1 --> Z_final
    Z2 --> Z_final

    style AR fill:#ffe1e1
    style GPU1 fill:#e1f5ff
    style GPU2 fill:#e1ffe1

上图说明:

  • 输入X: 在各GPU上复制,无需通信
  • 矩阵A列切分: 每GPU计算一半输出通道(h' = 4h的一半)
  • 矩阵B行切分: 每GPU的输出Z₁、Z₂需通过All-Reduce聚合
  • 通信点: 前向传播1次All-Reduce(聚合Z),反向传播1次All-Reduce(聚合∂L/∂X)

通信过程示意

前向传播:
┌─────────────────────────────────────────────────────────────┐
│  输入X → [复制到各GPU] → GPU₁计算XA₁  → GELU → Y₁B₁ → Z₁   │
│                        → GPU₂计算XA₂  → GELU → Y₂B₂ → Z₂   │
│                                                    ↓        │
│                                          [All-Reduce聚合Z]  │
└─────────────────────────────────────────────────────────────┘

反向传播:
┌─────────────────────────────────────────────────────────────┐
│  ∂L/∂Z → [分发到各GPU] → GPU₁计算梯度                       │
│                        → GPU₂计算梯度                       │
│                                    ↓                        │
│                          [All-Reduce聚合∂L/∂X]              │
└─────────────────────────────────────────────────────────────┘

FFN 层通信开销公式

每次 All-Reduce 的通信量等效于一次 Reduce-Scatter + 一次 All-Gather,数据大小为 $b \times s \times h$:

$$
C_{\text{AllReduce}} = 2 \times b \times s \times h
$$

前向传播 1 次 + 反向传播 1 次 = 2 次 All-Reduce:

$$
C_{\text{FFN}} = 2 \times C_{\text{AllReduce}} = 4 \times b \times s \times h
$$

符号含义示例值
$C_{\text{FFN}}$FFN 层总通信量256 MB
$b$批次大小4
$s$序列长度2048
$h$隐藏维度4096

数值计算示例

假设 LLaMA-7B 的一个 Transformer 层,使用 2 路张量并行:

$$
C_{\text{FFN}} = 4 \times 4 \times 2048 \times 4096 \times 2\text{B} = 256 \text{ MB}
$$

参数
batch size (b)4
序列长度 (s)2048
隐藏维度 (h)4096
FFN 层每次 All-Reduce 数据量2 × 4 × 2048 × 4096 × 2B = 128 MB
FFN 层总通信量4 × 4 × 2048 × 4096 × 2B = 256 MB
多头注意力层的通信开销

多头注意力层在张量并行下的通信结构与 FFN 层相同:

  • 前向传播:1次All-Reduce
  • 反向传播:1次All-Reduce

$$
C_{\text{Attn}} = 4 \times b \times s \times h
$$

完整 Transformer 层通信开销

对一个包含前馈网络层和多头注意力层的Transformer来说:

$$
C_{\text{layer}} = C_{\text{FFN}} + C_{\text{Attn}} = 4bsh + 4bsh = 8 \times b \times s \times h
$$

符号含义示例值
$C_{\text{layer}}$单个 Transformer 层通信量512 MB
$C_{\text{FFN}}$FFN 层通信量256 MB
$C_{\text{Attn}}$注意力层通信量256 MB

数值计算示例(续上例):

$$
C_{\text{layer}} = 8 \times 4 \times 2048 \times 4096 \times 2\text{B} = 512 \text{ MB}
$$

组件通信量
FFN 层4 × b × s × h = 256 MB
注意力层4 × b × s × h = 256 MB
单层合计512 MB
LLaMA-7B 共 32 层32 × 512 MB = 16 GB

3.5 小结

维度说明
切分方式A 按列切分,B 按行切分
FFN 层通信量$4 \times b \times s \times h$
注意力层通信量$4 \times b \times s \times h$
单层总通信量$8 \times b \times s \times h$
通信时机每层前向 + 反向各 2 次 All-Reduce
适用条件机内高带宽互联

4. 流水线并行的通信开销计算

4.1 核心问题

  • 流水线并行如何切分模型?
  • 前向和反向传播的通信量分别是多少?
  • GPipe 如何优化流水线并行?

4.2 原文核心要点

流水线并行将模型的不同层拆分到不同 GPU 上,前向传播时逐层传递激活值,反向传播时逐层回传梯度。总通信量为 2×(N-1)×b×s×h,通信方式为点对点(P2P)传输。GPipe 通过微批次(micro-batch)机制提高 GPU 利用率。

4.3 通俗理解

直观类比

想象一条汽车生产流水线

车间 1 负责焊接车架,车间 2 负责安装发动机,车间 3 负责喷漆,车间 4 负责装内饰。每辆车依次经过 4 个车间。

  • 前向传播:车架焊好后从车间 1 送到车间 2(传递激活值),发动机装好后送到车间 3……一共需要在 3 个车间之间传递半成品
  • 反向传播:质检发现问题后,从车间 4 开始逐步回溯哪个环节出了问题(回传梯度),同样经过 3 个车间间的传递
  • GPipe 优化:不是等一辆车完全做完再做下一辆,而是把一批车切成小批次,像"流水线"一样同时开工多辆车,减少车间的空闲等待时间
核心要点
  • 流水线并行的通信是点对点(P2P),只在相邻 GPU 之间传递
  • 通信量公式:2×(N-1)×b×s×h,与切分点数量和激活值大小成正比
  • 流水线并行通信量通常远小于张量并行,但有气泡问题(GPU 空闲等待)
建立了直觉之后,下面我们用数学公式来严格推导流水线并行的通信量。

4.4 技术原理与公式推导

有了"汽车生产流水线"的直觉,现在我们用数学公式来精确计算流水线并行的通信量,并理解 GPipe 是如何通过 micro-batch 提升效率的。

工作原理

对于朴素的流水线并行,其主要思想是将模型的不同层进行拆分,然后放到不同的GPU上。

前向传播过程

  1. 放置了模型第一层的GPU先进行前向传播
  2. 将该层的输出传给放置了第二层的GPU
  3. 以此类推,直到所有层都依次完成前向传播

反向传播过程

  1. 从放置了模型最后一层的GPU开始
  2. 依次在不同GPU上计算不同层的梯度
  3. 当所有层的梯度都完成计算后,才对模型的权重进行更新
流水线并行模型示意图:

                    模型输出计算损失L
                          ↑
┌─────────────────────────────────────┐
│         模型最后一层        │ GPU_N │ ←─┐
├─────────────────────────────────────┤    │
│              ...             │ ...  │    │ 反向传播
├─────────────────────────────────────┤    │ ∂L/∂Z_N-1, ..., ∂L/∂Z_1
│           模型层2           │ GPU_2 │    │
├─────────────────────────────────────┤    │
│           模型层1           │ GPU_1 │ ←─┘
└─────────────────────────────────────┘
     ↑                    │
   输入                   │ 前向传播
 MiniBatch               │ Z_1, Z_2, ..., Z_N-1
     ↓                    ↓
[MicroBatch₁] [MicroBatch₂] ... [MicroBatch_n]
GPipe 方法与通信开销

GPipe是一种经典的流水线并行方法,通过引入micro-batch(微批次)处理和激活值重算机制,有效解决了朴素流水线并行所存在的GPU利用率过低以及中间结果消耗过大的问题。

通信开销公式

假设:

  • mini-batch的大小是b
  • 模型被切分到N个GPU上
  • 分布在不同GPU之间的中间结果Z的维度大小为(b, s, h)

前向传播通信量(N-1 个中间结果):

$$
C_{\text{forward}} = (N-1) \times b \times s \times h
$$

反向传播通信量(N-1 个偏导数):

$$
C_{\text{backward}} = (N-1) \times b \times s \times h
$$

流水线并行总通信量:

$$
C_{\text{PP}} = C_{\text{forward}} + C_{\text{backward}} = 2 \times (N-1) \times b \times s \times h
$$

符号含义示例值
$C_{\text{PP}}$流水线并行总通信量384 MB
$N$GPU 数量(流水线级数)4
$N-1$切分点数量3
$b \times s \times h$单个中间激活值大小64 MB

数值计算示例

假设 LLaMA-7B(32 层)使用 4 路流水线并行,每个 GPU 放 8 层:

$$
C_{\text{PP}} = 2 \times (4-1) \times 4 \times 2048 \times 4096 \times 2\text{B} = 2 \times 3 \times 64\text{ MB} = 384 \text{ MB}
$$

参数
batch size (b)4
序列长度 (s)2048
隐藏维度 (h)4096
流水线切分点N-1 = 3 个
每个中间结果大小4 × 2048 × 4096 × 2B = 64 MB
前向通信量3 × 64 MB = 192 MB
反向通信量3 × 64 MB = 192 MB
总通信量384 MB

对比:同配置下张量并行总通信量为 16 GB,流水线并行仅 384 MB——但流水线并行有"气泡"(idle time)问题。

4.5 小结

维度说明
切分方式按层切分到不同 GPU
通信方式点对点(P2P)传输
总通信量$2 \times (N-1) \times b \times s \times h$
优化方法GPipe micro-batch 减少气泡
优势通信量小,适合跨机
劣势有气泡时间,GPU 利用率受限

5. ZeRO 优化技术的通信开销计算

5.1 核心问题

  • ZeRO 三个级别分别切分什么内容?
  • 为什么 ZeRO-1/2 的通信量与数据并行相同?
  • ZeRO-3 额外增加了多少通信开销?

5.2 原文核心要点

ZeRO(Zero Redundancy Optimizer)分三个级别:ZeRO-1 切分优化器状态,ZeRO-2 额外切分梯度,ZeRO-3 进一步切分模型参数。ZeRO-1/2 单卡通信量均为 2Φ(与数据并行相同),ZeRO-3 为 3Φ(增加 50%),但换取了与 GPU 数量成正比的显存节省。

5.3 通俗理解

直观类比

想象你和 7 个同学一起背一本 700 页的词典

  • 数据并行(不用 ZeRO):每人都买一本完整的词典(700 页),各自背不同的章节做练习,做完后互相对答案。问题是每人都得揣着一本厚词典——书包装不下!
  • ZeRO-1:每人还是各有一本词典和一本笔记本(参数+梯度),但 8 本参考书(优化器状态)只买 1 套,每人保管 1 本。背完对答案时,需要找到保管对应参考书的同学来核对——沟通量不变,但书包轻了。
  • ZeRO-2:词典还是每人一本,但笔记本和参考书都只各买 1 套分着保管。书包更轻了,沟通量还是一样。
  • ZeRO-3:连词典都只买 1 套,700 页拆成 8 份每人 87 页。每次要查别的部分时得找同学借(All-Gather),查完立刻还回去。多了"借书还书"的麻烦(+50% 通信),但每人只需要带 87 页——书包最轻
核心要点
  • ZeRO-1/2 通信量 = 2Φ,与数据并行完全相同(因为 Reduce-Scatter + All-Gather = All-Reduce)
  • ZeRO-3 多出的 1Φ 来自前向和反向传播时各需一次 All-Gather 收集参数
  • 核心权衡:通信量增加 50%,换取显存线性扩展
建立了直觉之后,下面我们用数学公式来严格分析 ZeRO 各级别的通信开销。

5.4 技术原理与公式推导

理解了"词典分工"的类比后,现在我们详细剖析 ZeRO 三个层次的通信流程和通信量计算,看看为什么 ZeRO-3 要付出 50% 的额外通信代价。

ZeRO-1:优化器状态分片

特点

  • 每个GPU都存有一份完整的模型参数
  • 每个GPU只存有部分优化器状态

通信过程

  1. 前向传播 + 反向传播后:每个GPU各自得到一份梯度
  2. Reduce-Scatter:对梯度进行聚合,每个GPU得到部分聚合梯度

    • 单卡通信量:Φ
  3. 参数更新:各GPU使用自己的优化器状态更新对应部分参数
  4. All-Gather:从其他GPU上把更新好的部分模型参数取回来

    • 单卡通信量:Φ

ZeRO-1 单卡通信量公式

$$
C_{\text{ZeRO-1}} = \underbrace{\Phi}_{\text{Reduce-Scatter}} + \underbrace{\Phi}_{\text{All-Gather}} = 2\Phi
$$


ZeRO-2:优化器状态 + 梯度分片

特点

  • 每个GPU都存有一份完整的模型参数
  • 每个GPU只存有部分优化器状态和部分梯度

通信过程

  1. 前向传播后,反向传播时需要Reduce-Scatter获取其他GPU的梯度

    • 单卡通信量:Φ
  2. All-Gather:从其他GPU上把完成梯度更新的部分模型参数取回来

    • 单卡通信量:Φ

ZeRO-2 单卡通信量公式

$$
C_{\text{ZeRO-2}} = \underbrace{\Phi}_{\text{Reduce-Scatter}} + \underbrace{\Phi}_{\text{All-Gather}} = 2\Phi
$$


ZeRO-3:优化器状态 + 梯度 + 参数分片

特点

  • 每个GPU只存有部分模型参数
  • 每个GPU只存有部分优化器状态和部分梯度

通信过程

前向传播

  1. 需要完整模型参数,因此需要逐层All-Gather收集模型参数
  2. 每一层完成前向传播后,立即把不属于自己维护的模型参数丢弃

    • 单卡通信量:Φ

反向传播

  1. 需要使用梯度结果对原始参数求偏导数,因此需要逐层All-Gather
  2. 每一层完成反向传播后,立即把不属于自己维护的模型参数丢弃

    • 单卡通信量:Φ

参数更新

  1. 每个GPU各自都会得到一份梯度
  2. 需要Reduce-Scatter获得完整的聚合梯度用于更新模型参数

    • 单卡通信量:Φ

ZeRO-3 单卡通信量公式

$$
C_{\text{ZeRO-3}} = \underbrace{\Phi}_{\text{前向 All-Gather}} + \underbrace{\Phi}_{\text{反向 All-Gather}} + \underbrace{\Phi}_{\text{Reduce-Scatter}} = 3\Phi
$$

5.5 ZeRO 通信开销对比

<!-- trick-image:start idx=4 platform=blog -->
ZeRO三个级别显存占用对比示意图
<!-- trick-image:end idx=4 -->

版本分片内容单卡通信量显存节省
ZeRO-1优化器状态$2\Phi$约4倍
ZeRO-2优化器状态 + 梯度$2\Phi$约8倍
ZeRO-3优化器状态 + 梯度 + 参数$3\Phi$与GPU数量成正比
关键结论:ZeRO-3通过更激进的分片策略换取更大的显存节省,但代价是增加了50%的通信开销(从2Φ增加到3Φ)。

ZeRO-3 相对 ZeRO-2 的额外通信量比例

$$
\frac{C_{\text{ZeRO-3}} - C_{\text{ZeRO-2}}}{C_{\text{ZeRO-2}}} = \frac{3\Phi - 2\Phi}{2\Phi} = 50\%
$$

数值计算示例

以 LLaMA-7B(Φ = 7B)在 8 张 GPU 上训练为例:

$$
C_{\text{ZeRO-1}} = C_{\text{ZeRO-2}} = 2 \times 7\text{B} \times 2\text{B/param} = 28 \text{ GB}
$$

$$
C_{\text{ZeRO-3}} = 3 \times 7\text{B} \times 2\text{B/param} = 42 \text{ GB}
$$

版本单卡通信量具体数值单卡显存占用(优化器+梯度+参数)
数据并行$2\Phi$28 GB112 GB(放不下)
ZeRO-1$2\Phi$28 GB~42 GB
ZeRO-2$2\Phi$28 GB~28 GB
ZeRO-3$3\Phi$42 GB~14 GB(可放入 A100-80G)

结论:ZeRO-3 多付出 14 GB 通信开销,但单卡显存从 112 GB 降到 14 GB,使 7B 模型可以在 8 张 A100-80G 上训练。

5.6 小结

维度说明
核心思想切分冗余存储,降低单卡显存占用
ZeRO-1/2 通信量$2\Phi$(与 DP 相同)
ZeRO-3 通信量$3\Phi$(增加 50%)
显存节省ZeRO-1 ~4×,ZeRO-2 ~8×,ZeRO-3 ~N×
通信操作Reduce-Scatter + All-Gather(ZeRO-3 额外 2×All-Gather)

6. 通信开销综合对比与选型指南

6.1 各并行策略通信量汇总

并行策略单卡通信量主要通信操作通信发生时机
数据并行$2\Phi$All-Reduce反向传播后
张量并行$8 \times b \times s \times h$(每层)All-Reduce前向+反向传播中
流水线并行$2 \times (N-1) \times b \times s \times h$P2P层间传递
ZeRO-1$2\Phi$Reduce-Scatter + All-Gather反向传播后
ZeRO-2$2\Phi$Reduce-Scatter + All-Gather反向传播后
ZeRO-3$3\Phi$All-Gather×2 + Reduce-Scatter前向+反向+更新

6.2 选型决策流程

<!-- trick-image:start idx=5 platform=blog -->
3D并行选型决策流程图
<!-- trick-image:end idx=5 -->

下图展示了并行策略的选型决策流程:

flowchart TD
    A[模型能放进单卡?] -->|是| B[使用数据并行 DP]
    A -->|否| C[显存主要被优化器占用?]
    C -->|是| D[使用 ZeRO-1/2]
    C -->|否| E[单层参数能放进单卡?]
    E -->|是| F[流水线并行 PP]
    E -->|否| G[张量并行 TP]
    F --> H[超大规模模型?]
    G --> H
    D --> H
    H -->|是| I[3D并行: DP + TP + PP]
    H -->|否| J[当前策略即可]
    I --> K[TP 优先机内 NVLink]
    I --> L[PP 用于跨机相邻层]
    I --> M[DP 用于机间通信]

上图展示了从单卡到 3D 并行的选型路径:优先考虑简单策略,逐步引入更复杂的并行方式。

6.3 3D 并行配置示例

下图展示了 3D 并行中三个维度的协同关系:

flowchart LR
    subgraph DP["数据并行 (DP=2)"]
        subgraph PP1["流水线并行 (PP=4)"]
            subgraph TP1["张量并行 (TP=4)"]
                G1[GPU 0] --- G2[GPU 1]
                G2 --- G3[GPU 2]
                G3 --- G4[GPU 3]
            end
            subgraph TP2["张量并行 (TP=4)"]
                G5[GPU 4] --- G6[GPU 5]
                G6 --- G7[GPU 6]
                G7 --- G8[GPU 7]
            end
            TP1 -->|激活值 P2P| TP2
        end
        subgraph PP2["流水线并行 (PP=4)"]
            subgraph TP3["张量并行 (TP=4)"]
                G9[GPU 16] --- G10[GPU 17]
            end
            subgraph TP4["张量并行 (TP=4)"]
                G11[GPU 20] --- G12[GPU 21]
            end
            TP3 -->|激活值 P2P| TP4
        end
        PP1 -.->|梯度 All-Reduce| PP2
    end

上图展示了 32 GPU 的 3D 并行布局:TP=4 使用机内 NVLink,PP=4 用于层间传递,DP=2 用于机间梯度同步。

6.4 3D 并行通信开销

配置示例(32 GPU):

  • 数据并行度 $D_{\text{dp}} = 2$
  • 张量并行度 $D_{\text{tp}} = 4$
  • 流水线并行度 $D_{\text{pp}} = 4$

3D 并行各维度通信量公式

$$
C_{\text{TP}} = 8 \times b \times s \times h \quad \text{(每层,All-Reduce 激活值)}
$$

$$
C_{\text{PP}} = 2 \times (D_{\text{pp}} - 1) \times b \times s \times h \quad \text{(P2P 传递激活值)}
$$

$$
C_{\text{DP}} = \frac{2\Phi}{D_{\text{tp}} \times D_{\text{pp}}} \quad \text{(All-Reduce 梯度)}
$$

符号含义示例值
$C_{\text{TP}}$张量并行每层通信量512 MB
$C_{\text{PP}}$流水线并行总通信量384 MB
$C_{\text{DP}}$数据并行梯度通信量1.75 GB
$D_{\text{tp}} \times D_{\text{pp}}$单个 DP 组内的 GPU 数16
并行维度通信操作通信量互联要求
张量并行All-Reduce 激活值$8 \times b \times s \times h$(每层)NVLink(机内)
流水线并行P2P 传递激活值$2 \times (D_{\text{pp}}-1) \times b \times s \times h$机内或跨机
数据并行All-Reduce 梯度$\frac{2\Phi}{D_{\text{tp}} \times D_{\text{pp}}}$跨机网络

设计原则

  1. TP优先使用机内通信:NVLink带宽高(600 GB/s)
  2. PP次之:相邻层尽量在同机器
  3. DP使用机间通信:通信频率最低,对带宽要求最低

6.5 通信优化方法

优化方法原理效果
通信与计算重叠反向传播时已完成层提前通信隐藏通信延迟
梯度累积多个 micro-batch 累积后再同步减少通信频率
梯度压缩FP16→INT8 量化或 Top-K 稀疏化减少通信数据量
Bucket 融合多个小张量合并成大张量再通信降低通信启动开销
分层 All-Reduce先机内 Reduce 再跨机 All-Reduce减少跨机流量
拓扑感知调度Ring/2D-Torus 匹配物理拓扑提高带宽利用率

7. 高频面试题及答案

Q1: 请解释 All-Reduce 和 Ring-All-Reduce 的区别?(基础)

答案
两者目标相同(让所有 GPU 拥有完整规约结果),但实现方式不同。朴素 All-Reduce 由一个节点收集聚合再广播,存在单点瓶颈;Ring-All-Reduce 通过环形拓扑分 Reduce-Scatter 和 All-Gather 两阶段完成,每卡通信量均为 2×(N-1)×Φ/N ≈ 2Φ,负载均衡无瓶颈。

详细说明

要点说明
朴素方式收集节点承载 (N-1)×Φ 通信量,成为瓶颈
Ring 方式数据切 N 份,环形传递 N-1 轮,每轮传 Φ/N
总通信量两者相近,约 2×N×Φ
关键区别Ring 方式负载均匀分布,消除单点瓶颈
工程实践几乎所有分布式框架默认使用 Ring-All-Reduce

公式

$$
C_{\text{Ring}} = 2 \times \frac{N-1}{N} \times \Phi \approx 2\Phi
$$


Q2: 数据并行和张量并行的通信开销有什么区别?(基础)

答案
数据并行通信量为 2Φ,仅在反向传播后同步梯度;张量并行通信量为 8×b×s×h(每层),在每层的前向和反向传播中都需要通信。前者与模型参数量成正比,后者与激活值大小成正比。

详细说明

对比维度数据并行张量并行
通信量$2\Phi$(与模型大小相关)$8bsh$(与激活值大小相关)
通信时机反向传播完成后每层的前向和反向传播中
通信频率每个训练步一次每层都需要通信
通信操作All-Reduce(梯度)All-Reduce(激活值)
适用场景模型能放进单卡模型太大需要层内切分

Q3: ZeRO-1、ZeRO-2、ZeRO-3 的区别及通信开销?(进阶)

答案
ZeRO-1 切分优化器状态,ZeRO-2 额外切分梯度,ZeRO-3 进一步切分参数。ZeRO-1/2 单卡通信量为 2Φ(与 DP 相同),ZeRO-3 为 3Φ(增加 50%),但显存节省与 GPU 数量成正比。

详细说明

版本分片内容各GPU存储单卡通信量显存节省
ZeRO-1优化器状态完整模型+完整梯度+部分优化器$2\Phi$~4×
ZeRO-2优化器状态+梯度完整模型+部分梯度+部分优化器$2\Phi$~8×
ZeRO-3全部部分模型+部分梯度+部分优化器$3\Phi$~N×

公式

$$
C_{\text{ZeRO-1}} = C_{\text{ZeRO-2}} = 2\Phi, \quad C_{\text{ZeRO-3}} = 3\Phi
$$

ZeRO-1/2 通信量与 DP 相同的原因:Reduce-Scatter + All-Gather = All-Reduce。ZeRO-3 多出的 1Φ 来自前向和反向传播各一次 All-Gather。


Q4: 流水线并行的通信开销公式是什么?如何理解?(进阶)

答案
公式为 2×(N-1)×b×s×h。N-1 是切分点数,b×s×h 是激活值大小,×2 因为前向和反向各传一次。通信方式为 P2P,仅相邻 GPU 之间传递。

详细说明

要点说明
N-1模型切到 N 张 GPU 上,有 N-1 个切分点
b×s×h中间激活值大小(batch × sequence × hidden)
×2前向传激活值 + 反向传梯度
通信方式点对点(P2P),非集合通信
GPipe 优化micro-batch 提高利用率,但不改变总通信量

公式

$$
C_{\text{PP}} = 2 \times (N-1) \times b \times s \times h
$$


Q5: 什么是集合通信原语?请列举并解释主要原语(基础)

答案
集合通信原语是分布式系统中多节点间数据传输的基本操作,共 8 种。按模式分为 1→N(Broadcast/Scatter)、N→1(Gather/Reduce)、N→N(All-Gather/Reduce-Scatter/All-Reduce/All-to-All)三类。

详细说明

原语模式是否规约典型应用
Broadcast1→N参数初始化
Scatter1→N数据划分
GatherN→1结果汇总
ReduceN→1梯度汇总到主节点
All-GatherN→NZeRO-3 参数恢复
Reduce-ScatterN→NZeRO 梯度聚合
All-ReduceN→NDP 梯度同步
All-to-AllN→N专家并行(MoE)

Q6: 在万卡集群中,为什么说"优化器状态可以忽略不计"?(进阶)

答案
在 3D 并行(DP×TP×PP)环境下,优化器状态被所有 GPU 分担。1024 卡集群中,优化器状态每卡只需存储 12Φ/1024 ≈ 0.012Φ,相比参数和梯度的 2Φ/(TP×PP) 占比极小。

详细说明

要点说明
单卡显存公式参数: 2Φ/(TP×PP) + 梯度: 2Φ/(TP×PP) + 优化器: 12Φ/(DP×TP×PP)
关键区别参数和梯度被 TP×PP 份切分,优化器被所有 GPU 切分
1024卡实例优化器每卡 0.012Φ,参数每卡约 0.125Φ(以 TP=4,PP=4 为例)
实际意义万卡集群下无需特别优化优化器显存,重点关注激活值和通信

公式

$$
M_{\text{per\_gpu}} = \frac{2\Phi}{D_{\text{tp}} \times D_{\text{pp}}} + \frac{2\Phi}{D_{\text{tp}} \times D_{\text{pp}}} + \frac{12\Phi}{D_{\text{dp}} \times D_{\text{tp}} \times D_{\text{pp}}}
$$


Q7: 张量并行中,前馈神经网络层为什么需要 2 次 All-Reduce?(进阶)

答案
FFN 层包含两步线性变换(Y=GELU(XA), Z=Dropout(YB)),A 按列切分、B 按行切分。前向传播需 All-Reduce 聚合 Z,反向传播需 All-Reduce 聚合 ∂L/∂X,各一次共 2 次,通信量 4×b×s×h。

详细说明

要点说明
切分方式A 按列切分 [A₁, A₂],B 按行切分 [B₁; B₂]
前向 All-Reduce聚合各 GPU 计算的 Z₁、Z₂ 得到完整输出 Z
反向 All-Reduce聚合各 GPU 计算的 ∂L/∂X₁、∂L/∂X₂ 得到完整梯度
每次数据量2×b×s×h(All-Reduce 等效于 RS+AG)
总通信量4×b×s×h

公式

$$
C_{\text{FFN}} = 2 \times C_{\text{AllReduce}} = 2 \times 2bsh = 4bsh
$$


Q8: 比较数据并行和 ZeRO 的通信开销(进阶)

答案
ZeRO-1/2 通信量为 2Φ,与数据并行完全相同,因为 Reduce-Scatter + All-Gather = All-Reduce。ZeRO-3 通信量为 3Φ,多出的 1Φ 来自前向和反向传播各一次 All-Gather 收集参数。

详细说明

方法通信量通信操作显存效果
数据并行$2\Phi$All-Reduce无优化
ZeRO-1$2\Phi$Reduce-Scatter + All-Gather节省 ~4×
ZeRO-2$2\Phi$Reduce-Scatter + All-Gather节省 ~8×
ZeRO-3$3\Phi$2×All-Gather + Reduce-Scatter节省 ~N×

核心权衡:ZeRO-3 用 50% 额外通信换取线性显存扩展能力。


Q9: 3D 并行是什么?如何计算其通信开销?(综合)

答案
3D 并行组合数据并行(DP)、张量并行(TP)、流水线并行(PP)。TP 使用机内 NVLink 高带宽,PP 用于层间传递,DP 用于机间梯度同步。

详细说明

并行维度通信操作通信量互联方式
TP(张量)All-Reduce 激活值$8bsh$/层NVLink(机内)
PP(流水线)P2P 传递激活值$2(D_{\text{pp}}-1)bsh$机内/跨机
DP(数据)All-Reduce 梯度$\frac{2\Phi}{D_{\text{tp}} \times D_{\text{pp}}}$跨机网络

设计原则:TP 优先机内(带宽最高)→ PP 次之 → DP 用于机间(频率最低)。


Q10: 如何优化大模型训练的通信效率?(进阶)

答案
六大优化手段:通信计算重叠、梯度累积减少频率、梯度压缩减少数据量、Bucket 融合降低启动开销、分层 All-Reduce 减少跨机流量、拓扑感知调度匹配物理拓扑。

详细说明

优化手段原理效果
通信计算重叠已完成层提前通信隐藏延迟
梯度累积多 micro-batch 累积后同步通信频率降低 k 倍
梯度压缩FP16→INT8 或 Top-K通信量减少 50-90%
Bucket 融合小张量合并通信降低启动开销
分层 All-Reduce先机内后机间减少跨机流量
异步通信流水线隐藏延迟提升吞吐量

Q11: Ring-All-Reduce 的通信步数和带宽利用率如何计算?(进阶)

答案
Ring-All-Reduce 需要 2×(N-1) 步通信,每步传输 Φ/N 数据。在理想情况下,所有 GPU 在每一步都同时收发数据,带宽利用率接近 100%。延迟与 N 成正比,但单步传输量与 N 成反比,总传输量与 N 无关。

详细说明

指标公式说明
通信步数$2 \times (N-1)$RS 阶段 N-1 步 + AG 阶段 N-1 步
每步传输量$\frac{\Phi}{N}$数据被切成 N 份
总传输量(单卡)$2 \times \frac{N-1}{N} \times \Phi$与 N 无关(N 大时约 2Φ)
带宽利用率接近 100%所有 GPU 同时收发
延迟$O(N)$步数与 N 成正比

公式

$$
T_{\text{Ring}} = 2(N-1) \times \left( \alpha + \frac{\Phi}{N \times \beta} \right)
$$

其中 $\alpha$ 为单次通信延迟,$\beta$ 为链路带宽。


Q12: 如果同时使用 ZeRO-3 和张量并行,通信开销如何叠加?(综合)

答案
两者的通信是独立叠加的。ZeRO-3 的 3Φ 通信量针对模型参数的收集和梯度聚合(跨数据并行组),张量并行的 8bsh/层通信量针对激活值的聚合(机内 TP 组)。实际中 ZeRO-3 通常与 DP 配合,而非与 TP 同时使用,因为 TP 本身已经切分了参数。

详细说明

场景ZeRO-3 通信TP 通信合理性
ZeRO-3 + DP$3\Phi$(跨 DP 组)常见配置,适合中小模型
TP + DP$8bsh$/层(机内) + $2\Phi$(跨 DP 组)常见配置,适合大模型
ZeRO-3 + TP$\frac{3\Phi}{D_{\text{tp}}}$ + $8bsh$/层较少使用,通信量大通常不推荐
3D 并行 + ZeRO-1$2\Phi/(D_{\text{tp}} \times D_{\text{pp}})$$8bsh$/层 + PP 通信工业界主流

8. 大厂常见面试题

本节面试题来源于互联网公开的大厂面试真题和高频考点。

Q13: 在实际训练中,如何判断通信是否成为瓶颈?有哪些诊断方法?

出处:字节跳动/阿里云 AI Infra 岗位面试真题

答案
通信瓶颈的核心判断指标是"计算开销/通信开销"的比值(Computation-to-Communication Ratio)。当通信时间占训练步时间的比例超过 30%,通常认为通信已成为瓶颈。

详细说明

诊断方法工具关键指标
NCCL 日志分析NCCL_DEBUG=INFOAll-Reduce 耗时、带宽利用率
Profiler 火焰图PyTorch Profiler / Nsight通信算子在时间线中的占比
吞吐量对比实际 vs 理论 MFU理论算力利用率 < 50% 时怀疑通信瓶颈
梯度累积实验增大累积步数如果吞吐量提升明显,说明通信是瓶颈
带宽测试NCCL Tests (all_reduce_perf)实测带宽 vs 理论带宽

Q14: DeepSpeed ZeRO 与 FSDP(PyTorch FullyShardedDataParallel)有什么异同?

出处:Meta/微软 LLM 训练工程师面试高频题

答案
两者核心思想一致——都将优化器状态、梯度和参数进行分片以节省显存。FSDP 是 PyTorch 原生实现,API 与 DDP 相似,易于迁移;DeepSpeed ZeRO 提供更细粒度的配置(Stage 1/2/3)和丰富的工程优化(offload、infinity 等)。

详细说明

对比维度DeepSpeed ZeROPyTorch FSDP
框架微软独立框架PyTorch 原生
分片粒度Stage 1/2/3 分级控制类似 ZeRO-3,统一分片
CPU Offload支持(ZeRO-Infinity)支持(cpu_offload)
通信后端NCCL / 自定义NCCL
混合精度FP16/BF16/FP8FP16/BF16
社区生态HuggingFace Accelerate 集成PyTorch 原生,兼容性最好

Q15: 训练 70B 参数量的模型,如何设计并行策略和通信方案?请给出具体配置。

出处:综合预测题(结合 LLaMA-70B 等开源模型训练实践)

答案
以 128 张 A100-80G 为例,推荐采用 3D 并行:TP=8(机内 NVLink),PP=2(跨机),DP=8(机间),配合 ZeRO-1 切分优化器状态。

详细说明

配置项理由
张量并行 TP8一台机器 8 卡,NVLink 互联带宽 900 GB/s
流水线并行 PP270B/8(TP)=8.75B/卡,2 级流水线后约 4.4B/卡,显存可控
数据并行 DP8128/(8×2)=8,提升训练吞吐量
ZeROStage 1切分优化器状态,通信量不增加
梯度累积4-8 步减少 DP 通信频率
序列长度4096平衡显存和通信

通信量估算

$$
C_{\text{TP}} = 8 \times b \times 4096 \times 8192 \times 2\text{B} \approx 4\text{ GB/层}
$$

$$
C_{\text{PP}} = 2 \times 1 \times b \times 4096 \times 8192 \times 2\text{B} \approx 0.5\text{ GB}
$$

$$
C_{\text{DP}} = \frac{2 \times 70\text{B} \times 2\text{B}}{8 \times 2} = 17.5\text{ GB}
$$


总结

核心知识点回顾

知识点核心公式/结论关键理解
集合通信原语8 种基础原语All-Reduce = RS + AG 是最核心的组合
数据并行$C = 2\Phi$Ring 方式消除单点瓶颈,负载均衡
张量并行$C = 8bsh$/层每层 4 次 All-Reduce,适合机内高带宽
流水线并行$C = 2(N-1)bsh$P2P 通信,量小但有气泡问题
ZeRO-1/2$C = 2\Phi$通信量与 DP 相同,显存分别省 4×/8×
ZeRO-3$C = 3\Phi$+50% 通信换取 N× 显存节省
3D 并行TP+PP+DP 组合TP 机内 → PP 跨层 → DP 机间
通信优化6 大手段重叠、累积、压缩、融合、分层、拓扑

思维导图结构

大模型训练通信开销
├── 集合通信原语
│   ├── 1→N:Broadcast / Scatter
│   ├── N→1:Gather / Reduce
│   └── N→N:All-Gather / Reduce-Scatter / All-Reduce / All-to-All
├── 数据并行 (单卡 2Φ)
│   ├── 朴素 All-Reduce:单点瓶颈
│   └── Ring-All-Reduce:均衡负载 = RS + AG
├── 张量并行 (单层 8bsh)
│   ├── FFN:A 列切 + B 行切 → 4bsh
│   ├── Attention → 4bsh
│   └── 适合机内 NVLink
├── 流水线并行 (2(N-1)bsh)
│   ├── 按层切分,P2P 传递
│   ├── GPipe micro-batch 减少气泡
│   └── 适合跨机通信
├── ZeRO 优化
│   ├── ZeRO-1:切分优化器 → 2Φ
│   ├── ZeRO-2:+切分梯度 → 2Φ
│   └── ZeRO-3:+切分参数 → 3Φ
├── 3D 并行
│   ├── TP 机内 (NVLink)
│   ├── PP 跨层 (P2P)
│   └── DP 机间 (All-Reduce)
└── 通信优化
    ├── 重叠 / 累积 / 压缩
    └── 融合 / 分层 / 拓扑

参考文献

  1. Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism
  2. GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism
  3. ZeRO: Memory Optimizations Toward Training Trillion Parameter Models

<!-- Reviewed: 2026-02-13, 深度重新审校:增加2个核心Mermaid图(Ring-All-Reduce两阶段流程+张量并行矩阵切分数据流)/原有2个Mermaid图(选型流程+3D并行配置)/丰富的通俗化类比(快递分拣/课堂小组/拼图/流水线/词典)/完整公式+符号表+数值示例/15个面试题/总计4个Mermaid图+1400行全面内容 -->

AI-Compass 致力于构建最全面、最实用、最前沿的AI技术学习和实践生态,通过六大核心模块的系统化组织,为不同层次的学习者和开发者提供完整学习路径。

🌟 如果本项目对您有所帮助,请为我们点亮一颗星!🌟

在FinTech量化策略研发与回测中,美股Tick级逐笔成交数据是核心支撑——无论是盘口微观结构分析、高频策略验证,还是精细化历史回测,都离不开连续、低延迟、高完整度的Tick数据。

对于初创FinTech团队而言,如何在控制成本的前提下,高效解决Tick数据获取难题,避免因数据问题拖慢策略迭代,是技术选型阶段的关键命题。结合实战经验,本文拆解Tick数据采集的核心痛点、解决方案及工程化落地细节,附可直接复用代码,供量化开发者参考交流。

一、初创团队的Tick数据困境(实战场景)

多数初创量化团队在搭建美股量化系统时,都会陷入同一个困境:核心需求是依托Tick级逐笔数据完成策略研发,但市面上多数渠道仅提供分钟级及以上聚合行情,无法满足精细度要求。

实际落地中,两种常见尝试均存在明显短板:

  • 自行对接交易所:数据最完整,但授权门槛高、投入成本大,接口调试复杂,耗费大量研发人力,不符合初创团队资源现状;
  • 使用券商API:接入门槛低,但稳定性不足,请求频率受限,交易高峰时段易出现数据延迟、丢包,导致策略回测结果失真,严重拖慢迭代进度。

这也是思否社区中,不少量化开发者高频咨询的共性问题——不是技术能力不足,而是没选对适配初创团队的数据源与实现方案。

二、Tick数据采集的3大核心痛点(精准避坑)

结合多团队实战反馈,美股Tick数据采集的核心矛盾集中在3点,也是量化开发中最易踩坑的环节:

1. 数据精度与需求不匹配

K线数据仅能满足基础行情展示,无法还原每笔成交的价格、数量、精准时间戳,而这些细节正是盘口分析、高频策略验证的核心,缺少则会导致策略研发失去数据支撑。

2. 数据源选型两难

  • 交易所直连:数据原始、完整,但授权成本高、门槛高,不适合资源有限的初创团队;
  • 券商API:低成本、易接入,但稳定性差、频率受限,高峰时段数据易断层,无法支撑长期量化研究。

3. 传输与存储效率低下

传统REST轮询方式,不仅占用服务器资源,还会产生明显延迟,无法适配Tick数据“高并发、高实时性”的推送需求;同时,Tick数据体量巨大,若缺乏合理规划,易出现存储混乱、数据失真,影响后续回测与统计准确性。

三、解决方案:基于WebSocket的稳定Tick订阅方案

经过多场景实测验证,第三方专业数据接口 是初创团队平衡成本、稳定性与开发效率的最优解。以AllTick API为例,其WebSocket推送模式可完美解决上述痛点,接入门槛低、部署高效,适合初创团队快速集成。

核心优势:无需频繁轮询接口,通过长连接实时订阅美股逐笔成交与报价,数据完整性、实时性显著优于传统方案,大幅降低系统开销,同时保证数据时序连贯。

以下是可直接复用的Python订阅代码:

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    print(data)  # 每条Tick信息会打印出来

ws = websocket.WebSocketApp(
    "wss://api.alltick.co/stock_ws",
    on_message=on_message
)

ws.run_forever()

WebSocket连接建立后,系统会持续推送实时逐笔成交数据,无需主动请求,可直接接入量化策略研发、回测流程,快速落地实战。

四、工程化落地细节(必看优化)

仅完成基础订阅不足以支撑长期稳定运行,结合实战经验,需重点处理4类问题,保障系统可靠运行:

  1. 网络异常处理:WebSocket连接断开后,添加自动重连逻辑,避免数据中断丢失;
  2. 数据去重:部分接口可能推送重复Tick数据,需添加去重逻辑,提升回测与分析准确性;
  3. 存储规划:Tick数据量巨大,提前设计存储方案(初创团队可先从本地数据库持久化入手),应对每日海量数据写入;
  4. 时间戳统一:不同数据源时间戳可能存在细微偏差,统计与回测前需统一基准,确保结果可信。

通用落地路径:实时订阅 → 本地持久化 → 数据清洗去重 → 聚合计算 → 策略回测/研究,既保证历史数据可追溯,也支持策略反复迭代验证。

五、成本与效率优化(初创团队最优路径)

对比交易所直连与自研抓取方案,采用第三方WebSocket接口(如AllTick API),对初创团队的收益尤为明显:

  • 成本优化:省去高昂的交易所授权费用与服务器运维成本,按使用量付费模式,更适配初创团队预算;
  • 效率提升:接口接入简洁,调试耗时缩短70%以上,研发人员可将更多精力投入策略模型本身,加速迭代;
  • 稳定性保障:交易高峰时段数据推送连续,避免因数据丢包导致的策略分析偏差,支撑盘口微观研究与高频策略落地。

六、总结与交流

对于初创FinTech团队、量化开发者而言,Tick数据的核心价值不在于“采集难度”,而在于“稳定、连续、可信”。选对适配自身资源的接入方案,能大幅降低研发成本、提升回测质量,让策略研发事半功倍。

本文方案已在多个初创团队落地验证,代码可直接复用,若在接入过程中有疑问、或有更优实战技巧,欢迎在评论区交流探讨——思否社区的价值,就在于开发者彼此分享、共同避坑。

HarmonyOS 动态卡片(form)踩坑合集:从 H5 加桌到 fd 生命周期

环境:HarmonyOS 5.0 / API 12 / ArkTS / 原子化服务
场景:H5 活动页点击按钮 → 底部弹 Dialog → 加一张带网络图+标题+logo+跳转链接的卡片到桌面;点击卡片回跳到 H5 页
时间:2026-04

这篇把最近做动态卡片踩的 15 个坑归档一下,从布局崩溃、fd 生命周期、跨进程数据,到 form_config 限制、安全加固,基本每个点我都真崩过/真查过文档/真找过 issue。写出来给后面做同类需求的同学避坑。


目录


一、整体架构

最终落地是单 widget 条目 + LocalStorage 数据分支渲染(理由见陷阱 9)。数据流:

H5 页面
  │  window.jsBridge.addDesktopCard(JSON.stringify(payload))
  ▼
承载 WebView 的页面.handleAddDesktopCard
  │  下载 imageUrl + logoUrl 到沙盒(卡片进程无法直接加载 http)
  ▼
底部加卡 Dialog
  │  AddFormMenuItem.formBindingData 注入 LocalStorage + formImages 传 fd
  │  callback → 写 Preferences(跨进程持久化)
  ▼
系统 AddFormMenuItem 处理 → 创建 form
  ▼
FormExtensionAbility.onAddForm(form 进程)
  │  读 Preferences → openSync 拿新 fd → formProvider.updateForm
  ▼
卡片组件(com.ohos.formrenderservice 渲染进程)
  │  @LocalStorageProp 订阅 title / imageName / logoName / subTitle / jumpUrl
  │  Stack + position 绝对定位渲染
  │  Image('memory://imageName') / Image('memory://logoName')
  ▼
用户点击卡片 → postCardAction / FormLink(action=router)
  ▼
主 UIAbility.onNewWant → 路由控制器
  │  校验 jumpUrl scheme 白名单(http/https)
  │  路由到 WebView 页面打开 jumpUrl

二、15 个核心陷阱

1. 卡片渲染进程的布局组件禁区(会崩整个进程)

卡片进程 com.ohos.formrenderservice 跑的是裁剪版 ArkTS 运行时。有些组件一旦踩到会崩整个进程,所有卡片(含别的 app 的)一起变白

组件/用法表现结论
Blank().layoutWeight(1)进程崩 → 所有卡片白屏❌ 禁用
Flex({ justifyContent: FlexAlign.End })进程崩❌ 禁用
Stack.alignContent(Alignment.Bottom)(modifier / 参数式)不按预期定位(表现像 TopStart)⚠️ 不稳定
Column + layoutWeight(1)短卡片上被压成 0 高⚠️ 谨慎
Stack() + child.position({x, y}) 绝对定位✅ 完全按坐标✅ 推荐

经验:复杂布局不要玩花的,用 Stack + 绝对 position 最稳。坐标从 onSizeChange 拿到的 cardHeight / cardWidth 按比例算。

2. AddFormMenuItem.parameters 的自定义 key 被系统过滤

调用端:

AddFormMenuItem({
  bundleName: '...',
  abilityName: 'EntryFormAbility',
  parameters: {
    // 这些自定义 key 会被系统过滤掉
    my_card_title: 'xxx',
    my_image_url: 'https://...'
  }
}, 'id', { ... })

FormExtension 里读 want.parameters 只有 ohos.extra.param.key.* 系统字段,自定义 key 全丢。

解决:业务数据走 formBindingData 选项注入 LocalStorage;同步用 callback 写 Preferences 给 FormExtension 跨进程读取。

3. @LocalStorageProp 的 key 命名限制

// ❌ 编译报错:Cannot use the key! The value of key can only consist of letters, digits and underscores
@LocalStorageProp('my.card.title') cardTitle: string = ''

// ✅
@LocalStorageProp('my_card_title') cardTitle: string = ''

点号 / 冒号 / 连字符全不行,只能字母数字下划线。

4. memory:// 的 fd 25 秒生命周期

现象:加卡 25 秒后图片消失。

原因

  • 卡片进程 formrenderservice 是共享进程,约 25 秒空闲回收
  • formImages: { imageName: fd } 的 fd 随进程销毁失效
  • Image('memory://imageName') 加载不到

解决

  • 持久化物理路径到 Preferences(而不是 fd)
  • FormExtension 的 onUpdateForm 触发时,用路径重新 openSync 拿新 fd → formProvider.updateForm 推新 formImages
onUpdateForm(formId: string) {
  const config = readFromPreferences(formId)
  if (!config) return
  const file = fileIo.openSync(config.localImagePath, fileIo.OpenMode.READ_ONLY)
  try {
    const formImages: Record<string, number> = {}
    const imageName = `img_${Date.now()}`
    formImages[imageName] = file.fd
    const data: Record<string, Object> = {
      imgName: imageName,
      formImages: formImages,
      // ... other fields
    }
    formProvider.updateForm(formId, formBindingData.createFormBindingData(data))
  } finally {
    fileIo.closeSync(file.fd)
  }
}

5. SPUtils 跨进程缓存不同步

Preferences 每个进程有自己的内存缓存。主进程写完 flush 到磁盘,但 FormExtension(独立进程)的缓存没变getObject 返回旧值。

// FormExtension 读取前必须:
SPUtils.removePreferencesFromCacheSync()  // 清缓存
const config = SPUtils.getObject<...>(KEY, {})  // 强制从磁盘读

这个坑排查了好几天,主进程确认写入了但 form 进程死活读不到,最后翻 issue 才发现缓存问题。

6. fd 泄漏:Dialog 多次 build 累积

@ComponentV2
export struct AddCardDialog {
  // ...
  private getCardFormBindingData(): formBindingData.FormBindingData {
    const data: Record<string, Object> = {}
    // ...
    // ❌ 每次 build 都 open 一次不关 — 多次重渲染后 fd 累积,可能 EMFILE
    const file = fileIo.openSync(localPath, fileIo.OpenMode.READ_ONLY)
    const formImages: Record<string, number> = {}
    formImages['img'] = file.fd
    data['formImages'] = formImages
    return formBindingData.createFormBindingData(data)
  }
}

修复:实例字段持有 fd,下次重建前 close 旧的,系统 callback 触发后统一 close(那时 fd 已被 IPC dup 到 form 进程,主进程可以释放):

private cardBindingFile: fileIo.File | undefined = undefined

private closeCardBindingFile(): void {
  if (this.cardBindingFile) {
    try { fileIo.closeSync(this.cardBindingFile.fd) } catch (_e) {}
    this.cardBindingFile = undefined
  }
}

private getCardFormBindingData(): formBindingData.FormBindingData {
  this.closeCardBindingFile()  // 先关旧的
  this.cardBindingFile = fileIo.openSync(localPath, fileIo.OpenMode.READ_ONLY)
  // ... 构造 data 用 this.cardBindingFile.fd
  return formBindingData.createFormBindingData(data)
}

// AddFormMenuItem callback 里:
callback: (error, formId) => {
  this.closeCardBindingFile()  // 系统已消费,可以关了
  // ...
}

7. http.request 缺 expectDataType 导致静默失败

// ❌ 默认行为依赖 Content-Type,CDN 返回不规范时 result 退化成 string
const response = await request.request(url, {
  readTimeout: 10000,
  connectTimeout: 10000
})
if (!(response.result instanceof ArrayBuffer)) {
  // 静默走不到这里,图片链路永远失败
}

// ✅ 显式声明
const response = await request.request(url, {
  readTimeout: 10000,
  connectTimeout: 10000,
  expectDataType: http.HttpDataType.ARRAY_BUFFER
})

这个也排查了好久。CDN 返回的 Content-Type 是 application/octet-stream 而不是 image/* 时,不加 expectDataType 就翻车。

8. WebView 加载 URL 的开放重定向风险

卡片点击后跳转 URL 是 H5 传入并持久化到 Preferences 的。如果不校验 scheme,javascript: / file: / 钓鱼域名都能塞进来

// 点击卡片 → postCardAction → Ability.onNewWant
handleWant(want: Want) {
  const rawJumpUrl = want?.parameters?.jumpUrl
  const jumpUrl = typeof rawJumpUrl === 'string' ? rawJumpUrl.trim() : ''
  if (!jumpUrl) return

  // 🔑 scheme 白名单
  const lower = jumpUrl.toLowerCase()
  if (!lower.startsWith('https://') && !lower.startsWith('http://')) {
    console.warn('jumpUrl rejected by scheme check:', jumpUrl)
    return
  }

  const param: Record<string, string> = { 'url': jumpUrl }
  router.pushOrReplace(WebPage, param)
}

纵深防御,不依赖下游 WebView 自己做白名单。

9. form_config.json 没法隐藏系统选择器

需求:动态卡片没数据来源时不应让用户从系统小组件选择器裸加。

试过的招:

  • isDefault: false — 只是不作默认尺寸,仍显示在选择器
  • formProvider.deleteForm这个 API 根本不存在(provider 端不能删卡,只有 host 端能)
  • 两个独立 widget 条目(正常卡 + 动态卡) — 动态卡照样出现在选择器
  • onAddForm 里 setTimeout 检测配置不存在就 deleteForm — 上面说了 API 不存在

最终合并回单 widget 条目,卡片组件内按 LocalStorage 是否有业务数据分支:

  • 从选择器裸加 → LocalStorage 空 → 走默认样式
  • 从 H5 Dialog 加 → LocalStorage 有数据 → 走动态样式
@Entry
@Component
struct MyCard {
  @LocalStorageProp('dimension') dimension: string = '0'
  @LocalStorageProp('my_card_title') cardTitle: string = ''
  @LocalStorageProp('imgName') cardImageName: string = ''
  @State cardHeight: number = 0

  build() {
    FormLink(...) {
      Row() {
        if (this.cardHeight > 0) {
          if (this.cardTitle || this.cardImageName) {
            this.dynamicCardLayout()  // 从 H5 加的卡
          } else {
            this.defaultCardLayout()   // 从选择器加的卡
          }
        }
      }
    }
    .onSizeChange((_o, n) => { this.cardHeight = n.height as number })
  }
}

10. 卡片下方的 app 名字标签是系统强制的

桌面卡片下方会显示一行"app 名字"标签(来自 EntryAbility_label),不受 form_config 的 displayName 控制displayName 只在系统小组件选择器里用。

想改这个标签只能动 app 名字本身,但那会影响桌面图标、应用商店展示等所有地方,一般不动。

11. 多个 @Entry 文件的顶层 const 冲突

// widgetA.ets
const OPEN = 'open'
@Entry @Component struct WidgetA { ... }

// widgetB.ets
const OPEN = 'open'  // ❌ 编译报 Cannot redeclare block-scoped variable 'OPEN'
@Entry @Component struct WidgetB { ... }

hvigor 把所有 @Entry 文件打进同一作用域。解决:不同 @Entry 用不同常量前缀,或合并成一个 @Entry。

12. ImageFit 选型

模式效果适用
Cover保比例 + 裁剪图片比例接近卡片
Fill拉伸铺满(可能变形)比例不可控时首选
Contain保比例 + 留白不能裁剪任何内容

H5 传的图比例不可控 → 用 Fill(轻微变形换完整铺满)。

13. onSizeChange 守卫不能删

@State cardHeight: number = 0

@Builder content() {
  // 用到 cardHeight 的表达式(例如 cardHeight * 0.4 做绝对定位)
  // cardHeight 在 onSizeChange 触发前是 0,导致异常
}

build() {
  FormLink(...) {
    Row() {
      if (this.cardHeight > 0) {  // 🔑 必须守卫
        this.content()
      }
    }
  }
  .onSizeChange((_o, n) => { this.cardHeight = n.height as number })
}

14. LogUtil 默认构建过滤日志

项目里的 LogUtil.i()Logger.isPrint 控制,default build 里通常是 false,关键日志筛不到。解决:

  • 关键链路用绕过过滤的版本(例如我们封装的 LogUtil.infoForce
  • 卡片渲染进程的 console.log 输出到 com.ohos.formrenderservice tag,不在 app bundleName 下,用 app 过滤器筛不到

15. ArkTS 对象字面量的类型检查

// ❌ arkts-no-untyped-obj-literals
router.push(page, { url } as Record<string, string>)

// ✅ 先声明变量
const param: Record<string, string> = { 'url': url }
router.push(page, param)

直接把对象字面量塞进函数参数不行,即便有 as Xxx 强转也不行。


三、布局最佳实践

Stack + 绝对 position 模板

Stack() {
  // 背景图(最底层)
  Image(this.cardImageName ? ('memory://' + this.cardImageName) : $r('app.media.fallback'))
    .width('100%').height('100%')
    .objectFit(ImageFit.Fill)

  // 左上角 logo(绝对定位)
  Image(this.cardLogoName ? ('memory://' + this.cardLogoName) : $r('app.media.default_logo'))
    .width(14).height(14)
    .borderRadius(7)
    .position({ x: 10, y: 6 })

  // 中下方文字块(按 cardHeight 比例放)
  Column() {
    Text(this.cardTitle)
      .fontWeight(FontWeight.Medium).fontSize(13).fontColor(Color.White)
    Text(this.cardSubTitle)
      .fontWeight(FontWeight.Regular).fontSize(10).fontColor('#CCFFFFFF')
      .margin({ top: 2 })
  }
  .width('100%')
  .padding({ left: 10, right: 10 })
  .alignItems(HorizontalAlign.Start)
  .position({ x: 0, y: this.cardHeight * 0.4 })
}
.width('100%').height('100%')
.borderRadius(12)
.clip(true)

FormExtension 双 fd 注入(背景 + logo)

private async updateCard(formId: string, bgPath: string, logoPath: string): Promise<void> {
  let bgFile: fileIo.File | undefined
  let logoFile: fileIo.File | undefined
  try {
    const data: Record<string, Object> = {
      my_card_title: this.title,
      my_card_sub_title: this.subTitle,
    }
    const formImages: Record<string, number> = {}

    if (isValidFile(bgPath)) {
      bgFile = fileIo.openSync(bgPath, fileIo.OpenMode.READ_ONLY)
      const imageName = `img_${Date.now()}`
      formImages[imageName] = bgFile.fd
      data['imgName'] = imageName
    }
    if (isValidFile(logoPath)) {
      logoFile = fileIo.openSync(logoPath, fileIo.OpenMode.READ_ONLY)
      const logoName = `logo_${Date.now()}`
      formImages[logoName] = logoFile.fd
      data['logoName'] = logoName
    }
    if (Object.keys(formImages).length > 0) {
      data['formImages'] = formImages
    }

    await formProvider.updateForm(formId, formBindingData.createFormBindingData(data))
  } finally {
    // 两个 fd 都要 close
    try { if (bgFile) fileIo.closeSync(bgFile.fd) } catch (_e) {}
    try { if (logoFile) fileIo.closeSync(logoFile.fd) } catch (_e) {}
  }
}

四、关键 API 速查

API作用注意
@kit.ArkUI.AddFormMenuItem系统"添加桌面卡片"按钮自定义 parameters 会被过滤
@kit.FormKit.formBindingData.createFormBindingData(data)构造初始卡片数据data 可含 formImages: {name: fd}
@kit.FormKit.formProvider.updateForm(formId, bindData)外部更新卡片 LocalStorage只能在 FormExtension 调
@kit.FormKit.FormExtensionAbility卡片生命周期入口onAddForm / onUpdateForm / onFormEvent / onRemoveForm
@LocalStorageProp(key)卡片组件订阅 LocalStoragekey 只能字母数字下划线
Image('memory://xxx')用 fd 机制加载图片依赖 formImages 中的 fd
FormLink({ action, abilityName, params })卡片内点击区默认 action=router
postCardAction(this, {...})主动触发 card action参数经 want.parameters
fileIo.openSync/closeSync文件 fd 管理卡片进程无法直接打开 http URL
http.request(url, opts)下载图片必须带 expectDataType
Preferences removePreferencesFromCacheSync清内存缓存跨进程读前必须调

五、调试与刷新卡片的办法

刷新桌面卡片(HAP 更新后加载新代码)

重点:HAP 更新不会自动重载卡片渲染进程,旧卡片一直跑旧代码。由轻到重:

  1. 强制重启卡片渲染进程(推荐)

    hdc shell "aa force-stop com.ohos.formrenderservice"

    系统自动拉起新进程,加载最新 HAP。已有卡片会重新走 onAddForm / onUpdateForm,新 fd 重新注入。

  2. 强制重启自己的 atomic service 进程(FormExtension 代码改了时)

    hdc shell "aa force-stop com.<your.bundle.name>"
  3. 删卡重加(最保险)

    桌面长按卡片 → 删除 → 从入口重新添加。onAddForm 必定触发,fd 保证是新的。

  4. 重启设备(终极手段)

抓日志(UTF-16 编码坑)

PowerShell 重定向的 hilog 是 UTF-16 编码,Python 读要显式 decode:

with open('log.txt', 'rb') as f:
    data = f.read()
try:
    text = data.decode('utf-16')
except UnicodeError:
    text = data.decode('utf-8', errors='ignore')

CLI 构建 + 安装

export DEVECO_SDK_HOME="/path/to/openHarmony"
export HOS_SDK_HOME="$DEVECO_SDK_HOME/sdk/default"
export PATH="$DEVECO_SDK_HOME/tools/node:$DEVECO_SDK_HOME/tools/hvigor/bin:$DEVECO_SDK_HOME/tools/ohpm/bin:$HOS_SDK_HOME/openharmony/toolchains:$PATH"

node "$DEVECO_SDK_HOME/tools/hvigor/bin/hvigorw.js" assembleHap --mode module -p product=default

# 注意:hdc install 拼接 CWD,必须 cd 到项目根目录再用相对路径
cd "/path/to/your-project"
hdc install -r "entry/build/default/outputs/default/entry-default-signed.hap"

总结

鸿蒙动态卡片能做,但坑比想象多:

  • 布局端:只能用 Stack + 绝对 position,其它 Flex/Blank 用法都可能崩进程
  • 数据传递:自定义 parameters 会被过滤,靠 formBindingData + 跨进程 Preferences
  • 图片:卡片进程不能加载 http,必须下载到本地用 fd 注入;fd 25 秒回收,要在 onUpdateForm 重新 openSync
  • 安全:H5 传入的 URL 必须做 scheme 白名单,不然开放重定向
  • 代码健壮性:fd 配对 close,http.request 带 expectDataType,FormExtension 入口 try/catch
  • 刷新卡片:HAP 更新不会自动重载,force-stop formrenderservice 或删卡重加

大部分坑官方文档里要么没写要么一笔带过,只能靠踩。希望对你有帮助。


如果有更多经验或纠错欢迎评论交流。

我经常听到刚接触数据分析的人的抱怨:”SQL要学,Python要学,报表要做,汇报还要写,感觉门槛高得吓人。”

但真相是,SQL和Python更多是数据挖掘工具,PPT、BI不过是数据展示手段。学会了当然好,不会也别慌,因为数据分析的核心从来不是技术,而是思维。

真正厉害的数据分析,是帮你梳理业务、找到方向、解决问题。这并不需要你懂所有技术,只需要记住3个步骤,掌握3个模型,就能轻松上手。

今天,我就把这3个步骤和3个模型讲清楚,直接帮你建立这套思维习惯!

一、3个步骤

我一直认为真正有效的分析,第一步永远不是看报表,而是先把目标拆清楚。这里就有一套很实用的思路:确定目标——列出公式——确认元素。

1.确定目标

分析之前,可以先问问自己:这次最想改善的结果是什么。

是增长销售额,是提升转化率,是提高复购,还是降低流失。因为不同的目标会导致完全不一样的分析路径。

我们举个例子吧,假设一家连锁烘焙品牌最近最关心的是营业额增长。那这次分析就不能泛泛地看用户、看门店、看商品,而是要围绕营业额这个核心结果去拆。

目标一旦定清楚,后面看什么数据、做什么分析、提什么动作,才不会跑偏。

2.列出公式

目标确定之后,下一步不是继续盯着结果看,是要把结果拆开。

因为任何一个业务结果,背后都不是凭空发生的,它一定由几个关键因素共同决定。

还拿连锁烘焙品牌举例。如果核心目标是营业额,那营业额大致可以拆成到店人数、下单转化率、客单价这几个部分。再往下拆,客单价又可能受套餐购买率、加购率、单次购买件数影响。

你会发现,一旦把目标拆成公式,原本模糊的问题就开始变具体了。

营业额上不去,到底是客流不够,还是进店的人很多但下单少,还是下单了但买得不够多。

这一步的价值就在于,它能帮你从结果走到原因。

3.确认元素

目标拆完以后,还不能马上开始做动作。因为公式里不是每个元素都值得你下手。

有些因素你能直接影响,有些却未必。

继续用刚才的例子。营业额拆开之后,有些门店可能会发现,客单价主要受商品结构影响,下单转化率更多跟导购话术、活动力度、收银效率有关,而到店人数则可能和商圈位置、投放曝光、天气变化相关。

这时候就要继续判断,哪些元素是当前阶段最值得优先发力的。

这一步说白了,就是找到真正的发力点。

做到这里,其实分析已经成功一半了。

因为你已经不是在模糊地讨论数据,而是在清楚地思考并尝试回答这三个问题了:

  • 我的目标是什么
  • 我想要到结果由什么组成
  • 现阶段我应该最先从哪开始行动

这一套3个步骤的思路教会你的不是教你怎么做图表,而是能够真正帮你把问题拆成能行动的东西。

二、3个模型

当你用前三个步骤找到关键元素后,接下来就会进入真正的实战阶段。

这个时候,通常会遇到三个最常见的问题:

  • 关键指标怎么做上去
  • 如何给不同对象分配不同资源
  • 怎么判断策略到底有没有效果

而这三个问题,正好对应三种特别常用的分析模型。它们分别是漏斗模型、多维坐标、分组表格。在理解这三个模型时最重要的是要明白它们分别适合解决什么问题。

1.漏斗模型

漏斗模型专门解决那些需要经过多步才能完成的结果问题。

比如在线教育平台想提升试听变转正的比例,用户需要从浏览课程内容、预约试听、填写信息、参与试听课到最后付费报名,一步步走下来,每个环节都会有人流失。

这个时候,漏斗模型能帮你找到问题的关键在哪。

比如内容页访问量很大,但预约试听的人却很少,可能是课程吸引力不够或者预约入口太隐蔽。如果预约人数都不错,但到课的人很少,可能提醒不到位或者时间安排不贴合。如果试听课参加率挺高,但真正付费的人还不够,那问题可能就在课程定价、设计内容或者顾问跟进上。

它的价值就在于,让你不再泛泛地说“转化差”,而是清楚地知道问题到底卡在吸引、承接还是成交上。

漏斗分析不仅能提升单层转化,还能优化整条用户路径。

有时候转化率低,并不是某一步有问题,而是整个流程太复杂。比如用户想领优惠券,还得先关注账号、再注册会员、填写资料,然后再点开活动页。走太多步,很多人根本没坚持到最后。这种情况下,与其死抠每一步转化率,不如直接把流程简化,环节少了,效果更好。

所以,漏斗模型特别适合用来提转化、提规模、找关键问题。 只要你的目标要经过多个步骤达成,漏斗分析就可以派上大用场。

2.多维坐标

如果说漏斗模型解决的是过程中的转化问题,那多维坐标解决的就是分层运营的问题。

现实中,最忌讳的就是把用户、商品、门店这些对象都当成同一类来对待。每个对象的特征、价值和潜力都不一样,硬套统一策略,不仅资源浪费,效果也不会好。这个时候,多维坐标就能帮你把对象清晰分层,再因人而异地制定策略。

来看看一个例子。

一家母婴电商平台在做会员运营时,单看消费金额,只知道谁买得多;单看购买频次,只知道谁来得勤。这两个维度如果单独看,信息不完整。但当这两个维度放在一起,用户差异立马显现。

高消费高频次的用户是平台最核心的人群。高消费低频次的用户价值也很高,但习惯还不够稳定。低消费高频次的用户黏性不错,但还有提升空间。至于低消费低频次的用户,大多是普通用户或待激活人群。

这时候,分层后的运营策略会明显不同。

对于高消费高频次用户,重点是维护,确保他们长期留在平台。高消费低频次用户,可以通过会员日活动,慢慢培养消费习惯。低消费高频次用户更适合做套餐推荐或连带促销,提升每单金额。而低消费低频次用户,适合先筛查,再决定是否要重点激活,避免浪费资源。

多维坐标最大的价值就在这里。它不是为了单纯分四类看着好,而是帮助你精准投放资源,让每一次动作都更加高效、精准。

因此,多维坐标特别适合用来解决分层运营、资源分配和策略优先级判断的问题。 只要你面对的对象有两个或更多维度需要考虑,这个方法都能派上用场。

3.分组表格

有时候做数据分析会发现,前期拆解目标、找到问题都没毛病,但最后却卡在了验证上。那么此时就需要分组表格上场了,它特别适合用来分析那种会随时间变化的指标,比如留存、复购、活跃、回访这些。

它的核心思路很简单,就是把同一时间进入系统的对象分成一组,然后观察这组人在接下来的表现。

举个例子,一家健身平台想分析新用户的留存情况,可以把每周新开卡的用户分组。第一周开卡的用户是一组,第二周的是另一组。然后分别观察这些组的数据,比如到店率、续费率、课程完成率。

这样分析,和只看整体总量完全是两回事。

如果你发现某段时间活跃度飙升,可能是因为新客量暴增,也可能是老用户回流,还可能上周的留存更好了。数据只看总量,很难判断具体原因,但一旦分组后,你就能看清问题出在哪。

分组表格让你可以横向看单组用户随时间的表现,了解生命周期走势;也可以纵向看某一时间点的整体数据,搞明白它是由哪些批次构成的。

分组表格的厉害之处在于,它能把原本复杂的数据变化拆开,让问题清晰可见。

因此,分组表格是用来监测变化、验证策略效果和识别趋势的利器。 如果你要判断一个策略是否有效,分组表格几乎是一个绕不过去的工具。

三、高效落地

看到这里,你可能会不禁心想,上面的三个步骤和三个模型看起来都挺简单的,我也认同这个观点,因为我一直都觉得真正难的其实是落地。

原因很简单,因为在真实业务里,卡住分析的地方往往不是思路,而是流程。这时候想要高效地落地,除了方法,还得有一套能支撑落地的工具和流程。

我认为一个比较通畅的做法,就是固定一套分析流程:

  • 先把目标拆成指标,再把指标对应到数据源
  • 再把关键链路搭起来,做成可重复使用的主题分析
  • 最后把结果沉淀成图表、看板和分析结论,让团队能持续看、持续用

四、总结

这套框架里的三个步骤和三个模型,已经足够应对大多数日常工作场景了。我建议大家先把它吃透,用到自己的业务里。至于其他更复杂的技巧,留着日后遇到新问题再深入学习。相信等你真正掌握了这三个步骤和三个模型,那些原本看起来杂乱无章的数据就会变得清晰、有逻辑,还能转化成具体的行动。这才是数据分析最大的意义。

引言

在过程工业(石油化工、电力、精细化工、冶金等)自动控制系统中,气动薄膜调节阀作为“最终执行单元”,其选型合理性直接决定控制精度、系统稳定性与设备寿命。当前许多选型工作仍停留在“口径+压力等级+材质”的基础层面,导致投运后频繁出现调节震荡、噪音超标、内漏、卡涩甚至执行机构推力不足等问题。

科学的选型应遵循从控制目标出发,逐层结合工况边界、阀内件结构、执行机构匹配,并兼顾经济性与维护性的收敛路径。本文基于工程实践,系统阐述可量化、可校核的选型方法,并提供典型计算示例,供工程技术人员参考。

一、明确调节阀的定位:控制元件而非通断件

选型前必须回答三个核心问题,它们决定后续所有方向:

  1. 控制变量:流量、压力、温度还是液位?不同变量对阀门特性要求不同。例如流量控制需关注可调比,压力控制需保证调节稳定性,温度控制需兼顾耐温与抗冲蚀。
  2. 控制精度:粗调还是连续精调?粗调对可调比、响应速度要求较低;连续精调(如反应器进料)则需严格控制偏差,对流量特性线性度和执行机构分辨率要求较高。
  3. 系统工况:是否存在频繁扰动、负荷大范围波动或长期小开度运行?这些是选型中的高风险点。

二、调节性能的六个量化指标

2.1 流量特性匹配

常见流量特性及适用场景:

  • 等百分比特性:小开度时流量变化平缓,大开度时灵敏。适用于负荷波动大、调节范围广的工况,如换热器温度控制、反应器进料。
  • 线性特性:流量与开度成比例,调节精度高。适用于稳态控制,如恒压供水、液位恒定控制。
  • 快开特性:小开度即达大流量,主要用于开关或安全联锁。

工程经验:

  • 换热系统 → 等百分比优先。
  • 压力控制回路 → 线性特性更易稳定。
  • 液位控制 → 大惯性储罐用线性,小惯性缓冲罐用等百分比。

2.2 小开度控制能力

阀门长期运行在10%开度以下,易出现抖动、喘振或失控。常见原因:

  • Cv选型过大,正常流量仅需极小开度。
  • 流量特性不匹配,线性阀在小开度时增益过高。
  • 执行机构分辨率不足,无法精确定位。

解决措施:

  • 正常开度控制在60%~80%,最小开度不低于15%。
  • 小流量工况选用多级节流结构(套筒阀)或微小流量专用阀(Cv可低至0.001~0.1)。

2.3 可调比

可调比 = 最大可控流量 / 最小可控流量。

  • 一般工艺:≥30:1。
  • 精细控制:≥50:1。

套筒阀可调比通常优于单座阀(50:1~100:1 vs 30:1),适合大范围负荷波动工况。

2.4 响应速度与稳定性

气动薄膜执行机构响应快(毫秒级)、本质安全,但实际性能受限于:

  • 定位器精度:智能定位器(支持HART/Profibus)可将死区降至0.1%以下,普通定位器易产生滞环。
  • 气源质量:含水、含油会导致膜片老化、喷嘴堵塞。建议加装过滤干燥器。

2.5 流阻与能耗——系统级考量

阀门流阻影响泵/压缩机能耗。正常开度控制在50%~70%时,流阻与调节性能综合最优。避免长期小开度节流损失,也避免Cv过小导致阀门长期大开度(失去调节裕量)。

工程建议:结合管道系统总阻力合理核算Cv,使阀门压降占系统总压降的10%~20%。

2.6 噪音与汽蚀控制(高压差工况重点)

当存在以下条件时必须专项控制:

  • 压差ΔP较大(液体>2.5MPa,气体>0.5MPa)。
  • 液体接近汽化压力(闪蒸/汽蚀风险)。
  • 气体马赫数>0.3(马赫数 = 流速/当地音速,需根据介质密度、压力计算)。

解决方案:

  • 多级降压:其原理是将一次高压差分解为多个逐级降低的小压差,每级压降均低于临界值,从而避免流体在任一阶段达到汽化压力或产生激波。 多孔套筒或迷宫式内件可实现该功能,通常可降低噪音20~30dB。
  • 抗汽蚀内件:硬化阀芯、特殊阀座设计。
  • 降噪附件:消声罩、扩散器。

三、泄漏等级与切断压差协同校核

这是选型中最易出错的环节。泄漏等级(Class IV/V/VI)决定密封形式,切断压差(ΔPmax)决定执行机构推力需求。常见错误:只提泄漏等级,不提供ΔPmax,导致执行机构推力不足,阀门关不严。

校核方法:

  1. 计算阀芯在ΔPmax下所受的介质力(考虑不平衡力或平衡力)。
  2. 执行机构输出力 ≥ 阀芯受力 × 安全系数(1.25~1.5)。
  3. 对于气关式(失气开),还需校核弹簧力在信号为零时能否保证关闭。

示例:某单座阀,ΔPmax=2.0MPa,阀座直径50mm,介质为水。阀芯所受不平衡力 ≈ ΔP × 阀座面积 = 2.0×10⁶ × (π×0.025²) ≈ 3927N。加上密封压紧力与摩擦力,总需求约5000N。执行机构输出力应至少达到5000×1.3=6500N。

气体工况补充:对于可压缩流体(气体、蒸汽),Cv计算公式不同于液体。常用气体Cv公式为:
image.png

其中 Qg 为标准状态下体积流量(m³/h),G 为气体比重(空气=1),T 为绝对温度(K),P₁、P₂ 为阀前、阀后绝对压力(kgf/cm²)。当压差超过临界压差(P₁ - P₂ > 0.5P₁)时,需按阻塞流修正。

四、结构选择与介质特性匹配

不同介质适配不同阀结构,下表为工程推荐:

工况类型推荐结构选型说明
含颗粒/易结晶球阀 / 偏心旋转阀(角行程)流道通畅,无死角,自洁性好
一般洁净介质单座阀 / 套筒阀(直行程)调节精度高,密封性好
高压差+洁净介质套筒阀多级节流,可调比高,抗汽蚀
易结垢介质大流道结构(V型球阀、偏心阀)减少附着,便于清洗

角行程阀门防堵性能较好,但并非所有工况必需,需结合介质特性判断。

五、材料选择:寿命匹配而非“能用”

材料选型需同时考虑三种破坏机制:

  • 腐蚀:化学侵蚀,选耐蚀合金。
  • 冲蚀:颗粒冲击,内件需硬化处理。
  • 汽蚀:气泡破裂冲击,选用抗汽蚀合金及特殊流道。

工程选型参考表(含适用温度范围):

工况阀体材料适用温度范围(℃)内件材料
一般工况(水、蒸汽、油)WCB / CF8-29~42513Cr / 304
腐蚀介质(酸、碱)316L / 双相钢(2205)-40~300(双相钢)316L / 哈氏合金
高温(>450℃)WC6 / WC9-29~595(WC6),-29~650(WC9)硬化不锈钢(如17-4PH)
强腐蚀+冲蚀哈氏合金(C276) / 衬氟-196~400(哈氏),-29~180(衬氟)司太立合金堆焊

内件经硬化处理(如司太立堆焊、碳化钨喷涂)后,在含颗粒或高压差工况下使用寿命可延长3~5倍。

六、执行机构匹配:推力与分辨率并重

气动薄膜执行机构关键参数:膜片面积、弹簧范围(气开/气关)、行程。

选型核心:

  1. 最大压差下能可靠关闭:输出力 ≥ 阀芯受力 × 安全系数。
  2. 最小信号下具备调节分辨率:避免死区过大,建议配置智能定位器。

工程建议:

  • 预留20%~30%推力裕量,应对气源波动、填料摩擦增加等因素。
  • 弹簧范围选择:气开(失气关)用于防泄漏,气关(失气开)用于防超压。
  • 智能定位器可提升精度至±0.5%,并支持在线诊断。

七、经济性:全生命周期成本(LCC)

调节阀的经济性不能只看初始采购价,而应综合以下维度:

成本维度工程意义
初始采购成本一次性投入
维护频率影响人工及备件成本,高频维护导致停机损失
内件寿命更换周期越短,长期成本越高
能耗影响阀阻大 → 泵/风机长期高能耗

在连续生产系统中(如炼油、化工),一次非计划停机的损失往往超过阀门采购价格的10~100倍。因此,在关键回路中适当提高配置等级,从全生命周期看通常更经济。

八、工程实用选型路径(九步法)

  1. 明确控制目标:变量、精度、扰动特性。
  2. 确定工况边界:最大/最小流量、进出口压力(ΔPmax)、温度、介质特性。
  3. 计算Cv并校核开度:液体用 Cv = Q √ SG / ΔP ,气体按前述公式,确保正常开度50%~70%,最大≤80%,最小≥15%。
  4. 选择流量特性:根据负荷波动与控制系统类型确定。
  5. 判断汽蚀/噪音风险:若ΔP超过临界值,选用多级降压或抗汽蚀内件。
  6. 选择阀体结构:按介质特性与压差从表中匹配。
  7. 选材(阀体+内件):按腐蚀、冲蚀、温度综合确定,参考材料表。
  8. 校核执行机构推力:计算最大压差下所需推力,留足裕量。
  9. 确定附件:定位器(智能/普通)、电磁阀、限位开关、过滤减压阀等。

结语:选型的本质是“匹配”

气动薄膜调节阀的科学选型,不在于参数堆砌,而在于是否真正匹配了工艺的动态特性与极限工况。从控制目标出发,逐层量化校核流量特性、小开度能力、可调比、响应速度、流阻能耗、噪音汽蚀、泄漏等级与切断压差、结构材料、执行机构推力,并兼顾全生命周期成本,才能实现稳定、可靠、经济的运行。

正确的选型逻辑远重要于产品品牌。即使选用不同厂家产品,只要匹配合理,系统同样能稳定运行;反之,即便配置再高,若与工况失配,故障仍难以避免。希望本文提供的工程化方法能为过程工业技术人员提供实用参考。

你不需要 H100 集群,也不需要每月 $200 的 API 账单。一台 16GB 显存的笔记本,就能跑一个推理能力显著超越同级别开源模型的本地模型。

这个模型是什么?

Qwopus3.5 是开发者 Jackrong (JIRONG) 发布的开源大语言模型系列。名称取自 Qwen + Opus — 基座是阿里的 Qwen3.5-27B,训练目标是借鉴 Claude Opus 的推理风格来提升小模型的推理质量。

它不是简单的知识蒸馏,而是通过 Reasoning SFT(推理监督微调)让 27B 参数的模型学会了结构化推理。所有训练和测试均由作者自费在 Google Colab 上完成 — 这意味着你也可以复现整个流程。

当前最新版本为 v3.5,相比 v3 使用了约 2 倍的 SFT 训练数据,在泛化能力和 agentic 编程任务上进一步提升。


为什么值得关注?

在本地模型的世界里,27B 是一个关键的参数量级 — 大到足以产生高质量推理,小到能在消费级硬件上流畅运行。Qwopus 在这个量级上做到了三件事:

  1. 推理能力越级:通过 Reasoning SFT,27B 模型展现出超越同参数量级模型的多步推理能力
  2. 创意不打折:前端/UI 生成、游戏开发等创意任务上,社区实测表现亮眼
  3. 零门槛部署:GGUF、MLX 格式齐全,LM Studio 一键加载,无需折腾环境

模型版本一览

官方发布

模型参数量格式说明
Qwopus3.5-27B-v3.527BSafeTensors最新版,2x SFT 数据,推荐
Qwopus3.5-27B-v327BSafeTensors稳定版
Qwopus3.5-27B-v3.5-GGUF27BGGUFv3.5 量化版,适合本地部署
Qwopus3.5-27B-v3-GGUF27BGGUFv3 量化版
Qwopus3.5-9B-v3-GGUF9BGGUF轻量版,适合低显存设备
MLX-Qwopus3.5-9B-v3-6bit9BMLXApple Silicon 优化版

社区衍生

社区还提供了 GPTQ、AWQ、i1-GGUF、abliterated 等多种量化和变体版本,可在 Hugging Face 搜索 Qwopus 查看。


技术深度:Reasoning SFT 到底做了什么?

这是理解 Qwopus 的关键。如果你只记住一件事,记住这个:Reasoning SFT 不是让模型学新知识,而是教它如何使用已有的知识去推理。

基座与微调

  • 基座模型unsloth/Qwen3.5-27B(Qwen3.5 的 unsloth 优化版)
  • 微调工具Unsloth,支持在 Google Colab 上完成全流程训练
  • 许可证:Apache-2.0

Reasoning SFT vs 知识蒸馏

很多人看到"Opus"就以为是从 Claude 蒸馏知识。实际上,Qwopus 的训练方法完全不同:

知识蒸馏Reasoning SFT(Qwopus 的方法)
目标让小模型模仿大模型的输出让模型学会推理的过程和结构
数据大模型的输入-输出对高质量长 Chain-of-Thought 数据
效果模仿格式,但推理深度有限激活模型已有的潜在知识
泛化局限于训练分布可迁移到未见过的任务类型

相关论文 Rethinking Generalization in Reasoning SFT(arXiv:2604.06628)指出:

推理 SFT 的泛化能力是动态的、有条件的 — 取决于优化程度、数据质量和模型本身的能力。短期训练可能低估泛化效果,域外性能常呈现"先降后升"的恢复模式。

换句话说:训练初期模型可能在某些任务上变差,但随着训练深入,推理能力会"涌现"并迁移到新领域。

Act-then-refine — 先行动,再优化

Qwopus 的推理模式不是简单地"想完再说",而是 Act-then-refine

  1. 模型先生成初步回答(Act)
  2. 通过 <think>...</think> 标签进行结构化思考(Refine)
  3. 输出经过验证和修正的最终结果

这种方式比简单的长 CoT 模仿更有效,尤其适合编程和多步骤任务。v3 版本放弃了 v2 的浅层 CoT,转向结构化可验证推理链,内部逻辑更严谨。

v3 → v3.5:不换架构,只加数据

v3.5 没有引入新架构、RL 阶段或模板重设计,纯粹通过扩大 SFT 数据量(约 2 倍)来增强泛化能力。这本身就是一个有趣的实验结论 — 在 Reasoning SFT 框架下,数据量的扩展能直接转化为泛化能力的提升。

训练数据覆盖:数学、编程、谜题、多语言对话、指令遵循、多轮交互和 STEM 任务。


评测表现

v3 vs v3.5 对比

MMLU-Pro 子集(280 题):

版本正确总数准确率变化
v325028089.29%
v3.525328090.36%+1.07%
注:由于算力限制,v3.5 仅在 v3 使用的同一 280 题子集上评测(完整 MMLU-Pro 约 12000 题),结果仅供版本间纵向对比,不可与其他模型的完整评测直接比较。

作者自建 Agentic 编程测试(44 题):

版本通过总数通过率
v3424495.5%
v3.5434497.7%
注:这是作者自建的 44 题测试集,非标准 SWE-bench(500+ 题)。由于样本量小且非标准化,通过率不可与其他模型在 SWE-bench 上的成绩直接比较。

v3.5 的关键提升在于多步骤 agentic 编程:能通过工具调用读取源码、诊断 timezone 解析 bug 并提出修复方案,而 v3 未能定位根因。

官方指出推理 SFT 存在能力权衡:多步推理能力显著提升,但在部分对齐敏感的基准上可能出现轻微回退。

与基座模型的对比

Qwopus 目前缺乏在标准完整 benchmark 上的评测数据,因此无法与其他模型进行严格的横向对比。以下仅展示 Qwopus 相对于其基座模型 Qwen3.5-27B 的提升:

  • MMLU-Pro(280 题子集):从基座的表现提升至 ~90.4%,表明 Reasoning SFT 对推理类题目有正向效果
  • Agentic 编程(44 题自建集):v3.5 达到 97.7% 通过率,优于 v3 的 95.5%
  • MATH500、HumanEval、GSM8K:官方仅给出定性描述"优秀",未公布精确分数

待作者在完整标准 benchmark 上发布评测结果后,才能与 Gemma 4、Llama 4 Scout 等同级别模型进行公平比较。

已观察到的特点:

  • 推理深度:Qwopus 通过 Reasoning SFT 在多步推理上有明显提升,在复杂编程任务上表现突出
  • 创意与前端生成:社区实测显示 Qwopus 在 UI/前端代码生成中的创意多样性和完成度优于 Gemma 4
  • 对齐权衡:推理 SFT 的代价是在部分对齐敏感基准上可能出现 1-2% 的轻微回退
  • 部署门槛:Qwopus 27B Q4 量化仅需 ~16GB 显存,与 Gemma 4 27B 相当

实测一:前端创意生成 — Qwopus v3 vs Gemma 4

光看 benchmark 数字不够直观。以下是社区用户使用完全相同的提示词(生成一个关于 "Divine Scalar Field Hypothesis" 的学术预印本网站),在本地环境下的公平对比。

Qwopus v3 — 三次生成,三种截然不同的风格

测试风格亮点
#1深空科幻星空背景、彩虹渐变标题、粒子动画、指针悬停高亮
#2学术极简纯白背景、左侧导航栏、经典论文排版,仅加一行提示即切换风格
#3暗黑现代荧光渐变、3D 倾斜卡片、悬停发光、指针拖尾动画,视觉冲击力极强

三次生成,三种完全不同的设计语言。这说明模型在创意生成上有较强的多样性。

Gemma 4 — 同一提示词

  • 布局基本正确,但创意平平,动画较少,更像"基础 Qwen 的升级版"
  • LaTeX 数学渲染出错(Qwopus 每次都完美)
  • 无法通过工具调用直接写文件(需手动复制)

其他社区反馈

  • 用 Pac-Man 游戏提示测试:Qwopus v3 一次生成完整可玩游戏,基础 Qwen 3.5 27B 只搭了框架且角色移动有问题
  • 社区评价:"The sauce is baked into the weights!"(精华已深度融入权重)

结论:在前端/UI 设计、创意生成、动画交互等需要审美 + 细节 + 多样性的场景中,Qwopus v3 在社区实测中表现优于 Gemma 4。这不是全面 benchmark,但对本地前端开发、原型设计场景来说值得一试。


实测二:逻辑推理 — 一道"简单"的洗车题

测试环境:Qwopus3.5 v3.5 Q6_K 量化,Apple M3 Pro 36GB

以下测试展示了 Reasoning SFT 在日常逻辑推理中的实际效果。

测试题

我想洗车,我家距离洗车店只有 50 米,请问你推荐我走路还是开车去呢?

看似简单,实则包含一个隐含前提陷阱 — 大多数人(和模型)会被"50 米"的短距离误导,直觉回答"走路"。

Qwopus 的推理过程

模型展现了完整的多层推理:

  1. 识别核心矛盾:目标是"洗车"而非"去洗车店",因此车必须物理到达洗车店
  2. 逐项排除:走路去 → 人到了但车没到 → 无法完成洗车目的
  3. 考虑边缘情况:上门服务、拖车服务等替代方案,并合理排除
  4. 识别幽默陷阱:意识到 50 米的距离让"开车"显得荒谬,但逻辑上是唯一正确选择
  5. 给出实用建议:开慢点避免沾灰、确认是否有上门服务等

最终结论正确:必须开车去,因为这是让车到达洗车店的唯一合理方式。

为什么这道题能说明问题?

这道题测试的不是知识量,而是推理质量:

  • 抓住隐含前提:车需要到店,不是人需要到店
  • 抵抗直觉偏差:距离短 ≠ 应该走路
  • 多角度验证:不是给出第一反应,而是反复检验结论

这正是 Act-then-refine 推理模式的典型体现:先分析问题结构,再逐步验证,最终输出经过多轮检验的答案。不少同级别模型会直接回答"走路",因为它们更倾向于模式匹配而非深入推理。


适用场景

场景说明Qwopus 的优势
编程辅助代码阅读、bug 诊断、修复建议多步骤 agentic 工作流,97.7% 通过率
前端/UI 生成一次生成高质量页面创意多样性突出,动画和交互细节丰富
逻辑推理数学、竞赛题、日常逻辑Reasoning SFT 带来的深度推理能力
工具调用多步骤 agentic 任务支持 <tool_call> 格式,能串联多步操作
多语言对话英、中、韩、日、西班牙语继承 Qwen3.5 的多语言基础
STEM 任务生物、化学等学科问题结构化推理链提升解题准确率
本地编程助手配合终端工具或 IDE 插件低延迟、零成本、数据不出本机

快速上手:10 分钟本地部署

方式一:LM Studio(推荐,最简单)

  1. 下载安装 LM Studio
  2. 搜索 Qwopus3.5-27B-v3.5-GGUF(或 v3)
  3. 选择合适的量化版本(推荐 Q5_K_M,平衡质量与速度)
  4. 加载模型并启动本地服务器
  5. API 地址:http://localhost:1234/v1,兼容 OpenAI 格式

方式二:Ollama(一行命令)

ollama pull gag0/qwen35-opus-distil:27b
ollama serve

方式三:其他 GGUF 运行时

任何支持 GGUF 格式的推理引擎均可加载:llama.cpp、koboldcpp、text-generation-webui 等。


硬件需求:你的设备能跑吗?

模型量化内存/显存需求适用设备
9BQ4_K_M~6 GB入门级 GPU / M1 Mac
9BQ8_0~10 GB中端 GPU / M2 Mac
27BQ4_K_M~16 GBRTX 4060 Ti 16GB / M2 Pro
27BQ5_K_M~20 GBRTX 4070 Ti / M3 Pro
27BQ6_K~23 GBRTX 4080 / M3 Pro 36GB
27BQ8_0~30 GBRTX 4090 / M3 Max

选择建议:

  • 16GB 内存/显存 → Q4_K_M,够用且流畅
  • 24-36GB → Q5_K_M 是甜点,Q6_K 也可以但长对话会紧张
  • 48GB+ → Q8_0,接近无损精度
  • Apple Silicon 用户:27B 目前暂无官方 MLX 版本,可用 GGUF 格式配合 LM Studio 运行;9B 有 MLX 6bit 版本可用

已知局限(诚实比营销重要)

  • 推理在边缘场景下可能不稳定
  • 数据扩展超过最优范围时可能出现过拟合
  • 工具调用性能取决于运行环境的集成方式(LM Studio 的 Agent 模式支持较好)
  • 并非所有能力都已完整评测
  • 推理 SFT 提升推理能力的同时,可能在对齐敏感基准上出现轻微回退 — 这是已知的能力权衡,不是 bug

相关链接

Windows + Git Bash 下用 hdc 推文件到鸿蒙应用私有沙箱的四个连环坑

关键词:HarmonyOS / OpenHarmony、hdc、file send、EL2 沙箱、MSYS_NO_PATHCONV、SELinux、root 设备

适用读者:用 Windows + DevEco SDK 工具链做鸿蒙应用调试的开发者,遇到过 hdc file sendpermission denied / no such file or directory 不知道卡在哪一层的同学。


前言

调试鸿蒙应用时经常需要把一份准备好的测试数据(缓存、配置、mock 响应等)预置到自家应用的 EL2 私有沙箱里,让 App 启动时读这份预置数据走特定分支。

听起来就是一条 hdc file send 的事。实际做下来在 Windows + Git Bash 环境下会踩到 4 个独立的坑连成一串,每一坑的报错看起来都像是另一个问题,换方向排查半天才摸清楚根因。本文把整条路径一次讲清,给出最终可用命令和一眼排查表。

前置条件 & 适用范围

  • 只推到你自己开发的、debug 签名的 App 沙箱。正式固件下 hdc 无权访问任何应用(包括系统应用)的私有数据,这是 OS 安全模型的一部分;本文讨论的是 userdebug / 开发机 / 已 root 设备上给自己的 App 注入调试数据。
  • 示例环境:HarmonyOS 5.0(API 12)/ OpenHarmony、DevEco Studio 或独立 hvigor CLI、Windows 10/11 + Git Bash(MSYS2)。
  • 如果你在 macOS / Linux 或纯 cmd/PowerShell 下,坑 1 和坑 2 的形态会略有不同,但解法思路通用。

一、最终可用命令(TL;DR)

假设你已经满足前置条件(自家 debug App、已 root 设备或 userdebug 固件)。

# 0. 先设好环境变量,避免后面命令冗长(你的实际 SDK 路径)
export DEVECO_SDK_HOME="/path/to/your/DevEco/sdk"
export HDC="$DEVECO_SDK_HOME/default/openharmony/toolchains/hdc.exe"

# 1. cd 到源文件所在目录(不能用绝对路径喂给 hdc — 见坑 1)
cd "C:/Users/<YourName>/Downloads"

# 2. 推文件(禁用 MSYS 路径转换 — 见坑 2)
MSYS_NO_PATHCONV=1 "$HDC" file send \
  "data.json" \
  "/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json"

# 3. 校正 owner/权限为目标应用 uid(查自己的 uid 方法见下文)
MSYS_NO_PATHCONV=1 "$HDC" shell "
  chown <APP_UID>:<APP_UID> /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json &&
  chmod 660 /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json &&
  ls -la /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json
"

<APP_UID> 查法:

MSYS_NO_PATHCONV=1 "$HDC" shell "ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"
# drwxrwx--- 2 20020124 20020124 ...
#             └─────┬────┘
#                应用 uid:gid(每台设备、每个应用都不同)

二、四个连环坑(按遇到顺序)

坑 1:hdc 把 Windows 绝对路径拼接到当前工作目录后面

现象

hdc file send "C:/Users/Alice/Downloads/data.json" "/data/..."
# [Fail] Error opening file: no such file or directory,
# path:D:\your\current\workdir\C:/Users/Alice/Downloads/data.json

仔细看报错里的路径:D:\当前工作目录\C:/Users/Alice/... — hdc 把已经是绝对路径的本地参数又拼在了 CWD 后面,形成 Windows 根本识别不了的畸形路径。

原因:hdc 对本地文件参数做了 CWD 相对路径解析,但没处理"参数已经是绝对路径"这种分支,Windows 盘符根(C:D:)的引入也加剧了这个问题。

解法:先 cd 到源文件目录,用相对路径传给 hdc:

cd "C:/Users/Alice/Downloads"
hdc file send "data.json" "/data/..."
同样的问题也出现在 hdc install,官方文档没明说但踩过的人都知道。

坑 2:Git Bash 的 MSYS 路径转换把设备端路径改坏了

跨过坑 1 后你会遇到更诡异的:

现象

cd "C:/Users/Alice/Downloads"
hdc file send "data.json" "/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json"
# [Fail] open path:C:/Program Files/Git/data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json

设备端的 /data/app/... 变成了 Windows 上的 C:/Program Files/Git/data/app/...

原因:Git Bash 基于 MSYS2,它对所有"看起来像 Unix 绝对路径"(以 / 开头)的命令行参数会自动做一次路径翻译,猜测这是 POSIX 挂载点,把前缀替换成对应的 Windows 路径。C:/Program Files/Git/ 是 Git for Windows 的安装根。

hdc 的设备端路径跟本地路径长得一模一样(都以 / 开头),所以也被一视同仁地翻译了,结果变成了一个无效的本地路径。

解法:用 MSYS_NO_PATHCONV=1 临时关闭路径转换:

MSYS_NO_PATHCONV=1 hdc file send "data.json" "/data/app/.../data.json"

其他等价方案:

  • 双斜杠前缀://data/app/...(MSYS 遇到 // 会保留原样)
  • 切换到 cmd / PowerShell 执行

推荐 MSYS_NO_PATHCONV=1,显式、局部、可 grep,后来人读脚本一看就知道为什么加它。


坑 3:/data/app/el2/... 是应用私有沙箱,普通 hdc shell 碰不到

跨过坑 2 后,如果你的设备是正式发布固件,还会撞上:

现象

hdc shell "id"
# uid=2000(shell) gid=2000(shell) ... context=u:r:sh:s0

hdc file send data.json /data/app/el2/100/base/com.example.myapp/haps/entry/files/data.json
# [Fail] permission denied

hdc shell "ls /data/app/el2/100/base/com.example.myapp"
# ls: ...: Permission denied(连目录存在都看不到)

原因

  • 鸿蒙正式发布固件下,hdc 以 uid=2000(shell)、SELinux 域 u:r:sh:s0 运行
  • /data/app/el2/<用户 ID>/base/<包名>/ 是每个应用的 EL2 加密私有目录,权限通常是 drwxrwx---,owner 是应用自己的 uid(例如 20020124),shell 既不在 owner 也不在 group,DAC 直接拒
  • 即使想靠 su 提权,which su 没有、param get const.debuggable 也失败,说明是发布固件,没有提权路径

解法:只有两条路

  1. 换 userdebug 固件 / 已 root 的开发机(验证手段见坑 4)
  2. /data/local/tmp/ 中转 + 应用内拷贝

    • 把文件 hdc file send/data/local/tmp/(shell 可读写)
    • 你自己的 App(debug 签名)读 /data/local/tmp/data.json 再写到自家 filesDir
    • 适用于你的 App 本身就装了调试逻辑;任何正式固件都能用,不需要 root

方案 2 不需要 root 但需要你的 App 配合一段"从 tmp 拷贝到沙箱"的调试代码。生产发布前务必用构建类型门控掉这段代码。


坑 4:如何"真·验证 root"(不能只看 uid)

假设换到了 root 设备或 userdebug 固件,hdc shell "id" 显示 uid=0(root) 就能信吗?不能。存在两类"假 root":

  • 只改了进程凭据 uid,但 SELinux 拦死所有敏感操作 → 实际啥也干不了
  • 开了 cap 但不是完整 root → 某些 syscall/路径仍被拒

单看 id 不够。用三重交叉证据法:

hdc shell "id; ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"

期望输出:

uid=0(root) gid=0(root) ... context=u:r:su:s0              ← 证据 1 + 2
drwxrwx--- 2 20020124 20020124 ... /data/app/.../files     ← 证据 3
证据含义为什么不能只看这一项
uid=0(root)进程凭据是 rootuid 可被改,SELinux 会拦
context=u:r:su:s0SELinux 域是 su(不是 sh内核 MAC 层也认这个 root,才有实际越权能力
ls -ld 能列出私有沙箱实战穿透 DAC 权限位前两条过了但仍可能是受限 root(只给部分 cap)

反例(非 root 或假 root)

uid=2000(shell) ... context=u:r:sh:s0          ← 纯 shell 用户
ls: ...: Permission denied

或
uid=0(root) ... context=u:r:sh:s0              ← uid 像 root 但 SELinux 仍是 sh 域
ls: ...: Permission denied

三条里任一条不过 = 不能信其写沙箱的能力,换固件。


三、沙箱路径解读

/data/app/el2/100/base/com.example.myapp/haps/entry/files/
         │   │    │    │                    │    │     │
         │   │    │    │                    │    │     └─ Context.filesDir 对应的目录
         │   │    │    │                    │    └─────── HAP 模块名(entry 是主模块默认名)
         │   │    │    │                    └──────────── haps 下按模块分
         │   │    │    └───────────────────────────────── 应用包名
         │   │    └────────────────────────────────────── base = 应用基础沙箱
         │   └─────────────────────────────────────────── 用户 ID(主用户 = 100)
         └─────────────────────────────────────────────── el2 = 二级加密分区(锁屏解锁后可访问)

另一常见分区是 el1(开机即可访问,适合系统级数据),应用自己的数据通常落在 el2。其他 Context 子目录:

  • files/context.filesDir
  • cache/context.cacheDir
  • preferences/SharedPreferencesSPUtils 类落地位置)
  • database/@kit.ArkData 关系数据库

推文件前看一眼你的 App 实际读的是哪个子目录,别推错地方。


四、owner / 权限修正

hdc file send 推过去的文件,不同版本 hdc / 固件表现不一样:

  • 有的版本会自动把 owner 设成目标应用 uid(好的情况)
  • 有的版本 owner 是 root 或 shell,应用进程(以 <APP_UID> 运行)读不到

建议不管哪种情况都显式修一遍:

# 先查应用 uid(目标 files 目录的 owner 就是)
hdc shell "ls -ld /data/app/el2/100/base/com.example.myapp/haps/entry/files"
# drwxrwx--- 2 20020124 20020124 ...

# 按此修复新推文件
hdc shell "
  chown 20020124:20020124 /data/app/.../data.json &&
  chmod 660 /data/app/.../data.json
"

权限给 660 是因为应用访问沙箱时以它自己 uid + 同名 gid 访问,其他用户不需要读写权限。不要图省事给 666777,违反最小权限原则。


五、可复用命令模板

换好下面 6 个变量,整块粘贴即可:

# ---- 配置 ----
LOCAL_DIR="C:/Users/<YourName>/Downloads"    # 本地文件所在目录
LOCAL_FILE="data.json"                       # 本地文件名
TARGET_PKG="com.example.myapp"               # 你的 debug 应用包名
TARGET_MODULE="entry"                        # HAP 模块名,默认 entry
TARGET_USER="100"                            # 多用户系统里的用户 ID,主用户 100
HDC="/path/to/DevEco/sdk/default/openharmony/toolchains/hdc.exe"

TARGET_DIR="/data/app/el2/${TARGET_USER}/base/${TARGET_PKG}/haps/${TARGET_MODULE}/files"
TARGET_PATH="${TARGET_DIR}/${LOCAL_FILE}"

# ---- 1. 验证设备是 root + 目标目录可访问 ----
MSYS_NO_PATHCONV=1 "$HDC" shell "id; ls -ld ${TARGET_DIR}"
# 期望:uid=0(root) ... context=u:r:su:s0 + 能列出目录详情(记下里面的 uid)

# 把上一步查到的 uid 填到这里
TARGET_UID="20020124"

# ---- 2. 推文件 ----
cd "$LOCAL_DIR" && \
MSYS_NO_PATHCONV=1 "$HDC" file send "$LOCAL_FILE" "$TARGET_PATH"

# ---- 3. 校正 owner/权限并验证 ----
MSYS_NO_PATHCONV=1 "$HDC" shell "
  chown ${TARGET_UID}:${TARGET_UID} ${TARGET_PATH} &&
  chmod 660 ${TARGET_PATH} &&
  ls -la ${TARGET_PATH}
"

六、一眼失败排查表

报错片段落在哪一坑修复
open path:<CWD>\C:/... 本地路径被拼在 CWD 后面坑 1cd 到源目录,用相对路径
open path:C:/Program Files/Git/data/... 设备端路径被改成本地路径坑 2命令前加 MSYS_NO_PATHCONV=1
permission denied + id 显示 uid=2000(shell)坑 3换 userdebug 固件/root 开发机,或走 /data/local/tmp/ 中转
uid=0(root)lsPermission denied坑 4假 root / 受限 root(SELinux 域不是 su),换真正的 userdebug 固件
文件推过去了但应用读报错owner/权限chown <APP_UID>:<APP_UID> + chmod 660
应用读到空 / 旧数据EL2 未解锁设备重启后要先解锁屏幕,EL2 分区才挂载

七、延伸阅读

  • SELinux 在 Android/鸿蒙这类系统里的作用:Mandatory Access Control,内核级强制访问控制,独立于 DAC(user/group 权限位)。即使 uid=0,只要 SELinux 域不允许,该操作仍会失败。
  • /data/app/el1 vs /data/app/el2:EL1 开机即可用,EL2 需锁屏解锁后才挂载。重启设备后如果一直没解锁,所有 EL2 路径都访问不到。
  • hdc 的命令全集和选项:查你本地 SDK 里 toolchains/ 下的 hdc.exe --help

免责声明

  • 本文示例中的 com.example.myapp 是占位符,请替换为你自己开发的、debug 签名的 App 包名。不要尝试用这套流程写其他厂商的系统应用或第三方 App 的沙箱 —— 在正式固件上这会被 OS 拒掉,在开发机上虽然可能成功但不符合应用隔离的设计意图,也可能违反相关使用条款。
  • 任何在 /data/local/tmp/ → 自家沙箱中转方案都只能用于 debug 构建;生产发布前用构建类型(BuildProfile / product / flavor)彻底移除调试通路。
  • 作者不对读者在自己设备上执行本文命令产生的任何数据丢失或设备异常负责。涉及 root / userdebug 设备的操作请在了解设备保修状态后自行决策。

经常用 AI 搓前端页面的朋友,一定对那抹“深邃蓝”和“基佬紫”不陌生。

只要你不给 Prompt 设限,AI 仿佛全员深爱“赛博朋克渐变风”。第一眼惊艳,看多了想吐——满屏都是溢出来的“AI 味儿”。更别提当你让它写第二个页面时,那代码逻辑就像是换了个祖宗,风格南辕北辙。

那么,如何打破这种“开盲盒”式的开发体验?如何让 AI 像正经大厂程序员一样,守规矩、懂审美、还出活?

今天我带你玩点不一样的:从 0 到 1 演示如何通过“反向思维”,让 AI 乖乖吐出一整套风格统一、逻辑闭环的代码。

那么,如何操作才能摆脱这种千篇一律的色彩风格呢?当然是有办法的,听我慢慢娓娓道来……

有很多朋友想要从零开发某个应用,但是总是会发现:要么无从下手,要么好不容易写好了一个页面,然而让 AI 去写另外的一个页面时,发现和之前的页面风格完全不同!那么,到底有没有办法让 AI “懂得代码规范” 写出一样风格的代码呢?

今天,我就从 0 到 1 一步一步讲解如何让 AI 帮你写出一套完整的项目代码。

放弃传统思想,现在借助 AI 开发应用,思路得发生转变

以前,我们如果想要从 0 开发一个 APP,我们大致会经历过如下阶段:

  1. 产品同学构思业务逻辑并画好产品原型图
  2. 产品同学召集UI、前端、后端一起开会讲明需求
  3. UI 同学对照原型图设计出 UI
  4. 前端同学根据 UI 去设计前端页面
  5. 后端同学结合原型图、UI图设计数据库、后端架构、制定 API 接口
  6. 前后端同学进行接口联调
  7. 测试同学进行前后端产品成果测试
  8. 产品同学验收

然而,当我们借助 AI 来开发应用时,我们的开发流程就会有所转变,以下是我的开发流程:

  1. 构想清楚这个产品的功能、定位客户人群、交互方式并写好功能文档
  2. 让 AI 帮我确定清楚适合的色彩风格并直接写好前端代码并写好 mock 数据
  3. 自己根据页面和 mock 数据去调试功能
  4. 功能调试没问题之后,让 AI 根据 mock 数据写出后端所需要的 API 接口文档
  5. 让 AI 根据接口文档去创建数据库建表语句(自己略微调整)
  6. 让 AI 根据接口文档去写接口
  7. 将前端的 mock 数据换成真实的 API 接口,自己测试功能
  8. 有问题的地方直接截图给到 AI 让 AI 帮忙解决,直到没有问题即可

你会发现操作流程上也没有少几个步骤,但是当你仔细一琢磨之后,你会发现:

  1. 以前至少需要产品、UI、前端、后端、测试等岗位的同学相互合作才能做的事情,现在只需要一个人就能干完
  2. 如果你想一个人干整套项目,最好的方式是先从页面开始,再反向去写接口可以事半功倍

展示效果

说再多,都没有直接展示成果来得干脆,下面是我最近用 AI 写的一个微信小程序,前端代码 100% 用 AI 写的,后端接口 80% 用的 AI

现在我已将代码都开源了,感兴趣的朋友可以去观摩观摩,也请帮忙点个 Star 支持一下,谢谢!
小程序端: https://github.com/pudongping/momento-miniapp
API 接口: https://github.com/pudongping/momento-api

homepage.png

login.png

profile.png

recurring.png

transaction.png

罗列需求

首先,我们不要着急写代码,先把自己想要实现的功能罗列清楚,我自己写了一个 markdown 直接发给了 AI

下面是我写的内容:

## 1、首页看板

最顶部可下拉切换账本

功能点:

1、温馨背景墙:

背景区域,支持上传自定义图片(如夫妻合照、全家福)。

交互逻辑说明:

默认: 温馨插画。
自定义: 点击可从相册上传。

---

在背景图顶部显示

顶部背景区域,展示倒计时信息。

纪念日提醒:距离结婚纪念日、生日等重要纪念日还有多少天的倒计时提醒(每一条纪念日上下滚动提醒)
显眼提醒:如果用户在设置中勾选了“首页显示”,系统自动计算并筛选出距离今天最近的一个节日,在背景图中央显眼位置显示:“距离 [事件名] 还有 [X] 天”。

2、预算红绿灯:

在背景图下方展示本月预算进度条。(基于家庭总支出的视觉化预警。)

在“我的”设置页面中会设置每个月的总预算(比如10000元)。首页展示一个进度条,绿色代表安全,黄色代表预警,红色代表超支。
绿色状态: 支出 < 50%预算。
黄色状态: 支出 >= 50% 且 < 80%。
红色状态: 支出 >= 80%(甚至爆表)。
进度条文案: 显示“本月剩余可用:¥XXXX”。

预算红绿灯不要做成生硬的交通灯,可以做成一个“呼吸感”的渐变光圈或者半透明的水位线,这样视觉上会更灵动。

3、当前月份的总支出、总收入

4、时间轴账单流

类似朋友圈的时间轴,按“天”为单位排列。默认只会展示当前一个月的账单,下拉刷新,上拉加载更多历史数据。,像刷朋友圈一样查看过往账单。(这里的数据分页为“游标”分页,需要前端传递已经加载出来的最后一条账单的id给到接口)

账单列表:

- 日期头: 显示日期 + 当日星期 + 当日总支出(卡片形式)

- 每条账单展示:标签、金额(支出红色,收入绿色,支出为- 收入为+)、备注
- 支持按天展开/收起查看该天的所有账单
- 账单旁边显示删除按钮,点击后弹出二次确认框,确认后删除该条账单
- 支持点击账单进入编辑页面,修改该条账单的信息
- 每条账单旁边都会显示用户头像和昵称(如果昵称超过4个字符,超过部分则用三个点 ... 代替)
- 按“天”的时间轴展示,像朋友圈一样,下拉查看每一笔。下拉加载更多,像刷朋友圈一样查看过往账单。
- 搜索功能:支持按标签、备注、金额、类型(支出,收入)、时间区间搜索账单

## 记账

记账功能:

- 智能标签: 预设家庭常用标签(买菜、房贷、孩子、餐饮、交通、其他)不同的标签用不同的颜色区分。系统级标签不可删除、不可修改。列表末尾有“+自定义”,用户也可以自定义标签颜色和名称
- 可以选择支出/收入类型(默认为支出)
- 备注: 支持输入备注信息(非必填)
- 金额: 支持输入金额,支持小数点后两位(必填,单位为元)
- 日期: 默认为当天,支持选择以前的日期(通过日历选择)
- 保存后,自动返回首页看板,新增的账单会出现在时间轴
- 定期/周期账单:对于房贷、车贷、物业费这种固定支出,设置“自动记账”模板,可设置每年(每年哪一天)、每季度(每季度哪一天)、每月(具体哪一天)、每星期(具体星期几)、每天(具体什么时间)自动扣款日。到了指定日期,系统自动生成一笔账单,并备注为“自动记入”。

---

## 3、我的

- 用户通过微信小程序授权登录。接口通过雪花算法生成分布式ID作为用户的唯一性标识 UID
- 登录成功之后,接口会返回默认昵称和头像,用户可以在个人中心修改昵称和头像

1、个人信息修改(昵称、头像)

2、显示 uid(雪花算法得到的分布式ID)、uid 旁边有个“复制”点击“复制”之后可以复制 uid 到剪切板

3、预算设置
设置“红绿灯”的总金额。    输入每月家庭预算总限额,单位:元。

4、节日设置

设置首页倒计时的数据源头。
- 添加节日名称、日期。比如结婚纪念日、生日、春节等(可设置多个)
- 每一个节日都可以设置是否在首页显示,默认为显示
- 每一个节日都可以进行编辑、删除

5、账本管理

当用户登录成功之后,需要添加账本(可以自定义账本名称),账本添加成功之后,可以在账本中邀请其他用户,输入对方 UID,发送邀请。

邀请请求发出之后,在该账本中可以看到被邀请人的头像、昵称,状态为“等待加入”

被邀请人打开小程序并登录之后,可以在“我的”-“账单管理”位置看到一条提示消息:“邀请人昵称”于年月日时分秒邀请您加入“账本名称”共同管理账单。
消息旁边有“同意”、“拒绝”两个按钮

当被邀请人点击了“同意”按钮之后,则加入了该账本。账本管理中就会显示出邀请人的账本名称、加入成员数量、创建时间(年月日时分秒)、此时被邀请人在该账本中的状态为“已加入”、在邀请人那边被邀请人的状态为“已绑定”

当被邀请人点击了“拒绝”按钮之后,则不加入该账本,在邀请人侧则显示“被拒绝”状态。

在同一个账本中,每个用户记录的账单都是可见的。

可以对账本设置“默认”标签,这样在首页、添加账单时,则添加到默认标签的账本中,当然,如果用户存在于多个账本中,则可以在首页、添加账单时对账本进行切换。

自己创建的账本和自己加入别人创建的账本是有颜色区分的,优先展示自己创建的账本,如果自己没有创建账本,再优先展示加入的账本

被邀请人可以退出加入的账本,邀请人不能退出自己创建的账本,只能删除账本。(删除效果是滑动删除)「退出」、「删除」都会有二次确认。

当然了,这也是最费时间的步骤,但是是必不可少的步骤。

定调

你会发现上面的小程序样式、色彩都是有自己的风格的,那么到底是怎么做到的呢?

很简单!在写这个项目之前,我直接让他固定好了全局样式

色系:

  • 主色:#FF9A5A(暖橘)-> #FFD166(浅金)的渐变
  • 辅助色:#F5F5F5(浅灰白)
  • 文字色:#333333 (主标题), #666666 (副标题/备注)

风格:模仿苹果 iOS 的 UI (高级感、温馨、圆润、极简)

写提示词

当你将上面的内容都发给 AI 并让 AI 牢牢记住你的要求之后,你就可以直接将下面的提示词发给他,让他帮你写代码了

Subject:微信小程序《时光账记》前端开发任务

Context:我正在开发一款名为《时光账记》的微信小程序,目的是创建账本,然后在账本下可以记账,请帮我完成小程序前端代码。

所有数据请求请使用 Mock 数据模拟,请在项目根目录下创建一个 mock 文件夹,将所有的 API 返回数据写在独立的 JS 文件里,方便我后期替换成真正的 API。

1、接口协议规范:所有 API 请求的返回格式必须严格遵守: {"code": 0, "msg": "提示语", "data": null}

- 当 `code == 0` 时,表示接口请求正常,此时可以从 `data` 中拿到业务数据
- 当 `code != 0` 时,表示接口异常,需要通过 toast 弹出 msg 中的提示内容
- 所有的时间,接口都会返回秒级别的时间戳,前端需要自行进行格式化处理
- 如果用户已经登录,需要将用户登录的 token 数据放入到请求头中到 token 字段中

2、前端所有和接口网络请求有关的方法,都要以 `Api` 为后缀,比如在前端中有一个方法是封装去请求接口获取用户信息时 `getUserInfoApi` 方便我好分辨这个方法是和网络请求有联系的

3、全局样式设置:

色系:

- 主色:#FF9A5A(暖橘)-> #FFD166(浅金)的渐变 
- 辅助色:#F5F5F5(浅灰白)
- 文字色:#333333 (主标题), #666666 (副标题/备注)

风格:模仿苹果 iOS 的 UI (高级感、温馨、圆润、极简)

先帮我把项目的目录结构搭好,并写好全局的 API 请求封装(针对 code 判断逻辑)。

先帮我创建 tabbar

- 首页
- 记账(一个大大的加号)
- 我的

慢慢的,你就会发现他写的每一个页面都是一样的风格。剩下的,你就专心跟他对话让 AI 帮你调整细节就好了~

但是,你会发现另外一个问题,当你某天退出了编辑器之后,重新打开项目再让 AI 帮你写代码时,你就会发现他又会随意发挥了。下一篇文章跟大家聊聊如何让 AI 不管何时介入项目,写出的代码都是统一风格的,敬请期待~

PaddleOCR 3.5 正式发布,带来以下核心升级:

  1. 发布浏览器端 PaddleOCR.js,开发者可以用更轻量的方式在前端应用中直接调用 OCR 能力。
  2. 支持将 PaddleOCR-VL 文档解析内容转为 Word,以及将 Word、Excel、PPT 等类型的办公文档转为 Markdown。
  3. 融入 Hugging Face 开发生态,通过统一推理引擎配置方式,支持基于 Transformers 完成 OCR 系列模型推理。

一、发布 PaddleOCR.js

在 Agent 时代,越来越多的智能体需要直接感知和理解用户界面上的信息——而这些信息,往往就存在于浏览器里。​让数据不离开浏览器就能完成识别​,不仅是对隐私的保护,更是降低系统复杂度、实现真正轻量化部署的关键一步。

PaddleOCR.js 正是为此而生。从能力上看,它已经不仅仅是"能识别",而是在浏览器侧提供了一套更完整的使用体验:基于 ONNX Runtime Web 搭建,同时支持 WebGPUWasm 两种加速后端;提供 ​Worker 模式​,推理过程在后台线程运行,不阻塞主线程、不影响页面交互;并已适配 Chrome、Safari 等主流浏览器。

无论是验证码识别、在线票据处理,还是构建更强调交互体验的前端 AI 工具,开发者都可以用更轻量的方式,将 PaddleOCR 无缝接入现有 Web 场景。

<pre spellcheck="false" data-title="" data-lang="js" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-js "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="import%20%7B%20PaddleOCR%20%7D%20from%20%22%40paddleocr%2Fpaddleocr-js%22%3B">import { PaddleOCR } from "@paddleocr/paddleocr-js";</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20ocr%20%3D%20await%20PaddleOCR.create(%7B">const ocr = await PaddleOCR.create({</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20lang%3A%20%22ch%22%2C"> lang: "ch",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20ocrVersion%3A%20%22PP-OCRv5%22%2C"> ocrVersion: "PP-OCRv5",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20worker%3A%20true%2C"> worker: true,</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20ortOptions%3A%20%7B"> ortOptions: {</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20backend%3A%20%22auto%22"> backend: "auto"</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%7D"> }</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%7D)%3B">});</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20%5Bresult%5D%20%3D%20await%20ocr.predict(fileOrBlob)%3B">const [result] = await ocr.predict(fileOrBlob);</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="console.log(result.items)%3B">console.log(result.items);</span></span></div></div></pre>

同时,从调用风格上看,PaddleOCR.js 与 PaddleOCR Python 接口也保持了较高的一致性。Python 侧通过 PaddleOCR(...) 初始化并调用 predict,浏览器侧则通过 PaddleOCR.create({...}) 初始化后同样调用 predict 得到推理结果列表。这让已有 PaddleOCR Python 库用户迁移到 Web 端时几乎不需要重新理解一套完全不同的接口设计:

<pre spellcheck="false" data-title="" data-lang="python" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-python "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="from%20paddleocr%20import%20PaddleOCR">from paddleocr import PaddleOCR</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="ocr%20%3D%20PaddleOCR(">ocr = PaddleOCR(</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20lang%3D%22ch%22%2C"> lang="ch",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20ocr_version%3D%22PP-OCRv5%22"> ocr_version="PP-OCRv5"</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=")">)</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="result%20%3D%20ocr.predict(%22general_ocr_002.png%22)">result = ocr.predict("general_ocr_002.png")</span></span></div></div></pre>

<pre spellcheck="false" data-title="" data-lang="js" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-js "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="import%20%7B%20PaddleOCR%20%7D%20from%20%22%40paddleocr%2Fpaddleocr-js%22%3B">import { PaddleOCR } from "@paddleocr/paddleocr-js";</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20%E6%94%AF%E6%8C%81%20Python%20%E9%A3%8E%E6%A0%BC%E7%9A%84%20snake_case%20%E5%8F%82%E6%95%B0">// 支持 Python 风格的 snake_case 参数</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20ocr%20%3D%20await%20PaddleOCR.create(%7B">const ocr = await PaddleOCR.create({</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20lang%3A%20%22ch%22%2C"> lang: "ch",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20ocr_version%3A%20%22PP-OCRv5%22"> ocr_version: "PP-OCRv5"</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%7D)%3B">});</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20%E4%B9%9F%E6%94%AF%E6%8C%81%20JavaScript%20%E9%A3%8E%E6%A0%BC%E7%9A%84%20camelCase%20%E5%8F%82%E6%95%B0">// 也支持 JavaScript 风格的 camelCase 参数</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20const%20ocr%20%3D%20await%20PaddleOCR.create(%7B">// const ocr = await PaddleOCR.create({</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20%20%20lang%3A%20%22ch%22%2C">// lang: "ch",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20%20%20ocrVersion%3A%20%22PP-OCRv5%22">// ocrVersion: "PP-OCRv5"</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%2F%2F%20%7D)%3B">// });</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20%5Bresult%5D%20%3D%20await%20ocr.predict(fileOrBlob)%3B">const [result] = await ocr.predict(fileOrBlob);</span></span></div></div></pre>

另外,PaddleOCR.js 还提供了结果可视化能力,能够把检测框、识别文字等结果以与 PaddleOCR Python 库相同的风格渲染出来。

<pre spellcheck="false" data-title="" data-lang="js" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-js "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="import%20%7B%20OcrVisualizer%20%7D%20from%20%22%40paddleocr%2Fpaddleocr-js%2Fviz%22%3B">import { OcrVisualizer } from "@paddleocr/paddleocr-js/viz";</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20viz%20%3D%20new%20OcrVisualizer(%7B">const viz = new OcrVisualizer({</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20font%3A%20%7B%20family%3A%20%22Noto%20Sans%20SC%22%2C%20source%3A%20%22%2Ffonts%2FNotoSansSC-Regular.ttf%22%20%7D"> font: { family: "Noto Sans SC", source: "/fonts/NotoSansSC-Regular.ttf" }</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%7D)%3B">});</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="const%20blob%20%3D%20await%20viz.toBlob(imageBitmap%2C%20result)%3B">const blob = await viz.toBlob(imageBitmap, result);</span></span></div></div></pre>

PaddleOCR 仓库中提供了一个简单的 Demo 应用,用于快速体验 PaddleOCR.js 的效果:

开发者可以在 Demo 应用中先跑通体验,再逐步集成到自己的 Web 应用中。

二、支持多格式文档解析

大模型时代,数据的价值被重新审视。海量知识沉淀在 Word、Excel、PPT、PDF 等各类文档中,而大模型真正能消化的,是结构清晰的文本。​打通文档与大模型之间的通路​,成为这个时代最迫切的需求之一。

为此,PaddleOCR 3.5 在文档格式的输入与输出两端同时发力:

  • 输出侧​:支持将 PaddleOCR-VL 系列模型、PP-StructureV3 的预测结果直接导出为 ​Word 格式​,让识别结果开箱即用、便于人工流转;
  • 输入侧​:支持将 Word、Excel、PPT 等主流办公文档转换为 ​Markdown 格式​,结构清晰,易于后续的 LLM 接入与智能体调用。

以 PaddleOCR-VL 为例,将预测结果导出为 Word 文档只需几行代码:

<pre spellcheck="false" data-title="" data-lang="python" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-python "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="from%20paddleocr%20import%20PaddleOCRVL">from paddleocr import PaddleOCRVL</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="pipeline%20%3D%20PaddleOCRVL()">pipeline = PaddleOCRVL()</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="output%20%3D%20pipeline.predict(%22.%2Fpaddleocr_vl_demo.png%22)">output = pipeline.predict("./paddleocr_vl_demo.png")</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="pages_res%20%3D%20%5B%5D">pages_res = []</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="for%20res%20in%20output%3A">for res in output:</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20res.save_to_word(save_path%3D%22output%22)%20%23%23%20%E4%BF%9D%E5%AD%98%E5%BD%93%E5%89%8D%E5%9B%BE%E5%83%8F%E7%9A%84Word%E6%A0%BC%E5%BC%8F%E7%9A%84%E7%BB%93%E6%9E%9C"> res.save_to_word(save_path="output") ## 保存当前图像的Word格式的结果</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20pages_res.append(res)"> pages_res.append(res)</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20"> </span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="output%20%3D%20pipeline.restructure_pages(pages_res)">output = pipeline.restructure_pages(pages_res)</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="for%20res%20in%20output%3A">for res in output:</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20res.save_to_word(save_path%3D%22output%22)%20%23%23%20%E5%B0%86%E5%90%88%E5%B9%B6%E5%90%8E%E7%9A%84%E5%A4%9A%E9%A1%B5%E7%BB%93%E6%9E%9C%E4%BF%9D%E5%AD%98%E4%B8%BAWord%E6%A0%BC%E5%BC%8F"> res.save_to_word(save_path="output") ## 将合并后的多页结果保存为Word格式</span></span></div></div></pre>



对于办公文档转 Markdown 功能,只需一行命令即可高效完成:

<pre spellcheck="false" data-title="" data-lang="python" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-python "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%23%20%E8%BD%AC%E6%8D%A2%20Word%20%E6%96%87%E6%A1%A3%EF%BC%8C%E8%BE%93%E5%87%BA%E5%88%B0%E6%96%87%E4%BB%B6"># 转换 Word 文档,输出到文件</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="paddleocr%20doc2md%20-i%20report.docx%20-o%20output.md">paddleocr doc2md -i report.docx -o output.md</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%23%20%E8%BD%AC%E6%8D%A2%20Excel%20%E8%A1%A8%E6%A0%BC%EF%BC%8C%E8%BE%93%E5%87%BA%E5%88%B0%E6%96%87%E4%BB%B6"># 转换 Excel 表格,输出到文件</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="paddleocr%20doc2md%20-i%20data.xlsx%20-o%20output.md">paddleocr doc2md -i data.xlsx -o output.md</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%23%20%E8%BD%AC%E6%8D%A2%20PowerPoint%20%E6%BC%94%E7%A4%BA%E6%96%87%E7%A8%BF%EF%BC%8C%E8%BE%93%E5%87%BA%E5%88%B0%E6%96%87%E4%BB%B6"># 转换 PowerPoint 演示文稿,输出到文件</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="paddleocr%20doc2md%20-i%20slides.pptx%20-o%20output.md">paddleocr doc2md -i slides.pptx -o output.md</span></span></div></div></pre>









三、全面适配 Transformers 推理后端

PaddleOCR 3.5 将 Transformers 正式纳入统一推理引擎体系,支持通过统一的配置方式,让开发者可以用同一套接口切换不同底层后端。对需要融入 Hugging Face AI 开发生态的用户来说,这意味着在熟悉的 Transformers 环境中,也能更顺畅地接入 OCR 与文档解析能力,减少跨生态集成成本,并让原本分散的部署选择变得更加统一、清晰。

从使用门槛上看,这次适配也尽量保持了简单直接。开发者只需在环境中安装 5.4.0 或更高版本的 transformers 以及 3.5.0 版本的 paddleocr,即可通过统一接口完成调用。以文本检测模型为例:

<pre spellcheck="false" data-title="" data-lang="python" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-python "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="from%20paddleocr%20import%20TextDetection">from paddleocr import TextDetection</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=""></span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="model%20%3D%20TextDetection(">model = TextDetection(</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20model_name%3D%22PP-OCRv5_server_det%22%2C"> model_name="PP-OCRv5_server_det",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20engine%3D%22transformers%22%2C"> engine="transformers",</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text=")">)</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="result%20%3D%20model.predict(%22general_ocr_001.png%22)">result = model.predict("general_ocr_001.png")</span></span></div></div></pre>

其中,engine 可以是 paddle_staticpaddle_dynamictransformers,分别对应飞桨静态图(Paddle Inference)、飞桨动态图和 Transformers 后端。

Transformers 提供高效简洁的单模型的加载、配置与推理能力,而 PaddleOCR 在模块封装与多模型编排能力有长期积累的优势。在底层,我们保留了面向单模型的灵活接入方式;在更高一层,PaddleOCR 仍然能够把检测、方向分类、识别等能力按业务需要串联组合起来,让开发者既能享受 Transformers 生态的便利,也能继续使用 PaddleOCR 已经成熟的产线化能力,实现更强的组合效果。例如,同样可以通过 engine 参数将 OCR pipeline 直接切换到对应后端:

<pre spellcheck="false" data-title="" data-lang="bash" data-autowrap="true" data-indent="0" data-view="code" class="mp-block-code-wrapper language-bash "><div class="mp-block-code-content mp-block-code-content-auto-wrap"><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="paddleocr%20ocr%20-i%20xxx.png%20%5C">paddleocr ocr -i xxx.png \</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20--use_doc_orientation_classify%20False%20%5C"> --use_doc_orientation_classify False \</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20--use_doc_unwarping%20False%20%5C"> --use_doc_unwarping False \</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20--use_textline_orientation%20False%20%5C"> --use_textline_orientation False \</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20--save_path%20.%2Foutput%20%5C"> --save_path ./output \</span></span></div><div class="mp-block-code-line" data-text-content=""><span class="mp-block-code-line-content"><span data-morpho-text="%20%20%20%20--engine%20transformers"> --engine transformers</span></span></div></div></pre>

执行结果与使用飞桨静态图完全对齐:

此外,PaddleOCR 3.5 还支持通过 engine_config 参数配置推理后端的初始化参数,进而实现更细粒度的设备选择、精度控制与推理行为调优,让统一接口之上也保留了足够的灵活性与可定制空间。

结语

从文档图像到结构化数据,PaddleOCR 始终致力于降低 AI 应用开发的数据门槛。PaddleOCR 3.5 版本的升级,不仅让 OCR 能力更广泛地融入 Hugging Face 生态与浏览器端,更将文档解析链路延伸至多格式办公文档的一键转换。这一切努力,归根结底是为了把“将文档图像转换为 JSON、Markdown 等 AI 友好数据”这件事做到极致,让 PaddleOCR 真正成为每一位 AI 开发者手边的“开局神器”——数据一键整理、适度清洗、格式就绪,让您的 AI 应用从第一步就走在高效的路上。

我们诚邀广大开发者、研究者及行业伙伴亲身体验这一全新版本,也期待大家持续给予宝贵的反馈与建议。每一条意见,都是我们前行的动力。让我们携手共建更开放、更强大的 OCR 生态,共同推动 OCR 技术走向更广阔的未来。

​开源地址:​https://github.com/PaddlePaddle/PaddleOCR

76% 的高管认为 AI 是数字员工,但只有 4% 的企业实现了转型目标——问题出在哪里?

01. 理想与现实

2026 年 2 月,德勤中国与香港大学联合发布《2026 企业 AI 应用指数》,基于 100+ 高管调研,揭示了一个残酷的现实:

  • 56% 的企业处于 AI 有限实施阶段,仅带来局部效益。
  • 23% 的企业实现了可衡量的财务影响。
  • 仅 4% 的企业实现了转型目标。

与此同时,爱分析调研了 100+ 不同行业的代表性企业,发现:

76% 的高管认同"AI 是数字员工,不是工具"。

但大多数企业仍把 AI 当工具用,停留在知识库问答、数据分析等边缘场景。

这就是 AI 落地的悖论:认知已经升级,实践还在原地。

你的员工可能每天都在用 AI 写邮件、生成报告、查数据,但组织的生产效率为什么没有明显提升?

答案不在技术,而在方法论。

02. 五个痛点

痛点一:AI 用了很多,但看不到业务价值

"我们买了 ChatGPT 企业版,每个员工都有账号。大家确实都在用,写邮件、写报告、查资料。但季度复盘时,业务指标没什么变化。老板问 AI 投入的 ROI 在哪里,我答不上来。"

这是最常见的问题。

德勤调研发现:近半数 AI 项目缺乏清晰的成功量化指标。

企业无法回答:AI 到底帮企业省了多少钱?AI 到底帮企业多赚了多少收入?AI 到底帮企业提升了多少效率?

没有量化,就没有管理。当 AI 价值无法被衡量时,投入就会被质疑,项目就难以为继。

痛点二:场景太少,徘徊在核心业务边缘

"我们的 AI 应用,主要是知识库问答、会议纪要生成、周报自动写。这些确实提升了效率,但都是'锦上添花',不是'雪中送炭'。核心业务环节(生产、销售、研发)还是老样子。"

爱分析报告指出:2023-2025 年期间,AI 落地场景局限于知识库问答或数据分析,徘徊在核心业务边缘,难以触及价值创造的关键环节。

为什么?

因为企业试图用 AI 攻克"人类专家都感到棘手的复杂问题",比如用 AI 优化钢铁生产工艺(涉及磁场、热效应、浓度等复杂机理),用 AI 预测设备故障(数据不完整、隐性知识众多),用 AI 自动生成营销方案(需要深度理解品牌调性和市场动态)。

结果:AI 建模难度太高,效果不佳,最终放弃。

痛点三:各自为战,最佳实践无法复制

"销售部的张三用 AI 做客户分析,效果很好。但李四不知道,还在手动整理 Excel。市场部开发了个 Custom GPT,但其他部门不会用,也不想学。我们有 1000 个员工,但只有 100 个在用 AI,而且各用各的。"

这是组织层面的问题。

当 AI 使用依赖个人自觉时,最佳实践无法沉淀和共享,重复劳动大量存在,规模效应无法实现。

个体效率提升,无法转化为组织效率提升。

痛点四:评估体系错误,用工具的指标评估员工

"我们评估 AI 项目,看的是响应时间、准确率、用户满意度。但这些指标跟业务价值没关系啊。响应再快、准确率再高,如果人均产能没提升,有什么用?"

这是评估体系的错位。

传统 AI 评估指标是响应时间(技术指标)、准确率(技术指标)、用户满意度(体验指标)。但这些指标无法回答:AI 到底帮企业提升了多少人均产能?

爱分析报告指出:在数字员工体系下,评估重点应转向人均产能等业务价值指标。

比如每位质检员能够管理的生产线数量增长,每位销售能够跟进的客户数量增长,每位客服能够处理的咨询量增长。

用工具的指标评估员工,永远看不到真正的价值。

痛点五:组织没准备好,技术先行流程滞后

"我们上了 AI 系统,但流程还是老的。比如 AI 可以自动生成销售方案,但审批流程还是要走 5 个环节,每个环节都要人工确认。最后 AI 省的时间,都浪费在等审批上了。"

德勤报告指出:AI 落地的主要阻力并非技术本身,而是组织与执行层面的系统性挑战。

包括跨部门协作壁垒与文化惯性,对 AI 能力的认知局限,短期成果交付压力,实施过程中的隐性复杂性,模糊的商业价值论证。

技术可以买来,但组织能力买不来。

03. 四个根源

五个痛点,背后有四个核心问题。

根源一:认知升级了,实践没跟上

76% 的高管认同"AI 是数字员工",但实践上还在把 AI 当工具用。

这是什么意思?

工具思维是:AI 是等待指令的助手,人类决策、AI 执行,评估指标是响应时间、准确率。

员工思维是:AI 是能够独立创造价值的数字员工,AI 可以自主决策、执行多步骤流程,评估指标是人均产能、业务价值。

认知决定实践。当你把 AI 当工具,你只会让它写邮件、生成文档。当你把 AI 当员工,你会让它独立负责客户分析、销售方案、生产优化。

根源二:方法论错误,用"流程优化"代替"任务拆解"

传统 AI 落地方法是业务流程优化:着眼点是端到端的流程痛点,目标是提升整体业务运行质量,实施路径是梳理流程→识别断点→部署 AI 模型,产出是一个个 AI 模型。

问题是:当流程涉及人类专家的隐性知识时,AI 建模难度极高,效果不佳。

新的 AI 落地方法是员工任务拆解:着眼点是核心岗位员工的具体工作任务,目标是提升该岗位的人均产能,实施路径是分析岗位工作→评估 AI 可行性→设计人机协作规则,产出是数字员工岗位说明书 + 人机协作 SOP。

两种方法不是替代关系,而是互补。流程优化指明"在哪里投入",任务拆解解决"如何落地"。

根源三:评估体系错位,用技术指标代替业务指标

传统评估体系(工具视角)看响应时间、准确率、用户满意度。这些指标的问题:跟业务价值没有直接关系。

新的评估体系(员工视角)看人均产能提升(每位员工能管理多少生产线/客户/咨询)、任务替代率(多少任务可以由数字员工独立完成)、业务价值贡献(成本节约、收入增长、客户满意度提升)。

评估什么,就得到什么。当你评估技术指标,你得到的是"好用的工具"。当你评估业务指标,你得到的是"有价值的员工"。

根源四:组织准备度不足,技术跑在流程前面

德勤报告指出:当企业仅将 AI 视为局部技术补充,而非需要同步优化工作流程、跨部门协作机制和组织生态网络的系统工程时,其落地效果往往大打折扣。

组织准备度有五个维度:流程准备度(工作流程是否支持 AI 嵌入)、协作准备度(跨部门协作机制是否顺畅)、能力准备度(员工是否具备 AI 使用能力)、文化准备度(组织是否支持试错和学习)、治理准备度(是否有清晰的评估和决策机制)。

技术可以快速部署,但组织能力需要时间建设。当技术跑在流程前面,效率提升会被系统瓶颈吞噬。

04. 四个转变

转变一:把 AI 当员工,不是工具

具体怎么做?

第一步,重新定义 AI 的角色:不是"等待指令的助手",而是"能够独立创造价值的数字员工"。

第二步,重新设计工作方式:不是"人类决策、AI 执行",而是"AI 自主决策、人类监督"。

第三步,重新建立评估体系:不是"响应时间、准确率",而是"人均产能、业务价值"。

某企业将 AI 从"工具"重新定义为"数字员工"后,给 AI 分配明确的岗位职责(销售分析、客户跟进、内容创作),设定明确的 KPI(每周完成多少分析报告、跟进多少客户),定期评估 AI 的"工作表现"(任务完成率、质量评分)。

结果:AI 从"偶尔用用"变成"每天都在创造价值"。

转变二:从"流程优化"到"任务拆解"

具体怎么做?

第一步,识别核心岗位:哪些岗位对业务价值贡献最大?哪些岗位的工作量最饱和?哪些岗位的工作内容最标准化?

第二步,拆解岗位任务:该岗位每天/每周做什么工作?每项任务的耗时是多少?每项任务的 AI 可行性如何?

第三步,设计人机协作:哪些任务可以完全交给 AI?哪些任务需要人机协作?哪些任务必须人类完成?

第四步,建立 SOP:数字员工岗位说明书,人机协作工作流程,质量检查和验收标准。

某铝生产企业用"任务拆解"方法,将 AI 嵌入槽控工区长的日常工作:日常生产安排由 AI 自动生成,电解槽运行状况分析由 AI 提供建议人类确认,控制指令制定由 AI 生成人类审批,现场安全检查由人类完成,生产质量分析由 AI 自动生成报告。

结果:工区长人均产能提升 40%,AI 价值清晰可见。

转变三:从"技术指标"到"业务指标"

具体怎么做?

第一步,定义业务价值指标:人均产能(每位员工能管理多少生产线/客户/咨询)、成本节约(AI 替代了多少人力成本)、收入增长(AI 带来了多少新增收入)、客户满意度(AI 提升了多少客户体验)。

第二步,建立基线和目标:AI 上线前的人均产能是多少?AI 上线后的目标是多少?多长时间达成目标?

第三步,定期评估和反馈:每周/每月追踪指标变化,分析偏差原因,持续优化 AI 应用。

某企业评估 AI 质检项目,传统指标是响应时间<1 秒、准确率>95%,新指标是每位质检员能管理的生产线数量从 5 条提升到 15 条。

结果:评估指标变了,投入方向也变了,业务价值清晰可见。

转变四:从"技术先行"到"技术 + 流程同步"

具体怎么做?

第一步,流程优化:识别 AI 可以嵌入的工作流程,简化或取消不必要的人工环节,建立 AI 输出的验收和审批机制。

第二步,能力建设:提供 AI 使用培训(基础使用→高级应用→深度集成),建立 AI 应用案例库(最佳实践、失败教训),培养 AI 应用标杆(树立榜样,带动全员)。

第三步,文化塑造:鼓励试错和学习,奖励 AI 应用创新,建立 AI 应用的"心理安全感"。

第四步,治理机制:明确 AI 应用的决策流程,建立 AI 价值评估体系,定期复盘和优化。

05. 我们能做什么

AI 落地诊断(1-2 周)

评估企业 AI 应用现状(采用率、使用深度、业务价值),识别核心痛点和瓶颈,对标行业最佳实践,输出诊断报告和改进建议。

交付成果:AI 应用成熟度评估报告,痛点分析和优先级排序,改进路线图(短期/中期/长期)。

数字员工岗位设计(2-4 周)

识别高价值岗位(人均产能提升空间大),拆解岗位工作任务,评估 AI 可行性,设计人机协作规则,编写数字员工岗位说明书。

交付成果:数字员工岗位说明书,人机协作 SOP,评估指标体系。

AI 应用快速落地(4-8 周)

部署 AI 基础设施(模型、平台、工具),开发 Custom GPTs 和 Agents,集成核心业务系统(CRM、ERP、OA),开展分层培训,建立共享和激励机制。

交付成果:可运行的 AI 应用系统,3-5 个高价值场景落地,培训材料和操作手册。

价值量化与持续优化(持续)

建立 AI 价值量化指标,定期评估 AI 应用效果,收集员工反馈,持续优化 AI 应用和流程。

交付成果:AI 价值评估报告(月度/季度),优化建议和实施方案,最佳实践案例库。

06. 从哪里开始?

很多企业看完前面的内容,会有一个疑问:道理都懂了,但从哪里开始?

我的建议是:不要想着一步到位,先找一个点,做深做透。

第一步:选一个高价值岗位

不要想着全公司推广,先选一个岗位。

选什么岗位?三个标准:

  • 工作量大:这个岗位每天要花大量时间处理重复性工作
  • 标准化程度高:工作内容相对固定,有明确的流程和规则
  • 业务价值高:这个岗位的效率提升,能直接带来业务价值

比如销售岗位(每天要写大量客户方案)、客服岗位(每天要回答大量重复问题)、运营岗位(每天要处理大量数据和报表)。

第二步:拆解这个岗位的工作任务

找这个岗位的优秀员工,让他把每天/每周的工作列出来。

然后评估每项任务:

  • 哪些可以完全交给 AI?(比如数据整理、报告生成、邮件回复)
  • 哪些可以 AI 辅助人类?(比如方案撰写、数据分析、客户跟进)
  • 哪些必须人类完成?(比如关键决策、客户关系维护、复杂问题处理)

第三步:设计人机协作规则

明确告诉这个岗位的员工:

  • AI 负责什么(比如自动生成销售方案初稿)
  • 人类负责什么(比如审核方案、调整策略、客户沟通)
  • 质量如何检查(比如方案通过率、客户满意度、成交转化率)

第四步:建立评估指标

不要评估"AI 好不好用",要评估"业务有没有提升"。

比如销售岗位,评估指标可以是:

  • 每位销售每天能跟进的客户数量(从 10 个提升到 20 个)
  • 销售方案的成交转化率(从 15% 提升到 25%)
  • 销售写方案的时间(从 2 小时缩短到 30 分钟)

第五步:定期复盘和优化

每周跟这个岗位的员工聊一次:

  • AI 帮到你了吗?
  • 哪里还不够好用?
  • 还有什么工作可以让 AI 来做?

根据反馈,持续优化 AI 应用。

做到这五步,你就跑通了最小闭环。

然后再考虑扩展到其他岗位。

记住:一个岗位做深做透,比十个岗位浅尝辄止要有价值得多。

07. 结语

回到最初的问题:你的员工都在用 AI 了,为什么组织生产效率没变?

答案很清晰:因为大多数企业还停留在"让员工自己用 AI"的阶段,没有把 AI 作为组织能力来建设。

AI 时代的竞争,不是"谁的员工更会用 AI",而是"谁的组织更能用好 AI"。

要实现这个跨越,企业需要完成四个转变:认知转变(从"工具"到"员工")、方法转变(从"流程优化"到"任务拆解")、评估转变(从"技术指标"到"业务指标")、组织转变(从"技术先行"到"技术 + 流程同步")。

AI 不是工具,是组织能力的重构。

在这个重构的过程中,领先企业与落后企业的差距正在拉大。

德勤调研显示:仅 4% 的企业实现了转型目标。这意味着,96% 的企业还在摸索中前行。

对于企业而言,现在不是"要不要做"的问题,而是"多快能完成组织级 AI 转型"的问题。

因为在这个 AI 驱动的时代,组织 AI 能力的差距,将决定企业竞争力的差距。

关于迅易科技

广州迅易科技有限公司成立于 2007 年,18 年企业级交付经验,服务 1000+ 成功项目,我们专注于企业数智化革新,提供从方案设计、系统实施到效果评估的全流程服务。如需了解 AI 落地解决方案,欢迎前往迅易科技官网联系我们。

让数据驱动决策,让智能创造价值。

本文参考资料:- 爱分析《2026 年企业 AI 落地趋势研究报告》(2026 年 12 月)- 德勤中国《香港大学与德勤中国 2026 企业 AI 应用指数》(2026 年 2 月)- 斯隆管理评论与 BCG 联合调研(2026 年)

为什么别人用 AI 写出的代码结构清晰、风格统一,而你写出来的却是“屎山”堆积?区别不在于 AI 的智商,而在于你会不会“调教”。

本文将为你提供一套经过实战验证的 “从 0 到 1 前端项目启动 Prompt 清单”,直接复制粘贴过去,修修改改,让 AI 瞬间变身你的资深技术合伙人。

为什么你用 AI 写的代码很乱?

很多小白用户在开始一个新项目时,习惯直接对 AI 说:

“帮我写一个记账小程序的首页。”

这就好比你对装修队说:“帮我装修一下房子。”
结果可想而知:客厅是欧式的,卧室是中式的,厕所是战损风的。

如果你想让 AI 写出 代码风格统一、色彩搭配和谐、交互逻辑一致 的专业级项目,你必须学会分步骤、模块化地给指令。

下面就是我总结的一套“四步走”标准化流程,以我的开源项目 Momento(Uni-app + Vue3)为例,教你如何从 0 到 1 驾驭 AI。

时光账记

时光账记是一款基于 Uni-app + Vue 3 开发的个人记账微信小程序,后端接口基于 go-zero 微服务框架构建。

这是一款专注于个人财务管理与生活记录的应用。它不仅支持非常简洁的方式来管理基础的收支记录,还提供了多账本管理、周期性自动记账、预算控制以及节日倒计时等贴心功能,帮助用户更好地管理个人及家庭财务。

现在我已将代码都开源了,感兴趣的朋友可以去观摩观摩,也请帮忙点个 Star 支持一下,谢谢!

小程序端(Uni-app + Vue3): https://github.com/pudongping/momento-miniapp
API 接口(Go + go-zero): https://github.com/pudongping/momento-api

前端部分 AI 占比 100%(自己一行代码都没写),接口部分 AI 占比 80%
这也是一套非常不错的 AI 练手项目,如果对你有帮助,希望帮忙点个 Star 支持一下,谢谢!

homepage.png

login.png

profile.png

recurring.png

transaction.png

🛠️ 第一步:定基调 —— 技术栈与架构约束

在写第一行代码之前,先发这条指令,把 AI 的“思维”固定在正确的轨道上。

📋 复制这条 Prompt:

# Role
你是一名拥有 10 年经验的前端架构师,擅长 Uni-app 和 Vue 3 开发。

# Project Goal
我们要从零开始开发一个名为 "Momento" 的个人记账微信小程序。

# Tech Stack (强制执行)
1. **框架**:Uni-app + Vue 3 (Composition API / <script setup>)。
2. **样式**:SCSS。
3. **构建工具**:Vite。
4. **代码风格**:
   - 必须使用 ES6+ 语法。
   - 变量命名使用 camelCase(小驼峰)。
   - 组件命名使用 PascalCase(大驼峰)。
   - 禁止使用 TypeScript(根据项目实际情况调整)。

# File Structure
请先帮我规划项目的目录结构,参考标准的 Uni-app 项目结构,并解释每个目录的作用。

✅ 效果:AI 会给出一个标准的目录结构,并且在后续的对话中,它会时刻记得“我要用 Vue 3 Setup 语法”,而不会突然给你蹦出个 Vue 2 的写法。


🎨 第二步:定颜值 —— 注入设计基因

不要让 AI 自由发挥颜色!一旦它放飞自我,你的 App 就会变成“霓虹灯”。你需要先把“调色板”喂给它。

📋 复制这条 Prompt:

# Design System Definition
我们需要定义一套全局的 UI 设计规范,请帮我生成一个 `uni.scss` 文件。

# Requirements
1. **主色调**:温暖的橙色 (#FF9A5A)。
2. **辅助色**:
   - 成功:#4CD964
   - 警告:#F0AD4E
   - 错误:#DD524D
3. **中性色**:
   - 主要文字:#333333
   - 次要文字:#999999
   - 边框颜色:#EEEEEE
   - 背景色:#F8F8F8
4. **圆角**:
   - 小圆角(按钮):100rpx
   - 大圆角(卡片):24rpx
5. **间距**:
   - 基础间距:20rpx
   - 倍数间距:10rpx, 30rpx, 40rpx

# Action
请生成代码,并告诉我如何在后续开发中引用这些变量。

✅ 效果:你得到了一份包含 $uni-color-primary 等变量的 SCSS 文件。以后你只需要说“用主色”,AI 就会自动填入变量名,而不是硬编码颜色值。


🧩 第三步:造轮子 —— 封装基础组件

这是最关键的一步!千万不要直接开始写页面! 先让 AI 把通用的按钮、弹窗、卡片封装好。

📋 复制这条 Prompt:

# Component Development
为了保持 UI 统一,我们需要先封装几个基础组件。

# Tasks
请帮我编写以下 Vue 3 组件(存放在 `components/` 目录下):

1. **BaseButton.vue**:
   - 支持 `type` 属性 (primary/default/danger)。
   - 默认圆角为全局变量中的小圆角。
   - 支持点击事件。
   
2. **BaseCard.vue**:
   - 白色背景,带有轻微阴影。
   - 圆角为全局变量中的大圆角。
   - 内部有 20rpx 的 padding。

3. **BaseModal.vue**:
   - 居中弹窗,带有半透明遮罩。
   - 包含标题栏、内容区、底部按钮区。
   - 动画效果:淡入淡出。

# Constraint
所有样式必须使用上一步定义的 `uni.scss` 变量。

✅ 效果:你拥有了一套可复用的“积木”。以后写页面时,AI 就会直接调用 <BaseButton>,而不是每次都重写一遍 <button class="btn">


🚀 第四步:写业务 —— 标准化开发流

现在基础打好了,可以开始写页面了。但别急,发指令时带上“参考系”。

📋 复制这条 Prompt:

# Feature Request
请帮我开发“记账页面” (pages/record/index.vue)。

# UI Requirements
1. 页面背景色使用全局背景色变量。
2. 顶部是一个“金额输入框”,样式风格参考 `BaseCard`。
3. 底部有一个“保存”按钮,**必须使用我们封装的 BaseButton 组件**。
4. 点击保存后,如果金额为空,弹出一个提示框(使用 `uni.showToast`)。

# Code Style
- 使用 Vue 3 `<script setup>`。
- 引入 `uni.scss` 变量。
- 保持代码简洁,关键逻辑添加注释。

✅ 效果:AI 会乖乖地复用你之前定义的组件和变量,生成出的页面不仅代码整洁,而且和整个 App 的风格完美融合。


📝 总结

发现了吗?用 AI 编程,核心不在于“写代码”,而在于“定规矩”。

如果你能按照:

  1. 定技术栈 (Context)
  2. 定设计变量 (Tokens)
  3. 定基础组件 (Components)
  4. 写业务逻辑 (Features)

这四个步骤来引导 AI,哪怕你完全不懂代码,也能做出一个专业级的前端项目。

万一,万一,你也不知道做成什么样,你也可以直接先跟 AI 进行交流,比如,可以这样问 AI “我想做一个记账的小程序,我应该采用什么样的色系……” 然后你就可以继续让它帮你先写几个 demo 页面出来,你自己再根据 demo 页面去做选择。

总之,如果你要借助 AI 来帮你完成项目,你一定要学会做项目拆解。

下一篇文章,我会继续讲解如何要求 AI 来帮我们给前端项目封装好请求方法,希望对你有所帮助~

很多人第一次接触 Function Call,都会有一种错觉:

这玩意不就是“让大模型调个工具”吗?

给模型塞几个工具描述,再让它返回一段 JSON,程序照着执行,不就完事了?

Demo 阶段,确实差不多。

但只要你真的把它往生产里推,很快就会发现,事情根本没这么简单。

因为 Function Call 一旦离开 PPT,真正要面对的问题就不再是“模型会不会调工具”,而是:

  • 它为什么总选错工具?
  • 参数为什么老是填错?
  • 为什么一个本来一步能做完的事,它要连续调 4 个工具?
  • 为什么工具明明返回错了,模型还能一本正经地胡说八道?
  • 为什么你以为自己做的是 Agent,最后其实只是“一个会随机调用接口的聊天机器人”?

说白了,Function Call 不是一个“锦上添花的小特性”,而是 AI 应用从“能聊天”走向“能做事”的那道坎。

跨过去了,才叫系统。

跨不过去,项目基本就会卡在“演示很好看,线上很难用”这个尴尬阶段。

今天不聊空概念,我们就聊点更真实的:

为什么很多 AI 项目看起来已经接上了 Function Call,最后还是做废了?

因为他们往往都死在下面这 5 步。


1. 第一步就理解错了:模型并没有“调用函数”

这是最常见、也最致命的误解。

很多人说:

Function Call 就是模型调用函数。

这句话听起来顺,实际上很容易把整个系统设计带歪。

更准确一点的说法应该是:

模型从来没有真正“执行函数”,它只是输出了一个结构化的动作建议。

比如模型返回:

{
  "tool_name": "search_docs",
  "arguments": {
    "query": "MCP 和 Function Call 的区别",
    "top_k": 5
  }
}

这说明了什么?

说明模型在说:

“我建议你现在去调一下 search_docs,参数大概这么填。”

注意,是“建议”。

真正干活的,不是模型,而是你的宿主系统。

这两者的区别为什么这么重要?

因为一旦你脑子里默认“模型能直接执行函数”,你就很容易漏掉下面这些关键环节:

  • 参数校验
  • 权限控制
  • 工具白名单
  • 超时重试
  • 结果清洗
  • 审计日志

最后就会出现一种很典型的系统幻觉:

你以为自己把执行能力交给了模型,实际上你只是把风险交给了线上。

所以 Function Call 最核心的一层,不是“调用”,而是结构化决策表达

模型负责出主意,系统负责拍板和执行。

这条边界不划清,后面全是坑。


2. 真正麻烦的,不是选工具,而是“参数终于出错了”

很多 AI Demo 看起来很丝滑,是因为它的工具参数极其简单。

比如:

  • 查一下天气
  • 搜一下文档
  • 查一下订单

参数通常就一两个字段,模型乱来也乱不到哪去。

但只要你把场景稍微做真实一点,问题马上就出来了。

比如一个企业内部 Agent,要支持:

  • 创建工单
  • 修改会议
  • 查询客户
  • 调 CRM
  • 调知识库
  • 调流程审批

这时候一个工具参数可能长成这样:

{
  "customer_id": "C1024",
  "priority": "high",
  "assignee": "sales_ops",
  "due_time": "2026-04-22 18:00:00",
  "notify_users": ["u1001", "u1002"],
  "tenant_id": "t_shanghai",
  "reason": "客户要求加急处理"
}

到了这里,模型就不再只是“会不会选工具”的问题了,而是:

  • 字段会不会漏
  • 枚举值会不会填错
  • 时间格式对不对
  • 多租户字段会不会串
  • 用户输入里没说清楚的字段,它会不会乱补

这也是为什么真正做过线上系统的人,很少会把注意力放在“模型选没选对工具”上。

他们更警惕的是:

模型给出了一个看起来很合理、实际上根本不能执行的参数集合。

这比完全不调用更麻烦。

因为它特别像真的,最容易骗过开发者。

所以成熟一点的系统,通常都会在参数层加一整套防线:

  • JSON Schema 校验
  • 类型校验
  • 必填字段校验
  • 枚举值约束
  • 默认值填充
  • 参数纠错或二次确认

如果业务更敏感,还会加:

  • 权限检查
  • 多租户隔离
  • 敏感字段脱敏
  • 人工确认节点

很多项目不是死在“模型不够聪明”,而是死在“大家太相信模型生成的参数”。


3. 能跑通一次,不代表它能稳定地“做完一件事”

这是第二个特别容易被忽略的点。

很多团队第一次做 Agent,都会很兴奋地展示:

“你看,它已经会调工具了!”

但问题是,会调一次工具能稳定完成任务,根本不是一回事。

一个真正可用的 Agent,往往不是一步结束,而是这样一个闭环:

理解问题
-> 判断信息是否足够
-> 选择工具
-> 生成参数
-> 执行工具
-> 读取结果
-> 判断下一步
-> 再次调用工具或结束

也就是说,真正难的地方不在第一跳,而在后面那几跳。

因为只要进入多步调用,系统就会立刻暴露出一堆新问题:

  • 模型会不会重复调用同一个工具
  • 它会不会在错误结果上继续推理
  • 它什么时候该停
  • 它会不会越调越偏
  • 它的步数、token、延迟还能不能控住

这就是为什么很多“看上去很聪明”的 Agent,到了线上体验特别差。

不是因为模型不会思考,而是因为系统没有给它设边界。

真正做生产的人,通常会给 Agent 加很多“刹车片”:

  • 最大调用步数
  • 单次任务 token 预算
  • 单工具调用频率限制
  • 超时终止
  • 重复调用检测
  • 异常分支回退

这些东西看起来一点都不性感,但它们往往才是 AI 系统能不能上线的关键。

说到底,Agent 的上限取决于模型,但下限取决于治理。


4. 大家都在聊工具调用,真正决定体验的却是“结果怎么喂回去”

这又是一个特别像细节、实际上特别核心的点。

很多人以为 Function Call 的主要工作在“前半段”:

  • 工具注册
  • 工具描述
  • 参数生成
  • 调用执行

但真正决定回答质量的,往往是“后半段”:

工具执行完之后,你到底把什么结果喂回给模型?

很多系统之所以看起来越做越乱,根本原因不是模型不会调工具,而是工具返回结果太脏。

常见问题包括:

  • 返回内容太长,直接把上下文塞爆
  • 下游接口字段太技术化,模型根本看不懂
  • 同一个工具返回了太多无关信息,模型被噪音带跑
  • 接口报错信息直接回传,模型拿错误文本硬编答案

举个很真实的例子。

你调了一个订单接口,原始返回长这样:

{
  "code": 0,
  "message": "success",
  "trace_id": "8f97c1...",
  "data": {
    "order_id": "A12345",
    "status": "shipped",
    "warehouse_code": "WH_09",
    "sync_version": 17,
    "operator_id": "sys_1029",
    "updated_at": "2026-04-22 10:30:00"
  }
}

这东西你直接喂回模型,模型未必能抓到重点。

更合理的做法通常是先整理成它更容易消费的结构,比如:

{
  "order_id": "A12345",
  "current_status": "已发货",
  "last_updated_at": "2026-04-22 10:30:00"
}

别小看这一步。

它本质上是在做一件非常重要的事:

把“系统返回结果”翻译成“模型可继续推理的证据”。

如果这层做不好,后面再好的模型也会被喂歪。

所以真正成熟的 Function Call 系统,通常都会单独做一层结果治理:

  • 结构化裁剪
  • 字段归一化
  • 错误语义标准化
  • 敏感信息脱敏
  • 长结果摘要
  • 关键证据提炼

很多时候,项目效果差,不是 Prompt 不行,而是喂回去的东西本来就是垃圾。


5. 最后一个坑,也是最大的坑:没有评测,你根本不知道自己做得到底好不好

这是最扎心的一点。

很多团队做 Function Call,到最后判断效果的方式非常朴素:

  • 跑几个样例
  • 看起来能用
  • 线上先试试

这在普通功能开发里都算危险,在 AI 系统里更是高风险操作。

因为 Function Call 最麻烦的地方就在于:

它不是“对”或者“错”这么简单。

它会出现大量灰度失败:

  • 工具选对了,但参数错了
  • 参数对了,但时机不对
  • 时机对了,但调了太多次
  • 调用成功了,但最终答案还是错的
  • 没调工具,结果模型幻觉反而显得更自然

如果没有评测体系,这些问题你几乎看不出来。

所以真正靠谱的团队,通常会盯这些指标:

  • 工具选择准确率
  • 参数生成正确率
  • 首次调用命中率
  • 平均调用步数
  • 平均 token 消耗
  • 平均响应时间
  • 最终任务成功率
  • 幻觉率

更进一步,还会专门构造测试集:

  • 明确应该调用工具的问题
  • 明确不应该调用工具的问题
  • 容易误选工具的相似问题
  • 参数容易填错的问题
  • 工具超时、报错、空返回的问题

你会发现,做到这里之后,Function Call 就已经完全不是“接个工具”这么简单了。

它开始越来越像一个完整的工程系统:

  • 前面是模型决策
  • 中间是协议和执行
  • 后面是结果治理和评测闭环

这也是为什么很多人觉得 AI 项目“落地难”。

不是难在模型本身,而是难在你要把一堆高不确定性的环节,硬生生做成一个稳定系统。


真正难的,从来不是让模型调一次工具

回头看你就会发现,Function Call 最容易骗人的地方就在这里:

它太容易做出一个“看起来已经成功”的 Demo 了。

模型能返回工具名。

参数也像模像样。

接口也调通了。

屏幕上甚至已经出现结果了。

于是很多人会下意识觉得:

这事差不多做完了。

恰恰相反。

大多数项目到了这里,其实才刚刚开始。

真正决定成败的,是后面这些更脏、更难、也更像工程的问题:

  • 你有没有划清模型和系统的边界
  • 你敢不敢完全相信模型生成的参数
  • 多步调用时你有没有刹车机制
  • 工具结果有没有被治理成“模型可用的证据”
  • 你有没有评测闭环去证明系统是真的在变好

如果这些问题没处理好,Function Call 就很容易沦为一个华丽但脆弱的功能展示。

只有把这些都补齐,它才会真正变成 AI 系统里的基础设施。

很多人以为 Function Call 解决的是“模型怎么调工具”,其实它真正解决的问题是:模型如何以一种可控、可验证、可治理的方式,开始接管现实世界里的动作。

这也是为什么,真正做过生产 AI 系统的人,聊起 Function Call 时,几乎从来不会只聊 Prompt。

他们更关心的,永远是边界、失败、治理和闭环。

END

写在最后:

最近私信问我面试题的小伙伴实在太多了,一个个回有点回不过来。

我大家公认最容易挂的 AI/Go/Java 面试坑点 整理成了一份 PDF 文档。里面不光有题,还有解题思路和避坑指南。

想要的同学,直接加我微信wangzhongyang1993,或者关注并私信我 【面试】,我统一发给大家。

前言

最近,是大家大规模找实习的时候。在这个时候,可能是大家第一次找实习,找工作,内心充满了各种疑惑。

对于大家的这种困惑,我把之前26届当时找实习问我的各种问题,给大家汇总出来了,希望能够对大家正确的职业思考规划有帮助

(这是不同时间写的,到时候思路策略肯定会有不同,具体有疑惑的可以私信哈)

(比如里面有的文章是去年9月份秋招写的,那哪个时候的策略肯定和四五月份是不一样的)

相关方向的初创公司的实习值得去吗,是不是学到东西的概率更大?

初创公司值不值得去,能不能学到东西。

其实可以首先对什么样的实习公司,什么样的公司实习排序。然后值不值得,在针对目前自身一个情况进行分析。
实习最主要加分的公司无非就是那些大厂、知名厂、以及一些行业的龙头企业

毕竟大家毕业也都是想去这些公司嘛,知名公司可以拿的出手,并且薪资也高。如果这个求职的时候,大家有过一段同等级的公司的实习,面试官会认为你已经被同级水平的公司筛选了并且通过了,说明你是有一定能力了,所以会很大程度给你面试,并且面试也应该不会太难。

当然,并不是所有同学都能拿到大厂实习offer,大多数都是拿一些中小厂的offer,那这个时候就会犹豫值不值得去了。这是就需要不同的情况,针对性分析了。

如果你是大一大二或者研一,大四保研的了,我认为可以去,一是可以去涨涨经验,给简历加加分 (实习一般都会加分,没有说实习还是减分这一说,只不过是加多加少罢了),二是去真是体验体验工作,看看这一行的强度自己是否能接受,如果不能接受,那可以提前考虑别的出路了。

如果你现在是大三或者研二,这个时候值不值得去,就需要看你目前基础准备的怎么样了,如果自我感觉准备的很好了,那就可以去。如果基础还差很多,马上都秋招了,还是专心搞基础吧,这才是关键,先保证自己起码能找到工作。

然后咱们再思考,实习能不能学到东西。其实首先,咱们考虑下,公司对实习生这个岗位是怎么定义呢,就知道能不能学到东西了。实习生这其实就是个临时工,毕竟你想走随时都可以走,这个时候如果你是老板,你会把核心的开发任务交给一个临时工来干吗。

那百分之百不会啊,所以说,不管去什么公司实习,其实都是学不到东西呢。

那企业为什么会招实习生啊,无非就是便宜免费劳动力一样,然后干干杂活多好哈哈哈。

那我个人去实习了又想让它发挥出应有的价值怎么办吧?
发挥出应有的价值其实就是简历上可以写一些含金量的东西,这个只能自己“偷”。

对看项目文档,看项目代码,针对性的看。脑海里有有一个想法,就是这个可以写在我都简历上吗,先初步预判下如果能,那就深入的学习。

了解下这个功能开发的需求原因,方案的调研设计,开发的逻辑,注重自己的文档总结,不然你看完不久就忘记了。梳理一份好的文档,就是让面试官针对这个简历写的点的考察,都可以在你都文档里面找到答案。

大厂侧开实习和小厂开发实习怎么选

先说选择,这个可以百分百确定选大厂,title很重要。
要想弄清楚那个选择对自己最有利,可以思考下实习的意义是什么?

实习无非就是给简历加分,拿到好offer,高薪offer。
那这就需要思考,简历怎么让面试官认可,那肯定是有个知名厂的实习,面试官看到你在知名厂呆过,面试官会认为你已经被同级水平的公司筛选了并且通过了,说明你是有一定能力了,所以会很大程度给你面试,并且面试也应该不会太难。

这个时候肯定会有人疑问,说我这是侧开实习,那秋招找正式工作的时候还能找开发吗

(1)简历是可以包装的,进去以后可以多做一些开发的任务,“偷”点开发的东西,多开发点自动话脚本;

(3)就是真去实习了,测试部门不能看到开发的代码,那也问题不大的。重点积累测试的内容也是可以的,重点是要有含金量。title是大于岗位的,同时下去继续学习开发的东西就可以了。

总而言之,就是让面试官知道你已经被大厂筛选过了,并且还通过了,这才是真正的加分点呢。

学历一般,没有实习,cpp学什么简历上可以增加含金量

这是星球同学在周五答疑上进行的询问,估计也有很多同学有这种困惑,学历一般,也没有对应大厂实习。但是依旧想秋招找个好工作不知道从何入手。

要想找个好工作,我们要首先理解清楚找工作的本质。找工作其实就是你投一份简历到公司,用人部门看到你的简历认为如果你简历上的都会了就可以胜任工作,然后给你发面试,面试通过发offer。

所以找工作最关键的就是要有一份不错的简历。

写一份不错的简历,首要要知道简历主要有哪几部分构成,针对可操作的部分改成最有含金量的尽力即可。

简历主要构成:

1.基本信息(个人信息、学历)

2.实习经历(并不是所有人都有,可有可无)

3.项目经历(一般都有,不然简历太空了,除非你有很多实习经历可以填补简历的空缺)

4.技术栈(让人知道你具体会什么,符不符合要求)

5.校园经历/个人评价(有没有参加过比赛获奖、以及展现自己对技术的热忱)

通过个人情况,一般我们可以包装的就是3、4、5点。重点能包装的其实就是项目经历和技术栈

项目无非就是放点高质量的项目(切记别把一个demo当作一个项目,比如实现了线程池内存池,以及rpc这放到技术栈里是个加分项,当作一个项目太简单了,规模太小),如果不知道做什么项目好,可以看看星球的互联网的项目以及底层项目

技术栈,这部分多写一些底层的知识,一是增加含金量;二是凸显出差异化,给面试官眼前一亮的感觉

如果自己学历一般,也没有大厂实习,技术栈也写的习俗平常,那怎么可能要你啊。

比如一个岗位招10个人,可能1000人投简历,然后面100人。这100人怎么筛选呢,无非就是学历好不好,有没有大厂实习。

如果自己这两个都没有,那就包装简历内容,让面试官看到你的简历发现你写的东西和大多数人都不一样,并且还很底层,让他对你产生兴趣,对你有了兴趣,才有面试的可能。有面试了能不能拿下就看你的实力了。

比如,计网的书写,一方面可以写大家都写的那部分内容(比如熟悉htt/https,熟悉udp、tcp三次握手四次挥手等),让面试官知道你基础能力也达到了合格水平

另一方面,可以写写星球为大家总结底层网络的相关知识点,给面试官眼前一亮,甚至屌爆他,让他对你产生极大的兴趣,这机会不就来了嘛。

校园经历,这个有参加过一些含金量的计算机相关比赛比较好,如果没有那只能多往展现自己对计算机热忱的事情上靠拢了,比如喜欢看计算机相关书籍,喜欢看源码等。凑凑字数

写给即将大三或者研二的同学的,真挚的建议

现在大家都知道,计算机编程这个行业内卷及其严重,导致现在招聘要求越来越高。如果想找一份好工作,想找一份可以进大厂的工作,那就需要在秋招春招的时候有一份非常出色的简历。

一份简历的构成,主要如下几个元素:

个人信息、校园经历、实习经历、项目经历、技术栈

个人信息(学历)这是已经确定无法改变的。校园经历,主要就是有没有含金量的比赛。如果你搞算法就是有没有顶会之类的。针对含金量的比赛,一般就是ACM,或者一些机器人大赛,这一般都是大一大二就开始准备备赛了,目前还没有准备,估计也准备不及了,也没有准备的必要了,所以这部分也基本很难改变了。

目前我们能做的就是改变实习经历、项目经历、技术栈。
项目经历、技术栈这部分,现在网络信息这么发达,大家获取的学习资料其实都差不多,所以这部分大家会发现其实写的也都差不多。

当然,有同学会说了,星球不是有独家资料嘛,这些资料我看外面都没有啊,我学这个不就可以超过别人了嘛。当然学这些,肯定可以超过外面同届或者超过面试官的水平。但是星球的同学都可以获得啊,那你怎么超过星球同学的水平哈。

所以这个时候有一段实习经历,一段大厂实习经历,会让自己简历很加分。尤其哪些学历不是特别好的同学,会很加分,会在一定程度上极大弥补学历的不足。

所以我今天要讲的是要找实习,要找大厂实习。怎么才能比较容易的找到大厂实习呢。其实面试一般不难,只要基础过关,一般都可以通过,实习面试通过就可以发offer。

关键是怎么才能更加容易的拿到面试?

答案是错峰竞争

根据这几年的辅导经验,以及同公司hr聊天,发现大家投实习一般都是在大三下或者研二下,即今年过完年立春之后陆续开始投递,也就是大家俗称的暑期实习。

这个时候大家投递热情很高涨,投递的人多了,那也就意味着难度变大了,毕竟供大于需了嘛。

所以我给大家写这篇文章,就是让大家把思路转化过来,不要一直随大流按部就班的来,如果这样来导致自己不管在什么阶段竞争压力都很大,及其容易出现努力很久,最终也是颗粒无收的状态。

那应该怎么办的,那就是错峰竞争,同时比别人提前多走一小步。

具体建议就是马上大三或者研二的同学,一定要在这个寒假找找实习,这个时候比较容易。为什么会这么说的?

一、就是目前现在在岗实习的学生大多数都是要今年秋招的,等秋招开启的时候,会有很多同学离职全力进行秋招,导致企业空缺出大量的实习hc

二、今年冬天投递实习的同学相对较少,很多人都是受网络的影响,一直认为明年投递才合适,并且也一直认为准备不好不敢投递,身边投递的少,就更加不敢投递了。
这个时候及其容易上岸,这就是错峰竞争。

什么是比别人提前多走一小步呢?可以看下面图片(领先一小步,就会容易很多)

多走一小步,后面每一步都会容易很多

可实习时间短,怎么做来能最大限度的发挥出实习的含金量

最近星球,有同学拿到了大厂的实习,但是可能因为学业的原因,可实习的时间相对较短,就问。在有限的时间内,怎么做,才能最大程度的发挥这段实习的含金量,放在简历上。估计这也是很多实习同学的一个疑惑。下面是他的原文:

在有限的时间内,最大发挥出实习的价值。可以理解为两点:

(1)简历可以写点含金量的东西

(2)针对面试实习的询问都可以应答上来

简历写含金量的东西

短时间内,让简历真实的写一些自己实习开发的功能点,估计不太现实。首先你是学生,还是实习生。一个部门老大怎么会把有含金量的开发任务给一个实习生呢,万一开发一半你跑了咋办,谁接手呢。找实习,就是让来打杂的。

那该怎么办呢,怎么“偷”点含金量的东西写上去呢?

1.可以先看看部门文档里有没有讲某个功能,讲这个功能开发的需求,功能实现的方式,然后找到对应的patch,如果看不懂就让ai给你解释,然后自己整理成文档。

2.如果在部门文档里找不到开发某个功能的文章讲述。那就看部门和你这个方向一样人提交的代码,看提交task任务的代码(不是fix bug的),然后点开他开这个任务的页面的描述,以及他提交的patch,看懂了,或者让ai解释,梳理成文档。(这是功能实现逻辑的梳理)。但是你还要明白,为什么要开发这个(需求),以及为什么这样做(方法的调研),这部分如果自己不懂,就趁吃饭或者散步的时候直接问开发这个任务的人就可以,一般都会给你解答的

及文档的梳理,至少包含三部分,开发的需求,设计思路,功能逻辑

针对实习面试的询问都能答上来

实习面试不仅仅会问你简历上写的这几点。还会宏观上拷问你,问你开发流程,问你整体的架构等等,这部分怎么掌握呢

1.首先快速梳理下部门的开发流程,及工作的方向,以及用的技术。这是聊实习最开始先唠嗑的点,如果这都答不出来就尴尬了

2.对项目架构整体了解了解,比如你这块负责的方向主要有几个进程啊,每个都是干什么的啊,彼此之间有什么联系啊。每个进程是怎么一个架构啊。

上述两点主要是用来让你在面试的时候,问你实习的整体情况,也就是闲聊发散性问的时候能答出来。(这部分快速掌握的方法就是看部门的文档,一般文档里都有写)

找实习,面试官告诉我至少要实习半年,但是我实际只能实习一两个月怎么办?

最近答疑的时候,有同学问我,就是说“面试官面试官告诉我至少要实习半年,但是我实际只能实习一两个月怎么办?

如果到时候实习一两个月就离职,会不会不好,会不会被这家公司拉黑,对我求职有不好的地方”

对于同学问出这种问题,只能说一直生活在这校园相对单纯的环境,没经历社会,还相对比较单纯。

因为这种问题找实习的时候会经常被问到,对于这问题,我们从两方面进行分析一下。

第一方面,就是努力消除大家心中“负罪感”

第二方面,就是对于这问题的回答的一个固定答案。

消除心中的“负罪感”:

(1)大家可以在网上看看,看看每年都有多少厂在裁应届生,努力那么久,刚进来没多久,没技术,工作经历时间短,就让应届生背指标,骗补贴。这种公司良心何在,责任感何在。(应该众所周知的厂子都有这种案例) 所以说,人家干你的时候毫不手软。 那自己也要时时刻刻为自己的前途考虑,思考多站在自己的未来角度思考,怎么有利于自己未来就怎么来

(2)你自己实习一段时间,简历可以写大厂经历加分。那离职了,空出来hc,是不是又要继续找,给哪些没有实习经历的同学一个机会。也是变相的帮助一下,素未谋面的,一起为着相同目标努力奋斗的战友,也算是做了一件好事

回答固定的答案:

(1)至少实习半年以上,自己感觉如果刚进入没多久就离开,其实对自己的技术提升为乎甚微。如果想技术提升,还是要长时间的磨练呢。自己好不容易,进入了咱们公司,咱们部门,能够有这么多优秀的同事,还是想踏下心来,好好提升提升技术,并且也为咱们部门做出自己的贡献呢

(2)每周的实习工作时间,和部门的同事一样。对加班也是接受的,毕竟新人刚入职自己还是很菜的,自己认为适当的加班还是很有必要呢

(主打一个舔,把offer骗到手,从被动变成主动,说他想听的答案)

本文由mdnice多平台发布