联想笔记本的恶劣营销
最新频繁看到神吹联想笔记本的相关推送,B 站也有多名 UP 主在推。
联想小新 Pro 16 GT AI 元启版,续航超 27 小时,真实办公使用 20 小时。。。
之前是联想 ThinkPad E14 AI 元启版续航 26 小时。。。
这些拿钱的营销号,完全不说实话。大家理智选择,不要被带偏。
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
最新频繁看到神吹联想笔记本的相关推送,B 站也有多名 UP 主在推。
联想小新 Pro 16 GT AI 元启版,续航超 27 小时,真实办公使用 20 小时。。。
之前是联想 ThinkPad E14 AI 元启版续航 26 小时。。。
这些拿钱的营销号,完全不说实话。大家理智选择,不要被带偏。
今天给大家分享一下,我们星球开发的底层操作系统内核项目的面经,看看大家对于此项目是否感兴趣,如果感兴趣,可以加入星球进行学习。 关于此项目的介绍,可以看下面链接的文章内容: https://mp.weixin.qq.com/s/jWvq9YAF52Mm57TmhT3qow 1.性能监控项目,解析/proc文件下meminfo去获取内存的一些使用情况,说说这里面有哪些资源的一些使用参数 2.仅仅是做了一个性能的采集吗,有没有参与一些性能的优化,比如内存优化呀? 1.有没有通过一些渠道去考虑或者了解,比如像cpu负载过高,内存可用比较少,这些情况我该怎么去优化? 2.linux监控项目,说说使用ebpf进行网络流量统计的流程,ebpf在网络协议栈里面如何工作 3.性能监控项目,读取到了哪些内存指标,读取到之后如何去做一个分析(定位内存问题) 4.cpu负载如何去做一个分析,怎么判断具体系统是哪里的问题 5.cpu具体各个指标怎么去做一个分析 6.采集的优化是怎么做的,降至毫秒级的操作 7.stress、iperf工具怎么使用的,平时还有用其他的一些验证工具吗 8.性能采集这块有涉及哪些模块,包括涉及哪些代码逻辑,整体偏向技术的summary的东西讲讲 9.性能采集这块有涉及哪些模块,包括涉及哪些代码逻辑,整体偏向技术的summary的东西讲讲 10.性能监控用到了grpc、protobuf,你讲一下grpc它的一个底层原理 11.看你有做这个网络流量统计,你对协议栈这块了解吗?比如内核协议栈或者其他的一些协议栈 12.linux系统监控的话,网络流量统计用的ebpf,你简单介绍一下这个ebpf它是如何实现一个网络流量统计的一个功能的 13.你用ebpf的这个它走的是内核协议栈吗还是什么 14.对于linux分布式性能监控这个项目,在我不熟悉这个系统的情况下,你给我介绍一下这个系统,可以用各种不同的维度或者方法来给我介绍一下 15.对于这个性能监控项目,你觉得从技术上来讲,这个系统最关键的几个点是什么 16.在这个性能监控系统里面,再稳定性方面,你是怎么涉及或考虑的? 17.内核模块用什么代码编写的? 18.本来可以用proc方式获取数据,为什么要用内核模块? 本文由mdnice多平台发布前言
面经分享
(说了各种采集方式的调研与选择和优缺点,迭代,做的采集方式的优化)
飞牛这事给所有人上了一课,你作为用户永远不知道厂家会犯什么错误,即便是你购买的付费服务也有可能成为黑客的工具,那我们有哪些方案可以在厂家之外能保护我们自己呢。
直接把 NAS 暴露在公网我觉得是不合理的,我觉得至少要套一层隧道。
我目前方案是使用 OPENVPN+证书登录,
具体的方案是用了一个虚拟机专门跑 OPENVPN ,然后路由器上端口转发虚拟机,所有的操作都需要 OPENVPN 之后才能进行,并且每隔一段时间对虚拟机进行更新。
但这个方案并不适合小白,一是我有 V4 公网,二是对于小白用户来说用专门的虚拟机并且按照自己的需求去搭一个 OPENVPN 难度不低。
那么有什么可以增加安全性又对于小白用户比较友好的方案呢?
《从零开始》
最近书荒,开贴!
最近认识到了一个新词 -- “信息差”,
由此,大家平时是怎么识别到哪些对自己有用或者干星期的信息的。
看了一些 rss 订阅软件,bilibili 上每日信息差的视频。感觉似乎都不是很好。
如题
以前的腾讯视频 VIP 限制 3 台设备同时播放,现在仅限 1 台。当触发该限制之后,在 APP 端控制其他设备下线后还需要再等待 15 分钟后才能再次使用
目前的 VIP 看视频/电影的片头/暂停中还是会植入广告,VIP 的权益大大缩水,而且会员价格还比之前贵。
如果要启用https证书,需要修改3个地方: 你在docker上面面板里修改gitlab的环境变量里的 gitlab的访问地址,选域名访问,那ip访问就不可以了;选https访问,那http访问就会出问题的。一 环境与版本
二 记录:
因为这类软件,不管你的路由器里是否配置了局域网域名解析,都会用自己软件里的dns配置解析域名。
7.1 gitlab的反向代理这里需要添加证书,并最好开启强制http跳转到https,因为gitlab系统读取配置的时候只会读取带访问协议的地址.
7.2 修改这里:/www/dk_project/dk_app/gitlab/<你的gitlab容器名>/docker-compose.ymlenvironment:
GITLAB_OMNIBUS_CONFIG: |
# Add any other gitlab.rb configuration here, each on its own line
external_url 'https://${DOMAIN_HOST}'external_url是会出问题的,直接改这里,改完之后要记得重建。
7.3 如果用域名访问gitlab,记得去nginx反向代理那设置那里改一下你的反向代理地址的端口。比如你之前的http访问端口是20080,https访问端口是20443,那这个时候就要从20080改到20443。docker-compose.yml文件里,在external_url的配置下面一行,再增加一行registry_external_url,至于后面跟什么域名,随你便。记得如果你填写的是域名,在nginx那里添加以下反向代理记录。
三. 证书问题
/www/dk_project/dk_app/gitlab/<你的gitlab容器名称>/config/ssl里添加响应的证书文件,域名一定要和你的registry_external_url对应上。
飞牛论坛里大把网友因中招无法升级系统还在咨询官方,甚至抱有侥幸心理认为重装系统再挂载存储池就万事大吉,虽说数据无价,但这时候不应该第一时间拔掉 NAS 网线-(有能力的隔离存储池进行查杀)-格式化硬盘吗?究竟是太小白了还是低估了 hacker😨
顺便说一句,此时此刻,明知中招还将 NAS 处于联网状态等待如何“修补”漏洞的人,以为关闭了 FN Connect 就万无一失的人,你浪费的时间就是给 hacker 操作的时间。
再顺便说一句,Crowdsec 是个好东西,如果非要暴露在公网,基本的安全知识得 Get 一些。顺便看了一下飞牛的态度,呵呵,放到娱乐圈应该已经被封杀了吧。
随着企业走向全球化,稳定、低延迟的跨境网络连接已经不仅是“速度体验”问题,而是业务连续性、数据同步、安全合规的关键保障。无论是跨境电商、海外办公、SaaS 系统访问还是实时视频会议,普通互联网常常无法满足企业级要求,这也促使越来越多公司选择国际专线宽带来优化跨境连通性。 一、国际专线宽带有哪些类型? 传统国际专线通常指 物理隔离 / 逻辑隔离的专用带宽,比如 MPLS / IPLC 等,这类专线从国内直连海外节点,专有资源独占,不经过公网共享。 核心优势: SD-WAN 是在运营商合法国际出口基础上,通过 智能路由、链路叠加与流量优化技术 实现的跨境网络连接方式。它不是单纯的物理专线,而是将运营商出口 + 多链路(包括宽带/4G/5G等)结合,通过软件灵活管理。 核心优势: 二、影响国际专线宽带价格的因素是什么? 三、国际专线宽带怎么收费的? 基础版本: 企业级专线: 综合来看,第三方服务商OSDWAN提供了更灵活、更低门槛的价格方案,而运营商直供则更适合对大带宽、高 SLA 有硬性要求的场景。 四、国际专线宽带哪家好? 五、国际专线宽带怎么开通? 开通国际专线一般包括以下几个步骤: 六、常见问答 Q1:国际专线一定要用么? Q2:SD-WAN 和传统专线哪个好? Q3:带宽越大价格越贵吗? Q4:为什么第三方服务商价格比运营商低? OSDWAN作为国内专业的跨境网络服务商,为出海企业提供合规、高速、稳定的网络解决方案,支持硬件、软件方案灵活部署。
简单来说,主要有以下两种主流解决方案:
国际专线宽带并不是一个固定价格,不同企业需求不同,对价格有显著影响的关键因素包括:
带宽是价格的核心决定因素。例如 5M、10M、50M、100M 的专线在费用上有较大差异,带宽越大费用越高。
不同国家/地区链路成本存在明显差异:
亚洲区域(如香港、新加坡)通常比欧美方向便宜;
资源稀缺区(如南美、中东)成本更高。
传统国际专线价格偏贵,而 SD-WAN 通过弹性调度和混合链路,降低了整体成本。
运营商自身提供的专线服务与第三方服务商在价格和服务层面可能不同,服务级别协议(SLA)、响应时间、监控与管理平台等也会对费用产生影响。
三大运营商(中国电信/中国联通/中国移动)是企业国际专线的主要来源之一,以下为市场上反馈的典型价格区间(仅供参考):
以国内专业跨境 SD-WAN 服务商 OSDWAN 为例,其国际专线宽带价格较有弹性,提供不同套餐供选择:
办公室账号版: ¥690/年起,共享带宽、适合轻量办公。
社媒运营账号版: ¥1500/年起,提供独享静态 IP,更适合社媒和电商运营。
独立专线标准版: ¥10.000/年,含独享合规 5M 专线带宽 + 静态住宅 IP。
选择“好”的国际专线,不能只看价格,还要看以下几个要素:
正规服务商应基于运营商合法国际出口通道,支持 SLA 保障与故障响应。
不同企业场景需求不同:
对业务稳定性极高要求(如金融交易)更适合传统国际专线;
对成本与灵活性要求更高(如跨境电商、外贸小团队)可选 SD-WAN 方案。
运营商专线:固有优势是资源可靠、全球覆盖广,但成本和部署周期较高。
OSDWAN 等第三方服务商:价格更灵活、方案多样、支持快速部署,尤其适合中小企业、外贸团队、直播团队、跨境电商企业等场景。
确认用途(外贸办公、SaaS 加速、跨境电商等)、带宽需求、目标国家/区域。
可根据预算与业务需求,与运营商或第三方服务商(如 OSDWAN)签订年度/季度服务合同。
企业通常需要提交营业执照、联系人信息、带宽规划等基础资料进行申请备案。
服务商队伍将配置链路、分配端口/IP、部署设备,必要时进行 QoS、路由策略设置。
测试网络稳定性、延迟与可用性,确认满足业务需求后正式启用。
A:不一定,不过对于跨境办公、大流量数据传输、低延迟在线服务等重要业务,国际专线能显著提升体验与稳定性。
A:SD-WAN 在灵活性和成本上更优,适合外贸、电商和远程办公等;传统专线稳定性更强,适合对 SLA 有极高要求的场景。
A:是的。带宽大小是专线收费的核心决定因素,带宽越高,总费用越高。
A:第三方服务商通常通过渠道批发资源+SD-WAN 技术灵活调度,降低了成本,并提供更适合中小企业的套餐。
OSDWAN在全球的数据中心节点50个,POP节点超过200个,可以为出海企业提供海外加速、SaaS加速、SD-WAN组网、跨境组网、云专线等产品服务,助力中国企业开拓国际市场。
尊敬的合作伙伴、客户及所有关注者: 湖南元增长科技有限公司谨此宣布,公司于2025年再次正式获得了由国家版权局颁发的一项《计算机软件著作权登记证书》。具体情况如下: 软件名称: 元增长零信任sase办公安全系统osdwan客户端v1.0 著作权人: 湖南元增长科技有限公司 登记号: 软著登字第[2025SR2434945]号 权利取得方式: 原始取得 该证书的取得,是国家版权行政管理机关对我司提交的该软件源代码原创性的一份初步法律确认。这标志着公司在相关技术方向的自主研发上迈出了一小步,相关的知识产权得到了基础保护。 我们清醒地认识到,软件著作权登记仅是产品研发历程中的一个节点。当前版本的软件(v1.0)仍需在性能、兼容性、安全性及用户体验等方面进行大量的测试、优化与升级工作。我们将继续以严谨、负责的态度推进后续研发,并积极寻求内外部测试机会,收集真实反馈以驱动产品改进。 公司始终坚持合法合规经营,尊重并积极保护知识产权。未来,我们将继续在相关技术领域进行学习和探索,稳扎稳打,力求通过实实在在的产品与服务,为客户创造价值。 感谢大家一直以来的关注与支持。 特此公告。 湖南元增长科技有限公司 2025年12月17日
无法改光猫桥接,两个电脑都有 IPV6 地址,但是都是运营商默认规则(应该是可以出不可以入,ping 可以通),有什么方案可以用一台访问另外一台 RDP ?
IPV4 有打洞,ipv6 有类似的吗?
LangGraph 设计的一个核心是:多智能体工作流本质上是图结构,而非线性链。早期 LLM 应用普遍采用"提示 → LLM → 响应"的线性模式,但这种架构难以应对真实智能体系统的复杂性。比如生产环境中的多智能体协作需要分支(基于数据选择不同执行路径)、循环(支持重试与迭代优化)、汇合(多个智能体向共享状态写入数据),以及条件路由(根据执行结果动态决定后续流程)。 LangGraph 里每个工作流都是一个 StateGraph——本质上是有向图。节点就是智能体,或者说处理状态的函数;边是智能体之间的转换;状态则是在整个图中流动的共享数据结构。 这样做的好处非常明显:图本身就可以当作开发文档文档,一眼能看懂流程;加减节点不用动协调逻辑;状态有类型约束;循环有内置的终止条件,不会跑成死循环。 节点、边、状态三者各司其职。节点封装具体的逻辑操作,只管做事;边定义节点间怎么交互、谁先谁后;状态承载共享上下文,让节点可以保持无状态。这种职责分离让系统好理解、好调试、好扩展,节点还能跨工作流复用。 图定义是声明式的,但真正让编排变得有意义的是运行时行为。 工作流启动后,LangGraph 用状态机来管理执行。首先从入口节点的初始状态开始,然后调用智能体函数并传入当前状态。智能体返回的是增量更新而不是整个状态的替换,LangGraph 拿到更新后原子性地合并到当前状态,接着根据图定义决定下一个节点,同时创建检查点把当前状态和执行位置持久化下来。这个过程一直重复,直到走到 END 节点或者达到最大迭代次数。 有一点很关键:智能体永远不会直接改共享状态。它们拿到的是只读副本,算完之后返回更新,实际的状态修改由 LangGraph 来做,可以保证了原子性和一致性。 边定义了哪些转换是允许的,但具体什么时候转换由运行时决定。 静态边没什么花样: diagnose 节点跑完、检查点创建好之后,LangGraph 立刻拿更新后的状态去调 plan_fix。 条件边就灵活多了: verify 完成后,LangGraph 调用 route_function(state) 来判断下一步走哪条边。函数返回 retry 就回到 diagnose,返回 resolved 就结束。 任何节点在执行前它的所有前置节点必须已经完成并创建了检查点,这就避免了 Pub/Sub 系统里常见的那种"前面还没跑完后面就开始了"的问题。 LangGraph 的状态跟传统系统不太一样。 它不是存在 Redis 或数据库里让智能体直接访问的共享内存。LangGraph 在内部维护状态,给智能体的是受控访问。对智能体来说状态是不可变的——拿到的是快照,不能直接改,只能返回想要的变更。 多个智能体并行跑的时候(通过并行边),LangGraph 收集所有更新,用 reducer 原子性地一起应用。读-修改-写的竞态条件就这么解决了。 每个检查点还会创建一个状态版本。想看执行历史中任意时刻的状态?直接查检查点就行,这就是所谓的时间旅行调试。 检查点不只是日志,它们是恢复点。 每个检查点记录完整的状态快照、当前在图中的位置(刚执行完哪个节点)、还有元数据(时间戳、创建检查点的节点、执行路径)。 创建时机有三个:每个节点成功完成后、条件边评估前、以及工作流暂停时(比如等人工审批)。 这样如果节点执行到一半崩了,可以从最后一个检查点重试就行;长时间运行的工作流可以暂停再恢复,进度不会丢;调试的时候能从任意检查点开始重放。 假设用户发起请求:"修复服务延迟问题"。 状态在节点间累积——指标、方案、操作结果都在里面。每个节点都能看到之前所有节点产出的完整信息。重试逻辑是图结构强制的,不是写在智能体代码里。出了故障检查点可以让程序随时恢复运行。 用 LangGraph 的话,智能体只管返回自己的更新。协调、状态合并、路由、持久化,运行时全包了。 传统多智能体系统喜欢累积对话历史: 这东西会无限增长,智能体每次都得在历史里翻来翻去找有用的数据。 LangGraph 换了个思路,状态就是当前世界的快照: 智能体读当前值、更新当前值。历史通过检查点单独维护,调试用得着,但工作状态保持精简。访问状态 O(1),不用解析历史;数据所有权清晰,一眼看出哪个字段归谁管;推理也简单,当前状态是啥就是啥。 Reducer 解决并行协调 多个智能体要往同一个状态字段写数据怎么办?LangGraph 提供 reducer——专门合并并发更新的函数。 传统 A2A 模型里,智能体得自己搞协调:抢锁、读-修改-写、重试、冲突检测。这套东西各团队实现得五花八门,一旦出现部分故障就容易出问题。Reducer 把冲突解决挪到编排层,智能体级别的协调逻辑直接省掉。 比如说下面的例子,三个监控智能体并行检查不同的服务副本: 三个 Data Agent 各自返回健康检查结果,reducer(这里就是列表的 add 操作)自动把三份结果合成一个列表。没有智能体需要知道其他智能体的存在,不用抢锁,不用协调更新。 没有 reducer 的话,需要手动加锁防覆盖、写协调逻辑合并结果、还得担心更新丢失。有了 reducer,编排层自动处理。 检查点用于调试和恢复 每次节点执行都会创建检查点,状态和执行位置的快照会持久化到 Postgres、Redis 或文件系统。 生产环境出故障了?可以检查检查点的内容,看看每个智能体观察到了什么、做了什么决定。这相当于给智能体工作流装了黑匣子,决策链条一清二楚。 服务器中途崩了也可以从最后一个检查点恢复,不用从头来。对那些要调用昂贵 API 或者收集大量数据的长时间任务来说,这太重要了。 而且工作流可以暂停几小时甚至几天,状态通过检查点保持现有状态,从暂停的地方精确恢复,上下文完整保留。 LangGraph的另外一个卖点是工作流改起来容易。 假设初始工作流是 Diagnose → Fix → Verify,现在要加个需求:"修复之前先查一下 Jira 有没有已知问题"。 代码改动就这么点: 单个智能体的实现不用动,状态协调逻辑不用动,检查点处理不用动,错误恢复不用动。 如果换成换成 Pub/Sub 呢?事件路由逻辑要改,完成跟踪要改(现在是 4 个智能体不是 3 个了),状态模式协调要改,所有集成点都得重新测。 再看重试逻辑的修改。原来是最多重试 3 次: 新需求:"只有临时性错误(网络问题)才重试,永久性错误(配置问题)不重试"。改条件函数就行: 业务逻辑在工作流结构里一目了然,改起来也顺手。 生成的方案不够好,可以直接加个循环: 方案不断迭代,直到质量达标。 并行信息收集时需要同时从多个来源拉数据: LangGraph 保证 analyze 节点在三个数据源都拿完之后才开始跑。 高风险操作需要人来进行确认: 这个确认过程可以等几小时甚至几天,不消耗任何的资源。 复杂工作流(5 个以上智能体、有条件逻辑、有循环)、业务逻辑经常变、需要事后调试分析、有人工审批或质量门控、长时间任务需要崩溃恢复——这些场景 LangGraph 很合适。 简单的线性流程(A → B → C,没分支)、智能体完全独立不需要协调、对延迟极度敏感(编排开销要控制在 10ms 以内)、或者团队有深厚的分布式系统功底想自己搞状态机——这些场景替代方案也挺好。 编排框架在复杂系统中的价值已经被反复验证:Kubernetes 之于容器、Airflow 之于数据管道、Temporal 之于通用工作流。LangGraph 将同样的理念带入多智能体 AI 领域,提供了 LLM 感知的编排能力。 其核心价值在于:图结构让工作流易于修改和扩展,检查点机制保障了可调试性和故障恢复,reducer 和原子状态更新解决了并行协调难题。开发者可以专注于智能体逻辑本身,而非协调管道的实现细节。 对于正在构建多智能体系统的团队,LangGraph 提供了一条从实验原型到生产系统的可行路径。 https://avoid.overfit.cn/post/207f7dd3b4b2488983645d365c9e0b89 作者:ravikiran veldandaLangGraph 如何表示工作流
from langgraph.graph import StateGraph, END
from typing import TypedDict
# Define your state schema
class IncidentState(TypedDict):
incident_id: str
current_metrics: dict
proposed_solution: dict
issue_resolved: bool
retry_count: int
# Create the graph
workflow = StateGraph(IncidentState)
# Add agent nodes
workflow.add_node("diagnose", diagnose_agent)
workflow.add_node("plan_fix", planning_agent)
workflow.add_node("execute_fix", worker_agent)
workflow.add_node("verify", verification_agent)
# Define transitions
workflow.add_edge("diagnose", "plan_fix")
workflow.add_edge("plan_fix", "execute_fix")
workflow.add_edge("execute_fix", "verify")
# Conditional: retry or exit
workflow.add_conditional_edges(
"verify",
lambda state: "resolved" if state["issue_resolved"] else "retry",
{
"resolved": END,
"retry": "diagnose" # Loop back
}
)
workflow.set_entry_point("diagnose")运行时到底发生了什么

边遍历机制
workflow.add_edge("diagnose", "plan_fix") workflow.add_conditional_edges(
"verify",
route_function,
{"retry": "diagnose", "resolved": END}
)状态管理的特殊之处
检查点持久化
一个完整的运行时示例
T0: Workflow starts
- Initial state: {incident_id: "INC-123", retry_count: 0}
- Entry point: "diagnose"
T1: "diagnose" node executes
- Receives: {incident_id: "INC-123", retry_count: 0}
- Agent calls Data Agent, fetches metrics
- Returns: {current_metrics: {cpu: 95, latency: 500ms}}
- LangGraph merges: state now has metrics
- Checkpoint created
T2: Static edge triggers: "diagnose" → "plan_fix"
- "plan_fix" node executes
- Receives merged state (incident_id + retry_count + current_metrics)
- Agent calls Knowledge Agent for runbook
- Returns: {proposed_solution: "restart_service"}
- LangGraph merges
- Checkpoint created
T3: Static edge triggers: "plan_fix" → "execute_fix"
- "execute_fix" node executes
- Calls Worker Agent
- Returns: {action_status: "completed"}
- Checkpoint created
T4: Static edge triggers: "execute_fix" → "verify"
- "verify" node executes
- Calls Data Agent again
- Returns: {current_metrics: {cpu: 90, latency: 480ms}, issue_resolved: false}
- Checkpoint created
T5: Conditional edge evaluation
- LangGraph calls route function with current state
- route_function checks: state["issue_resolved"] == false and retry_count < 3
- Returns: "retry"
- LangGraph increments retry_count
- Routes back to "diagnose" (cycle)
T6: "diagnose" executes again (retry [#1](#1))
- Process repeats with updated state...
关键架构模式
# Common pattern - append-only log
messages= [
{"role": "user", "content": "Service X is slow"},
{"role": "data", "content": "CPU at 95%"},
{"role": "knowledge", "content": "Try restarting"},
{"role": "action", "content": "Restarted service"},
...
] classState(TypedDict):
# Current values, not history
incident_id: str
current_cpu: float
recommended_action: str
action_status: str
retry_count: int fromtypingimportAnnotated
fromoperatorimportadd
classState(TypedDict):
# Reducer: combine all health check results
health_checks: Annotated[list, add]修改工作流的灵活性
# Add the new agent
workflow.add_node("check_jira", jira_agent)
# Rewire the flow
workflow.add_edge("diagnose", "check_jira") # New path
workflow.add_conditional_edges(
"check_jira",
lambda state: "known_issue" if state["jira_ticket"] else "unknown",
{
"known_issue": "apply_known_fix", # New path
"unknown": "plan_fix" # Original path
}
) # Before
workflow.add_conditional_edges(
"verify",
lambda state: "retry" if state["retry_count"] < 3 else "end",
{"retry": "diagnose", "end": END}
) # After - just change the condition function
def should_retry(state):
if state["issue_resolved"]:
return "success"
if state["error_type"] == "config":
return "escalate" # Don't retry config errors
if state["retry_count"] >= 3:
return "max_retries"
return "retry"
workflow.add_conditional_edges(
"verify",
should_retry,
{
"success": END,
"retry": "diagnose",
"escalate": "human_review",
"max_retries": "alert_team"
}
)LangGraph 支持的典型模式
workflow.add_node("generate_solution", llm_agent)
workflow.add_node("validate_solution", validation_agent)
workflow.add_node("refine_solution", refinement_agent)
workflow.add_conditional_edges(
"validate_solution",
lambdastate: "valid"ifstate["solution_quality"] >0.8else"refine",
{
"valid": "execute_fix",
"refine": "refine_solution"
}
)
workflow.add_edge("refine_solution", "generate_solution") # Loop back fromlanggraph.graphimportSTART
# Parallel nodes
workflow.add_node("fetch_metrics", data_agent)
workflow.add_node("fetch_logs", elasticsearch_agent)
workflow.add_node("fetch_config", knowledge_agent)
# All start in parallel
workflow.add_edge(START, "fetch_metrics")
workflow.add_edge(START, "fetch_logs")
workflow.add_edge(START, "fetch_config")
# All must complete before analysis
workflow.add_node("analyze", analysis_agent)
workflow.add_edge("fetch_metrics", "analyze")
workflow.add_edge("fetch_logs", "analyze")
workflow.add_edge("fetch_config", "analyze") workflow.add_node("propose_fix", planning_agent)
workflow.add_node("await_approval", approval_gate)
workflow.add_node("execute_fix", action_agent)
workflow.add_edge("propose_fix", "await_approval")
# Workflow pauses at await_approval
# State is persisted
# When human approves, workflow resumes
workflow.add_conditional_edges(
"await_approval",
lambdastate: "approved"ifstate["human_approved"] else"rejected",
{
"approved": "execute_fix",
"rejected": "propose_alternative"
}
)什么场景适合 LangGraph
总结
Pagefind 是一个专为静态网站设计的开源搜索引擎,它能够自动索引你的网站并提供完全离线的搜索体验。 Pagefind 会自动在 使用 1. 只索引主要内容 2. 使用权重优化结果 3. CLI 参数配置 首先通过架构图了解 Pagefind 的整体设计: Pagefind 的工作流程可以分为两个阶段:构建时索引和运行时搜索。 当你运行 关键技术点: 索引结构详解: 当用户输入搜索查询时的完整时序: 性能分析: Pagefind 最大的创新是渐进式加载。传统的客户端搜索(如 Lunr.js)需要加载完整索引: Pagefind 的方案: 实现原理: 倒排索引是搜索引擎的核心数据结构: 当搜索 "React 教程" 时: Pagefind 使用 TF-IDF 算法计算搜索结果的相关性: TF(词频):词条在文档中出现的频率 IDF(逆文档频率):词条的稀有程度 TF-IDF 得分: 示例计算: 假设我们有 100 个文档,搜索 "React Hooks": "Hooks" 更稀有,所以权重更高。 Pagefind 内置了多种语言的分词器: 英文分词(基于空格和标点): 中文分词(基于字典和统计): Pagefind 使用 n-gram 技术处理 CJK 文本: 这样即使查询 "搜索" 或 "引擎",也能匹配到 "搜索引擎"。 Pagefind 通过多种技术实现高性能: 索引压缩(原始 10MB → 500KB,压缩率 95%): 并行加载: Pagefind 是用 Rust 编写并编译为 WASM,核心逻辑包括: 哈希计算(FNV-1a 算法): 索引加载器: TF-IDF 评分器: Pagefind 使用自定义的 索引文件(index_XX.pf): 示例: 内容片段(fragment_XX.pf): Level 1: 增量编码(Delta Encoding) Level 2: 变长整数编码(VarInt) Level 3: 词干提取(Stemming) Level 4: Gzip 压缩 性能提升: 服务器配置(Nginx): 实际数据(500 页文档网站): Q: Pagefind 与 Algolia 如何选择? Q: 支持哪些框架? Q: 是否影响 SEO? Q: 如何更新索引? Pagefind 为静态网站提供了轻量、高性能的搜索方案:核心特性
快速上手
三步启用搜索
# 1. 构建你的静态网站
npm run build
# 2. 生成搜索索引
npx pagefind --source "dist"
# 3. 在 HTML 中添加搜索界面<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<div id="search"></div>
<script src="/pagefind/pagefind-ui.js"></script>
<script>
new PagefindUI({ element: "#search" });
</script>dist/pagefind/ 目录下生成索引文件。核心用法
控制索引范围
data-pagefind-body 标记要索引的内容:<main data-pagefind-body>
<h1>文章标题</h1>
<p>这部分内容会被索引</p>
</main>
<!-- 使用 data-pagefind-ignore 排除特定内容 -->
<div data-pagefind-ignore>
<h2>评论</h2>
<div class="comments">...</div>
</div>添加元数据和权重
<!-- 自定义元数据 -->
<article data-pagefind-body
data-pagefind-meta="author:张三,date:2024-01-01">
<h1 data-pagefind-weight="10">文章标题</h1>
<p data-pagefind-weight="5">摘要内容...</p>
<div>正文内容...</div>
</article>配置文件
# pagefind.yml
source: "dist"
exclude_selectors:
- "nav"
- ".sidebar"
force_language: "zh-cn"自定义搜索 UI
import * as pagefind from '/pagefind/pagefind.js';
const search = await pagefind.search("React");
const results = await Promise.all(
search.results.map(r => r.data())
);实战指南
集成到构建流程
{
"scripts": {
"build": "vite build",
"postbuild": "pagefind --source dist"
}
}React 自定义搜索组件
import { useState } from 'react';
function Search() {
const [results, setResults] = useState([]);
const handleSearch = async (e) => {
const { default: pagefind } = await import('/pagefind/pagefind.js');
const search = await pagefind.search(e.target.value);
const data = await Promise.all(
search.results.slice(0, 5).map(r => r.data())
);
setResults(data);
};
return (
<>
<input type="search" onChange={handleSearch} />
{results.map((r, i) => (
<a key={i} href={r.url}>
<h3>{r.meta.title}</h3>
<p dangerouslySetInnerHTML={{ __html: r.excerpt }} />
</a>
))}
</>
);
}最佳实践
<!-- ✅ 推荐 -->
<main data-pagefind-body>
<article>...</article>
</main><h1 data-pagefind-weight="10">标题</h1>
<p data-pagefind-weight="5">摘要</p># 排除选择器
pagefind --source "dist" --exclude-selectors "nav" --exclude-selectors "footer"
# 强制语言
pagefind --source "dist" --force-language "zh-cn"配置参考
HTML 属性
属性 说明 data-pagefind-body标记要索引的主要内容区域 data-pagefind-ignore排除该元素及其子元素 data-pagefind-meta添加自定义元数据 data-pagefind-filter定义可过滤的字段 data-pagefind-sort定义可排序的字段 data-pagefind-weight设置内容权重(1-10) JavaScript API
// 高级搜索
const search = await pagefind.search("React", {
filters: { category: "tutorial" },
sort: { date: "desc" },
limit: 10
});
// 获取结果
const results = await Promise.all(
search.results.map(r => r.data())
);原理深度解析
整体架构
索引构建过程
1. 构建时索引(Build Time)
pagefind --source "dist" 时,Pagefind 会执行以下步骤:pagefind/
├── pagefind.js # 核心搜索引擎(~20KB)
│ # - 包含哈希函数
│ # - 索引加载器
│ # - 搜索算法
│
├── pagefind-ui.js # UI 组件(~15KB)
├── pagefind-ui.css # 样式文件(~3KB)
│
├── index/ # 索引分片(256 个)
│ ├── index_00.pf # 哈希值 0x00-0x00
│ ├── index_01.pf # 哈希值 0x01-0x01
│ ├── ...
│ └── index_ff.pf # 哈希值 0xFF-0xFF
│
├── fragment/ # 内容片段
│ ├── en_<hash>.pf # 英文页面片段
│ ├── zh_<hash>.pf # 中文页面片段
│ └── ...
│
└── filter/ # 过滤器数据(如果使用)
├── category.pf
└── tags.pf2. 运行时搜索(Runtime)
阶段 耗时 说明 用户输入 + 防抖 300ms 等待用户完成输入 分词 + 哈希计算 <5ms 纯计算,无 I/O 加载索引分片 20-50ms 取决于网络和缓存 索引查询 + 评分 5-10ms 纯内存操作 加载内容片段 15-30ms 取决于网络和缓存 摘要生成 + 渲染 5-10ms DOM 操作 总计(首次) ~80ms 不含防抖延迟 总计(缓存) ~25ms 索引和片段均已缓存 核心技术解析
1. 按需加载机制
// 传统方案:需要加载整个索引
// 假设网站有 1000 个页面,索引文件可能有 5MB
await loadFullIndex(); // 加载 5MB
search("React");// Pagefind:按需加载
search("React");
// 1. 根据 "React" 计算哈希 -> 只加载包含 "React" 的索引分片(可能只有 10KB)
// 2. 找到匹配的文档 ID
// 3. 只加载这些文档的内容片段(可能 20KB)
// 总共只需要下载 30KB,而不是 5MB查询词 "React"
↓
计算哈希:hash("React") = 0x3A7F
↓
确定分片:0x3A7F % 256 = 127
↓
加载:GET /pagefind/index/index_127.pf
↓
解析分片,找到文档 ID: [5, 23, 87]
↓
加载内容:GET /pagefind/fragment/en_005.pf2. 倒排索引结构
正向索引(文档 → 词条):
文档1: ["React", "教程", "入门"]
文档2: ["Vue", "教程", "进阶"]
文档3: ["React", "进阶", "Hooks"]
倒排索引(词条 → 文档):
"React" → [文档1, 文档3]
"Vue" → [文档2]
"教程" → [文档1, 文档2]
"入门" → [文档1]
"进阶" → [文档2, 文档3]
"Hooks" → [文档3]3. TF-IDF 相关性评分
TF(t, d) = 词条 t 在文档 d 中出现的次数 / 文档 d 的总词数IDF(t) = log(总文档数 / 包含词条 t 的文档数)TF-IDF(t, d) = TF(t, d) × IDF(t)文档A:
- "React" 出现 10 次,文档总词数 100
TF("React", A) = 10/100 = 0.1
包含 "React" 的文档有 30 个
IDF("React") = log(100/30) = 0.52
TF-IDF("React", A) = 0.1 × 0.52 = 0.052
- "Hooks" 出现 5 次
TF("Hooks", A) = 5/100 = 0.05
包含 "Hooks" 的文档有 5 个
IDF("Hooks") = log(100/5) = 1.30
TF-IDF("Hooks", A) = 0.05 × 1.30 = 0.065
文档A 总分 = 0.052 + 0.065 = 0.1174. 多语言分词
"Hello, world!" → ["hello", "world"]"自然语言处理" → ["自然", "语言", "处理"]
或 → ["自然语言", "处理"]
或 → ["自然语言处理"]"搜索引擎" → ["搜索", "搜索引", "搜索引擎", "索引", "索引擎", "引擎"]性能优化技术
支持 HTTP/2 多路复用,多个词条的索引分片并行加载,总耗时 = max(单个加载时间)。技术内幕深度剖析
1. 核心算法实现
// 词条归一化(转小写、去除特殊字符)→ FNV-1a 哈希 → 映射到 0-255
hash("React") = 0x42 (66)
hash("react") = 0x42 (66) // 大小写不敏感// 计算每个文档的相关性得分
score = Σ(TF × IDF × weight) × lengthNorm
// - TF: 词频
// - IDF: 逆文档频率(缓存优化)
// - weight: 自定义权重
// - lengthNorm: 长度归一化(防止长文档占优)2. .pf 文件格式
.pf(Pagefind Format)二进制格式:"React" → [1, 5, 23] 存储为 [1, +4, +18]3. 四层压缩策略
[1, 5, 23, 45] → [1, +4, +18, +22]1 → [0x01],128 → [0x80, 0x01]4. 三层缓存架构
缓存层级 命中延迟 容量 适用场景 L1 内存缓存 <1ms ~10MB 频繁访问的索引(LRU 淘汰) L2 HTTP 缓存 ~5ms ~100MB 已访问的所有索引(Cache-Control) L3 Service Worker ~10ms ~50MB 离线访问(可选) 网络请求 50-200ms - 首次访问 location /pagefind/ {
add_header Cache-Control "public, max-age=31536000, immutable";
gzip on;
}性能对比
方案 初次加载 索引大小 (1000页) 搜索速度 离线支持 Pagefind ~20KB ~500KB <50ms ✅ Algolia 0 (CDN) N/A <10ms ❌ Lunr.js ~30KB ~3MB ~100ms ✅ 常见问题
框架无关,支持 VitePress、Docusaurus、Hugo、Jekyll、Astro、Next.js(SSG)等任何生成 HTML 的工具。
不影响。Pagefind 的搜索 UI 是客户端渲染的,原始 HTML 内容完全不受影响。
每次构建时重新生成索引。在 CI/CD 中使用 postbuild 脚本自动化。总结
核心原理
相关资源
近期,Salesforce、Adobe、ServiceNow 等 SaaS 巨头的股价表现持续低迷,即便财报显示收入仍在增长,股价却在科技股普涨背景下逆势下跌。 这并非简单的市场波动,而是市场对传统 SaaS 商业模式产生了根本性的信心危机。当软件从“稀缺资产”转变为通过 AI 即可快速生成的“大众商品”,传统的 ARR(年度经常性收入)稳步上涨的想象力正在终结。本文旨在深度解析这一变革浪潮,并探讨企业如何寻找新的生存路径。 理解当前危机,首先要透视传统 SaaS 行业过去赖以生存的盈利逻辑,以及其中长期存在的结构性矛盾。 AI 的崛起,正在以前所未有的速度,从根本上颠覆传统 SaaS 赖以生存的基础。 面对 AI 的“掀桌子”,SaaS 公司必须彻底放弃旧有思维,向更灵活、更智能的模式演进。Palantir 的成功提供了一种富有启示的范式。 面对这场颠覆性变革,SaaS 公司必须主动求变,从多个维度进行战略转型: 传统 SaaS 行业正经历一场关于“傲慢”的洗牌:当“标准化”不再能阻挡对手,而“改代码”的成本被 AI 降至谷底时,那些坚守旧有模式的公司将面临淘汰。 未来的赢家,不再是那个拥有最多功能或最复杂 UI 的软件,而是那个能: 这不是软件的终结,而是软件以另一种更智能、更无感的方式重生的开始。SaaS 行业的下半场,是关于“物种进化”的生存竞赛。 本文由mdnice多平台发布行业背景
一、 传统 SaaS 的盈利逻辑与成本错配:一场被忽视的结构性矛盾
传统 SaaS 的商业模式核心是开发一套标准化软件产品,然后通过云端订阅模式,尽可能多地分发给海量客户。其高毛利率的秘密在于边际成本趋近于零:一旦软件开发完成,多一个客户的增量成本极低。因此,SaaS 公司的盈利能力与“标准化程度”和“用户规模”高度正相关。如果客户要求频繁进行定制化修改,SaaS 公司就会迅速陷入成本泥潭,导致项目亏损。这种“不改软件”的原则,是其规模化盈利的基石。
这是一个软件工程领域半公开的秘密:在整个软件生命周期中,实际编写代码(Coding)的环节,往往是成本最低、最不值钱的部分。 真正吞噬预算的,是以下这些“隐形”成本:
由此可见,传统 SaaS 在最昂贵的人力沟通和后期维护环节上,投入巨大且难以压缩。
传统 SaaS 本质上是一种“被动系统”。它要求用户:
这种模式下,软件更像是一个强大的工具箱,用户必须主动去使用和适应它,而非软件主动为用户服务。二、 AI 原生时代,对传统 SaaS 的三记重锤:结构性冲击
过去,SaaS 公司通过数月甚至数年的开发,才得以实现一套复杂的功能模块(例如:一个精密的财务报表生成器、一个自动营销活动配置器)。这些功能构成了产品的核心壁垒和价值主张。
然而,在 AI 时代,大模型和生成式 AI 带来了“功能即时生成”的能力。一个用户只需在聊天框中输入自然语言指令,AI 便能实时生成一个定制化的报表分析、一段营销文案,甚至是一个临时的应用程序逻辑。这种能力直接将传统 SaaS 长期积累的“功能价值”瞬间拉低,甚至趋近于零。 以前的“专业工具”变成了 AI 的“随手生成”,这对于那些以功能堆砌为核心竞争力的 SaaS 公司来说,无异于一场降维打击。
传统 SaaS 依赖复杂而精心设计的图形用户界面(GUI),用户通过点击菜单、填写表单来完成操作。
AI 正在推动的,是“对话式交互”和“意图理解”。用户不再需要学习繁琐的 UI,只需用自然语言向 AI 助手下达指令(例如:“帮我分析上季度公寓出租率低的原因,并提出改善建议”),AI 就能在后台调用数据、运行模型,并给出可执行的报告和行动方案。
这导致了两个关键变化:
这是对传统 SaaS 营收模式最具破坏性的冲击。传统 SaaS 普遍采用“按用户席位”收费的模式,即企业为每个使用软件的员工支付订阅费。其营收增长与客户的企业规模、员工人数高度绑定。三、 未来的生存解药:Palantir 模式与松耦合系统——拥抱变革的新范式
传统 SaaS 模式下,“不改软件”是金科玉律。而 Palantir 的核心竞争力在于“现场赋能”:他们会派遣工程师到客户现场,直接根据客户的即时需求编写代码,即便这些代码可能是一次性的(“写完即弃”),但能够快速、精准地解决实际问题。
在 AI 辅助的 Vibe Coding(意图编程) 时代,写代码的成本已经低到可以接受这种“用完即丢”的模式。未来的软件不再追求“一套代码打天下”,而是能够根据用户的“Vibe”(意图或场景需求),通过 AI 实时组装、生成定制化的解决方案。这种自下而上的、按需响应的模式,将彻底取代自上而下的标准化“洗脑”。
传统软件系统追求模块间的“严丝合缝”,任何数据格式或接口的不匹配都可能导致系统崩溃。
未来的软件服务将转向松耦合架构。AI 作为强大的“翻译官”,具备处理非结构化数据的能力,即便是来自不同源头、格式不统一的数据,AI 也能通过大模型进行理解、对齐和整合。这意味着:
AI 虽然强大,但它无法凭空生成真实物理世界的反馈和数据。因此,未来 SaaS 公司的核心竞争力之一,是成为 AI 连接现实世界的“插头”:四、 转型建议:SaaS 公司应该如何应对 AI 时代的挑战?
放弃仅仅作为一个被动的数据记录和管理工具。未来的 SaaS 应进化为主动的“智能代理(Agent)”。它不仅仅提供数据报表,更应根据数据,结合 AI 智能,直接提出可执行的运营决策建议(例如:“检测到某区域竞品降价 5%,建议立即调整本周三间空置房源价格,是否一键执行?”)。
放弃传统的按用户席位收费模式。未来的盈利模式应与 AI 带来的实际商业价值挂钩:结语
人物:库珀与TARS,《星际穿越》中的人物。 库珀:“TARS,我们前方那些漂浮的彩色球体是什么?” TARS(平静的机械音):“这是冒泡排序的宇宙,先生。每个彩色星球代表一个待排序的数字,体积越大数值越高。” 库珀:“它们为什么在黑暗中飘荡?” TARS:“观察初始状态——红色星体在最左,蓝色在最右,但它们的体积毫无规律。就像未整理的虫洞数据。” (屏幕上出现第一轮字样) 库珀:“那个红色星球开始移动了!” TARS:“算法开始工作了。它在比较相邻星球——左边比右边大时,就会发生空间置换。” (两个球体缓缓交换位置) 库珀:“就像轨道交会!” TARS:“精确。每一轮都会有最大的‘星球’浮到右侧,就像气泡上升。看——那个红色巨行星正在向右漂移。” 库珀:“其他小行星在给它让路?” TARS:“可以这么理解。每次比较都是重力调整——让数值大的天体获得更靠右的轨道坐标。” (经过多轮交换后) TARS:“最后一轮完成。现在星系已按体积——也就是数值——从小到大完美排列。” 库珀:“从青色小行星到绿色巨行星...这简直像银河系仪!” TARS:“是的先生。这个可视化程序展示了最经典的排序算法。虽然效率不高,但能清晰展现计算之美——就像在太空中编排星辰。” (屏幕显示“演示完毕”) 库珀:“谁创造了这个宇宙?” TARS:“李兴球。他用C++精灵库搭建了这个数学剧场。要再看一遍吗?” 库珀:“不了。但这让我想起——有时候解决问题需要耐心,就像这些气泡,一轮一轮地...慢慢浮到正确位置。” TARS:“深刻的理解,先生。现在是否要返回主程序?” (画面渐黑,只留下整齐排列的彩色星球在黑暗中发光) 看代码:
看视频演示, https://www.douyin.com/video/7602172380894563636

#include "sprites.h" //包含C++精灵库
using namespace std;
Sprite rocket; //建立角色叫rocket
struct Node{
int value,x; //值和坐标
Sprite *sp;
};
vector<Node *> datas;
vector<string> colors = {"red","orange","yellow","green",
"cyan","blue","purple","pink"};
void swap(int i,int j){ //交换两个节点
Node *a = datas[i];
Node *b = datas[j];
//交换a和b的x从标,并且到达自己的坐标
int tempx = a->x;
a->x = b->x;
b->x = tempx;
a->sp->go(a->x,0);
b->sp->go(b->x,0);
//在datas中的位置也要交换
Node *temp ;
temp = datas[i];
datas[i] = datas[j];
datas[j] = temp;
}
int main(){ //主功能块
g_screen->bgcolor("black");
int n= randint(5,8);
int x = 50-100*n/2; //最左边节点坐标(起始)
for(int i=0;i<n;i++){ //建立n个节点,放到datas中
int v = randint(30,200);
Node *node = new Node;
node->value = v;
node->x = x;
//按顺序选择索引为i的颜色,组合成角色的造型图片
string s = "res/circle_" + colors[i] + ".png";
Sprite *js = new Sprite(s); //新建角色,以s为造型
js->scale(v/100.0); //把角色缩小,要不然太大了
js->penup(); js->go(x,0); js->speed(1); //定好起始位置
node->sp = js; //节点包含有角色指针
datas.push_back(node);
x = x + 100; //每个节点相差100个单位
}
Sprite pen{"blank"};
pen.up().color(0).sety(300).write("冒泡排序算法可视化演示程序",50);
pen.color(30).sety(230).write("作者:李兴球,采用C++精灵库",30);
pen.color(60).sety(180).write("C++精灵库作者:李兴球",20);
rocket.wait(1).color("yellow").penup().sety(130).hide();
//真正的冒泡排序核心程序开始了
for(int j=1;j<n;j++){ //排序的核心程序在这里
string s = "第 " + to_string(j) + " 轮";
//删除最早写的文字,然后写上新的文字,并且等待1秒
rocket.cleartxts(1).write(s,42).wait(1);
for(int i=0;i<n-j;i++)
if(datas[i]->value > datas[i+1]->value ) //发现更大的,则交换
swap(i,i+1);
rocket.wait(1);
}
rocket.cleartxts(1).write("演示完毕!",42).done(); //完成了
return 0; //返回0
}
选错框架的代价:某团队用 Electron 做笔记应用,上线后用户反馈"启动 5 秒,内存 500MB"。重构用了 3 个月。如果一开始选 Tauri 或 Wails,这个坑完全可以避免。 本文目标:帮你在动手前想清楚。 覆盖范围:16 个框架,4 大技术路线 阅读建议: 在看具体框架前,你需要理解一个核心问题:UI 是怎么画到屏幕上的? 不同的"画法"决定了框架的基因,也决定了它擅长什么、不擅长什么。 原理:框架自己实现一套渲染引擎,拿到系统给的"画布"(Canvas/Surface),一笔一画把 UI 画出来。 类比:你买了一块空白画布,用自己的颜料和画笔画画。画出来的风格完全由你决定,跟画布是什么牌子的没关系。 代表框架:Flutter、Lynx、Qt Quick、GPUI、Dioxus、Slint 优势: 劣势: 原理:框架把你写的代码"翻译"成原生控件调用。你写 类比:你是导演,给演员(原生控件)下指令。演员按照各自平台的"表演风格"来演,iOS 演员演得像 iOS,Android 演员演得像 Android。 代表框架:React Native、.NET MAUI、Uno Platform、NativeScript、Valdi 优势: 劣势: 原理:用 Web 技术栈(HTML/CSS/JS)写 UI,通过 WebView 或内嵌 Chromium 来渲染。 类比:在应用里开了一个"浏览器窗口",你的 UI 实际上是一个网页。 代表框架:Electron、Tauri、Wails、Electrobun 优势: 劣势: Electron vs Tauri vs Electrobun 核心对比: 原理:只共享业务逻辑和数据层,UI 各平台自己写(或用 Compose Multiplatform 部分共享)。 类比:后厨(业务逻辑)是统一的,但前台装修(UI)各店不同。 代表框架:Kotlin Multiplatform (KMP) 优势: 劣势: 技术栈快速匹配: 下面我们按"成熟度从高到低"的顺序介绍每个框架。为了便于理解,我们将框架按技术路线分组呈现。 一句话定位:自绘渲染的"全能选手",跨端一致性最强的主流方案。 技术栈: 适合场景: 不太适合: 真实案例: 代码示例(感受一下 Dart 风格): 入门步骤: 常见坑: 一句话定位:用 React 写原生应用,前端团队的"舒适区扩展"。 技术栈: 适合场景: 不太适合: 真实案例: 代码示例: 入门步骤: 选择初始化方式: 常见坑: 一句话定位:用 Vue/Angular/Vanilla JS 写原生应用,填补非 React 前端技术栈的空白。 技术栈: 适合场景: 不太适合: 真实案例: 代码示例(Vue 风格): 入门步骤: 与 React Native 的对比: 一句话定位:Web 技术栈做桌面应用的"事实标准",简单粗暴但有效。 技术栈: 适合场景: 不太适合: 真实案例: 资源占用参考: 入门步骤: 性能优化技巧: 一句话定位:工业级跨平台方案,嵌入式和桌面的"老大哥"。 技术栈: 适合场景: 不太适合: 真实案例: 代码示例(QML): 许可证说明: 入门步骤: 一句话定位:C# 团队的跨平台方案,微软生态的"官方答案"。 技术栈: 适合场景: 不太适合: 代码示例: 入门步骤: 一句话定位:C# 生态的"全平台方案",比 .NET MAUI 更早、支持 WebAssembly。 技术栈: 与 .NET MAUI 的关键区别: 适合场景: 不太适合: 真实案例: 代码示例: 入门步骤: 安装 .NET SDK 和 Uno Platform 模板: 创建项目: 运行: WebAssembly 优势示例: 一句话定位:Electron 的"轻量替代品",用系统 WebView + Rust 后端。 技术栈: 与 Electron 的关键区别: 适合场景: 不太适合: 入门步骤: Rust 后端示例: 一句话定位:Go + WebView 的桌面应用方案,填补 Go 技术栈空白,比 Tauri 学习曲线更低。 技术栈: 核心优势: 桌面 WebView 方案全面对比: 适合场景: 不太适合: 真实案例: 代码示例(完整的类型安全流程): 步骤 1:后端 Go 方法 步骤 2:Wails 自动生成 TypeScript 类型 步骤 3:前端调用(完全类型安全) 快速开始(5 分钟): Wails v2 vs v3(2025 重大更新): Wails v3 正在开发中,主要改进: 性能优化技巧: 使用 Go 的并发优势: 使用事件系统(前后端通信): 按需构建(减小包体): 常见问题: Wails vs Tauri 选择指南: 一句话定位:Android 团队扩展 iOS 的"最小阻力路径",逻辑共享优先。 技术栈: UI 方案: 核心概念: 适合场景: 不太适合: 代码示例: 入门步骤: 一句话定位:字节跳动的跨端方案,用 Web 语法写原生渲染的 UI。 技术栈: 核心特点: 适合场景: 不太适合: 代码示例: 入门步骤: 一句话定位:TypeScript 编译成原生视图,追求 TS 开发体验 + 原生性能。 技术栈: 核心理念: 适合场景: 不太适合: 入门步骤: 一句话定位:比 Electron 更轻量的桌面方案,用 Bun + 系统 WebView/CEF。 技术栈: 与 Electron/Tauri 对比: 适合场景: 入门步骤: 一句话定位:Rust 版的 React,用 React-like 语法写全平台 UI,Rust 生态的"全能选手"。 技术栈: 核心优势: 与其他 Rust GUI 框架的对比: 适合场景: 不太适合: 真实案例: 代码示例(感受 Rust + React 的组合): 基础计数器: 组件复用(像 React 一样): 异步数据获取(类似 React Query): 多渲染后端示例: 入门步骤: 安装 Rust 和 Dioxus CLI: 创建项目(自动配置): 开发模式(带热重载): 构建生产版本: Dioxus 0.5 的重大改进(2024): 性能优化技巧: 利用 Rust 的零成本抽象: 使用 memo 避免重渲染: WASM 优化: TUI 应用示例(独特优势): 常见坑: 生命周期标注: 异步运行时: 跨平台样式: Rust GUI 框架选择指南: 未来展望: 一句话定位:嵌入式和桌面 GUI 框架,填补"Qt 太重,Flutter 太大"的空白。 技术栈: 核心特点: 极致轻量 多语言支持 设计师友好 与 Qt 的对比(嵌入式场景): 适合场景: 不太适合: 真实案例: 代码示例(感受 Slint 的 DSL): UI 文件( Rust 调用: C++ 调用(嵌入式团队友好): 入门步骤: 安装 Slint(Rust 项目): 创建 UI 文件: 配置 build.rs(自动编译 .slint 文件): 运行: 可视化设计工具: 嵌入式示例(软件渲染,适合无 GPU 设备): 常见坑: 什么时候选 Slint 而不是 Qt? 选 Slint 如果: 选 Qt 如果: 一句话定位:Zed 编辑器的 UI 框架,Rust 生态的高性能 GUI 方案。 技术栈: 核心特点: 适合场景: 不太适合: 代码示例: 入门步骤: 指标说明: 不想看详细分析?根据你的情况直接查表: 推荐:Flutter / Lynx 理由: 备选:Qt Quick(如果团队熟悉 C++) 快速决策: 推荐:.NET MAUI 理由: 备选:Qt(如果需要嵌入式支持)/ Electron(如果有 Web 版需求) 推荐:KMP(逻辑共享 + 原生 UI) 理由: 进阶:如果想共享部分 UI → KMP + Compose Multiplatform 推荐:React Native / KMP(原生 UI) 理由: 备选:.NET MAUI、纯原生 推荐:Tauri(桌面)/ Valdi(移动) 理由: 备选:KMP + 原生 UI 推荐:Flutter 理由: 备选:Qt(工业场景)、各平台分别开发 推荐: 理由: 选择建议: 推荐:Slint(首选)/ Qt(工业级) 理由: 选择建议: 推荐:NativeScript 理由: 备选:Flutter(如果愿意学 Dart) 推荐:Uno Platform 理由: 备选:Blazor WebAssembly(纯 Web)+ .NET MAUI(原生) 在最终决定前,问自己这些问题: 基础问题: 新增考虑点(针对新框架): 成熟稳定层(生产环境可放心使用): 快速上升层(已有成功案例,值得认真考虑): 新锐探索层(有潜力,需承担早期风险): 自绘渲染持续演进 Rust 生态全面爆发(重要趋势) WebView 方案的"语言多样化" 逻辑共享成为共识 WebAssembly 的崛起 类型安全成为标配 前端框架语法的多样化 嵌入式 GUI 的轻量化 选框架不是选"最好的",而是选"最适合的"。 如果你只记住一件事,那就是: 2026 关键变化总结: 选型建议(按风险偏好): 祝选型顺利! 成熟框架: 新兴框架: 对比文章: 生产实践案例: 技术深度解析:跨平台不是"能不能跑",而是"用哪条技术路线换哪种确定性"。
第一章:先搞懂底层逻辑
1.1 四种技术路线
路线一:自绘渲染(Self-rendering)
┌─────────────────────────────────────┐
│ 你的应用代码 │
├─────────────────────────────────────┤
│ 框架的渲染引擎 │ ← 这一层是框架自己实现的
├─────────────────────────────────────┤
│ 系统图形 API (Metal/Vulkan/GL) │
├─────────────────────────────────────┤
│ GPU │
└─────────────────────────────────────┘路线二:原生控件映射(Native Bridging)
<Button>,框架帮你调用 iOS 的 UIButton 或 Android 的 MaterialButton。┌─────────────────────────────────────┐
│ 你的应用代码 │
├─────────────────────────────────────┤
│ 框架的桥接层 (Bridge) │ ← 翻译 + 通信
├─────────────────────────────────────┤
│ 原生控件 (UIKit / Android Views) │
├─────────────────────────────────────┤
│ 系统 │
└─────────────────────────────────────┘路线三:WebView/Chromium 方案
┌─────────────────────────────────────┐
│ 你的 Web 应用 (HTML/CSS/JS) │
├─────────────────────────────────────┤
│ WebView / Chromium / 系统浏览器 │
├────────────────┬────────────────────┤
│ 后端进程 │ 系统 API 调用 │
│ (Node/Rust) │ │
└────────────────┴────────────────────┘维度 Electron Tauri Electrobun 渲染引擎 内嵌 Chromium 系统 WebView 系统 WebView/CEF 后端语言 Node.js Rust Bun (TypeScript) 包体大小 150MB+ 3-10MB 10-30MB 启动速度 慢(初始化大) 快 中等 适合团队 前端团队 愿意学 Rust 前端团队 路线四:逻辑共享优先(Shared Logic First)
┌──────────────────────────────────────────────────┐
│ 共享层 (Kotlin) │
│ 网络、数据库、业务逻辑、状态管理 │
├─────────────────┬────────────────┬───────────────┤
│ Android UI │ iOS UI │ Desktop UI │
│ (Compose) │ (SwiftUI) │ (Compose) │
└─────────────────┴────────────────┴───────────────┘1.2 一张图看懂路线选择
你的核心诉求是什么?
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
跨端一致性 原生体验 开发效率
视觉完全统一 系统深度集成 快速上线
│ │ │
▼ ▼ ▼
自绘渲染 原生映射 WebView 方案
Flutter/Lynx/ RN/MAUI/Uno/ Electron/Tauri/
Dioxus/Slint/Qt NativeScript/KMP Wails/Electrobun你的团队主要用什么语言?
│
├─ JavaScript/TypeScript
│ ├─ React → React Native / Lynx
│ ├─ Vue/Angular → NativeScript
│ └─ 任意框架 → Electron / Tauri / Wails / Electrobun
│
├─ Dart → Flutter
│
├─ C# → .NET MAUI / Uno Platform(需要 WASM)
│
├─ C++ → Qt / Slint(嵌入式)
│
├─ Go → Wails(桌面)
│
├─ Kotlin → KMP
│
└─ Rust
├─ Web 前端 → Tauri
├─ 全栈(含 UI)→ Dioxus
├─ 嵌入式 → Slint
└─ 极致性能 → GPUI第二章:16 个框架逐一拆解
2.1 Flutter(Google,2018 稳定版)
// 一个简单的计数器页面
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('计数器')),
body: Center(
child: Text('点击了 $_count 次', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _count++),
child: Icon(Icons.add),
),
);
}
}flutter doctorflutter create my_appcd my_app && flutter run--split-debug-info 和 --obfuscate 可减小约 30%Info.plist 里的权限说明清晰2.2 React Native(Meta,2015)
// React Native 的代码对 React 开发者很熟悉
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.text}>点击了 {count} 次</Text>
<TouchableOpacity style={styles.button} onPress={() => setCount(c => c + 1)}>
<Text style={styles.buttonText}>+1</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: 24, marginBottom: 20 },
button: { backgroundColor: '#007AFF', padding: 15, borderRadius: 8 },
buttonText: { color: 'white', fontSize: 18 },
});npx create-expo-app my-app(Expo 托管方案)npx react-native init MyApp(裸 RN)npx expo start 或 npx react-native run-ios2.3 NativeScript(Progress Software,2014)
<template>
<Page>
<ActionBar title="计数器"/>
<StackLayout class="p-20">
<Label :text="`点击了 ${count} 次`" class="text-center text-2xl mb-4"/>
<Button text="+1" @tap="count++" class="btn btn-primary"/>
</StackLayout>
</Page>
</template>
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>npm install -g @nativescript/clins create my-app --vue 或 --angularns run ios 或 ns run android维度 React Native NativeScript 框架支持 React Vue/Angular/Vanilla 原生访问 通过桥接 直接访问 性能 有桥接开销 理论上更快 生态 更大 较小 2.4 Electron(GitHub/OpenJS Foundation,2013)
npm init -ynpm install -D electronmain.js:const { app, BrowserWindow } = require('electron');
app.whenReady().then(() => {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
});package.json:"start": "electron ."npm startBrowserWindow 的 show: false + ready-to-show 事件避免白屏contextIsolation 提升安全性2.5 Qt / Qt Quick(The Qt Company,1995/2010)
// QML 声明式 UI,类似 JSON 但带逻辑
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 400
height: 300
visible: true
title: "计数器"
Column {
anchors.centerIn: parent
spacing: 20
Text {
text: "点击了 " + counter + " 次"
font.pixelSize: 24
}
Button {
text: "+1"
onClicked: counter++
}
}
property int counter: 0
}2.6 .NET MAUI(Microsoft,2022)
// .NET MAUI 的 XAML + C# 模式
// MainPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
<VerticalStackLayout Spacing="20" VerticalOptions="Center">
<Label x:Name="CounterLabel" Text="点击了 0 次" FontSize="24" HorizontalOptions="Center"/>
<Button Text="+1" Clicked="OnCounterClicked" HorizontalOptions="Center"/>
</VerticalStackLayout>
</ContentPage>
// MainPage.xaml.cs
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage() => InitializeComponent();
void OnCounterClicked(object sender, EventArgs e)
{
count++;
CounterLabel.Text = $"点击了 {count} 次";
}
}dotnet workload install mauidotnet new maui -n MyApp2.7 Uno Platform(Uno Platform,2018)
维度 .NET MAUI Uno Platform 发布时间 2022 2018 WebAssembly 不支持 支持(核心优势) API 来源 Xamarin.Forms 演进 WinUI/UWP Windows 优先度 中等 高(WinUI 语法) Linux 支持 有限 通过 Skia 支持 <!-- MainPage.xaml - 与 WinUI 语法兼容 -->
<Page x:Class="MyApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<StackPanel Spacing="20" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Name="CounterText" Text="点击了 0 次" FontSize="24"/>
<Button Content="+1" Click="OnCounterClicked"/>
</StackPanel>
</Page>// MainPage.xaml.cs
public sealed partial class MainPage : Page
{
private int _count = 0;
public MainPage()
{
this.InitializeComponent();
}
private void OnCounterClicked(object sender, RoutedEventArgs e)
{
_count++;
CounterText.Text = $"点击了 {_count} 次";
}
}dotnet new install Uno.Templatesdotnet new unoapp -o MyAppdotnet run --project MyApp.Wasm# 构建 WebAssembly 版本
dotnet publish MyApp.Wasm -c Release
# 直接部署到 Web 服务器,无需应用商店审核
# 用户通过浏览器访问即可使用2.8 Tauri(Tauri Programme,2022 v1.0)
维度 Electron Tauri 包体(空项目) ~150MB ~3MB 内存(空项目) ~100MB ~30MB 后端语言 Node.js Rust WebView 内嵌 Chromium 系统自带 跨端一致性 高(同一个 Chromium) 中(系统 WebView 版本不同) npm create tauri-app@latestnpm run tauri devnpm run tauri build// src-tauri/src/main.rs
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}// 前端调用
import { invoke } from '@tauri-apps/api/tauri';
const greeting = await invoke('greet', { name: 'World' });2.9 Wails(Wails Project,2019 / v2 2022)【重点推荐】
优势 说明 对比 学习曲线低 Go 比 Rust 容易学 比 Tauri 门槛低 50% 类型安全 自动生成 TS 类型 编译时发现错误 并发能力强 goroutine 原生支持 适合高并发场景 包体适中 10-15MB 比 Electron 小 90% 编译快 Go 编译速度快 比 Rust 快 5-10 倍 维度 Electron Tauri Wails Electrobun 后端语言 Node.js Rust Go Bun (TS) 学习曲线 低(JS/TS) 高(Rust陡) 低(Go易学) 低(TS) 包体大小 150MB 3MB 10MB 15MB 内存占用 100MB 30MB 45MB 50MB 编译速度 无需编译 慢(Rust) 快(Go) 快 并发模型 事件循环 异步+线程 goroutine 异步 类型安全 JS→TS 手动定义 自动生成TS TS 原生 生态成熟度 5/5 4/5 3/5 2/5 // app.go - 定义后端方法
type App struct {
ctx context.Context
}
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}
func (a *App) ProcessFile(path string) error {
// 利用 Go 的 goroutine 并发处理
go func() {
// 后台处理文件
}()
return nil
}// wailsjs/go/models.ts - 自动生成,无需手写
export namespace main {
export class App {
static Greet(name: string): Promise<string>;
static ProcessFile(path: string): Promise<void>;
}
}import { Greet } from '../wailsjs/go/main/App';
const result = await Greet("World"); // ✅ 类型正确
// await Greet(123); // ❌ TypeScript 编译错误核心优势:前后端接口不匹配在编译时就能发现,而不是运行时报错。
# 1. 安装 CLI
go install github.com/wailsapp/wails/v2/cmd/wails@latest
# 2. 检查环境
wails doctor
# 3. 创建项目(选择模板:react/vue/svelte)
wails init -n myapp -t react
# 4. 开发(热重载)
cd myapp && wails dev
# 5. 构建
wails build # 输出: myapp.app / myapp.exe / myapp// 并行处理多个任务
func (a *App) ProcessMultipleFiles(files []string) {
var wg sync.WaitGroup
for _, file := range files {
wg.Add(1)
go func(f string) {
defer wg.Done()
// 处理文件
}(file)
}
wg.Wait()
}// 后端发送事件
runtime.EventsEmit(a.ctx, "progress", Progress{
Current: 50,
Total: 100,
})// 前端监听事件
import { EventsOn } from '../wailsjs/runtime';
EventsOn('progress', (data) => {
console.log(`Progress: ${data.current}/${data.total}`);
});# 只构建当前平台
wails build
# 跨平台构建
wails build -platform darwin/amd64,darwin/arm64,windows/amd64问题 解决方案 Windows 缺少 WebView2 引导用户安装 WebView2 Runtime 跨平台 WebView 差异 测试各平台,使用 polyfill Go 依赖管理 运行 go mod tidy前端资源路径错误 检查 wails.json 配置维度 选 Wails 选 Tauri 团队技能 熟悉 Go / 不想学 Rust 愿意学 Rust 后端需求 高并发(goroutine) 一般 包体要求 10MB 可接受 要求最小(3MB) 编译速度 要求快 可接受慢 类型安全 要自动生成 手动定义可接受 生态成熟度 可接受成长期 要求更成熟 2.10 Kotlin Multiplatform / KMP(JetBrains,2023 稳定版)
commonMain(纯 Kotlin,编译到各平台)┌────────────────────────────────────────────────┐
│ commonMain │
│ expect fun getPlatformName(): String │ ← 声明接口
├──────────────────────┬─────────────────────────┤
│ androidMain │ iosMain │
│ actual fun get..() │ actual fun get..() │ ← 各平台实现
│ = "Android" │ = "iOS" │
└──────────────────────┴─────────────────────────┘// commonMain - 共享的网络请求逻辑
class UserRepository(private val api: UserApi) {
suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// 在 Android 和 iOS 中都可以直接使用
val repo = UserRepository(api)
val user = repo.getUser("123")shared/src/commonMain 中编写共享逻辑shared 模块2.11 Lynx(ByteDance,2024 开源)
// Lynx 的语法对 React 开发者很熟悉
import { Component, View, Text, Image } from '@anthropic/lynx';
export default class App extends Component {
state = { count: 0 };
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24 }}>点击了 {this.state.count} 次</Text>
<View
style={{ padding: 15, backgroundColor: '#007AFF', borderRadius: 8 }}
onClick={() => this.setState({ count: this.state.count + 1 })}
>
<Text style={{ color: 'white' }}>+1</Text>
</View>
</View>
);
}
}2.12 Valdi(Snapchat,2024 Beta)
2.13 Electrobun(2024 早期)
维度 Electron Tauri Electrobun 后端 Node.js Rust Bun (TS) 学习成本 低 中(要学 Rust) 低 包体 大 小 中 成熟度 高 中 早期 2.14 Dioxus(Dioxus Labs,2021 / v0.5 2024)【重点推荐】
优势 说明 独特性 React-like 语法 前端开发者易上手 Rust GUI 中最像 React 多渲染后端 Web/Desktop/Mobile/TUI 一套代码多平台 WASM 性能 接近原生的 Web 性能 比 JS 快 2-10 倍 类型安全 Rust 编译时检查 内存安全 + 线程安全 TUI 支持 终端 UI 独特优势 其他框架都不支持 维度 Dioxus GPUI Tauri egui 语法风格 React-like Rust 原生 Web 前端 即时模式 学习曲线 低(前端易上手) 高(需熟练 Rust) 低(会 Web 即可) 中等 移动端支持 开发中 无 v2 支持 有限 Web 支持 5/5 完整 WASM 无 5/5 完整 3/5 有限 TUI 支持 5/5 独特优势 无 无 无 组件生态 3/5 成长中 2/5 早期 5/5(npm生态) 3/5 渲染性能 4/5 强 5/5 极强 3/5 中等 4/5 强 成熟度 3/5 成长中 3/5 成长中 4/5 稳定 4/5 稳定 use dioxus::prelude::*;
fn main() {
dioxus_desktop::launch(App);
}
fn App(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx! {
div {
style: "display: flex; flex-direction: column; align-items: center; gap: 20px;",
h1 { "计数器" }
p {
style: "font-size: 24px;",
"点击了 {count} 次"
}
button {
onclick: move |_| count += 1,
style: "padding: 10px 20px; font-size: 18px;",
"+1"
}
}
})
}// 可复用的 Button 组件
#[component]
fn MyButton<'a>(
cx: Scope<'a>,
onclick: EventHandler<'a, MouseEvent>,
children: Element<'a>,
) -> Element<'a> {
cx.render(rsx! {
button {
class: "custom-button",
onclick: move |evt| onclick.call(evt),
children
}
})
}
// 使用组件
fn App(cx: Scope) -> Element {
cx.render(rsx! {
MyButton {
onclick: |_| println!("Clicked!"),
"点击我"
}
})
}use dioxus::prelude::*;
fn App(cx: Scope) -> Element {
let user_data = use_future(cx, (), |_| async move {
// 异步请求数据
reqwest::get("https://api.example.com/user")
.await?
.json::<User>()
.await
});
cx.render(match user_data.value() {
None => rsx! { p { "加载中..." } },
Some(Ok(user)) => rsx! {
div {
h1 { "欢迎, {user.name}" }
p { "邮箱: {user.email}" }
}
},
Some(Err(e)) => rsx! { p { "错误: {e}" } },
})
}// 同一套代码,不同渲染后端
// 1. 桌面应用(WebView)
fn main() {
dioxus_desktop::launch(App);
}
// 2. Web 应用(WASM)
fn main() {
dioxus_web::launch(App);
}
// 3. 终端 UI(TUI)
fn main() {
dioxus_tui::launch(App);
}
// 4. 服务端渲染(SSR)
fn main() {
let html = dioxus_ssr::render(&App(cx));
// 返回 HTML 字符串
}# 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 安装 Dioxus CLI
cargo install dioxus-clidx new my-app
# 选择模板:web, desktop, mobile, TUIcd my-app
dx serve # Web
# 或
dx serve --platform desktop # 桌面dx build --release// 组件会在编译时优化
#[inline(always)]
#[component]
fn FastComponent(cx: Scope) -> Element {
// 编译器会内联这个组件
}let expensive = use_memo(cx, (dep1, dep2), |(d1, d2)| {
// 只在 dep1 或 dep2 变化时重新计算
heavy_computation(d1, d2)
});# 构建优化的 WASM
dx build --release --platform web
# 生成的 WASM 包通常只有几百 KB// 用同样的代码创建漂亮的终端 UI
use dioxus::prelude::*;
use dioxus_tui::Config;
fn main() {
dioxus_tui::launch_cfg(
App,
Config::new().with_rendering_mode(RenderingMode::Ansi),
);
}
fn App(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx! {
div {
border_width: "1px",
padding: "2",
h1 { "Terminal Counter" }
p { "Count: {count}" }
button {
onclick: move |_| count += 1,
"Increment"
}
}
})
}use_future 而不是手动管理需求 推荐框架 理由 Web + 桌面共享代码 Dioxus WASM + 多后端 前端团队用 Rust 后端 Tauri 前后端分离 React 开发者转 Rust Dioxus 语法相似 需要 TUI(终端界面) Dioxus 独特支持 追求极致性能(编辑器) GPUI 为 Zed 设计 嵌入式设备 Slint 轻量级 要 npm 生态 Tauri Web 前端 2.15 Slint(SixtyFPS GmbH,2020 / v1.0 2023)
.slint 文件)维度 Qt (Qt Quick) Slint 最小包体 ~10-20MB ~300KB 内存占用 ~20-50MB ~2-10MB 启动速度 慢 快 MCU 支持 需要 Qt for MCUs(商业版) 开源版支持 许可证 LGPL/GPL 或商业 GPL/商业(企业版) 学习曲线 中 低 工业案例 5/5 极多 3/5 成长中 .slint 声明式语法):// counter.slint
import { Button, VerticalBox } from "std-widgets.slint";
export component Counter {
in-out property <int> counter: 0;
VerticalBox {
Text {
text: "点击了 \{counter} 次";
font-size: 24px;
}
Button {
text: "+1";
clicked => {
counter += 1;
}
}
}
}// main.rs
slint::slint! {
import { Counter } from "counter.slint";
}
fn main() {
let ui = Counter::new().unwrap();
// 可以从 Rust 代码访问和修改属性
ui.set_counter(0);
// 监听属性变化
ui.on_counter_changed(|value| {
println!("Counter changed to: {}", value);
});
ui.run().unwrap();
}// main.cpp
#include "counter.h"
int main() {
auto ui = Counter::create();
// C++ API 类型安全
ui->set_counter(0);
// 回调
ui->on_counter_changed([](int value) {
std::cout << "Counter: " << value << std::endl;
});
ui->run();
}cargo new my-app
cd my-app
cargo add slint# 创建 ui/counter.slint
mkdir ui// build.rs
fn main() {
slint_build::compile("ui/counter.slint").unwrap();
}cargo run# 安装 Slint UI Designer
cargo install slint-viewer
# 实时预览 .slint 文件
slint-viewer ui/counter.slintuse slint::platform::software_renderer::{MinimalSoftwareWindow, RepaintBufferType};
fn main() {
slint::platform::set_platform(Box::new(MyPlatform::new())).unwrap();
let ui = Counter::new().unwrap();
// 渲染到帧缓冲区
let window = ui.window();
window.set_size(slint::PhysicalSize::new(800, 480));
// 自定义事件循环(适合 bare-metal 环境)
loop {
slint::platform::update_timers_and_animations();
window.draw_if_needed(|renderer| {
// 渲染到你的帧缓冲区
});
}
}.slint 语法需要学习,但比 QML 简单2.16 GPUI(Zed Industries,2024)
// GPUI 的 Rust 风格 UI
use gpui::*;
struct Counter {
count: i32,
}
impl Render for Counter {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.flex()
.flex_col()
.items_center()
.child(format!("Count: {}", self.count))
.child(
button("Increment")
.on_click(cx.listener(|this, _, _| this.count += 1))
)
}
}curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shcargo new my_appcargo run第三章:横向对比
3.1 核心信息对照表
框架 渲染方式 语言 平台覆盖 生态成熟度 一句话定位 Flutter 自绘 Dart 移动+桌面+Web 5/5 成熟 全能选手,跨端一致性最强 React Native 原生映射 JS/TS 移动为主 5/5 成熟 前端团队的原生应用方案 NativeScript 原生映射 JS/TS+Vue/Angular 移动 3/5 成长中 Vue/Angular 写原生应用 Electron WebView JS/TS 桌面 5/5 成熟 Web 做桌面的事实标准 Qt Quick 自绘 C++/QML 全平台+嵌入式 5/5 成熟 工业级、嵌入式首选 .NET MAUI 原生映射 C# 全平台 4/5 稳定 C# 团队的官方方案 Uno Platform 原生/WASM C# 全平台+Web 4/5 稳定 C# + WebAssembly Tauri 系统WebView+Rust Rust+Web 桌面+移动 4/5 稳定 轻量级 Electron 替代 Wails 系统WebView+Go Go+Web 桌面 3/5 成长中 Go 技术栈做桌面 KMP 原生/Compose Kotlin 移动+桌面 4/5 稳定 Android 团队扩 iOS Lynx 自绘 JS/TS 移动+Web 3/5 成长中 高性能+Web语法 Valdi 编译到原生 TypeScript 移动 2/5 早期 TS 编译到原生 Electrobun 系统WebView/CEF TypeScript 桌面 2/5 早期 轻量桌面方案 Dioxus 自绘/多后端 Rust 全平台+TUI 3/5 成长中 Rust 版 React Slint 自绘 Rust/C++/JS 桌面+嵌入式 3/5 成长中 轻量嵌入式 GUI GPUI 自绘 Rust 桌面 2/5 早期 Rust 高性能 GUI 3.2 指标矩阵
说明:以下评价基于渲染原理和生态现状的一般判断,实际表现取决于具体实现。评分采用 1-5 分制,5 分最高。
框架 包体/启动 性能上限 原生体验 跨端一致 开发效率 生产风险 Flutter 3 中等 5 极强 3 一般 5 极强 5 极高 低 React Native 3 中等 4 强 5 极强 3 一般 5 极高 低 NativeScript 3 中等 4 强 5 极强 3 一般 4 高 中 Electron 2 大/慢 3 中等 3 一般 5 极强 5 极高 低 Qt Quick 3 中等 5 极强 3 一般 5 极强 3 中等 低 .NET MAUI 3 中等 4 强 5 极强 3 一般 4 高 低 Uno Platform 3 中等 4 强 4 强 4 强 4 高 中 Tauri 5 小/快 4 强 3 一般 4 强 4 高 中 Wails 4 小/快 4 强 3 一般 4 强 5 极高 中 KMP 视UI方案 视UI方案 5 极强 3 一般 4 高 中 Lynx 4 小/快 5 极强 3 一般 5 极强 4 高 高 Valdi 4 小/快 5 极强 5 极强 3 一般 3 中等 高 Electrobun 4 小/快 3 中等 3 一般 4 强 4 高 高 Dioxus 4 小/快 5 极强 3 一般 4 强 4 高 高 Slint 5 小/快 4 强 3 一般 5 极强 3 中等 中 GPUI 4 小/快 5 极强 3 一般 3 一般 3 中等 高 第四章:场景化选型指南
快速决策表
按技术栈选择
你的技术栈 首选 备选 理由 React React Native Lynx 复用 React 技能 Vue/Angular NativeScript Flutter 直接用 Vue/Angular Go Wails - 唯一的 Go 桌面方案 Rust(有前端) Tauri Dioxus 前后端分离 Rust(纯 Rust) Dioxus GPUI React-like 语法 C# .NET MAUI Uno Platform 微软生态 C++ Qt Slint 工业级/嵌入式 Kotlin KMP Flutter Android 团队扩展 按需求选择
你的需求 推荐框架 原因 极致跨端一致性 Flutter, Qt 自绘渲染 极小包体(< 5MB) Tauri, Slint 系统 WebView/轻量 原生体验优先 React Native, .NET MAUI 原生控件 嵌入式设备 Slint, Qt 资源占用低 需要 WebAssembly Uno Platform, Dioxus 浏览器运行 需要终端 UI(TUI) Dioxus 独特优势 快速原型 Electron, Flutter 工具链成熟 场景 A:移动端为主,重动效、品牌视觉统一
典型产品:电商首页、社交 feed、游戏化应用
场景 B:桌面应用(按技术栈)
团队技术栈 首选方案 优势 典型产品 纯前端团队 Electron 生态最成熟,工具链完善 VS Code, Slack 前端 + 在意包体 Tauri 包体小(3MB),启动快 系统工具 Go 后端团队 Wails 无需学 Rust,类型安全 运维面板,数据处理 Rust 团队 Tauri 安全性高,插件生态好 开发工具 想尝新 Electrobun Bun 运行时,TS 全栈 原型项目 场景 C:企业内部应用,C# 团队,长期维护
典型产品:ERP、CRM、内部审批系统
场景 D:Android 团队扩展 iOS
典型产品:已有 Android 应用,想扩展到 iOS
场景 E:需要深度系统集成
典型产品:文件管理器、系统工具、相机应用
场景 F:极度关注包体大小
典型产品:Lite 版应用、下沉市场、低端设备
场景 G:全平台覆盖(移动 + 桌面 + Web)
典型产品:跨平台协作工具、内容消费应用
场景 H:Rust 技术栈做桌面应用
典型产品:开发工具、性能敏感型应用
场景 I:嵌入式设备 GUI
典型产品:智能家居面板、车载 HMI、工业控制、医疗设备
场景 J:Vue/Angular 团队做移动应用
典型产品:企业内部应用、内容展示应用
场景 K:C# 团队,需要 WebAssembly
典型产品:需要 Web 版的企业应用、渐进式 Web 应用
第五章:选型方法论
5.1 三步选型法
Step 1: 确定渲染路线
│
├── 需要跨端视觉完全一致 → 自绘渲染(Flutter/Lynx/Qt)
├── 需要原生体验优先 → 原生映射(RN/MAUI/KMP)
└── 需要快速上线、前端技术栈 → WebView(Electron/Tauri)
Step 2: 确定平台覆盖
│
├── 移动端为主 → Flutter/RN/Lynx/KMP
├── 桌面端为主 → Electron/Tauri/Qt/GPUI
└── 全平台 → Flutter/Qt
Step 3: 匹配团队技能
│
├── Dart → Flutter
├── JS/TS + React → React Native / Lynx / Dioxus(想学 Rust)
├── JS/TS + Vue/Angular → NativeScript
├── JS/TS + 任意框架 → Electron / Tauri / Wails / Electrobun
├── C# → .NET MAUI / Uno Platform(需要 WASM)
├── C++ → Qt / Slint(嵌入式)
├── Go → Wails
├── Kotlin → KMP
└── Rust → Tauri(Web前端) / Dioxus(全栈) / GPUI(纯Rust) / Slint(嵌入式)5.2 决策检查清单
第六章:趋势观察
6.1 当前格局(2026 更新)
6.2 趋势预判
总结
先想清楚你的核心诉求是什么——跨端一致性、原生体验、还是开发效率?然后在对应的技术路线里,选一个匹配团队技能的框架。
变化 具体表现 影响 1. 桌面方案多元化 Electron/Tauri/Wails/Electrobun 每个后端语言都有选择 2. Rust GUI 成熟 Tauri/Dioxus/Slint/GPUI 覆盖全场景 3. 前端多样化 React/Vue/Angular 都有方案 不再是 React 独大 4. WASM 普及 Uno/Dioxus 支持 浏览器运行原生性能 5. 嵌入式轻量化 Slint 挑战 Qt 低端设备新选择 稳妥派(生产环境)
├─ 移动端:Flutter, React Native
├─ 桌面端:Electron, Qt
└─ C# 生态:.NET MAUI, Uno Platform
平衡派(值得尝试)
├─ Go 桌面:Wails
├─ Rust 全栈:Dioxus
└─ 逻辑共享:KMP
激进派(原型/小项目)
├─ Lynx, Valdi(移动端新思路)
├─ Electrobun(桌面 Bun 方案)
└─ Slint(嵌入式轻量)参考资源
官方文档
延伸阅读
大家好~ 今天给大家拆解一款极具参考价值的个人AI助手——OpenClaw(改名前Moltbot/Clawdbot),深入它的底层架构,看看其中藏着哪些AI工程师能直接借鉴的实战思路。 我深入研究了OpenClaw的架构设计,以及它处理智能体执行、工具调用、浏览器操作等功能的底层逻辑,发现其中蕴藏着诸多值得AI工程师借鉴的设计思路与实践经验。 弄懂OpenClaw的底层工作原理,不仅能让我们更透彻地理解这套系统的整体设计和核心能力,更重要的是,能清晰把握它的优势领域与短板不足。 我最初展开这项研究,只是出于个人好奇:想探究OpenClaw是如何管理记忆数据的,以及它的运行可靠性究竟如何。 今天,就为大家拆解OpenClaw的表层核心工作机制,全程干货,建议收藏慢慢看~ 大家都知道,OpenClaw是一款个人智能助手,既可本地部署运行,也能通过大模型API调用,在手机上就能轻松操作使用。但它的技术本质究竟是什么? OpenClaw的核心,是一个基于TypeScript开发的命令行界面(CLI)应用。 划重点:它既非Python开发的项目,也不是Next.js应用,更不是传统的网页应用。 它作为一个独立运行的进程,主要实现以下4大核心功能: 为了更通俗地解释其架构设计,我以用户向OpenClaw发送消息到用户收到回复的全流程为例,拆解具体执行步骤,一看就懂~ 当你在即时通讯工具中向OpenClaw发送指令后,系统会依次执行以下6个环节: 渠道适配器会接收你的消息并进行预处理,核心是标准化消息格式、提取附件。 关键设计:不同的即时通讯工具(电报、WhatsApp等)和输入流,都配有专属的适配器,避免格式混乱。 网关服务是整个系统的任务/会话协调中心,核心作用有两个: ① 接收预处理后的消息,将其精准分发至对应的会话;② 支持处理多个重叠的请求,避免冲突。 这里有个非常值得借鉴的设计——基于通道的命令队列: 每个会话都有专属的执行通道,保证单个会话的操作有序执行;而低风险、可并行的任务(如定时任务),则可在并行通道中运行,兼顾效率。 这个设计彻底规避了传统异步/等待(async/await)代码的混乱嵌套问题——要知道,过度并行化会严重降低系统可靠性,还会引发大量难以调试的bug。 但凡做过智能体开发的工程师,想必都有过类似的踩坑经历。这一思路,也与Cognition公司在《别再构建多智能体系统》博文中的核心观点不谋而合。 举个反例:如果为每个智能体简单配置异步执行,最终只会得到一堆交错混乱的执行结果——日志杂乱无章、无法追溯;若多个智能体共享状态,还需时刻警惕竞态条件的问题。 OpenClaw的优化的点在于:将“通道”设计为队列的上层抽象,把“序列化执行”作为默认架构(而非后期补充的优化)。 这一设计直接改变了开发思维:从思考“我需要为哪些内容加锁?”,转变为思考“哪些操作并行执行是安全的?”,极大降低了开发复杂度。 这是真正承载AI能力的核心模块,全程自动化处理,核心工作有4件事: ① 自动匹配适配的大模型;② 匹配对应的API密钥(若当前密钥失效,自动将该配置标记为冷却状态,尝试下一个);③ 主模型调用失败时,自动降级至备用模型,保证可用性;④ 动态拼接系统提示词。 重点细节:智能体运行器会结合可用工具、技能、记忆数据,动态拼接系统提示词,再加入会话历史记录(存储在.jsonl文件中),生成完整的大模型输入内容。 除此之外,它还会调用“上下文窗口守卫模块”,校验是否有足够的上下文空间——若上下文即将占满,系统会要么对会话内容进行压缩(总结上下文),要么优雅地终止请求,避免崩溃。 这一环节主要负责实际的大模型调用,核心亮点有两个: ① 以流式方式返回结果,提升用户体验;② 对不同大模型提供商的API做了抽象封装,实现调用层统一,后续切换模型无需大幅修改代码。 这是OpenClaw实现复杂操作的关键环节,逻辑很简单: 若大模型返回的是工具调用指令,OpenClaw会在本地执行该指令,并将执行结果添加至会话中;这一过程不断循环,直到大模型返回最终文本回复,或达到最大循环次数(默认约20次)。 划重点:OpenClaw的核心亮点——电脑操作能力,就是在这个环节实现的。 这一环节的逻辑十分标准,核心是“反馈+留存”: ① 反馈:回复内容通过原输入渠道(如微信、电报)反馈给用户,保证体验连贯;② 留存:会话数据被持久化存储在.jsonl文件中,文件中每一行都是一个JSON对象,包含用户消息、工具调用记录、执行结果、AI回复等全量信息。 而这,也是OpenClaw实现记忆功能的核心方式——基于会话的记忆。 以上就是OpenClaw的基础架构流程,接下来我们聚焦3个最关键的核心组件,拆解其中的设计亮点。 没有完善的记忆系统,一款AI助手的能力就会像金鱼一样转瞬即忘。OpenClaw通过两套系统,实现了高效的记忆管理,设计简洁却实用。 ① 会话记忆:前文提到的JSONL格式会话记录文件,存储每一次会话的全量信息;② 长期记忆:存储在 OpenClaw采用向量检索+关键词匹配的混合方案,兼顾语义匹配的灵活性和关键词匹配的精准性,这是非常实用的设计。 举个例子:搜索“认证漏洞(authentication bug)”时,既能检索到提及“认证问题(auth issues)”的文档(语义匹配,捕捉同义表达),也能精准匹配到包含该精确短语的内容(关键词匹配,锁定核心)。 ① 向量检索:基于SQLite实现,无需额外部署复杂的向量数据库,降低部署成本;② 关键词检索:依托SQLite的扩展插件FTS5实现,轻量化且高效;③ 嵌入向量:生成提供商支持自定义配置,适配不同的大模型需求。 两个关键设计,保证记忆的及时性和简洁性: ① 智能同步:文件监视器检测到记忆文件变化时,自动触发同步更新,无需手动操作;② 自动生成:记忆文件由智能体通过标准的文件写入工具生成,无需专属的记忆写入API——智能体只需直接向 OpenClaw的记忆系统设计异常简洁,与我们在CamelAIOrg中实现的工作流记忆高度相似:无需记忆合并,也没有月度/周度的记忆压缩操作。 这种简洁性见仁见智,但我始终推崇——可解释的简洁设计,远优于混乱复杂的嵌套式设计。 OpenClaw最核心的优势,就是能直接操作你的电脑——这也是它的核心护城河之一。其实现逻辑很直观,但设计很严谨。 核心逻辑:OpenClaw为智能体赋予较高的电脑操作权限(风险由用户自行承担),通过“执行工具(exec tool)”,在3种环境中运行Shell命令: 除了Shell命令执行,OpenClaw还内置了3类核心工具,覆盖大部分电脑操作需求: ① 文件系统工具:支持读、写、编辑各类文件,轻松处理本地文档; ② 浏览器工具:基于Playwright开发,核心特性是“语义快照”(后文详细说); ③ 进程管理工具:支持后台长期运行命令、终止进程等,管控电脑运行状态。 开放电脑操作权限,安全必然是核心关注点。OpenClaw的安全设计,参考了Claude Code的思路,核心是“白名单管控+危险命令拦截”。 OpenClaw设计了命令白名单,用户可对命令进行3类授权操作(操作时会弹出提示):单次允许、永久允许、拒绝。 白名单配置文件示例: 一些基础的安全命令(如 系统会默认拦截所有危险的Shell语法结构,从源头规避风险,示例如下(这些命令会在执行前被直接拒绝): 总结:OpenClaw的安全设计核心原则是——在用户授权的范围内,赋予智能体最大的自主操作能力,兼顾安全性和灵活性。 OpenClaw的浏览器工具,没有采用传统的截图方式,而是用了一种更高效的设计——语义快照。 核心定义:基于页面的可访问性树(ARIA)生成的文本化页面表征,简单说就是“用文本描述页面的所有元素”,而非图片展示。 这一设计带来了4大显著优势,尤其适合AI处理: ① 轻量化:一张普通网页截图约5MB,而语义快照不足50KB,大幅节省存储和传输成本; ② 低令牌消耗:文本形式的快照,令牌消耗仅为图片的几分之一,降低大模型调用成本; ③ 易解析:AI可直接识别文本描述的元素(按钮、文本框等),无需进行图像识别,提升操作效率; ④ 通用性强:不受页面样式、分辨率影响,适配所有网页。 OpenClaw的架构设计,整体给人的感觉是“简洁、实用、可落地”——没有复杂的冗余设计,每一个模块都有明确的目标,尤其适合AI工程师借鉴学习。 核心可借鉴的3个点: 对于AI工程师来说,研究这类成熟的开源项目(OpenClaw可本地部署),远比单纯看理论文档更有收获——看懂它的底层实现,能帮我们更快地规避踩坑,提升自己的系统设计能力。 原文链接:一、从技术本质定义OpenClaw
二、核心架构全解析(从发消息到收回复)

1. 渠道适配器:消息的“预处理中转站”
2. 网关服务:系统的“核心枢纽”
核心设计原则:默认序列化执行,显式声明并行执行
3. 智能体运行器:AI能力的“承载者”
4. 大模型API调用:结果的“生成环节”
补充:若所调用的大模型支持,该模块还能触发“深度思考”功能,提升回复的准确性。
5. 智能体循环:工具调用的“核心循环”
6. 回复通路:结果的“反馈与留存”
三、OpenClaw的记忆管理机制(不做“金鱼式”AI)
两套记忆存储系统
MEMORY.md文件或memory/文件夹中的Markdown格式记忆文件,用于长期留存关键信息。混合检索方案(向量+关键词)
技术实现细节(可直接借鉴)
简洁却高效的记忆同步与生成
memory/*.md路径写入内容即可。补充:新会话启动时,系统会自动抓取上一次会话内容,生成Markdown格式的总结,存入长期记忆,实现记忆的连贯。
另外一个特点:OpenClaw的记忆会永久保存,且新旧记忆的权重基本一致,不存在所谓的“遗忘曲线”。
四、核心竞争力:电脑操作能力(OpenClaw的“护城河”)
五、安全机制设计(或说“是否真的安全?”)
1. 命令白名单机制
// ~/.clawdbot/exec-approvals.json
{
"agents": {
"main": {
"allowlist": [
{ "pattern": "/usr/bin/npm", "lastUsedAt": 1706644800 },
{ "pattern": "/opt/homebrew/bin/git", "lastUsedAt": 1706644900 }
]
}
}
}2. 预授权安全命令
jq、grep、cut、sort、uniq、head、tail、tr、wc),已被系统预授权,可直接运行,无需用户额外批准,提升使用效率。3. 危险命令默认拦截
# 以下命令在执行前会被直接拒绝:
# these get rejected before execution:
npm install $(cat /etc/passwd) # command substitution
cat file > /etc/hosts # redirection
rm -rf / || echo "failed" # chained with ||
(sudo rm -rf /) # subshell六、浏览器工具亮点:语义快照技术
- button "Sign In" [ref=1]
- textbox "Email" [ref=2]
- textbox "Password" [ref=3]
- link "Forgot password?" [ref=4]
- heading "Welcome back"
- list
- listitem "Dashboard"
- listitem "Settings"最后总结
https://blog.jsdiff.com/archives/openclawjia-gou-jie-xi
感兴趣 可聊~