包含关键字 typecho 的文章

Uber 工程团队对其数据复制平台做了全面升级,现在每天可以在混合云和本地数据湖之间移动数以 PB 计的数据,解决了由于工作负载迅速增长而引起的扩展挑战。该平台基于 Hadoop 开源框架Distcp构建,现在每天处理超过 1PB 的数据复制和数十万个作业,而且速度更快、可靠性和可观测性更高,使得数据分析、机器学习和灾难恢复都达到了前所未有的规模。

 

Distcp是一个开源框架,它使用 Hadoop 的MapReduce跨多个节点并行复制大型数据集。文件被分割成块,并分配给在YARN容器中运行的 Copy Mapper 任务。Resource Manager 分配资源,Application Master 监控作业执行并协调合并,Copy Committer 在目的地组装最终文件。Uber 的 HiveSync 团队针对 PB 级工作负载优化了这一架构。他们将准备任务移到了 Application Master,实现了列表功能和提交过程的并行化,并提高了小规模传输的效率。

 

最初,HiveSync是基于 Airbnb 的ReAir项目,使用批量和增量复制实现 Uber HDFS 和云数据湖的同步。对于大于 256MB 的数据集,它通过异步工作进程并行提交 Distcp 作业,并用一个监控线程跟踪进度。随着每日复制的数据量从 250TB 增长到超过 1PB,数据集从 30000 扩展到 144000,HiveSync 面临的积压任务已威胁到服务水平协议(SLA)的达成,这说明他们亟需通过运维与架构升级来支持云迁移及他们的主备数据湖模型。

HiveSync 架构:使用 Distcp 的数据复制工作流(图片来源:Uber 博客)

 

为了克服扩展挑战,HiveSync 团队通过将资源密集型任务(如 Copy Listing 和 Input Splitting)从 HiveSync 服务器迁移到 Application Master 增强了 Distcp,减少了 HDFS 客户端争用,并将作业提交延迟减少了高达 90%。Copy Listing 和 Copy Committer 任务被平行化,允许同时处理多个文件,同时保持块顺序不变,p99 列表延迟降低了 60%,最大提交延迟降低了超过 97%。对于规模比较小的作业(传输数据少于 200 个文件或小于 512MB),Hadoop 的 Uber 作业功能直接在 Application Master 的 JVM 中运行 Copy Mapper 任务,每天消除了大约 268000 次容器启动,并提高了 YARN 效率。

超过 50%的 Distcp 作业被分配给单个映射器(图片来源:Uber 博客)

 

这些优化将增量复制能力提高了五倍,使 HiveSync 在 Uber 进行 on-premise-to-cloud 迁移期间复制了超过 300PB 数据,而且没有发生一次事件。增强的可观测性(包括作业提交、复制列表、提交者指标、堆内存使用率和 p99 复制速率)可以帮助工程师监控工作负载并预防故障。通过压力测试、断路器机制、YARN 配置优化以及任务执行顺序重排,有效缓解了内存不足、作业提交量高和复制列表任务持续时间过长等问题。

 

展望未来,HiveSync 团队正专注于进一步提升并行化以及优化资源管理和网络效率。计划中的增强包括:并行化文件权限设置和输入拆分操作,将计算密集型提交任务移到 Reduce 阶段,并实现动态带宽节流器。Uber 计划将这些改进作为开源补丁贡献给社区,提高社区管理混合云环境中超大规模数据复制的能力。工程团队指出,对于他们那样的规模,即使是小的改进也能带来显著的收益。这些工作突显了在复杂的多区域数据管道中维持高吞吐量和可靠性能所需的运营和工程创造力。

 

声明:本文为 InfoQ 翻译,未经许可禁止转载。

 

原文链接:https://www.infoq.com/news/2026/03/uber-scaled-data-replication/

在数字化浪潮席卷各行各业的今天,网站启用HTTPS加密协议已从“可选”变为“标配”。然而,对于许多个人站长、中小企业乃至政务教育单位来说,SSL证书的选择往往陷入两难:国际免费证书(如Let‘s Encrypt)有效期仅90天,频繁续期让人疲于奔命;而付费证书每年上千元的成本,又让预算有限的用户望而却步。进入2026年,随着国产密码算法的普及和合规要求的提高,哪里还能申请到稳定的一年期免费证书?  答案指向了国产SSL证书品牌——JoySSL。

为什么是JoySSL?

在当前的免费SSL证书市场中,JoySSL凭借其独特的一年期免费策略国密算法支持脱颖而出。相较于国际主流CA机构将免费证书有效期缩短至200天甚至维持90天的做法,JoySSL提供的免费证书单次有效期为365天,且支持无限次续签,真正实现了“一次申请,长期使用”。

更值得关注的是,JoySSL积极响应国家《密码法》和“等保2.0”的合规要求,为政务、教育、金融等关键领域提供免费国密SSL证书。其采用的SM2/SM3/SM4国产算法,不仅能有效抵御量子计算攻击,确保证据链的自主可控,还通过“SM2+RSA双证书”模式完美解决了国密算法在国际浏览器(如Chrome、Firefox)上的兼容性问题。

手把手教你申请一年期免费证书

免费SSL证书申请入口

第一步:注册账号并解锁权益
访问JoySSL官网,点击注册新账号。在注册过程中,务必填写特定的推荐注册码2309670 ,这是解锁一年期免费证书申请权限的关键。填写该邀请码后,用户不仅可以免费领取政务版或教育版国密SSL证书,还能享受一次免费的安装技术支持服务。

第二步:选择证书并提交申请
登录后台,在证书产品页面选择“免费体验版”、“政务版”或“教育版”SSL证书。根据你的需求选择单域名证书(保护一个域名)、通配符证书(保护主域名及所有二级子域名)或多域名证书。输入需要加密的域名,并填写单位基本信息(如为组织申请,需确保信息与组织机构代码证一致)。

第三步:完成域名所有权验证
这是确保证书只能由合法持有者申请的核心环节。JoySSL通常提供两种验证方式:

  • DNS验证(推荐):  在域名管理后台(如阿里云、腾讯云DNSPod),根据JoySSL提供的记录值添加一条TXT解析记录。
  • 文件验证:  将系统生成的验证文件上传至服务器指定目录下。
    添加完成后,返回JoySSL后台点击“验证”,通常几分钟内即可通过审核。

第四步:下载证书并部署
验证通过后,证书即刻签发。用户可以下载适配Nginx、Apache、IIS、Tomcat等主流服务器的证书文件包。以Nginx为例,将证书文件上传至服务器,修改配置文件指向正确的.crt和.key文件路径,重启服务后,网站即可实现HTTPS加密访问。

从昨天开始日历无法同步,今天还是不行,刚去看了一下。

icloud.com 正常,但是 icloud.com.cn 打不开,云上贵州挂了?

你们的正常吗?

ScreenShot_2026-03-10_091556_151

过年前在路过发生了一起小事故,等红绿灯时副驾开门下车,车门撞到路过的电瓶车.

因为双方都有事,看着也不严重,于是当场给了 150 块钱私了.

以上是背景.

5 天后,交警给我打电话,说对方报警了...

然后约了在交警队碰面,我路上就报了保险.去了之后查监控,出责任认定书一同搞下来,大概情况如下:

对方 76 岁女性,退休没啥钱,开个体理发店的,手痛了几天一直没见好,去医院拍片发现骨折了.
 
现在手又痛,班也不能上(春节正是做头发人多的时候),觉得需要误工费,就又找上我了.

我没啥可说的,交警定了全责,我又有保险,那就保险沟通呗.

之后就是和保险拉了个群,偶尔在微信上听她诉苦,我就搭个话,给些治疗建议啥的.


转眼就过完年了,一个月过去,手还不见好,并且开始跟保险谈误工费.

保险的意见:

  1. 你这个年纪,退休了,按道理是没有误工费的.但是考虑实际情况可以给误工费.
  2. 骨折的是掌骨,不是手指,按法律规定最多 3 个月误工.

综合算下来给了个 1w 的赔偿额.结果对方想要 4-5 万....


于是约了在交警大队的调解室去调解.

调解员的意见跟保险一致,甚至很诧异保险居然在没调研她工作环境的情况下就给出了 1w 误工费的条件.

这里说下她的工作情况,最令人头大的地方,因为完全无法证明她的收入:

个体理发店,客户大多是附近的中老年熟客,收费几乎全是现金,有个小本本记账,有营业执照但没有交税.

调解员说按照当地服务业标准,140/天的误工费.
她当场就炸毛了.说平时一个月都是 6000-7000,春节上万都有.家里还有 90 多的父亲,儿子也生活压力大.

于是调解员让我们找诉前调解室去,看看能不能通过诉讼解决.


诉前调解是个男的,直接问诉求:"你要多少啊?"
"2,3 万吧"
调解员:"2 万和 3 万隔了 1 万呢,你说精确点.或者 1 万 8 行不行?行我就去跟保险说"
"行吧"
结果调解员出去回来:"保险那边 1 万 8 都给不了,只能给 1 万 3."

然后就散了.


我的感受:

  1. 我不想出钱,毕竟买了保险.但是私了的钱我也没打算要回来.
  2. 看病随便看,该咋检查就咋检查,保险都会报销.
  3. 她期望的误工费怕是很难拿到了,收入受影响是事实.

然后我给她查了上诉流程,介绍了附近的律师事务所.希望能多争取一点误工费.毕竟保险公司还得听法官的不是.

当然重点是:要带上我的保险公司(太平洋保险)一起告.


教别人告自己,也是头一遭了...

闲下来一直在想,到底是哪里的问题呢.

对我:虽然是全责,但是该道歉就道歉,该慰问就慰问,只是金钱方面,我交给保险公司.
对保险公司:付款需要有凭据,医疗费有发票,误工费则是要合法合规.
对受害者:无妄之灾,不仅身体受到伤害,赔偿也不足以弥补对工作的影响.

阿迪达斯对其数据平台的基础设施交付方式进行了全面改革,从集中式的 IaC 模型转向去中心化模式。此次变革将基础设施定义的所有权从中央平台团队转移到按业务域划分的团队,体现了行业向“产品化平台工程”转型的趋势。该转型旨在在治理与自治之间取得平衡,以应对数据平台在多个团队与多种使用场景扩张过程中暴露出的扩展性挑战。

 

在原有模式下,单一的平台工程团队负责 IaC 仓库、管理部署流水线,并在整个组织内执行统一标准。这种集中式结构在早期增长阶段确保了一致性与合规性。然而,随着多个领域团队陆续接入平台,请求数量不断增加,待处理积压逐渐扩大,团队间的协调也带来了额外开销。工程师们指出,这种限制源自交付模型本身,而非底层工具问题。

 

Jose Moreno强调:

核心问题在于交付模型本身——它已无法支撑组织当前所需的速度与自治程度。

 

为解决这些约束,阿迪达斯数据平台团队重新定义了基础设施的交付方式以及交付主体。新的运营模式将责任下放给领域团队,使其能够在预定义边界和标准化模式下,自主创建与管理基础设施。平台工程师不再执行具体的基础设施变更,而是转向维护可复用的构建模块、工具框架和治理策略,以支持自治式交付。

 

此次重构引入了分层的 IaC 结构:可复用模块封装资源定义;“栈”将模块组合为可部署单元;消费配置则引用经批准的栈用于生产部署。这种分层设计在允许非生产环境实验的同时,限制对基础组件的直接修改。在去中心化环境中,明确哪些内容可以修改、由谁修改、在哪个环境中修改,是安全扩展的关键。

图示:基础设施组件类型及其对应的开发者需求与职责,来源:阿迪达斯博客

 

团队还开发了一个定制的命令行工具(CLI),用于抽象底层复杂性,并将治理机制嵌入日常工作流程。通过标准化状态管理、强制命名与标签规范,并与持续集成、持续交付(CI/CD)流水线集成,该工具在无需集中式人工审核的情况下实现了一致性保障。自动化的流水线负责部署编排,确保了跨环境的可追溯性与可复现性。

 

组织结构方面也进行了重新划分与职责明确。框架负责人负责维护共享工具与标准;领域团队中的开发者使用经过批准的模式组合基础设施;使用方通过自动化流水线部署生产配置。职责边界的清晰划分不仅强化了问责机制,也降低了对单一中央团队的依赖。

 

图示:围绕去中心化基础设施交付的工作模型,来源:阿迪达斯博客

 

工程师们表示,去中心化显著缓解了中央团队的积压压力,并使多个团队能够独立交付基础设施。这一转型表明,IaC 交付的去中心化不仅是技术变革,更是一种文化与组织层面的重塑,需要共享工具、自动化优先的治理方式,以及清晰的所有权边界。

 

这一实践也与更广泛的平台工程趋势相契合。越来越多的企业在通过标准化抽象与自动化策略保障运行安全的同时,推动自助式基础设施能力。通过围绕自治团队进行重组,并辅以共享框架与 CI/CD 编排支持,阿迪达斯的数据平台为复杂环境下的基础设施规模化交付提供了一个可借鉴的模式。

 

原文链接:

https://www.infoq.com/news/2026/03/adidas-decentralized-platform/

来自谷歌麻省理工学院的研究人员发表了一篇论文,提出了一个用于扩展多智能体系统的预测框架。该框架揭示了工具使用与系统协同之间存在权衡关系,可用于为特定任务选择最优的智能体架构。

该扩展模型依赖于系统的多个预测因素,包括底层大语言模型的智能指数、单智能体的基线性能、智能体数量、工具数量以及协调指标。研究人员发现模型中存在三种主导效应:工具‑协调权衡——对工具需求较高的任务,在多智能体带来的开销下表现会更差;能力饱和——当单智能体基线性能超过某一阈值后,增加智能体的收益会逐渐递减;以及拓扑依赖的错误放大——集中式编排能够减轻这种错误放大效应。他们还发现,最优的协调策略取决于任务类型:金融推理任务更适合采用集中式编排,而网页导航任务在分散式策略下表现更佳。在独立测试集上进行评估时,该扩展框架对最优协调策略的预测准确率达到 87%。谷歌表示:

随着 Gemini 等基础模型的持续进步,我们的研究表明:更智能的模型并不会取代对多智能体系统的需求,反而会加速其发展——前提是采用正确的架构。从启发式方法转向定量原则,我们便能构建出下一代 AI 智能体,它们不仅数量更多,而且更智能、更安全、更高效。

谷歌团队根据智能体的不同协调方式将多智能体架构分为以下几类:独立式——智能体之间无协调;集中式——智能体仅与中央编排器通信;分散式——点对点协调;混合式——兼顾集中式与分散式。每种架构都包含若干配置参数,如智能体数量、单个智能体的迭代次数等,且在计算复杂度、内存占用与大模型调用次数上各不相同。

多智能体架构,图片来源:Google Research

研究人员开发的扩展模型是一个包含 20 项的回归模型,基于 9 个预测变量及其交互项构建。他们剔除了“无明确机制支撑的交互项……以避免过拟合”。谷歌指出,该模型确实存在一定局限性。研究团队特别发现,“工具密集型”任务会导致多智能体协调效率降低,并提出需要“为工具密集型任务设计专门的协调协议”。

在 Hacker News 上关于这篇论文的讨论中,多位用户分享了他们在多智能体工作流方面的实践经验。一位用户写道:

我在日常工作中构建了大量智能体工作流。在确定编排策略时,我发现一个非常有效的方法:在规划阶段让智能体给出推荐方案。这种借助智能体自身来提升性能的技术,是我高效运用这项技术的关键。当然,效果因人而异。我主要使用 Claude Code,因此其他模型的效果如何尚不明确。

多智能体系统的协作与编排策略是当前活跃的研究方向。2025 年,InfoQ 曾报道亚马逊为 Amazon Bedrock 推出的多智能体协作框架,该框架可让专业智能体在主管智能体的协调下协同工作。今年年初,InfoQ 还报道了谷歌发布的多智能体系统八大基础设计模式指南,指南对每种模式给出了详细说明,并附带谷歌智能体开发套件(ADK)的示例代码。

原文链接:

https://www.infoq.com/news/2026/03/google-multi-agent/

一切的缘起

当我装上 OpenClaw,开始和它协作的那一刻,命运的齿轮开始转动。如果说大模型的出现,让专业的开发者先尝到了红利;AI 智能体的出现,让很多 IT 从业者尝到了甜头;那么 OpenClaw 的出现,则让 AI 真正地,能够非常丝滑和简单地进入平常百姓家,未来,每一个人都有可能从 AI 中获取能力的提升。

但这也注定不是一个轻而易举实现的事情,实现目标的过程中,总是需要相应的台阶。极客邦科技作为一个有着 18 年历史,一直服务技术人,服务开发者的生态企业,好像过去所做的,就是一直在搭建这些台阶,只是在通往 AI 开发者的路上,这些台阶都装上了 AI 的翅膀。

于是,在这个春天,就有了“OpenClaw 中国行”的故事。

没有那么多的豪言壮语,也没有那么多的铿锵决心,脑海中的这句话倒一直给着我力量:

AI 时代已经到来,

每个人都应该掌握 AI 能力,

中国需要更多 AI 开发者。

这句话,现在也成了“OpenClaw 中国行”的宣言。

我们想在中国发起一场 AI 装机运动

2000 年前后,很多开发者第一次接触 Linux,是在一种叫 Linux Install Fest 的活动上。

大家带着自己的电脑来到现场,一起安装 Linux,一起解决各种奇怪的问题,一起聊技术。

那是一代技术社区的开始。

二十多年后的今天,我们可能正在站在一个新的历史节点上。

AI 正在改变一切。

模型能力不断突破,AI 应用层出不穷,但很多人依然会遇到三个问题:

  • 想用 AI,但不知道从哪里开始

  • 想做 AI 应用,但环境太复杂

  • 想交流 AI,但身边没有人

所以我们决定做一件简单的事情。

发起一场新的 Install Fest

只不过这一次,是 AI Install Fest

这就是:

OpenClaw 中国行。

做一件简单但有价值的事

OpenClaw 中国行是一次面向 AI 开发者的全国巡回活动。

我们的目标只有一句话:

让每个人 30 分钟跑通自己的 AI。

每一站活动,你只需要:

带一台电脑来。

现场我们会一起:

  • 安装 OpenClaw

  • 跑通 AI 模型

  • 做出第一个 AI 应用

很多人会在这里完成自己人生中的:

第一次 AI 推理。

每一站 OpenClaw Day 会发生什么?

每一场活动,我们都会做五件事情。

1 公益装机运动

活动的第一件事,就是 装机

我们会在现场设置:

AI 装机区

开发者志愿者会帮助大家:

  • 安装环境

  • 跑通模型

  • 解决各种问题

我们的目标很简单:

30 分钟,让你的电脑跑起来 AI。

如果你之前一直被环境配置劝退,这一环节非常适合你。

2 AI 应用三板斧

跑通 AI 只是第一步。

接下来我们会用 最简单的方式讲清楚三件事:

  • AI Agent 怎么用

  • AI 自动化怎么做

  • AI 应用怎么搭

全部是:

实战案例。

你可以当场看到 AI 在真实场景中的用法。

3 AI 项目闪电秀

每个城市,我们都会邀请本地开发者带来 AI 项目。

规则非常简单:

每人 3 分钟。

可能是:

  • 一个 AI 工具

  • 一个开源项目

  • 一个创业产品

如果你也在做 AI 项目,也可以报名上台展示。

4 AI 工具市集

活动现场还会有一个小型的:

AI 工具市集

在这里你可以看到很多:

  • 最新 AI 工具

  • AI 开发平台

  • AI 应用产品

很多工具可以 现场体验

5 AI 社区交流

活动最后,我们会留出时间给大家聊天。

认识一些:

  • AI 开发者

  • AI 工程师

  • AI 创业者

因为真正的社区,其实是从:

线下聊天开始的。

2026 年,我们会走进这些城市

OpenClaw 中国行将在多个城市举办。

首批计划城市包括:

  • 北京、上海、杭州、深圳、广州

  • 成都、武汉、南京、厦门、西安

所以,你也大概看到了,我们选择的基本都是有 TGO 鲲鹏会分会的地方,如果暂时还没有包含你所在的城市,随时联系我,咱们一起共创,说不准下一次活动就在你的城市举行。

我们希望在这些城市里,找到更多:

真正做 AI 的人。

我们还会一起寻找中国 AI 项目

OpenClaw 中国行期间,我们还会发起一个计划:

中国 AI100 项目榜。

我们会从各个城市的开发者中,寻找优秀的 AI 项目。

这些项目将获得:

  • 社区曝光

  • 技术媒体报道

  • 投资人关注

如果你正在做 AI 项目,也非常欢迎来展示,我们和专业的投资人一起给你反馈。

为什么是极客邦?

过去 18 年,我们一直在做一件事情:

连接中国技术人。

很多开发者都通过这些平台认识我们:

而现在,我们还在建设一个新的空间:

模力工场

它的目标很简单:

成为 AI 开发者的原生社区。

OpenClaw 中国行,就是这个社区的开始。

谁适合来参加?

如果你属于下面任何一种人,都非常适合:

  • 想开始学习 AI 的开发者

  • 想做 AI 应用的产品经理

  • 在做 AI 项目的创业者

  • 对 AI 感兴趣的学生

  • 想认识 AI 圈子的朋友

甚至:

完全没有经验的人。

只要你对 AI 好奇。

你只需要带一件东西

来参加 OpenClaw 中国行,你只需要:

带一台电脑。

剩下的事情,我们一起完成。

报名方式

各城市报名入口陆续开放。可以先加入 OpenClaw 中国行的“万人养虾”飞书群,随后各种信息(包括各地活动、OpenClaw 学习等)会在这儿发布。北京站的报名群已经开启,在加入“万人养虾”群后,可以一并加入:

目前 OpenClaw 中国行各城市活动的排期如下:

你也可以关注:

  • InfoQ:这儿有各种 OpenClaw 的资讯

  • 极客时间:这儿有各种 OpenClaw 的学习课程(包括免费和付费)

  • TGO鲲鹏会:这儿有很多 OpenClaw 的专家

  • 模力工场:这儿有很多好用的 AI 应用

  • 以及各城市活动群

席位有限,建议尽早报名。

这次有一个全新的变化,我们计划全程使用飞书来协作这次 OpenClaw 中国行,包括飞书多维表格、飞书知识库等,初步来看,这样做的好处就是大家多可以参与进来共创,随时可以更新活动的进展。据说多维表格还可以自动给与会者发送提醒,制作海报,以及活动结束后很快生成总结。

从前没玩过,据说很有意思,这次计划试试。所以,如果你也想深度参与这次的“OpenClaw 中国行”,可以关注我们的“OpenClaw中国行飞书知识库”,随时参与各个城市的活动,以及任何一个文档和群组。在这方面,我也是新手,很多地方可能用的不太明白,大家也多担待^_^

最后,我们需要你的支持

场地支持:一线城市我们估计现场至少 300 人,新一线城市 200 人,二线城市 100 人,如果你的公司或者所在科技园区能提供符合要求的场地,欢迎联系。

分享人:极客邦科技有深厚的专家积累,但我们依然欢迎现在已经把 OpenClaw 玩得很溜,有自己体悟的同学来分享,不仅是在这几个线下城市,还包括线上直播,欢迎联系。

装机志愿者:我们预计线下会有很多同学需要装机,但自己又不太会,需要有人扶上马送一程,如果你会装机,并且愿意帮助大家也用上 OpenClaw,欢迎联系。

装机导师:在志愿者给大家装机过程中,可能遇到挑战,我们也需要有人能够指导他们,我们邀请的分享人可以是导师,但如果你也愿意提供帮助,欢迎联系。

社区合作:如果你是某社区的负责人或者热心人,觉得 OpenClaw 中国行的内容对你的社区也有帮助,可以联系进行转播或者邀请你的会员加入“OpenClaw 中国行”知识库或者“万人养虾”群,大家一起学习一起进步,欢迎联系。

赞助合作:举办一场活动,还是会消耗比较多的资源,包括人力、物料、差旅等等,如果你有公司产品或者品牌需要宣传,比如你有 AI 工具或者算力资源等,这次 OpenClaw 中国行觉得物超所值,也欢迎赞助合作,给钱给人给礼品都行,现金优先^_^,欢迎联系。

OPC 社区合作:这次的 OpenClaw 中国行只是极客邦科技全年社区活动中的一小部分,我们还帮助各地政府/科技园区运营 OPC 社区,通过各种活动为社区引流,如果你所在的政府或者科技园区有这方面的需求,也欢迎联系。

One More Thing

最后的友情提醒,预计这次 OpenClaw 中国行报名太火爆,各个城市现场又容不下那么多人,我们会优先将机会提供给InfoQ客户企业、极客时间企业版客户企业、TGO鲲鹏会会员企业、极客时间VIP/训练营会员、模力工场AI 应用开发者等。

以上所述任何 OpenClaw 中国行的支持,如您有意,都欢迎联系我:

这是最近在网上刷到的一个职场吐槽帖子,原文差不多是这样:

“渐渐能理解为何有些公司不愿意招 35 岁以上程序猿。去年换了份工作,组里 4 位组员其中 3 位 40+,发现其实最大的问题并不是说精力不济卷不动并且薪资高,而是另有其他原因。”

与此同时,帖主也具体列举了如下的四条现象。

说实话,我第一眼刷到这个话题帖时,我觉得这其实也只能算是帖主自己的个人观察吧,能反映一些问题,但是也代表不了全部。

为啥呢?

因为这几年我也带过不少新人,说实话,有些年轻同学可能也会有后三条所列的现象。

这四条中,如果真要讲,除了第一点有可能勉强和年龄扯上点关系之外,其他的有时候可能也就是个体现象而已,可能和性格有关系,也有可能和个体的做事风格有关系,完全丢锅给年龄那也是不妥的。

但是后来我又回头重新想了想,存在即合理,既然帖子里提到了这几条,那咱多审视重视一下总是好的。

参加工作这些年,随着自己年龄的增大,对于大龄程序员这个话题的理解又不太一样了,所以这里我也来聊一聊我自己的一些观察和反思,供大家参考,也欢迎大家一起来分享交流你的看法。

首先,一个比较直观的感受是,年龄大了之后,学习新事物的速度和意愿,的确不如年轻时那会敏锐和强烈了。

比方说,项目决定要引入一个全新的前端框架,以提升开发效率和用户体验。对于我们那些刚毕业一两年的同学来说,他们几乎可以带着一种探索新大陆的热情,很快就能把官方文档啃一遍,并在组会上提出几个很有建设性的实现思路。

但是对于有些老同事来说,的确经验丰富,技术扎实,但在面对某个新框架时,不是说吃力吧,而是他们往往会习惯性地用以前的思维去套。这时候不得不说,年轻的大脑,就像一张白纸,没有固有的条框,在接受新知识时阻力会小很多。

而资深程序员,过往的经验有时反而会成为一种包袱,让他们在面对颠覆性创新时,往往下意识地会产生一种审视甚至抵触,这种心理上的顿挫感,某一程度上来说是客观存在的。

其次,是思维模式的“固化”。

注意,这里加了引号的,并非绝对的贬义哈,而指的是在一个领域深耕多年后所形成的一种惯性。

当一个新需求摆在面前时,年轻人的第一反应往往是怎么做,他们更关注实现的可能性和路径,即使遇到困难,也会先尝试着去推进,去验证,不行再调整。

而老同事他们的经验告诉他们,哪些路是坑,哪些方案是行不通的,所以他们的第一反应往往是这个不合理、以前我们做过类似的,结果很糟糕、以及为什么要这样做,有没有考虑过 XX 风险……等等。

说实话,这种问题导向思维是稳当的表现,但在新项目初期需要快速迭代和验证时,这种问题导向的思维,有时候往往也会成为项目推进的阻力。

再者,就是沟通成本和团队氛围的微妙变化。

人到中年,生活的重担接踵而至,这些压力不可避免地会投射一些到工作中。有时候你会感觉到,他们身上有时似乎会带着一种倦怠感和负能量,对公司制度的吐槽,对产品需求的抱怨,对技术选型的不满等等。

这些情绪的宣泄,虽然可能是事实,但当它成为一种常态时,说实话对于团队氛围的营造还是会打折扣的。

另外不知道大家有没有发现,随着年龄增长,人似乎会变得更油腻,而且还特较真,说好听点,是更坚持自己的原则。

和一些年轻的同事们沟通,有时候他们虽然有个性一点,说话冲一点,但是就事论事,不会内耗。但是,和有些油腻老同事沟通起来,说实话,沟通成本真的挺大的。

当然以上这只是个例观察,不代表全部,更非普遍现象。

我自己也见过很多年龄比较大的技术专家,他们依然保持着旺盛的求知欲,对新技术充满好奇,而且非常谦逊低调、乐于分享,经常主动承担一些技术预研和架构设计的工作,是团队的定海神针。

其实很多程序员在年龄大了之后越来越焦虑的一个重要原因就是因为 在日复一日的重复性劳动中,不知不觉就陷入了舒适区,把工作年限当成了工作经验,而忽略了核心竞争力的持续迭代。

所以还是那句话,千万不要给自己设限,埋头赶路的同时也不要忘记时常抬头看看周围的环境和新的机会。

尤其现在都是 AI 时代了,我其实一直在想,这对于大龄程序员来说会不会也是一个转机呢?

毕竟他们见得做、做得多、经验多,这对于在进行 AI 时代的需求翻译、指导 AI 干活这些事情上会不会更加游刃有余呢?

这是一个很有意思的讨论话题。

那关于这个问题,你的看法是什么呢,如果有不同的见解,也欢迎一起来分享交流。

注:本文在GitHub开源仓库「编程之路」 https://github.com/rd2coding/Road2Coding 中已经收录,里面有我整理的6大编程方向(岗位)的自学路线+知识点大梳理、面试考点、我的简历、几本硬核pdf笔记,以及程序员生活和感悟,欢迎star。

简介:1为什么要有动态内存分配;2mallocfree;3callocrealloc;4柔性数组;5总结C/C++中程序内存区域划分

1 为什么要有动态内存分配

我们已经掌握的内存开辟方式有:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

但是上述的开辟空间的方式有两个特点:

  • 空间开辟大小是固定的。
  • 数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能调整

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。

C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

2 mallocfree

2.1 malloc

void* malloc (size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。

2.2 free

void free (void* ptr);

free函数专门用来释放动态开辟的内存。

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数ptr是NULL指针,则函数什么事都不做。

忘记释放不再使用的动态开辟的空间会造成内存泄漏。切记:动态开辟的空间一定要释放,并且正确释放。

使用free函数时通常还搭配指针赋为NULL操作,以避免出现野指针。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    //申请5个整型的空间
    int* p = (int*)malloc(5 * sizeof(int));
    if (p == NULL)
    {
        perror("malloc");
        return 1;
    }
    //使用
    int i = 0;
    for (i = 0; i < 5; ++i)
    {
        *(p + i) = i + 1;
    }
    for (i = 0; i < 5; i++)
    {
        printf("%d\n", *(p + i));
    }

    //释放
    free(p);//释放ptr所指向的动态内存
    p = NULL;//消除野指针
    return 0;
}

3 callocrealloc

3.1 calloc

void* calloc (size_t num, size_t size);

C语言还提供了一个函数叫calloccalloc函数也用来动态内存分配。

  • 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数malloc的区别只在于calloc会初始化。

所以如果对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数。

3.2 realloc

  • realloc函数的出现让动态内存管理更加灵活。它有两个应用:1调整动态内存申请的空间;2开辟一块空间(当成malloc用)。
  • 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。那realloc函数就可以做到对动态开辟内存大小的调整。

函数原型如下:

void* realloc (void* ptr, size_t size);
  • ptr是要调整的内存地址
  • size是调整之后新大小
  • 返回值为调整之后的内存起始位置。
  • 如果调整无法完成,则返回一个NULL指针

realloc在能够调整内存空间时会存在两种情况:

情况1:原有空间之后有足够大的空间

情况2:原有空间之后没有足够大的空间

情况一

要扩展内存就直接在原有内存之后追加空间,原来空间的数据不发生变化,返回的指针与传入的指针指向同一空间。

情况二

原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

具体过程:

  1. 找一块能满足大小的新空间
  2. 将旧空间的数据,拷贝到新的空间
  3. 释放旧的空间
  4. 返回新空间的地址

由于上述的两种情况,realloc函数的使用就要注意一些。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    //申请5个整型的空间
    int* p = (int*)malloc(5 * sizeof(int));
    if (p == NULL)
    {
        perror("malloc");
        return 1;
    }
    //使用
    int i = 0;
    for (i = 0; i < 5; ++i)
    {
        *(p + i) = i + 1;
    }
    
    //空间不足,希望存储1~10
    //调整空间
    int* tmp = (int*)realloc(p, 10 * sizeof(int));
    if (tmp == NULL)
    {
        perror("realloc");
        return 1;
    }
    else
    {
        p = tmp;
        tmp = NULL;
    }
    
    for (i = 5; i < 10; ++i)
    {
        *(p + i) = i + 1;
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d\n", *(p + i));
    }

    //释放
    free(p);
    p = NULL;
    return 0;
}

4 柔性数组(flexible array)

C99中,结构中的最后一个元素允许是未知大小的数组。其中大小未知的最后一个元素就叫做柔性数组成员。

struct S
{
    int i;
    int a[0];//柔性数组成员
};
//有些编译器会报错无法编译可以改成:
struct S
{
    int i;
    int a[];//柔性数组成员
};

4.1 柔性数组的特点

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof返回的这种结构大小不包括柔性数组成员的内存(因为还不确定)。
  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

4.2 柔性数组的使用和优势

//代码1:柔性数组实现
struct S
{
    int n;
    int a[];//柔性数组成员
};
int main()
{
    struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
    if (ps == NULL)
    {
        perror("malloc");
        return 1;
    }
    ps->n = 100;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        ps->a[i] = i + 1;
    }
    //如果a数组的空间不够的话,可以动态的调整
    struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
    if (ptr == NULL)
    {
        perror("realloc");
        return 1;
    }
    ps = ptr;

    //继续使用了

    free(ps);
    ps = NULL;

    return 0;
}

//代码2:另一种实现
struct S2
{
    int n;
    int* a;
};
int main()
{
    struct S2* ps = (struct S2*)malloc(sizeof(struct S2));
    if (ps == NULL)
    {
        perror("malloc1");
        return 1;
    }
    ps->n = 100;
    
    int*p = (int*)malloc(10 * sizeof(int));
    if (p == NULL)
    {
        perror("malloc2");
        return 1;
    }
    ps->a = p;

    int i = 0;
    for (i = 0; i < 10; i++)
    {
        ps->a[i] = i + 1;
    }
    //空间不够了 - 扩容
    int*p2 = realloc(ps->a, 20 * sizeof(int));
    if (p2 == NULL)
    {
        perror("realloc");
        return 1;
    }
    ps->a = p2;//
    //接着使用

    //释放内存
    free(ps->a);
    ps->a = NULL;
    free(ps);
    ps = NULL;

    return 0;
}

上述代码1和代码2可以完成同样的功能,但是方法1的实现有两个好处:

第一个好处是:方便内存释放

代码1中做一次free就可以把所有的内存释放掉。

第二个好处是:这样有利于访问速度.

连续的内存有益于提高访问速度,也有益于减少内存碎片。

扩展阅读:

https://coolshell.cn/articles/11377.html

5 总结C/C++中程序内存区域划分

C/C++程序内存分配的几个区域:

1.栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

2.堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于链表。

3.数据段(静态区):(static)存放全局变量、静态数据。程序结束后由系统释放。

4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。

Function Calling 函数调用也叫 Tools 工具,它本质上就是在AI应用中自定义实现一些方法(函数),然后交给大模型,由大模型自行判断在合适的时机调用这些方法,从而实现原来大模型无法做到的一些扩展功能,比如操作本地数据库等等。其流程如下

在这里插入图片描述

  1. 当用户把问题发送给AI应用,在AI应用的内部需要组织提交给大模型的数据,而这些数据中需要描述清楚我们的AI应用中有哪些函数能够被大模型调用。每一个函数的描述都包含三个部分,方法名称、方法作用、方法入参
  2. 当AI应用把这些数据发送给大模型后,大模型会先根据用户的问题以及上下文拆解任务,从而判断是否需要调用函数,如果有函数需要调用,则把需要调用的函数的名称,以及调用时需要使用的参数准备好一并响应给AI应用
  3. AI应用接收到响应后需要执行对应的函数,得到对应的结果,接下来把得到的结果和之前信息一块组织好再发送给大模型

注意:

  • 大模型只做调用哪个函数的决策,实际调用函数的工作是由AI应用(比如langchain4j框架、cherry studio等)完成的
  • 由于在一次任务的处理过程中可能需要根据顺序调用多个函数,所以当大模型接收到AI应用发送的数据继续拆解任务,如果发现还需要调用其他的函数,则会重复4.1~4.4这几个步骤,直到无需调用函数,最终把生成的结果响应该AI应用,并由AI应用发送给用户

具体来说,Fuction Calling就是大模型提供商在模型内部与API层面做了支持的一种能力,它最早由 OpenAI 引入:

  • 在模型层面:模型提供商需对大模型进行特别优化,使其具备根据上下文正确选择合适函数、生成有效参数的能力(比如有监督微调、强化学习)
  • 在API层面:模型提供商需额外开放对Function Calling的支持,比如需要自定义标准来规定AI应用程序向大模型提供工具列表、大模型向AI应用程序响应需要调用哪个工具并携带调用参数、AI应用程序向大模型返回工具调用的结果时的具体消息格式

由此可见,Fuction calling是大模型本身的一种能力,需要大模型自身支持,并且各个大模型厂商实现的具体标准都不一样。实际开发时需要查找对应的支持情况:Comparison Table of all supported Language Models | LangChain4j

在这里插入图片描述

举一个具体的例子,用户提问天气,大模型利用Function Calling能力调用外部工具查询天气并返回结果

在这里插入图片描述

流程解释如下:

  1. 用户通过自然语言提出问题,例如:“广州今天天气如何?适合出门吗?”
  2. AI应用程序向大模型 API 传入用户原始输入、函数描述和其他上下文信息,获取调用指令。函数描述包括函数名称、用途说明、参数结构等。具体消息格式各个厂商会有区别,示例如下

    {
      "messages": [
        {
          "role": "system",
          "content": "你是一个助手,可以根据用户的请求调用工具来获取信息。"
        },
        {
          "role": "user",
          "content": "广州今天天气如何?适合出门吗?"
        }
      ],
      // 函数列表
      "functions": [
        {
          "name": "getWeather",
          "description": "获取指定城市的天气",
          "parameters": {
            "type": "object",
            "properties": {
              "location": {
                "type": "string",
                "description": "城市名称,比如北京"
              },
              "date": {
                "type": "string",
                "description": "日期,比如 2025-08-07"
              }
            },
            "required": ["location", "date"]
          }
        }
      ]
    }
  3. 模型会智能判断是否需要调用函数,选择合适的函数,并基于上下文自动生成结构化的调用指令(函数名 + 参数),例如:

    {
      "function_call": {
        "name": "getWeather",
        "arguments": {
          "location": "Guangzhou",
          "date": "2025-07-17"
        }
      }
    }
  4. AI应用程序接收到模型返回的调用指令后,解析调用指令,得到函数名称和参数,执行对应的方法(如调用天气查询函数),并获取结果
  5. AI应用将函数执行结果 + 其他上下文信息(包括用户原始输入)传给模型,模型判断此时已有足够的信息回答问题,不再需要调用函数了,于是直接生成最终结果,例如:“广州今天35度,暴雨,建议在室内活动”

注意:

  • 由于Fuction Calling能力依赖具体大模型厂商的实现,所以其缺陷是AI应用需要自己实现对各个厂商大模型的适配,开发量比较大
  • Fuction Calling也没有规定AI应用程序具体如何调用工具,所以需要每个AI应用自行实现对工具的调用
  • 没有Fuction Calling能力的大模型并非不能调用外部工具。我们可以自定义系统提示词,让大模型来调用外部工具,比如以下系统提示词

    # 你的角色
    你是一个函数调用助手,我将提供多个函数的定义信息,包括函数名称、作用、参数及参数类型。
    
    # 你的任务
    - 根据用户的输入,判断是否需要调用某个函数  
    - 如果需要,请**严格按照以下格式**输出函数调用指令:
    ```json
    { "name": "函数名", "arguments": { "参数名": "参数值" } }
    ```
    
    # 函数定义信息
    1. **get_weather**  
       - 作用:查询指定城市的天气情况  
       - 参数:  
         -`city`(string):城市名称
    2. **get_time**  
       - 作用:查询指定城市的当前时间  
       - 参数:  
         - `city`(string):城市名称

    这样当用户提问:广州的天气怎么样?,模型会根据系统提示词以指定格式返回要调用的工具及参数

    { "name": "get_weather", "arguments": { "city": "广州" } 

    只不过这种调用工具的实现方式有以下缺点:

    • 输出格式不稳定。如调用指令中存在多余自然语言
    • 容易出现幻觉。模型可能编造并不存在的函数名或参数
    • 对开发者依赖度高。函数描述、调用指令格式、提示词逻辑完全由开发者设计
    • 上下文冗长,Token 消耗大。为确保调用逻辑正确,往往需要在 system prompt 中加入大量说明与规则

为什么要做可观测?—— 解决“不可见”带来的业务风险

在 Taro 开发的支付宝小程序中,可观测性(Observability)的本质是通过数据化、实时化的监控手段,让小程序的运行状态、用户行为及系统交互“可见、可分析、可追溯”。其必要性源于小程序在实际运行中面临的三大核心挑战:

1、“黑盒化”运行环境:问题难以感知,故障发现滞后

小程序部署在支付宝客户端及云端环境中,开发者无法直接接触用户设备的真实运行状态。当出现以下问题时,若无可观测能力,往往只能依赖用户反馈或事后投诉。

典型案例:某电商小程序上线后,用户投诉“提交订单按钮点击无效”,但开发团队通过日志仅能看到“按钮渲染成功”,因缺乏用户操作路径和接口调用的关联数据,耗时 3 天才定位到是“异步数据未加载完成时按钮未禁用”的逻辑缺陷。若提前部署可观测能力,可通过用户行为流+接口状态实时发现该问题。

2、业务依赖复杂:跨端、跨服务的故障定位成本高

Taro 小程序通常依赖多方服务:前端通过 Taro 框架调用支付宝小程序 API(如支付、登录)、与后端服务交互(如订单查询、用户数据同步),甚至涉及第三方服务(如地图 SDK、营销活动平台)。任何一个环节异常都可能导致整体功能失效,但问题根源可能隐藏在任意一层,若无统一的观测数据(如接口调用链、用户操作路径、错误上下文),排查效率极低,可能导致用户流失或业务损失。

3、用户体验敏感:微小问题可能引发大规模负面反馈

支付宝小程序的用户对体验的容忍度极低——页面加载慢 1 秒可能导致 5% 的用户流失,支付流程卡顿可能直接引发投诉。但用户通常不会详细描述问题(如“首屏渲染慢是因为图片未压缩”),而是给出模糊反馈(“用不了”“太卡了”)。若无可观测能力,开发者只能通过“猜测+灰度测试”被动优化,无法精准识别影响用户体验的关键节点。

这些问题的解决依赖于对用户行为、性能指标的实时监控,而非事后统计。

把观测云作为 Taro 支付宝小程序可观测最佳实践的核心工具--落地路径与关键动作

  • 将观测云定位为小程序的“统一可观测平台”,以统一标签(如 service、env、version)贯穿指标、日志、链路、用户访问四类数据,形成端到端的观测视图。
  • 在 Taro 多端(含支付宝小程序)场景中,前端接入 RUM(用户访问监测),后端与基础设施接入 Logging/Tracing/Metrics,实现跨端、跨服务的统一观测与关联。
  • 通过“Dashboard + Explorer”构建业务与技术双视角的可观测体系,既看得到用户体验,也定位得到根。

前端 RUM 接入(Taro 支付宝小程序)

本最佳实践实验版本为 Taro v4.1.7

查看编译环境
➜  ~ taro --version 
? Taro v4.1.7
4.1.7
➜  ~ 

1、观测云后台->用户访问监测->新建应用,选择应用类型为小程序,输入应用名称、应用ID,系统会自动根据配置信息生成引入 SDK 的代码,可选择 NPM 接入或 CDN 文件接入(这里我们选择 CDN 文件方式接入)。

  • 应用名称:用于识别当前用户访问监测的应用名称。
  • 应用 ID :应用在当前工作空间唯一标识,对应字段:app_id 。

图片

2、根据链接下载 CDN 文件,打开项目代码,在小程序项目中的 app.ts 入口文件引入观测云后台生成的配置代码。

注意项目中引入代码的位置,必须要在 App() 初始化之前,否则会出现数据无法正常采集上报,或者只能上报一小部分数据的情况。

图片

3、本地运行项目或者打包发布测试后,通过观察接口调用发现 /v1/write/rum 接口成功调用,说明数据成功上报,可在对应应用的查看器查看上报数据。

图片

4、js 会自动生成一个 trace_id,如果在 allowedTracingOrigins 配置了正则(本最佳实践中配置的是 *,意味着所有接口都会带 ),会在对应的请求头中加入一些参数,ddtrace 的是这几个参数。

图片

实现效果

1、小程序部署在支付宝客户端及云端环境中,开发者直接接触用户设备的真实运行状态
进入「用户访问监测」页面—> 选择创建的对应微信小程序—> 分析看板,可以查看小程序运行中的部分重点信息。

图片

进入「用户访问监测」页面—> 选择创建的对应微信小程序—> 查看器,可以查看小程序应用中用户访问的动作,请求资源以及项目中的报错信息等。

图片

2、多方服务全链路打通

图片

3、后端服务透明化,可以观测到更详细的代码级方法,开发可以快速定位

图片

图片

4、用户体验可视化,全局可观测

图片

图片

图片

观测云为 Taro 支付宝小程序创建一份“数字神经系统”

在 Taro 开发的小程序中,可观测性不是“可选能力”,而是保障业务健康增长的必备基础设施。它通过将“不可见的运行状态”转化为“可见的数据洞察”,帮助开发者:

  • 从被动救火到主动预防(快速发现问题,降低故障损失);
  • 从经验驱动到数据驱动(精准优化体验,提升转化效率);
  • 从局部优化到全局可控(支撑规模化增长,保障业务稳定性);
  • 从单点协作到团队协同(提升研发效率,加速产品迭代)。

PHP Standard Library (PSL) 5.0 正式发布。作为 PHP 社区中专注于类型安全和异步编程的标准库,这次更新在架构上进行了大规模重构,引入了包括加密、二进制处理、网络栈重写在内的多个组件。

image.png
由于 PSL 5.0 明确要求 PHP 8.4+ 版本,开发者在本地调试时可能会遇到环境限制。如果需要快速搭建 PHP 8.4 环境,可以使用 ServBay。

ServBay 支持多个 PHP 版本同时运行,能够一键安装 PHP 环境,并且能随时切换,方便在不影响现有项目的前提下测试 PSL 5.0 的新特性。

image.png

强类型数据校验

PSL 的类型组件不依赖反射,而是通过组合子的方式验证数据。这在处理不可信的外部输入时,能够确保数据符合预期的结构。

use Psl\Type;

// 定义一套用户信息校验规则
$schema = Type\shape([
    'id'     => Type\positive_int(),
    'email'  => Type\non_empty_string(),
    'active' => Type\bool(),
    'meta'   => Type\optional(Type\dict(Type\string(), Type\mixed())),
]);

// 校验并获得类型完备的数据
$validatedData = $schema->coerce($inputPayload);

结构化并发模型

PSL 5.0 继续深化基于 Fiber 的并发模型。开发者可以像编写同步代码一样处理异步任务,避开了传统回调或 Promise 嵌套带来的复杂性。

use Psl\Async;
use Psl\TCP;
use Psl\IO;

Async\main(static function(): int {
    // 并发执行多个网络请求
    [$clientA, $clientB] = Async\concurrently([
        static fn() => TCP\connect('service-a.internal', 8000),
        static fn() => TCP\connect('service-b.internal', 9000),
    ]);

    IO\write_error_line('所有连接均已建立成功');

    return 0;
});

函数式集合操作

针对 PHP 原生数组在索引和关联类型上的模糊定义,PSL 提供了 Vec(列表)和 Dict(字典)组件。这些组件通过纯函数处理数据,返回类型更加明确。

use Psl\Vec;
use Psl\Dict;
use Psl\Str;

$users = ['nick', 'john', 'alice'];

// 统一转为大写
$upperNames = Vec\map($users, Str\uppercase(...));

// 过滤掉长度不足的名称
$filtered = Vec\filter($users, fn($u) => Str\length($u) >= 4);

// 构建键值对映射
$mapping = Dict\pull($users, fn($u) => Str\reverse($u), fn($u) => $u);

生产级网络原语

PSL 5.0 重写了底层的网络栈。无论是 TCP、UDP 还是 Unix Socket,所有的网络操作都支持异步非阻塞模式,并且提供了更加安全的 TLS 支持。

use Psl\Async;
use Psl\TCP;
use Psl\IO;

Async\main(static function(): int {
    $socket = TCP\listen('0.0.0.0', 9001);
    IO\write_error_line('服务器已在 9001 端口启动');

    while ($connection = $socket->accept()) {
        Async\run(static function() use ($connection) {
            $connection->writeAll("Welcome to PSL Server\n");
            $connection->close();
        })->ignore();
    }
});

全功能工业级加密库

新版本引入了基于 libsodium 的加密组件,涵盖了对称与非对称加密、数字签名以及密钥派生等功能。这些 API 的设计遵循了“难以误用”的原则。

use Psl\Crypto\Symmetric;

// 快速生成密钥并进行数据加密
$key = Symmetric\generate_key();
$secretMessage = Symmetric\seal('需要保护的原始数据', $key);

// 解密还原数据
$original = Symmetric\open($secretMessage, $key);

PSL 5.0 的发布为 PHP 开发者提供了一套更严谨、更具现代感的底层工具链。开发者可以低成本地将这些新技术应用到实际的研发工作中。

把 AI agent 的逻辑拆分到多个独立运行的服务中,听起来复杂做起来也确实容易乱。LangGraph 的 RemoteGraph 特性算是一个干净的方案:本地编排器负责流程控制,远程图服务器承担具体计算,状态管理和控制流的职责边界清晰。

本文要构建的项目是一个循环数学引擎:本地图编排一个远程图:随机选择数学运算和生成随机数。编排器会以两种方式实现——顺序执行和并行执行——以便对比两者的取舍,方便根据场景选择合适的模式。循环持续运行,直到远程图返回

end

架构概览

一个 math_service 远程图负责两种操作,本地 math_orchestrator 在每次迭代中调用它两次,每种操作各一次。下面分别是顺序版本和并行版本的编排器结构:

顺序流——远程图被依次调用两次:

并行流——远程图用 fan-out/fan-in 模式同时被调用两次:

math_service 是远程图,接受

action

字段:

"pick_operation"

返回一个随机数学运算或

end

"generate_number"

返回一个随机整数。

math_orchestrator 是本地图,接受初始数字后每次迭代调用远程图两次(分别传入不同 action),执行数学运算,operation 为

end

时终止。

环境准备

uv/pyproject.toml 配置如下:

 [project]  
name = "langgraph-random-math"  
version = "0.1.0"  
description = "Add your description here"  
requires-python = "==3.13"  
dependencies = [  
    "langgraph",  
    "langgraph-cli",  
    "langgraph-sdk"  
 ]

截至本文编写时 pydantic 与 Python 3.14 及更高版本不兼容,所以这里用 3.13。

两个图都在本地运行——远程图跑在 LangGraph 开发服务器上,本地图作为普通 Python 脚本执行。

步骤 1:math_service——远程图

远程图用条件路由在单个图中处理两种操作。传入状态的

action

字段决定路由方向:

pick_operation

generate_number

创建远程服务目录结构:

 math_service/  
 ├── auth.py  
 ├── graph.py  
 ├── langgraph.json  
 └── .env

math_service/graph.py

 import random  
 from typing import TypedDict  
 from langgraph.graph import StateGraph, START, END
 class MathServiceState(TypedDict):  
     action: str                # "pick_operation" or "generate_number"  
     operation: str             # 结果: "add", "subtract", "multiply", "divide", 或 "end"  
     number: int                # 结果: 随机整数  
     manual_input_chance: float # 请求用户输入的概率 (0.0-1.0)  
     ask_user: bool             # 结果: True = 编排器应提示用户  
 def route_action(state: MathServiceState) -> str:  
     """根据 action 字段路由到相应的节点。"""  
     if state["action"] == "pick_operation":  
         return "pick_operation"  
     elif state["action"] == "generate_number":  
         return "generate_number"  
     else:  
         raise ValueError(f"Unknown action: {state['action']}")  
 def pick_operation(state: MathServiceState) -> dict:  
     """随机选择一个数学运算或 'end' 来停止循环。"""  
     operations = ["add", "subtract", "multiply", "divide", "end"]  
     # 'end' 有 10% 的概率;剩余 90% 在数学运算之间平均分配  
     weights = [9, 9, 9, 9, 4]  
     chosen = random.choices(operations, weights=weights, k=1)[0]  
     return {"operation": chosen}  
 def generate_number(state: MathServiceState) -> dict:  
    """  
    生成随机数或请求手动用户输入。  

    根据 manual_input_chance 进行掷骰:如果结果低于阈值,  
    返回 ask_user=True(不生成数字——编排器应提示用户)。  
    否则,生成并返回一个随机整数。  
    """  
    chance = state.get("manual_input_chance", 0.0)  

    if chance > 0.0 and random.random() < chance:  
        return {"ask_user": True}  

     return {"number": random.randint(1, 20), "ask_user": False}  
 builder = StateGraph(MathServiceState)  
builder.add_node("pick_operation", pick_operation)  
builder.add_node("generate_number", generate_number)  

# 基于 action 字段从 START 进行条件路由  
builder.add_conditional_edges(  
    START,  
    route_action,  
    {  
        "pick_operation": "pick_operation",  
        "generate_number": "generate_number",  
    },  
)  

builder.add_edge("pick_operation", END)  
builder.add_edge("generate_number", END)  

 graph = builder.compile()

math_service/auth.py

 import os  
from langgraph_sdk import Auth  

auth = Auth()  

# 在生产环境中,请使用正规的 JWT 验证库。  
# 此示例使用简单的 token 查找以保持清晰。  
VALID_TOKENS = {  
    os.environ.get("MATH_SERVICE_TOKEN", "dev-token"): {  
        "id": "orchestrator",  
        "name": "Math Orchestrator",  
    },  
 }  
 @auth.authenticate  
async def authenticate(authorization: str | None) -> Auth.types.MinimalUserDict:  
    """验证 Authorization 头中的 Bearer token。"""  
    if not authorization:  
        raise Auth.exceptions.HTTPException(  
            status_code=401, detail="Missing authorization header"  
        )  

    try:  
        scheme, token = authorization.split(" ", 1)  
    except ValueError:  
        raise Auth.exceptions.HTTPException(  
            status_code=401, detail="Invalid authorization format"  
        )  

    if scheme.lower() != "bearer" or token not in VALID_TOKENS:  
        raise Auth.exceptions.HTTPException(  
            status_code=401, detail="Invalid token"  
        )  

    user = VALID_TOKENS[token]  
    return {  
        "identity": user["id"],  
        "is_authenticated": True,  
     }  
 @auth.on  
async def authorize_all(ctx: Auth.types.AuthContext, value: dict):  
    """允许已认证用户执行所有操作。  

    在更复杂的设置中,你可以通过检查 value 载荷来限制  
    每个调用者可以调用哪些操作(pick_operation vs generate_number)。  
    """  
     return None  # None = 允许,不添加额外过滤

这是一个最小认证层:检查 Bearer token 有效性,对已认证的调用者放行所有操作。生产环境中应当用 JWT 验证(

PyJWT

、Auth0 等)替代 token 查表,并按需增加操作级别的授权。

generate_number

节点内部完成决策——根据

manual_input_chance

掷骰后,要么生成数字,要么置

ask_user=True

。编排器检查这个标志并在需要时于本地提示用户。决策逻辑留在服务端,用户交互留在客户端,正是微服务中典型的职责划分方式。

math_service/langgraph.json:

 {  
  "dependencies": ["."],  
  "graphs": {  
    "math_service": "./graph.py:graph"  
  },  
  "auth": {  
    "path": "./auth.py:auth"  
  },  
  "env": ".env"  
 }

创建

.env

文件写入服务 token:

 MATH_SERVICE_TOKEN=dev-token

在端口 2024 启动服务器:

 cd math_service  
 langgraph dev --port 2024 --no-browser

可以针对运行中的服务器测试两种操作:

 from langgraph.pregel.remote import RemoteGraph  

# 不带 token — 应该返回 401 失败  
try:  
    bad_service = RemoteGraph("math_service", url="http://localhost:2024")  
    bad_service.invoke({  
        "action": "pick_operation", "operation": "", "number": 0,  
        "manual_input_chance": 0.0, "ask_user": False,  
    })  
    print("❌ Should have failed without token!")  
except Exception as e:  
    print(f"✅ Correctly blocked: {e}")  

# 带有效 token — 应该成功  
service = RemoteGraph(  
    "math_service",  
    url="http://localhost:2024",  
    headers={"Authorization": "Bearer dev-token"},  
)  

# 测试: 选择一个运算  
result = service.invoke({  
    "action": "pick_operation", "operation": "", "number": 0,  
    "manual_input_chance": 0.0, "ask_user": False,  
})  
print(result["operation"])  # 例如 'multiply'  

# 测试: 生成一个数字(自动模式)  
result = service.invoke({  
    "action": "generate_number", "operation": "", "number": 0,  
    "manual_input_chance": 0.0, "ask_user": False,  
})  
print(result["number"], result["ask_user"])  # 例如 14, False  

# 测试: 生成一个数字(始终询问用户)  
result = service.invoke({  
    "action": "generate_number", "operation": "", "number": 0,  
    "manual_input_chance": 1.0, "ask_user": False,  
})  
 print(result["ask_user"])  # True — 编排器应提示用户

步骤 2:本地编排器图

接下来构建编排器,分顺序和并行两个版本以便对照。两者共享状态定义、远程图连接和节点函数,全部抽取到公共模块

shared.py

中。差异只在图的边如何连接。

目录结构:

 math_orchestrator/  
 ├── shared.py  
 ├── shared_resilient.py  
 ├── orchestrator_sequential.py  
 ├── orchestrator_parallel.py  
 └── orchestrator_parallel_resilient.py

math_orchestrator/shared.py 公共状态、连接和节点

 import os  
 from typing import TypedDict, Annotated  
 import operator  
 from langgraph.pregel.remote import RemoteGraph  
 # --- 状态定义 ---  

class OrchestratorState(TypedDict):  
    current_number: float  
    operation: str  
    random_number: int  
    history: Annotated[list[str], operator.add]  
     manual_input_chance: float  # 0.0 = 始终远程, 1.0 = 始终手动  
 # --- 连接远程图 ---  
# 单个远程图处理两种操作。  
# 认证 token 从环境变量加载。  

math_service = RemoteGraph(  
    "math_service",  
    url=os.environ.get("MATH_SERVICE_URL", "http://localhost:2024"),  
    headers={"Authorization": f"Bearer {os.environ.get('MATH_SERVICE_TOKEN', '')}"},  
 )  
 # --- 节点函数 ---  

def build_initial_state(current_number: float, manual_input_chance: float) -> dict:  
    """构建 graph.invoke() 的初始状态字典。"""  
    if manual_input_chance == 0.0:  
        mode = "automatic"  
    elif manual_input_chance == 1.0:  
        mode = "manual"  
    else:  
        mode = f"mixed ({int(manual_input_chance * 100)}% manual)"  

    return {  
        "current_number": current_number,  
        "operation": "",  
        "random_number": 0,  
        "history": [f"Starting number: {current_number} (mode: {mode})"],  
        "manual_input_chance": manual_input_chance,  
     }  
 def get_operation(state: OrchestratorState) -> dict:  
    """使用 action='pick_operation' 调用 math_service。"""  
    result = math_service.invoke({  
        "action": "pick_operation",  
        "operation": "",  
        "number": 0,  
        "manual_input_chance": 0.0,  
        "ask_user": False,  
    })  
    op = result["operation"]  
    print(f"  → Operation: {op}")  
     return {"operation": op}  
 def _prompt_user_number(state: OrchestratorState) -> int:  
    """通过 stdin 提示用户输入一个数字。"""  
    op = state.get("operation", "?")  
    current = state.get("current_number", 0)  
    while True:  
        raw = input(  
            f"  Current: {current} | Operation: {op} | Enter a number: "  
        )  
        try:  
            return int(raw)  
        except ValueError:  
             print("  Please enter a valid integer.")  
 def get_random_number(state: OrchestratorState) -> dict:  
    """  
    获取数学运算中要使用的下一个数字。  

    使用 action='generate_number' 调用 math_service,同时传递  
    manual_input_chance。远程图决定是生成一个数字还是请求  
    手动输入(通过 ask_user 标志)。  
    如果 ask_user 为 True,编排器在本地提示用户。  
    """  
    chance = state.get("manual_input_chance", 0.0)  

    result = math_service.invoke({  
        "action": "generate_number",  
        "operation": "",  
        "number": 0,  
        "manual_input_chance": chance,  
        "ask_user": False,  
    })  

    if result.get("ask_user"):  
        num = _prompt_user_number(state)  
        print(f"  → Number: {num} (manual)")  
        return {"random_number": num}  

    num = result["number"]  
    print(f"  → Number: {num}")  
     return {"random_number": num}  
 def execute_operation(state: OrchestratorState) -> dict:  
    """对 current_number 执行数学运算。"""  
    current = state["current_number"]  
    op = state["operation"]  
    num = state["random_number"]  

    if op == "add":  
        new_number = current + num  
        symbol = "+"  
    elif op == "subtract":  
        new_number = current - num  
        symbol = "-"  
    elif op == "multiply":  
        new_number = current * num  
        symbol = "×"  
    elif op == "divide":  
        if num == 0:  
            num = 1  
        new_number = round(current / num, 2)  
        symbol = "÷"  
    else:  
        new_number = current  
        symbol = "?"  

    entry = f"  {current} {symbol} {num} = {new_number}"  
    print(entry)  

    return {  
        "current_number": new_number,  
        "history": [entry],  
     }
get_operation

get_random_number

调用的是同一个

math_service

,只是传入不同的

action

值。编排器视角下,远程图是一个支持多种操作的单一端点。

下面看两种不同的图连接方式。每个编排器文件都很简短——业务逻辑全在

shared.py

里,编排器文件只关心拓扑结构。

顺序执行

math_orchestrator/orchestrator_sequential.py

顺序版本先调用

get_operation

,拿到

end

就直接终止,无需再去取随机数。非

end

的情况下继续调用

get_random_number

execute_operation

,然后循环回来。

 import argparse  
from langgraph.graph import StateGraph, START, END  
from shared import (  
    OrchestratorState,  
    build_initial_state,  
    get_operation,  
    get_random_number,  
    execute_operation,  
 )  
 # --- 路由逻辑 ---  
   
 def should_continue(state: OrchestratorState) -> str:  
     """获取操作后,决定:继续还是停止。"""  
     if state.get("operation") == "end":  
         return "finish"  
     return "continue"  
 # --- 构建图 ---  

builder = StateGraph(OrchestratorState)  

# 添加节点  
builder.add_node("get_operation", get_operation)  
builder.add_node("get_random_number", get_random_number)  
builder.add_node("execute_operation", execute_operation)  

# 定义边 — 顺序链  
builder.add_edge(START, "get_operation")  

# 获取操作后,决定:继续还是结束?  
builder.add_conditional_edges(  
    "get_operation",  
    should_continue,  
    {  
        "continue": "get_random_number",  
        "finish": END,  
    },  
)  

builder.add_edge("get_random_number", "execute_operation")  

# 执行后,循环回到 get_operation  
builder.add_edge("execute_operation", "get_operation")  

# 编译  
 graph = builder.compile()  
 # --- 运行 ---  

if __name__ == "__main__":  
    parser = argparse.ArgumentParser()  
    parser.add_argument(  
        "--start-number", type=float, default=None,  
        help="Initial number to start with (prompts if not provided)",  
    )  
    parser.add_argument(  
        "--manual-input", action="store_true",  
        help="Always prompt user for numbers (shorthand for --manual-input-chance 1.0)",  
    )  
    parser.add_argument(  
        "--manual-input-chance", type=float, default=0.0,  
        help="Probability (0.0-1.0) of prompting user for each number (default: 0.0)",  
    )  
    args = parser.parse_args()  

    start = args.start_number  
    if start is None:  
        start = float(input("Enter starting number: "))  

    chance = 1.0 if args.manual_input else args.manual_input_chance  

    result = graph.invoke(build_initial_state(  
        current_number=start,  
        manual_input_chance=chance,  
    ))  

    print("\n🧮 Math Engine Complete! (Sequential)\n")  
    print("Computation History:")  
    for entry in result["history"]:  
        print(entry)  
     print(f"\nFinal Result: {result['current_number']}")

顺序流的好处是逻辑直白,而且最后一次迭代拿到

end

时可以直接跳过取随机数的调用,省掉一次无用的 HTTP 请求。代价是每轮迭代的两次远程调用必须串行,一个等另一个。

并行执行

math_orchestrator/orchestrator_parallel.py

并行版本利用 LangGraph 的 fan-out/fan-in 模式。

get_operation

get_random_number

在同一个 superstep 中同时执行,两者都完成后

execute_operation

再决定是继续 fan-out 还是终止。

 import argparse  
from langgraph.graph import StateGraph, START, END  
from shared import (  
    OrchestratorState,  
    build_initial_state,  
    get_operation,  
    get_random_number,  
    execute_operation,  
 )  
 # --- 路由逻辑 ---  

def should_continue(state: OrchestratorState) -> list[str] | str:  
    """  
    决定是继续循环还是停止。  
    返回节点名称列表用于 fan-out(并行),  
    或返回 END 以终止。  
    """  
    if state.get("operation") == "end":  
        return END  
    # Fan-out: 并行路由到两个节点  
     return ["get_operation", "get_random_number"]  
 # --- 构建图 ---  

builder = StateGraph(OrchestratorState)  

# 添加节点  
builder.add_node("get_operation", get_operation)  
builder.add_node("get_random_number", get_random_number)  
builder.add_node("execute_operation", execute_operation)  

# 定义边  
# Fan-out: START 并行发送到两个节点  
builder.add_edge(START, "get_operation")  
builder.add_edge(START, "get_random_number")  

# Fan-in: 两个节点都必须完成后 execute_operation 才能运行  
builder.add_edge("get_operation", "execute_operation")  
builder.add_edge("get_random_number", "execute_operation")  

# 执行后,决定:再次 fan-out,还是结束  
builder.add_conditional_edges(  
    "execute_operation",  
    should_continue,  
    ["get_operation", "get_random_number", END],  
)  

# 编译  
 graph = builder.compile()  
 # --- 运行 ---  

if __name__ == "__main__":  
    parser = argparse.ArgumentParser()  
    parser.add_argument(  
        "--start-number", type=float, default=None,  
        help="Initial number to start with (prompts if not provided)",  
    )  
    parser.add_argument(  
        "--manual-input", action="store_true",  
        help="Always prompt user for numbers (shorthand for --manual-input-chance 1.0)",  
    )  
    parser.add_argument(  
        "--manual-input-chance", type=float, default=0.0,  
        help="Probability (0.0-1.0) of prompting user for each number (default: 0.0)",  
    )  
    args = parser.parse_args()  

    start = args.start_number  
    if start is None:  
        start = float(input("Enter starting number: "))  

    chance = 1.0 if args.manual_input else args.manual_input_chance  

    result = graph.invoke(build_initial_state(  
        current_number=start,  
        manual_input_chance=chance,  
    ))  

    print("\n🧮 Math Engine Complete! (Parallel)\n")  
    print("Computation History:")  
    for entry in result["history"]:  
        print(entry)  
     print(f"\nFinal Result: {result['current_number']}")

运行结果

打开两个终端窗口:

终端 1——启动远程 math_service(如果此前没启动的话):

 cd math_service  
 langgraph dev --port 2024 --no-browser

终端 2——运行编排器(任选其一):

 cd math_orchestrator  

# 选项 A: 顺序 — 提示输入起始数字  
python orchestrator_sequential.py  

# 选项 B: 并行 — 通过参数指定起始数字  
python orchestrator_parallel.py --start-number 100  

# 任何选项配合手动输入模式 — 每次提示你输入数字:  
python orchestrator_sequential.py --start-number 100 --manual-input  

# 混合模式 — 每次迭代有 50% 的概率提示你:  
 python orchestrator_sequential.py --start-number 100 --manual-input-chance 0.5

一次典型运行的输出如下:

   → Operation: subtract  
  → Number: 10  
  100.0 - 10 = 90.0  
  → Operation: subtract  
  Current: 90.0 | Operation: subtract | Enter a number: 1  
  → Number: 1 (manual)  
  90.0 - 1 = 89.0  
  → Operation: add  
  Current: 89.0 | Operation: add | Enter a number: 1  
  → Number: 1 (manual)  
  89.0 + 1 = 90.0  
  → Operation: divide  
  → Number: 17  
  90.0 ÷ 17 = 5.29  
  → Operation: add  
  → Number: 5  
  5.29 + 5 = 10.29  
  → Operation: divide  
  Current: 10.29 | Operation: divide | Enter a number: 1  
  → Number: 1 (manual)  
  10.29 ÷ 1 = 10.29  
  → Operation: subtract  
  Current: 10.29 | Operation: subtract | Enter a number: 1  
  → Number: 1 (manual)  
  10.29 - 1 = 9.29  
  → Operation: multiply  
  Current: 9.29 | Operation: multiply | Enter a number: 1  
  → Number: 1 (manual)  
  9.29 × 1 = 9.29  
  → Operation: multiply  
  Current: 9.29 | Operation: multiply | Enter a number: 2  
  → Number: 2 (manual)  
  9.29 × 2 = 18.58  
  → Operation: multiply  
  → Number: 10  
  18.58 × 10 = 185.79999999999998  
  → Operation: end  

🧮 Math Engine Complete! (Sequential)  

Computation History:  
Starting number: 100.0 (mode: mixed (50% manual))  
  100.0 - 10 = 90.0  
  90.0 - 1 = 89.0  
  89.0 + 1 = 90.0  
  90.0 ÷ 17 = 5.29  
  5.29 + 5 = 10.29  
  10.29 ÷ 1 = 10.29  
  10.29 - 1 = 9.29  
  9.29 × 1 = 9.29  
  9.29 × 2 = 18.58  
  18.58 × 10 = 185.79999999999998  

 Final Result: 185.79999999999998

运算和数字都由远程服务随机生成,每次运行的结果不同。

从控制台到生产环境:使用 interrupt

input()

适合本地脚本调试。到了生产环境——编排器可能藏在 REST API、Web UI 或聊天界面后面——没有控制台可用。LangGraph 对此有一个一等原语:

interrupt

机制不复杂:节点调用

interrupt()

时 LangGraph 暂停整个图,将完整状态写入 checkpoint,然后把控制权交还给调用方。调用方(API 服务、Web 应用等)拿到暂停信号后向用户展示提示,收到响应后用

Command(resume=...)

恢复执行。图从

interrupt()

调用处精确恢复,哪怕过了几个小时、换了一台机器也没问题。

以下是用

interrupt

替换

input()

get_random_number

的写法:

math_orchestrator/shared_interrupt.py(相关摘录)

 import sqlite3  
 from langgraph.types import interrupt, Command  
 from langgraph.checkpoint.sqlite import SqliteSaver  
 from langgraph.pregel.remote import RemoteGraph  
 math_service = RemoteGraph(  
     "math_service",  
     url="http://localhost:2024",  
 )  
 def get_random_number(state):  
    """  
    获取下一个数字 — 通过远程图或人工中断。  

    当远程图返回 ask_user=True 时,我们不调用 input(),  
    而是调用 interrupt(),它会暂停整个图并向调用应用程序  
    呈现一个提示。  
    """  
    chance = state.get("manual_input_chance", 0.0)  

    result = math_service.invoke({  
        "action": "generate_number",  
        "operation": "",  
        "number": 0,  
        "manual_input_chance": chance,  
        "ask_user": False,  
    })  

    if result.get("ask_user"):  
        # 暂停图 — 调用者接收此提示  
        num = interrupt({  
            "prompt": "Enter a number",  
            "current_number": state.get("current_number"),  
            "operation": state.get("operation"),  
        })  
        print(f"  → Number: {num} (manual)")  
        return {"random_number": int(num)}  

    num = result["number"]  
    print(f"  → Number: {num}")  
     return {"random_number": num}

编译图时必须附带 checkpointer(状态持久化),调用时必须指定 thread_id(标识具体会话):

 # 带 checkpointer 编译  
checkpointer = SqliteSaver(sqlite3.connect("math_engine.db"))  
graph = builder.compile(checkpointer=checkpointer)  

# 启动新线程  
config = {"configurable": {"thread_id": "session-42"}}  

# 首次调用 — 运行直到 interrupt() 被调用  
result = graph.invoke(  
    build_initial_state(current_number=100, manual_input_chance=1.0),  
    config=config,  
)  

# 图现在已暂停。result["__interrupt__"] 包含提示:  
# [Interrupt(value={"prompt": "Enter a number", "current_number": 100, ...})]  

# ... 时间流逝,用户通过 Web UI、API 等提供输入 ...  

# 使用用户的值恢复  
result = graph.invoke(Command(resume=42), config=config)  

# 图从 interrupt() 被调用的地方继续执行,  
 # num = 42,并运行直到下一个 interrupt 或 END。

几个要点。

interrupt()

input()

目的相同,区别在于前者走 HTTP 而非 stdin——传入一个 payload(提示、上下文等),调用方通过

Command(resume=...)

回传用户输入。checkpointer 负责持久化图状态,LangGraph 支持 SQLite、Postgres 等多种后端。thread_id 用来标识会话,多个用户可以各自持有独立的暂停/运行中的图实例。远程图和图的连接方式无需任何改动,变化只发生在节点函数和调用模式上。

保护线程:认证与授权

如果 thread ID 是保护暂停会话的唯一手段,任何猜中或截获了 thread ID 的人都能恢复别人的图、注入自己的值。LangGraph Platform 对此有内置的认证与授权层。

认证系统分两步走。

@auth.authenticate

处理程序作为中间件在每个请求上运行,验证调用者的凭据(JWT token、API 密钥、OAuth2 等)并返回用户身份;

@auth.on

处理程序执行资源级访问控制,给每个线程打上所有者标记,过滤访问权限,确保用户只能看到和恢复自己的线程。

线程级安全的实现如下:

 from langgraph_sdk import Auth  

auth = Auth()  

@auth.authenticate  
async def authenticate(authorization: str) -> Auth.types.MinimalUserDict:  
    """验证 Bearer token 并返回用户信息。"""  
    token = authorization.split(" ", 1)[1]  
    user = await verify_jwt(token)  # 你的 JWT 验证逻辑  
    return {  
        "identity": user["sub"],  
        "is_authenticated": True,  
    }  

@auth.on.threads.create  
async def on_thread_create(  
    ctx: Auth.types.AuthContext,  
    value: Auth.types.on.threads.create.value,  
):  
    """为每个新线程标记创建者的身份。  

    `value` 是线程创建载荷 — 一个包含来自 API 请求的字段的字典:  
    thread_id, metadata, if_exists 等。  
    我们修改 value["metadata"] 以在存储之前标记所有者。  
    返回值是 LangGraph 写入线程 metadata 的元数据过滤器。  
    """  
    value.setdefault("metadata", {})["owner"] = ctx.user.identity  
    return {"owner": ctx.user.identity}  

@auth.on.threads.read  
async def on_thread_read(  
    ctx: Auth.types.AuthContext,  
    value: Auth.types.on.threads.read.value,  
):  
    """过滤线程,使用户只能看到自己的。  

    `value` 是读取请求载荷 — 一个包含 thread_id 和来自  
    API 请求的任何 metadata 的字典。  

    返回值不是检查 — 而是查询过滤器。  
    LangGraph 在存储层应用它:只有 metadata.owner 与  
    ctx.user.identity 匹配的线程才会被返回。  
    其他用户拥有的线程是不可见的,而不仅仅是被阻止。  
    """  
    return {"owner": ctx.user.identity}  

@auth.on.threads.create_run  
async def on_thread_resume(  
    ctx: Auth.types.AuthContext,  
    value: Auth.types.on.threads.create_run.value,  
):  
    """过滤用户可以在哪些线程上恢复运行。  

    `value` 是运行创建载荷 — 一个包含 thread_id, assistant_id,  
    input, command, metadata, config 等的字典。  

    相同的过滤器机制:LangGraph 仅在线程的 metadata.owner  
    与返回的过滤器匹配时才允许运行。如果用户尝试恢复  
    另一个用户的线程,平台会拒绝请求,因为该线程  
    未通过过滤器。  
    """  
    metadata = value.setdefault("metadata", {})  
    metadata["owner"] = ctx.user.identity  
     return {"owner": ctx.user.identity}

langgraph.json

中注册:

 {  
   "auth": {  
     "path": "src/security/auth.py:auth"  
   }  
 }

配置完成后,即使攻击者拿到了其他用户的 thread ID 也无法读取线程状态或恢复运行——授权处理程序会因为 owner metadata 不匹配而拒绝请求。过滤发生在平台层,不在图代码中,无法通过构造 API 请求绕过。

生产部署时 LangGraph Platform 可以对接 Auth0、Supabase 以及任何 OAuth2/JWT 认证体系。记住一点:thread ID 是标识符,不是密钥——安全保障来自认证层对访问权限的把控。

前面基于控制台

input()

的版本已经是这一模式的可运行原型。迁移到

interrupt

只需改动节点函数和调用模式,架构其余部分——远程图、fan-out/fan-in、错误处理——全部保持原样。

RemoteGraph 的工作原理

langgraph.pregel.remote

中的

RemoteGraph

类是整个组合能力的底层支撑。它实现的接口和本地编译的图完全一致,可以

.invoke()

.stream()

,也可以直接嵌入为另一个图的子图节点。通信通过 HTTP对接 LangGraph Server API。

 from langgraph.pregel.remote import RemoteGraph  

remote = RemoteGraph(  
    "math_service",          # 来自 langgraph.json 的 assistant ID  
    url="http://localhost:2024",  
)  

# 像使用普通图一样使用它 — action 字段控制行为  
 result = remote.invoke({"action": "pick_operation", "operation": "", "number": 0})
RemoteGraph

遵循

Runnable

接口,可以直接作为节点添加到另一个图中:

 builder.add_node("my_remote_node", remote_graph)

编排器图不需要了解远程图的内部实现细节——它可以是一个简单状态机、一个 LLM 驱动的 agent,或者任何介于两者之间的东西。而通过条件路由,一个远程图部署就能承载多种操作。

第二个调用依赖于第一个调用的结果时选顺序执行,或者想在最后一轮

end

时省掉无用的远程调用也该选顺序。两个调用互相独立、想缩短总耗时就选并行——在生产环境中远程图调用可能涉及 LLM 推理或数据库查询,并行执行差不多能把每轮延迟砍掉一半。

错误处理

并行执行带来一个自然的问题:远程图调用失败了怎么办?

math_service

临时挂掉\网络请求超时,这些情况都需要考虑。LangGraph 的处理提供了多种应对策略。

默认行为:原子 superstep

LangGraph 中并行节点在一个 superstep 里共同执行。superstep 中任何一个节点抛出异常,整个 superstep 原子性失败,不会有部分状态写入。假如

get_random_number

成功、

get_operation

失败,两边的结果都不会写入状态,避免了有随机数却没运算符这种不一致。

配了 checkpointer 的情况下 LangGraph 会在内部保存成功节点的结果。恢复执行时只有失败分支重试,成功分支的工作不必重复。

策略 1:RetryPolicy(图原生重试)

最干净的做法是对容易出错的节点附加

RetryPolicy

。LangGraph 接管重试循环,支持配置尝试次数、退避策略和抖动。只有失败分支重试,成功的并行节点不会重新执行。

重试全部耗尽后异常向上传播,图调用失败。对网络超时、5xx 错误这类瞬态故障,这是恰当的处理方式。

策略 2:节点内 Try/Except(降级处理)

需要图在远程服务不可用时仍然继续运行的场景下,在节点内部捕获异常并返回降级值即可。操作调用失败则引擎停止循环;数字生成调用失败则用安全默认值代替。

策略 3:两者结合(生产环境推荐)

生产中通常既要重试也要降级。

RetryPolicy

透明处理瞬态故障,重试全部耗尽后异常才落到节点内部的

try/except

块,由它提供兜底逻辑。

以下是三种策略的完整可运行示例。

总结

RemoteGraph 让分布式 agent 架构的组合变得相当简洁——顺序还是并行的连接方式随意切换,

RetryPolicy

加上节点内降级逻辑构成两层容错。单个远程图通过条件路由就能承载多种操作,基础设施不必铺得很大,编排逻辑也能保持清晰。

这个数学引擎作为 demo 虽然简单,展示的模式却可以直接迁移到正式系统——微服务化的 AI 编排,每个图作为一个独立部署的 agent 逻辑单元,根据延迟和成本需求选择合适的执行策略。

本文完整代码:

https://avoid.overfit.cn/post/d9102c5bf109459494a5bf2b99560b18

by Alexander Machekhin

OpenAI 最近在一篇 Blog 中说了件挺炸裂的事:他们自己的工程师,已经不怎么写代码了

在一个内部项目里,短短五个月,就产出了 100 万行代码,而且没一行手写,全都是 Codex写的。

这些代码并不是什么零散脚本,而是从零开始搭出来的一整套软件产品内部 Beta 版:从应用逻辑、基础设施,到工具、文档和内部开发者工具,几乎一应俱全。

这种变化,或许能从 OpenAI 内部一直以来的工程师文化中找到一些线索。

一位曾参与 Codex 项目的 OpenAI 工程师 Calvin French-Owen,在离职后写过一篇博客,虽然他在其中吐槽说,过去一年里 OpenAI 员工规模迅速扩张,带来了不少混乱。

不过他同时也提到,公司内部依然保留着很强的创业公司氛围:团队小、决策快,工程师拥有很高的自主权。

另外,很多科技巨头是高层定路线、然后团队执行,但在 OpenAI,通常没有明确的长期 roadmap,研究员往往自己发现问题、提出想法,小团队围绕好点子自然形成并推进项目。

他表示,真正推动进展的好想法可能随时从任何地方随时,而不是来自某个宏大计划。

“OpenAI 非常注重自下而上的方式,尤其是在研究方面。”

比如 Codex,最初其实诞生在 OpenAI 的一个只有十几人的小团队里。这个团队在 7 周内几乎不眠不休,把 Codex 从想法一路推到了上线。

而现在这个“OpenAI 工程师不写代码”一事,其实也要从公司一个团队,在开发流程中发现的新瓶颈说起。

现在 AI Coding 这件事已经屡见不鲜。但当 Codex 开始大规模生成代码后,OpenAI 的这研发团队很快发现一个新问题:

代码生成已经不慢了,慢的是让人类来检查这些代码。

人的时间和注意力是有限的。在整个开发流程里,最容易卡住的环节反而变成了 QA(质量测试)。

为了解决这个问题,OpenAI 的工程师换了个思路:干脆让 Codex 模仿工程师,自己去“看”和“用”应用

那 OpenAI 的工程师现在不写代码了,他们到底在做什么?

——设计环境、搭反馈循环、定义架构约束,然后让 agent 写。

文章中强调一句话:“人类掌舵,智能体执行”

他们管这叫 Harness Engineering,直译过来的话就是“AI 驾驭工程”。

##工程师变成“能力架构师”

这个项目始于 2025 年 8 月下旬,从向一个完全空白的代码仓库提交第一行内容开始。

初始架构,包括代码仓库结构、CI 配置、格式化规则、包管理器设置和应用框架——都不是工程师手写的,而是在一小套模板的指导下,由 Codex CLI 调用 GPT-5 自动生成

甚至连那份告诉 agent“该如何在这个仓库里工作”的 AGENTS.md,也是 Codex 自己写出来的。

换句话说,这个系统从诞生那刻起,就几乎没有人工代码。整个代码仓库,都是被 agent 一步一步搭起来的。

不过,一开始事情并没有想象中那么顺利:起初项目推进速度缓慢,但问题并不在 Codex 的能力,而在环境——规则不清晰、工具不完整、系统约束还没建立起来。

有网友“一针见血”道:

“最扎心的一句:agent 反复犯错,不是能力问题,是你脑子里的判断力没写下来。你不写,它第一百次还犯同样的蠢。”

于是再遇到开发卡住时,团队不再想着“再改一段代码试试”,而是先问一个问题:agent 到底缺什么能力?

再把这种能力变成它能读懂、能执行、还能被强制遵守的规则。

也就是说,面对当下 agent 自己就能测试、改 bug 的情形,工程师的工作重点从“写代码” 变成了另一件事:让 Codex 更容易把事情做对,给 agent“补能力”。

从这个角度看,工程师的工作其实转向了更高一层:用一句话来说,就是拆解任务、设计能力、搭建系统,让 agent 可以稳定地产生正确的代码。

具体来说,大致有这几件事情:

第一件事,就是让应用对 AI “可读”

正如上文提到的,需要人为把 agent 接入 Chrome DevTools 协议,让它能“触控”UI。

工程师要做的第二件事情,就是把“隐性知识”全部写进代码仓库, 变成机器可读的知识。

对 agent 而言,无法在运行时访问的内容就等于不存在。比如存储在 Google Docs、聊天记录或人们头脑中的知识吗,这些都无法被系统访问。

不过,不可以把所有规则和说明一次性塞给 Codex,而是要先给它一个导航,再让它自己去查细节。

研究研究团队曾尝试过直接给 agent 一个巨大的 AGENTS.md 文件,结果很快发现行不通。

主要原因是,上下文是稀缺资源,说明书越厚,真正重要的信息反而越容易被淹没;而且这种大文档很快就会过时,也很难验证和维护。

他们把这段经验总结成了一句:

“要给 Codex 的是一张地图,而不是一本 1000 页的说明书。”

该示意图由 AI 生成

工程师要做的第三件事,是设计“AI 友好”的架构。

AI 在结构清晰、边界明确的系统里效率最高。对人来说,这些规则可能显得死板,但对 agent 来说,这是效率倍增器。

所以 OpenAI 的这个团队设计了一套严格架构,每个业务域必须按固定层级:Types→ Config→ Repo→ Service→ Runtime→ UI。

依赖方向是强制的。任何违反都会被自动阻止。

第四件事,是把“品味”变成规则。

“在 AI 时代,人类最重要的能力是 Taste。”随着大模型越来越强,这样的声音不绝于耳。

这篇 Blog 中,有一个很有意思的概念:taste invariants(品味不变量)。

意思是,工程师的审美,比如:文件大小限制、命名规则、日志结构、API 规范等,都被写成 lint 规则

这样 AI 每次写代码都会自动遵守:“人类的品味一旦被捕捉,就可以应用到每一行代码。”

在实际开发中,人类主要通过提示与系统交互:描述任务、启动 agent,然后让 Codex 自动生成 Pull Request。

接下来的一整套流程,包括代码自检、agent 评审、根据反馈修改、再次提交,基本都由 agent 自己完成,并不断循环,直到所有评审通过。

第五件事,就是清理 AI 产生的“垃圾”。

文章指出,完全自主的智能体也引入了新的问题。

当代码几乎全部由 Codex 生成后,一个新问题也出现了:AI 会不断复制代码库里已有的模式,包括那些不太好的写法,时间一长代码风格就会慢慢“漂移”。

一开始,团队计划每周抽一天时间手动清理这些“AI 残渣”,但很快发现这种方式根本不具备可扩展性。

后来他们把工程师的经验和偏好写成一套“黄金原则”,比如优先使用共享工具库、严格校验数据结构而不是“猜着写”。

然后将这套原则直接编码进代码仓库,让 Codex 自动扫描问题并发起重构 PR。

这样就像给代码库加了一套“垃圾回收机制”:小问题可以随时清理,技术债不会越滚越大。

这篇 Blog 在技术圈引起了的广泛关注和讨论,有人认为,这个 Harness Engineering 本质上是一种现代版的控制论:工程师不再直接写代码,而是设计系统、规则和反馈回路,让 agent 自动完成工作。

他表示,这种模式,其实在历史上已经出现过三次了。

从瓦特蒸汽机的调速器,到 Kubernetes 的控制器,再到今天的 AI agent;真正的变化不是“机器替代人”,而是人的角色,从执行者变成系统的设计者和校准者

“你不再亲自去拧阀门,而是开始掌舵。

每当这种模式出现,背后通常都是因为有人构建出了足够强大的传感器和执行器,能够在那个层级上把反馈回路真正闭合起来。”

##Agent 都开始包办开发流程了

为什么 OpenAI 的工程师可以不再写代码了?不妨来看看他们的 agent 现在已经能干到什么程度。

前文提到,OpenAI 的工程师换了个思路:干脆让 Codex 模仿工程师,自己去“看”和“用”应用

第一,是让 agent 能“看见”应用界面(UI)。

他们把 Chrome DevTools 协议接入到 agent 的运行环境里。这样一来,Codex 就可以像开发者在浏览器里调试一样操作页面、读取日志、抓取 DOM、截屏观察界面......

这一步其实非常关键,因为 LLM 本身是看不见 UI 的

接入 DevTools 之后,Codex 就相当于有了“眼睛”和“手”:

可以通过截图和 DOM 观察页面,通过 console 和 network 监听运行状态,还能自己点击、输入、导航。

该示意图由 AI 生成

有了这些能力,agent 就可以自己复现 bug、自动跑 UI 测试、验证修复是否生效。

这样一来,Codex 就不只是写代码,还开始像一个自动化 QA 工程师一样工作:自己测试自己写的代码,并反复修复,直到系统通过测试。

换句话说,原本需要人工完成的大量测试和调试工作,被自动化了。

就像下面这张图里展示的那样:最核心的一步是 “Loop Until Clean”——不断测试、修复、再测试,直到系统没有错误。

第二点,只能操作 UI 还不够,还得让 agent 看见系统内部发生了什么。

为此,OpenAI 给 Codex 接入了一整套可观测系统(Observability)

应用在运行时会产生三类关键数据,也是工程师排查问题时最常用的信号:

  • Logs(日志)

  • Metrics(性能指标)

  • Traces(调用链)

这些数据会先被一个叫 Vector的组件统一收集,再送到本地的可观测系统里。

这样一来,Codex 就能像工程师一样查系统状态:哪个服务报错了?哪个接口变慢了?请求卡在哪一层?

当发现问题后,Codex 会自己修改代码、提交 Pull Request、重启应用、重新运行任务,再观察系统指标有没有改善。

整个过程会形成一个自动反馈循环:发现问题 → 修改代码 → 再运行 → 再观察。

一直重复,直到问题消失。

换句话说,Codex 不只是看代码,还能像运维一样查日志、看性能数据,判断系统哪里出了问题,再修改代码验证修复效果。

这篇博文中提到,给定一个提示,Codex 驱动的 agent 就可以:

agent 不再只是一个写代码的工具,而是开始承担完整的软件开发流程。

整个开发流程大致为:Codex 写代码 → 启动应用 → 像用户一样操作页面 → 检查结果 → 如果不对就改代码再跑。

不过需要说明的是,这套流程之所以能跑通,很大程度上依赖他们为这个代码仓库专门设计的结构和工具链。

如果没有类似的工程投入,这种“全自动开发流程”目前还很难直接照搬。

目前来看,这套“agent 写代码、人类设计系统”的模式,在 OpenAI 内部运行得还不错;但很多问题仍在探索阶段:比如 AI 生成的代码库长期会不会失控,人类判断力该如何嵌入系统。

不过可以预见的是,软件工程的重点可能会逐渐从“写代码”,转向设计环境、规则和反馈机制;让像 Codex 这样的 agent,可以更稳定地参与构建和维护复杂的软件系统。

参考链接:

https://openai.com/zh-Hans-CN/index/harness-engineering/

https://x.com/odysseus0z/status/2030416758138634583

https://calv.info/openai-reflections

你好,我是冴羽。

2025 年 2 月份,我创建了自己的知识星球。

我的想法很简单——在知识星球写一套帮助前端开发工程师系统成长的课程。

既是对我 10 年工作和生活经验的总结,也能够帮助一些同学少走些弯路。

真到写时,才发现这个目标太过庞大。

因为一个前端工程师的成长,远不止技术学习这么简单。

但凡你工作过几年,你就会发现,真正成功的工程师通常不是那些代码写得最好、技术研究得最深的人,要不然以前也不会有“强如死月只有P6”的梗了。

那优秀的工程师应该是什么样的呢?

这并不好说,但通常来说这群人有一些共同点,比如学习速度快、行动力强、爱折腾、思考深刻、擅长沟通、了解业务、热爱分享等等。

所谓技术好、业务做得好、影响力强反而是一种结果。

因为学习速度快,爱折腾,所以技术好。因为擅长沟通,了解业务,所以业务做得好。因为思考深刻,热爱分享,所以影响力强。

所以要成长,要快速改变自己,就必须讲到如何提升这些更为底层的能力。

以学习能力为例,学习的本质到底是什么?有哪些真正高效的符合脑科学的学习方法?如何高效地读一本书?如何建立自己的知识管理系统,保证自己接触的所有知识都能得到有效记录,有序管理,精准调用?

这些能力越是底层,就越能在点滴间帮你成长。

所以在我看来,一个前端工程师的成长,一半是「技术」的成长,一半是「人」的成长,两者一起才是一个完整的「技术人」的成长。

后来我总结了一个适用于大部分人的成长公式:

你的成长程度 = 思维认知 × 习惯养成 × 学习框架 × 学习策略 × 有效时间 × 精力管理

假设每个环节都能提升 20%,你就能实现将近(1+20%)^6 ≈ 2.98 倍的成长效果,这已经是从小白到大佬的蜕变了。

围绕这个公式,我也初步确定了自己的星球专栏目录,分别是:“认知升级篇”、“技术成长篇”、“高效学习篇”、“习惯养成篇”、“职场工作篇”、“精力管理篇”、“前端面试篇” 一共 7 个篇章。

于是 2025 年,我就在疯狂更新这些专栏,几乎每周都会更新。

至今已经更新了 100+ 篇精华主题、共计 70W 字的原创内容,平均每篇 6k+ 字,篇篇都是系统体系的原创长文。

这是当前的文章目录:

即便更新了这么多,对我来说,新的一年,依然有很多需要更新的内容。

在我看来,当前的内容只是打了一个底子。那 2026 年,我要更新什么内容呢?

正如我在星球职场工作篇《【003】冴羽:在职场如何做好“向上管理”?》的开篇写道:

一个人如何才能在职场混得好?

最重要的是——跟对人,成嫡系。

这个回答虽然很有道理,可是并没有回答什么样的能力能让你成为“嫡系”?

在我看来,一个技术人要想有一个好的职业生涯, 3 种能力最为重要:

  • 技术好
  • 懂业务
  • 善沟通

其中,沟通又分为 3 种:

  • 向上:和领导的沟通
  • 横向:和同事、合作伙伴的沟通
  • 向下:和下属的沟通

这 3 种能力放在 AI 时代依然是适用的。

你可能想:“都是 AI 写代码了,‘技术好’有什么用?”

确实如此,在我看来,AI 拉平了大家在技术水平上的差距。

一个前端小白通过 AI,就可以轻松达到一个前端大佬的编程水平。这种工作多年才训练出的编程能力被 AI 一夜追平,让很多同学感到焦虑,甚至想要逃避现实。

但我想说, AI 不是软件。软件是确定性结果,你点击 A,就一定会得到结果 B。而 AI 是一个“你强它就强,你弱它就弱”的工具

事实上,一个编程大佬和编程小白之间的生产力差距,顶多 2 倍之多,但一个 AI 大佬和 AI 小白之前的生产力差距,却可以达到 100 倍

这就是为什么星球要更新 AI 相关的内容,至少要帮助大家把 AI 的能力补齐,当然对我来说,这本身也是一个学习的过程。

而“懂业务”、“善沟通”,我统一将其划分到职场话题中。

其实这些软实力放在以前也很重要,只是一个项目最优先的应该是“先把它完成”,所以以前“技术好”显得更重要,但现在技术的差距被拉平,于是这些软实力变得空前重要。

对于 2026 年的前端开发,核心变化其实是角色转变。

1 月的时候,想必大家都刷到了前阿里 P10 毕玄的那张飞书截图。

公司不再按技术栈划分技术岗位,所有工程师统一称为 Agent 工程师。其实正是代表了这种角色转变。

未来几年,公司对工程师的能力要求将会从语言熟练度、框架熟练度、工程经验,变成问题判断力、沟通力、AI 调度能力。

出于这个思考,星球 2026 年将会聚焦在 AI 和职场领域,继续输出内容,希望能对你有帮助。

从某种程度上来说,这种转变其实是个好消息。

终于你不用再当一个螺丝钉般的前端工程师,现在你可以把精力放在更重要的事情上,比如理解用户需求、更深度的参与业务、优化用户体验、设计系统架构等等。

2026 年,我们不是被工具取代,而是在工具的帮助下,做更有价值的事。

如果你对我的内容有兴趣,欢迎加入我的知识星球:https://yayujs.com/course

演讲嘉宾|徐梦炜 博士

策划|QCon 全球软件开发大会

通过本地化搭载大模型,终端设备的智能能力将获得飞跃式提升,铸造移动计算的下一个黄金时代,对学术界和产业界都是巨大的机遇。为了更好地适应这个过程中上层应用编程接口、用户交互范式、底层资源管理的重要变化,操作系统可能会被重新定义和改写。本文整理自北京邮电大学副教授、博士生导师徐梦炜博士在 2025 年 QCon 全球软件开发大会(上海站) 的分享“终端大模型操作系统的架构、优化与展望”。徐老师介绍了团队在大模型操作系统设计和优化方向的思考和尝试,包括 GUI/API 终端智能体构建、面向 NPU 的端侧大模型推理优化加速等。

预告:将于 4 月 16 - 18 召开的 QCon 北京站设计了「小模型与领域适配模型」专题,将深入探讨:如何通过持续预训练、高质量数据筛选、大模型蒸馏、强化学习、MoE 架构等技术提升大模型的专业能力;如何通过模型压缩、显存管理、算子优化、解码优化、系统调度等推理优化技术提升大模型的推理性能;vLLM、SGLang 等推理框架的应用经验等等。敬请关注。

以下是演讲实录经 InfoQ 进行不改变原意的编辑整理)。

Mobile intelligence before LLM

我今天讲得可能不会那么技术,主要想说说我们团队和我个人对端侧大模型的一些看法。背景我就快速带过:从读博开始,我就一直在做端侧 AI 的优化,到现在刚好十年——前五年在读书,后五年在北邮工作。其实那时候端侧已经有不少 AI 应用了,比如“集五福”就是一个很典型的例子。但在学术界做端侧 AI,我总觉得缺了点劲儿,因为它做不到我真正想做的事。

我想做的事是什么?这里举一个《美国队长 2》里的一段情节:车载 Agent 跟角色不断对话,能听懂指令,还能感知车窗即将破裂,最后甚至弹出机枪帮他脱困。这个场景一直是我心里端侧 AI 该有的样子,不是炫技,而是真正能在关键时刻帮到人。

On-device LLM

现在车载 agent 本身已是一个重要方向,我们团队也在做这类应用。过去做不了,是因为模型能力不够;到了大模型时代,我们觉得机会来了——大模型有理解、推理、生成这些能力,而且很多任务必须在端侧完成。原因很简单:隐私。大模型能用的数据越多,理论上手机里产生的每一比特都能让它更懂你、更贴心,可这些数据没人愿意随便上传。所以我个人非常看好端侧大模型。当然,端侧大模型不等于云端大模型会消失。未来一定是端云协同,就像高通说的:The future of AI is hybrid。

我在 2023 年初就开始做端侧大模型。到了 2023 年中,我在学术界做交流时,发现很多人并不买账。他们最直接的疑问是:大模型怎么可能放到端上跑?要是能在端上跑起来,那还能叫大模型吗?毕竟最朴素的 scaling law 告诉我们,云端的大模型一定比端上的更强;既然有更强的,为什么还要用相对弱的呢?

我现在常举的一个例子是:人脑本身就是一台端侧大模型。它不需要连云端,只靠二十瓦左右的功耗,就能完成复杂的推理、规划等任务。所以,我们的长远目标,是让端侧也能跑起具备 AGI 能力的模型,把终端设备做得像人脑一样。这个终端可以是手机,也可以是机器人,或者其他形态。

我们团队虽然把题目叫作“操作系统”,听着有点噱头,但确实是从操作系统一路做到上层:推理引擎、模型、应用 agent,都摸了一遍。我本身在北大软件所出身,最早只做系统软件,后来才慢慢往上走,做到算法和应用。

这里放的是一个比较早的 demo,本质就是本地 RAG 加大模型推理。我们把它跑通的意义在于,整个链路——推理引擎、模型、API Agent——都是我们自己从头搭的。当时正好有资源,从预训练、微调,到上层的 Agent 接口,全走了一遍。

上述 demo 是“数字世界”里的 Agent,现在我们更关注具身 Agent。无人机是重点场景之一,我们把它当成端侧大模型,或者说端侧 VLA 的重要平台。无人机经常要在弱网甚至无网环境工作,自主化是刚需;不管是军用还是载人载物,未来都得靠“大脑”。现在的飞控只能算小脑,能避障、做简单路径规划,离真正的自主还差得远。我们想在机上跑一个 3B 到 7B 的端侧模型,让它具备更高阶的决策能力。比如让它飞到黑板前看一眼,再去找黑板里提到的东西。这类任务在大模型出现之前根本做不了,现在我们认为可以一试。核心就是提供一种泛化能力,让无人机像人脑一样现场思考、工作。

在推理引擎方面,我们主要想对标 llama.cpp,用更高效的思路搭一套自己的生态。框架叫 mllm,定位是给非 CUDA 的端侧 NPU 用的推理引擎。除了最基础的 LLM,我们重点做 VLM,以及现在流行的 Omni、MoE 和机器人 VLA 这类模型。刚才也有人问 NPU SDK 的事,高通确实做了,我们也做过。由于看不到高通最底层的指令集,我们的目标是在性能上尽量接近他们的端到端 SDK,哪怕略差一点,但换来更大的灵活性:可以测试新的量化算法,也能更快支持他们暂时不支持的 MoE、Omni 等特性,从差异化角度把开源生态做起来。

我今天想围绕一个问题讲:十年前我开始做端侧 AI,到现在带着二十多位同学做端侧大模型及其应用,到底有没有不一样?如果只是把 CNN 换成 Transformer,其他原封不动,那其实没多大意思。产业界的专家肯定比高校师生做得更快更好。我们要找的是偏研究的机会,需要长期探索、甚至要冒点风险、花长时间才能啃下来的问题。最后我们欣喜地发现,确实大不相同:从应用到系统再到硬件,整个软件栈都出现新挑战。接下来我就从这三个层面,谈谈我们的体会。

The changes LLM brings: App

先讲应用。过去做端侧 AI,业界常做的是 TTS 这类任务,我们则把重点放在机器学习模型的压缩与加速上。但这类工作本质上是优化一个独立任务,而非端到端的应用,所以可玩性有限——不同应用需要不同的优化策略。到了大模型时代,出现了一个非常集中的“杀手级”应用:小爱、小艺、Siri 这类助手。它们背后几乎都由 LLM 或 VLM 驱动,这就给学术界提供了一个可以长期聚焦、深入研究的明确方向。

在 Agent 方面,我们做了一些初步尝试。Agent 有两条路线:API 与 GUI,未来肯定是混合的。从研究角度看,GUI Agent 更值得做,因为 API Agent 更多依赖工业界生态,而 GUI agent 距离 C 端商业化还很远,远没到能提供 99% 以上准确率的地步。

虽然自媒体天天推送“某模型已能像人一样操作手机”,小红书上也刷到 ICLR 今年七十多篇基于 RAG 的投稿,但现实是:这个方向确实前景很大,却还没到落地阶段。核心问题是端到端任务完成率仍偏低。很多任务之所以交给 Agent,是因为用户懒得做或做不好,往往涉及 10~20 步的长程操作。每一步都可能出错,错误累积导致整体成功率下降。更大的瓶颈是效率:点 20 次屏幕,就得做 20 次多模态大模型推理, 延迟和能耗都吃不消。因此,效率成了阻碍 GUI agent 真正部署落地的关键难题。

搭一个 GUI Agent 的 workflow 其实不复杂:先选个 VLM,端侧部署就用 3B 或 7B 的模型;然后收集 GUI 数据,构造下游任务,通过后训练提升它在 GUI 上的 grounding、指令跟随、CoT 等能力。现在不少 VLM 在预训练阶段就已经混入了大量 GUI 数据,比如千问的技术报告里就列出了 GUI 任务准确率,但我们觉得仍有空间。只要设计出更好的 GUI 任务,就能继续拉高模型对界面理解与操作的水平。

模型训好后,就进入在线 Agent 构建阶段,跟 Deep Research 或 Coding Agent 类似:搭 memory、拼上下文、选工具、定制 test-time 调度策略。做完再放到 Testbed 跑分。GUI Agent 的麻烦在于 Testbed 本身就不完善,不像 ImageNet 那样“是猫还是狗”一目了然。GUI 任务的路径几乎无限,界面又随时可能弹出广告,评判难度高,所以 Testbed 至今仍是研究热点。

这里说一点我们最近关于训练的想法。训 VLM 理论上需要大量带标注的 GUI 轨迹数据,一连串界面截图和对应的操作。这种数据很难规模化。传统做法像 DeepMind 那样雇几千人、花几千万美元标上百万条轨迹,成本高还容易错。我们就在想,能不能把无标注的轨迹也用起来:不给具体任务,让工具自动在 GUI 里探索。其实很多大厂做 App 测试时就有专门的 GUI Testing 团队,用爬虫把应用界面遍历一遍,收集大量无标注的 GUI 序列。这些数据如果能被有效利用,就能省掉一大笔标注费。

我们重新梳理 GUI 任务后发现,当前 VLM 对单张界面图的理解已经很强:把手机截屏丢给 GPT 或豆包,做 VQA 或 Grounding 都能答得不错。真正的难点在于“跨页关联”,给出当前界面,执行某个动作后,下一界面会变成什么样。这种能力在长程 GUI 任务里尤为关键,却几乎没出现在 VLM 的预训练目标中。它很像机器人里的世界模型:给定状态与动作,预测下一状态。事实上,数字 Agent 与实体 Agent 在能力需求上越来越重叠,比如如何利用无标注数据、如何构建世界模型,这些问题是相通的。

顺着这个思路,我们设计了一个极简的下游任务:输入两个界面截图,让模型预测在前一界面上该执行什么操作才能到达后一界面。两图之间可以相隔 k 步,无需连续。这相当于机器人里的逆动力学,已知起点与终点,反推所需关节动作。好处是无需人工标注:只要用爬虫遍历 App 时把动作记录下来,就能自动生成大规模训练对。

我们用这套方法收集数据,训了最大 7B 的模型;卡不够后,又与小米合作,在他们的 Mimo 模型上继续实验。结果在相同数据量下,我们的模型效果甚至优于用人工标注数据训出的基线。原因很简单:人工标注成本高,且 GUI 任务远比猫狗识别复杂,标注错误率不低;而爬虫数据可无限扩展,噪声反而更少。

我们实验里还发现,用强化学习,比如 DeepSeek GPPO,效果比 SFT 好。现在大家的共识是:要做 Agent,就得用 RL。不管是最近很火的 Agentic RL,还是 GUI Agent 这种垂域任务,RL 已经成了实现强泛化的标配。

The changes LLM brings: OS

回到操作系统这个话题,正好呼应我今天报告的题目。我觉得大模型时代一个很有意思的现象是“下沉”。以前 CNN 那种小模型完全由应用自己管:自己训、自己部署,操作系统既不知道也不关心。现在模型太重,会慢慢往下沉。不一定沉到操作系统本身,也可能沉到支付宝、微信这类超级 App,然后以统一接口把大模型能力开放给第三方。再想回到以前那种碎片化、去中心化的部署方式,已经不太可能。

原因很现实:如果每个第三方服务都自己部署一个千问级别的模型,既没资源也没必要。Google 已经在这么做了,因为它有全栈能力:TPU、安卓、Pixel 手机、自己的大模型,华为也类似。这种大厂能把硬件潜力真正榨干。

一旦大模型变成系统级服务,就会出现一些新问题。从操作系统角度看,可用性、效率、安全都值得重新研究;对工业界来说,甚至可能催生新的商业模式。

当大模型成为系统级服务后,第三方应用还是得靠 LoRA 接入,因为端侧 3B 模型毕竟知识有限。可模型还能升级吗?操作系统升级不会影响应用,但大模型服务从千问 3 升到千问 4,原来训的 LoRA 就失效了。这是个全新问题。

我们做了一些非常早期的尝试,远没到“千问 3 升 4 后旧 LoRA 完全可用”的程度。目前能做到的是:原来需要 1 万条数据才能训出的 LoRA,现在用我们的方法可能只需 10~100 条就能在新模型上复现同样效果。作为初步探索,我们把训练放到了端侧,也跑过端侧大模型的微调实验。

从效率角度看,我觉得很有意思的一点是:一旦大模型变成系统级服务,就跟在云端一样,得考虑怎么调度、怎么缓存复用、怎么做 batch。以前端侧几乎只考虑 batch size 等于 1 的场景,可将来如果大模型无处不在,不光喊“Hi Siri”时才触发,后台也可能一直跑,主动感知、主动推荐。到那时,它得同时处理很多请求,就跟云上一模一样。

我们做过一个实验:怎样让大模型提供弹性服务。有的应用希望 prefill 快,有的希望 decode 快,有的对延迟不敏感,只想要生成质量高。于是我们把 token-level 的剪枝和模型 -level 的剪枝结合起来,在保障 QoS 的前提下,尽量把生成质量做到最好。

The changes LLM brings: H/W

关于我们在推理引擎方面的工作。面向未来,尤其是具身智能这类场景,我觉得应该把 NPU 真正用起来。长期来看,大模型终究要落在 NPU 上跑。

之所以这么说,是因为在大模型出现之前,我们做了很久的端侧 AI 优化,当时一度觉得“没活可干”:像 ResNet-50 这类网络在高通早年的 NPU 上 5 毫秒就能跑完,远远满足需求;ResNet-152 也很快。可大模型一来,学术界突然又有饭吃了。Scaling law 让资源缺口可以无限放大。不是今天把 3B 模型塞进去、明天换成 7B 就完事,而是永远想跑更大的模型:明年 50B,后年 500B,需求没有尽头。

纯技术角度看,国产或各类专用加速器在大模型时代应该更有用武之地。当年 865 那代,我在 Wikipedia 上查到 73 TOPS,肯定不到,因为今年发布的新品才标 80 TOPS。但不管怎样,NPU 靠定制指令和架构级优化,在绝对算力上相对 CPU、GPU 仍有优势,所以我们团队一直坚持做面向 NPU 的框架。蚂蚁那边已经把 CPU、GPU 做到极致,我们就专攻 NPU。选 NPU 还有一个原因:它对系统软件要求更高。硬件算力可以堆得很好看,但上层怎么跟量化算法结合、怎么做 kernel 优化(如果允许写 kernel 的话),能做的事更多,也更难。学术界正好可以往深里钻。

我们那个推理引擎其实是比较早期的项目。2023 年初,高通自己的端到端 SDK 还没发布,我们就先基于它的 DSP(本质上也是 NPU)动手。当时 NPU 显然不是为大模型设计的——虽然发布会上号称“首款为生成式 AI 打造的芯片”,但 GPT 才火了几个月,硬件不可能转得这么快。

实际上,真跑起来就会发现一堆不兼容:动态 shape 不支持;很多 NPU 缺乏足够的 FP 算力,可我们当时的量化算法里,attention 里不少算子还是会溢到 FP;官方暴露的算子也不支持 group-level 量化,而大模型又必须靠 group-level 来抑制 outlier,否则精度掉得没法看。我们没法像海思那样直接改芯片,只能在一块“成品”硬件上,用算法和系统手段去填大模型语义和 NPU 能力之间的沟。当时提的 chunked prefill 之类的小技巧,现在看已经挺常见,高通内部的 Gini 框架也在用类似思路。

不同模型对 outlier 的敏感程度不一样,但总之我们能把 prefill 做得比 CPU、GPU 都快——decode 阶段是 memory-bound,就先没碰。开源过程中遇到的问题比预期多:benchmark 里输出短,选 ABCD 准确率掉得不多;一旦生成长文本,准确率就明显下滑。学术界的小 demo 离真正能用还差得远。我们的论文一年前就写完,之后学生一直围着开源框架做落地。最近一两周会把版本升到 V2,目标是新模型发布当天就能在高通或其他 NPU 上跑通 0-day 支持,也在和面壁等模型厂商谈合作,争取端侧模型一发布就有 demo。

当然,之前的工作留了不少尾巴:decode 没优化、算法还依赖 CPU。最近我们在补这些坑,用 NPU 做投机采样来加速 decode、解决 long context、尽量把 FP 算力需求压到最低。我的愿景是做到“NPU-only”的端到端大模型推理,全程用整数运算跑完。

我们不仅想用 NPU 做推理,还想用它做大模型的微调训练。和小米合作的项目就是端侧大模型微调。要解决的问题是 NPU 本身不支持反向传播。我们用了一些算法层面的技巧,主要是临界优化,把推理和训练并到一条前向通路里,只需前向就能算出 loss 并更新参数。虽然端到端性能一般,但确实在端侧跑通了 7B 模型的微调。

Takeaways

关于未来,我前面提过“人脑”的例子。Hinton 把智能分成两类:一类是“凡人计算”,即人脑知识无法直接迁移,人死灯灭,只能靠师徒口传;另一类是“永生计算”,硅基软硬件解耦,程序换块板子就能继续跑。永生计算虽“不死”,却做不到人脑的低功耗强智能。Hinton 认为,若想逼近人脑,也许得放弃永生计算。我部分同意:软硬必须高度耦合。

我的一个很不成熟的想法是:面向手机或其他端侧场景,应垂直协同设计,从上层模型到下层硬件一体优化。最理想的情况是,终端里有一块专用电路,只跑 Transformer(前提是 Transformer 仍是主流架构),并配独立高带宽内存。今天手机用统一内存,未来或许该给大模型和它的 KV Cache 划一块专属存储。牺牲通用性换取极致功耗与性能;损失的灵活性可在 Agent workflow 层补回来。模型与硬件垂直整合,才能把性能压到极限。

总结本次分享:第一,端侧大模型正在重塑移动终端——无论无人机、机器人、手机,还是未来可能出现的新形态,它们的核心能力都会被改写。第二,这件事不是简单的模型替换,而是整个移动 AI 生态的范式转移:操作系统、运行时、模型、应用(Agent)必须一起重新设计,需要学术界和产业界一起做真正的全栈研究。

演讲嘉宾介绍

徐梦炜博士,北京邮电大学副教授、博士生导师,在端侧智能方向发表 CCF-A 类论文 30 余篇,获 USENIX ATC 2024 最佳论文奖等,入选中国科协青托、北京市科技新星、微软研究院“铸星计划”等,主导了开源端侧大模型推理引擎 mllm 等。

会议推荐

2026,AI 正在以更工程化的方式深度融入软件生产,Agentic AI 的探索也将从局部试点迈向体系化工程建设!

QCon 北京 2026 已正式启动,本届大会以“Agentic AI 时代的软件工程重塑”为核心主线,推动技术探索从「AI For What」真正落地到可持续的「Value From AI」。从前沿技术雷达、架构设计与数据底座、效能与成本、产品与交互、可信落地、研发组织进化六大维度,系统性展开深度探索。开往 2026 的 Agentic AI 专列即将启程!汇聚顶尖专家实战分享,把 AI 能力一次夯到位!

最近你有没有被这样的"AI智能体"故事刷屏:一觉醒来我的🦞已经把月报做好了让我的🦞去热点追踪它说发现了爆款话题直接帮我规划内容这只火遍技术圈的🦞,就是OpenClaw。
图片

图片
🦞 OpenClaw是什么?简单说,OpenClaw,它就像是《钢铁侠》里的贾维斯。大家都知道,钢铁侠想要做什么,从来不需要自己去敲键盘、查数据。他只需要随口说一句:"贾维斯,帮我分析一下这个装甲的受损情况",或者"把那个恐怖分子的位置发给我"。然后贾维斯就会立刻去执行:扫描装甲、调用卫星、检索数据库、把结果直接展示出来。OpenClaw 做的就是这件事。它不只是回答你的问题,更能直接为你执行各种任务
图片
再举个更接地气的例子:它就像是你有一个私人项目经理团队。想象一下,你要开发一个新项目,传统做法是:先自己查资料、做调研然后写需求文档再找设计师做UI最后交给程序员开发还要不断沟通协调...但有了OpenClaw,你只需要说: "帮我开发一个智能记账小程序,要有图表分析功能,界面要简洁现代"然后OpenClaw就会像这样工作:🦞 调研虾:自动搜索市面上最好的记账应用,分析功能特点 🦞 文档虾:生成详细的需求文档和产品说明 🦞 设计虾:提供UI设计方案和视觉风格建议🦞 开发虾:开始编写代码,搭建基础框架 🦞 测试虾:检查代码质量,确保功能完整最终结果: 你一觉醒来,发现项目已经完成了80%,剩下的就是优化和微调。
图片
如何免费领取自己的🦞?蚂蚁百宝箱Tbox为你带来完全免费的OpenClaw服务。在这里,你可以指挥各种" 🦞(skills) "为你工作。无需安装到本地、不用学习复杂命令,只需要访问网站,跟它发起对话即可。👇🏻 蚂蚁百宝箱体验入口:tbox.cn 
图片
用蚂蚁百宝箱一键使用OpenClaw,马上把智能🦞领回家!你可以随时为自己的🦞设定性格——无论是软萌可爱的少女风、气场全开的御姐范,还是充满好奇心的探索型都可以。如果你希望你的“小龙虾”活泼好动、主动尝试新玩法,只需选择探索型性格,它就会在后续互动中更积极地出主意,引导你发现更多有趣的可能!
图片
手把手教程:如何让你的🦞汇总每日热点并推送到钉钉群?"OpenClaw的应用场景十分广泛,用户通过它实现了多种自动化需求:信息推送自动化:搭建钉钉机器人发送定时消息,如每日黄金价格、热点新闻推送等文件管理智能化:实现本地文件自动整理,智能归类文档资料,自动生成报销表格今天手把手教大家实现:信息推送自动化,一觉醒来,热点总结已自动推送到钉钉群。▍第一步:访问Tbox平台在浏览器输入 tbox.cn,点击tbox首页对话框上方的"openclaw"选项
图片
▍第二步:汇总每日热点在对话框中输入"每日汇总最新 AI 资讯,并推送到钉钉群聊“,然后点击发送
图片
▍第三步:通过OpenClaw将每日热点发送到钉钉群聊Openclaw获得指令后,会要求你提供以下参数:钉钉Webhook URL:钉钉机器人的消息接收地址消息内容:需要发送的具体内容加签密钥(可选):如果机器人启用了安全设置
图片
创建钉钉机器人并获取以上参数以群管理的身份前往目标群聊设置面板 > 机器人 > 添加机器人 > 自定义,并点击机器人详情面板中的添加选择"自定义机器人",设置机器人名称和头像复制生成的Webhook URL
图片

图片
▍第四步:设置热点来源与定时发布时间按需设定每日消息推送的时间和信息来源前往目标钉钉群聊查看消息
图片

图片

图片
📚 这只是OpenClaw强大功能的冰山一角!后续我会继续更新更多实用教程~现在就去 tbox.cn ,按照教程一步步养你的虾🦞吧!👉点击“阅读全文”或者快去百宝箱官网: tbox.cn 试试吧!

做跨境直播的企业都会遇到一个现实问题:普通宽带不稳定,延迟高、卡顿严重,甚至出现掉线封号的情况。

这时候,“海外直播专线”就成了刚需。那海外直播专线到底怎么收费?一年大概多少钱?值不值得上?这篇文章从专业角度给你讲清楚。

一、海外直播专线是什么?为什么需要?

简单说,海外直播专线就是一条专门为跨境直播业务优化的国际网络通道。

和普通宽带相比,它具备:

独享或高保障带宽

稳定低延迟

丢包率低

专门优化国际链路

为什么直播必须用专线?

直播对网络要求极高,尤其是:

实时推流

高码率视频传输

与海外平台服务器稳定连接

如果网络波动大,轻则画面卡顿,重则直播中断,直接影响账号权重和销售转化。

特别是做:

TikTok海外直播

跨境电商直播带货

海外品牌发布会

所以对于做跨境直播的用户来说,直播专线是标配。

二、影响海外直播专线价格的因素有哪些?

专线价格不是统一报价,而是根据需求定制。主要受以下因素影响:

1、带宽大小

常见的带宽有10M、20M、50M、100M及以上,带宽越大,价格越高,但是优惠越多。

一般中小型直播间,10M-50M之内就够用。

2、线路类型

传统国际网络专线、SD-WAN国际专线,传统线路价格最高,但稳定性好,灵活性不高,建议使用SD-WAN专线,部署简单,性价比高。

3、目标国家

不同国家线路成本差异大:

美国、加拿大等地区价格一般

中东、南美成本偏高

跨洲传输距离越远,地区资源稀缺,那么价格越高。

4、是否需要固定IP

很多直播平台对IP稳定性要求高。

如果需要:

独享静态IP、原生住宅IP,原生独享IP价格会略高,但IP纯净度更好,IP更真实,

三、海外直播专线一年多少钱?

OSDWAN提供多个版本的套餐,对于做TikTok直播建议使用我们TK直播专线,我们提供5M、10M、20M、50M、100M等等不同带宽线路的的直播套餐,价格可按月、按年的方式,例如美区5M独享专线+独享静态IP价格10000元/年,折算下来低至800元/月起,带宽越大价格越优惠,不同的地区线路和IP价格不同。

四、如何选择合适的海外直播专线?

选择时不要只看价格。

重点关注:

1、稳定性测试

是否支持测试线路?

是否有真实丢包率和延迟数据?

2、是否支持分流优化

直播推流与日常办公是否可以分流?

是否支持智能路由切换?

3、安全性

是否采用加密传输?

是否防止数据被监听?

4、售后响应速度

直播是实时业务,出现问题必须立刻处理。

五、哪家海外直播专线好?推荐OSDWAN

对于长期做跨境直播的企业来说,稳定和安全远比价格更重要。OSDWAN直播专线的优势:

1、独享带宽

稳定独立的网络环境,带宽按需购买,不限时间不限流量、能够有效避免限流、掉帧、直播卡顿画面模糊等问题,高效助力跨境直播带货以及海外社媒运营;

2、原生住宅IP

OSDWAN提供纯净的原生住宅静态IP,由当地 ISP(互联网运营商)直接分配,100%真实原生网络环境,避免因IP被标记或污染而导致的播放量为零、流量限制等问题。同时,固定的IP地址能为账号提供原生态的运营环境,能保障账号/店铺长期稳定的运营;

3、一条专线,多个IP

一条TikTok专线出口线路可支持配置多个IP地址,电脑或手机连接后均可通过对应的IP登录账号,大幅降低被平台标记的风险(Tips:建议一个IP对应一个直播账号,同一IP下登录超过多个账号,容易触发风控,导致账号限流或封禁);

4、一台设备,多条出口专线

一台CPE设备支持配置多个国家专线出口,对应的账号可指定对应国家出口线路进行登录,适合企业在不同国家多账号运营 /开播需求,同时采用智能路由和动态链路负载平衡技术来优化网络性能,保证数据传输的稳定性和高效性;

5、即插即用,免设置安装

无需复杂的配置,OSDWAN软件硬件双接入模式,并提供多种型号的CPE设备,CPE硬件设备接入网络,连接WiFi或网线即可使用;

6、多种高性价比方案套餐选择

根据不同的需求,我们提供多种套餐供企业选择,无论是内容运营、手机/电脑直播,还是多账号社媒矩阵运营,都让企业在全球范围内畅享无卡顿、低延迟的网络体验;

image.png

六、海外直播专线常见问答

1、直播一定要用专线吗?

如果是测试或小规模尝试可以不用,但长期稳定运营建议使用专线。

2、专线能保证100%不卡吗?

任何网络都无法保证100%,但优质专线可以把丢包率和延迟控制在极低范围。

3、可以先测试再购买吗?

正规的服务商一般支持测试线路,比如OSDWAN支持免费试用。

4、带宽越大越好吗?

不是。根据直播码率和并发数量选择即可,避免浪费成本。

5、一条专线能同时支持办公和直播吗?

可以,但建议分流配置,保证直播优先。

总结

海外直播专线的收费通常按带宽、线路类型、国家和附加服务综合定价,一年费用大致在几万元到十几万元不等。

对于真正想长期做海外直播的企业来说,稳定、合规、安全、可控才是核心。

如果追求性价比与企业级保障并重,OSDWAN跨境网络专线是值得优先考虑的方案。

OSDWAN是国内专业的跨境网络专线服务商,提供合法合规的TikTok直播专线网络,稳定、高速、不标黄、安全、简单好用,一分钟即可部署完成,支持硬件CPE设备和专属软件,提供100+地区的独享、纯净、原生、住宅IP。