Flink 1.12任务调度全解析:从StreamGraph到ExecutionGraph的链路

做了几年实时计算,我发现不少人把Flink当成一个“黑盒子”:代码提交上去能跑就行,至于任务调度逻辑、资源怎么分配,全都交给框架。这种用法日常没问题,可一旦遇到任务卡住、重启很慢、或者slot不够用,就完全不知道从哪下手。这篇东西,我想围绕Flink 1.12把任务调度逻辑讲透:从你写的DataStream代码变成StreamGraph、JobGraph、ExecutionGraph的完整链路,再到调度策略、Task Slot分配、故障恢复时的调度行为。适合刚开始接触Flink内部机制、或者已经在用1.12但想优化资源利用率的同学,看完至少能知道作业从提交到运行,每一步踩了什么油门、挂了什么挡。

1. 调度链路全览:一次作业提交背后的三层图流转

1.1 StreamGraph、JobGraph、ExecutionGraph,三个图分别管什么

先建立整体认知。Flink里一个作业从用户代码到真正运行,会经历三次“图”的转换,每一层解决不同的问题。

第一层是StreamGraph,这是用户代码编译后的“逻辑DAG”。你在代码里addSource、map、keyBy、addSink,Flink会把这些算子节点以及它们之间的连接关系组织成StreamGraph。StreamNode就是算子节点,StreamEdge是算子之间的数据通道。这一层很接近代码本身,它不考虑多少台机器、多少并行度,只记录“计算逻辑长什么样”。

第二层是JobGraph,这是客户端在提交给JobManager之前生成的“可提交作业图”。StreamGraph在JobGraph阶段会做一件非常重要的事:Operator Chain合并。多个能串成一条线的算子会被合并成一个JobVertex,多个JobVertex之间通过IntermediateDataSet产生数据交换。提交给JobManager的就是这个JobGraph。

第三层是ExecutionGraph,这是JobManager真正用来调度和执行的“并行展开图”。JobGraph里一个JobVertex只是一个逻辑顶点,到了ExecutionGraph,它会按照并行度展开成多个ExecutionVertex。比如某个JobVertex并行度是4,那它就变成4个ExecutionVertex,每个ExecutionVertex代表一个可以独立调度的并行子任务。顶点之间的边也会展开成ExecutionEdge,代表实际的数据shuffle关系。

等TaskManager真正接收任务后,还会形成一张物理执行图:一个Task线程对应一个ExecutionVertex,中间通过网络连接完成上下游数据交换。通常我们说“调度”,核心就是围绕ExecutionGraph展开的。理解了这三层图,后面所有内容都不会跑偏。

1.2 Operator Chain把多个算子打包成单个任务

Operator Chain是调度逻辑里第一个影响资源形态的环节,也是最容易被忽视的。很多同学看Web UI时发现:“我明明写了source、map、sink三个算子,怎么界面上只有一个Task?”这就是因为Flink默认把可以合并的算子链成了一条线。

Chain的合并条件在源码里有明确判断,我总结下来主要是这几点:上下游并行度必须相同;上游到下游的分区方式必须是FORWARD,也就是数据直接从当前算子流到下一个算子,不需要经过keyBy、rebalance这类会改变分组和并行关系的操作;下游算子最好只有一个输入,union、多输入算子通常不适合链;另外两个算子都没有被显式禁止chain。满足这些条件,Flink就会把多个算子揉进同一个JobVertex,最终在TaskManager上生成一个Task线程。

这个机制对调度影响巨大。假设一个作业有5个算子,并行度都是4,如果不做chain,那ExecutionGraph里就有20个ExecutionVertex需要调度;做了chain并且合并成1个JobVertex后,调度单元直接变成4个。调度器要处理的顶点少了五倍,TaskManager之间也不会有无谓的网络传输。代价是任务粒度和故障恢复的边界变粗,一旦某个子任务异常,整条chain都要跟着重启。我自己的经验是:默认chain不要轻易关,除非某个算子需要独占线程做IO操作,或者你希望某个高负载算子单独隔离,否则关了chain只会白白增加调度和通信开销。

1.3 JobGraph是怎么变成ExecutionGraph的

JobGraph到ExecutionGraph的转换,是JobManager收到作业后做的第一件大事。这个过程有个很直白的名字,叫“并行化展开”。JobManager拿到JobGraph后,遍历每个JobVertex,根据并行度创建ExecutionJobVertex和它下面的多个ExecutionVertex。

我画过一条线帮助自己记忆:JobVertex是模板,ExecutionJobVertex是这个模板在某个作业执行周期里的实例,ExecutionVertex是实例里每个并行子任务的最小单位,而Execution则是这个ExecutionVertex每一次具体的执行尝试。比如一个子任务失败后重新调度,会生成新的Execution对象,老的Execution会被标记成FAILED。

每个Execution都有自己的状态机,常见几个状态包括CREATED、SCHEDULED、DEPLOYING、RUNNING、FINISHED、CANCELED、FAILED。你之后看日志或Web UI时,判断任务卡在哪一步,其实就是看这个状态机停在了哪里。从调度框架的角度说,JobGraph到ExecutionGraph转换完成之后,接下来所有动作都集中在“调度ExecutionVertex”这件事上。这也正好引出Flink 1.12里最值得聊的调度框架重构。

2. 1.12版本调度框架重构:从逐任务调度到批量调度

2.1 旧调度器的问题

Flink 1.12之前的调度器,核心逻辑大量集中在事件驱动和逐任务调度上。作业启动或者故障恢复时,调度器会从图里挑一个ExecutionVertex,先申请slot,等slot分配到了再部署Task,部署完成后再接着处理下一个。对于几十上百个并行度的作业,这个过程就像排队买票,一个一个来。

这种方式的直接后果是故障恢复很慢。尤其是一个大作业在运行中挂掉,你会看到任务不是一个一个起来的,但也不是整齐批量起来的,而是断断续续、像挤牙膏一样慢慢恢复。调度逻辑本身也不容易维护,新的功能比如动态并行度、自适应调度,想在旧框架上做改动非常吃力。流批一体推进之后,批任务希望空闲资源来多少用多少、逐步拉起来,流任务则希望尽可能快速恢复到稳定状态,两种诉求差异越来越大,旧调度器已经盖不住这栋楼了。

2.2 SchedulingStrategy与EagerSchedulingStrategy

1.12的最大变化,是把“如何安排任务执行顺序”这摊事抽象成了SchedulingStrategy接口。调度器依然负责和资源管理器交互、部署任务,但具体“先调度谁、后调度谁、一次调度多少”,交给策略决定。

默认的DefaultScheduler使用EagerSchedulingStrategy,这是一次性调度策略。它在调度开始时,会把当前可调度的所有ExecutionVertex全部提交调度请求,批量申请slot,拿到资源后并发部署。这种策略的好处非常明显:作业启动不再有漫长的排队,故障恢复时所有Task尽可能同时拉起,耗时大大缩短;调度逻辑也更简单,少了各种状态回调和中间打断。

除了Eager,Flink还保留并实现了LazyFromSourcesSchedulingStrategy。这种策略更偏向批处理场景,从Source节点开始,一层一层往sink方向推进,先把必要的最上游任务拉起来,再根据结果逐步拉起下游。它适合资源弹性比较弱、希望作业边跑边获取资源的场景。1.12还新增了Adaptive Scheduler,不过默认不启用,这个我后面再说。

这里必须提醒一点:Eager策略不是一个“既要又要”的银弹。它要求集群有足够资源一次性满足整个作业的slot需求。在Standalone模式下,如果资源不够,作业不会像之前那样慢慢等到部分资源再启动剩余任务,而是直接失败。这个坑后面排查部分会细讲。

2.3 Slot共享与资源申请的核心逻辑

说完策略,再说调度器到底在申请什么资源。Flink的资源单位是Slot,但这里的“Slot”不是简单的“一个Task占一个坑”这么粗暴。因为SlotSharingGroup的存在,同一个共享组里的多个ExecutionVertex,只要并行度相同,是可以复用同一个Slot的。

默认情况下,所有JobVertex都会属于同一个共享组。这就意味着,一个由source、map、window、sink串成且并行度一致的作业,理想状态下所有Task都能被装进同一组Slot里。比如并行度为2,那只需要2个Slot就能装下整条链上所有子任务,每个Slot里的Task线程负责跑一条完整的计算链。这样做的好处是减少网络传输、提高线程利用率;坏处是同一个Slot里的多个Task会抢同一个TaskManager的CPU和内存资源,遇到一个算子特别耗CPU时,会殃及同组其他任务。

调度器申请Slot的内部流程也不复杂:ExecutionVertex进入调度流程后,会向Scheduler请求Slot;Scheduler通过SlotProvider向ResourceManager发起申请;ResourceManager从当前可用的Slot池里分配一个Slot;最后执行部署。这中间如果Slot不够,在YARN模式可能会触发动态扩容等待,但在Standalone模式往往就是失败或等待超时。理解这条链路,后面排查“任务卡住”时会非常有底。

3. 一次完整的调度部署过程:从Job提交到Task运行

3.1 JobManager、ResourceManager、TaskManager各自的调度职责

很多新手分不清JobManager和ResourceManager到底谁负责什么。简单说,JobManager是“调度的大脑”,ResourceManager是“资源的仓库管理员”,TaskManager是“干活的工人”。

JobManager里运行着Scheduler和ExecutionGraph,它决定一个ExecutionVertex应该什么时候调度、部署到哪个TaskManager。ResourceManager只负责Slot的分配和回收,它不关心你跑的是什么算子,只需要知道当前有多少空闲Slot、每个Slot属于哪个TaskManager。TaskManager启动时会向ResourceManager注册自己拥有的Slot,之后接收JobManager的部署指令,在本进程内创建Task线程来跑真正的用户代码。

我用一个类比帮助理解:JobManager是项目经理,负责排工期、安排活;ResourceManager是仓库管理员,只管借出和回收工具;TaskManager是工人,工具到位了就干活。整个作业能不能跑起来,其实是三方配合的结果。

3.2 一个具体作业的调度走位示例

光说概念还是虚,我拿一个典型作业走一遍。假设代码结构是这样的:

DataStream<String> stream = env.addSource(new SocketTextStreamFunction("localhost", 9999)); stream .flatMap(new Tokenizer()).name("split") .keyBy(w -> w.f0) .timeWindow(Time.minutes(5)) .apply(new MyWindowFunction()).name("count") .map(new FormatResult()).name("format") .addSink(new DiscardingSink<>()).name("sink");

假设所有算子并行度都设置为2。由于flatMap与source之间是FORWARD关系,并且并行度相同,它们会chain成一个JobVertex;keyBy之后因为引入了key的shuffle,window和flatMap不可能chain,但window到之后的map、sink如果也是FORWARD,则它们可以继续chain。

最终ExecutionGraph里可能有3个左右JobVertex,并行度都是2,所以一共产生6个左右的ExecutionVertex。默认SlotSharingGroup把所有JobVertex归到一个共享组,并行度又一致,那么调度器实际只需要申请2个Slot,就能把这6个ExecutionVertex全部部署完成。更直观地说,每个TaskManager上有一个Slot,这个Slot里的Task线程会依次跑完整条计算链,从source读到数据,经过flatMap、window、map,最后写向sink。

现在反过来看,如果我把sink的并行度单独调成4,情况就变了。sink和前面的map并行度不一致,无法chain在一起;同时因为并行度不同,默认共享组里同一个Slot无法再完整容纳“一个上游子任务+一个下游子任务”的组合。调度器会让原有的2个Slot继续承载主链任务,额外再分配2个Slot给sink的4个并行实例中的另外2个。原本2个Slot就能解决的作业,现在需要4个Slot。调度逻辑的一举一动,就这样直接影响资源瓶颈。

3.3 从Web UI和日志观察调度结果

作业跑起来之后,我推荐大家养成看两个地方的习惯:Web UI和JobManager日志。

Web UI的Job Graphs页面能直接看到展开后的Task数量、每个Task的状态和所在TaskManager。如果一个Task一直显示SCHEDULED,说明调度器已经决定要调度它,但还没拿到Slot;如果显示DEPLOYING,说明Slot已经分配,正在下发部署指令。JobManager日志里通常会有“Deploying task”和“Successfully registered task”这类记录,可以从时间戳里推断调度耗时。

我曾经排查过一个作业,发现某些Task从SCHEDULED到RUNNING只用了3秒,另一些却花了半分钟,后来看日志发现是TaskManager分布在不同的物理机上,网络拉取大状态时慢。这些观察说到底,都是为了让调度过程“可见”,而不是遇到了再说。

4. 常见问题排查与故障恢复

4.1 任务一直SCHEDULED,怎么定位

任务卡在SCHEDULED状态,是我收到最多的求助类型之一。先说结论:绝大多数情况都是Slot申请不到。

第一步去JobManager日志里搜Could not allocate或者Not enough resources,如果看到了类似错误,那说明调度器正在等ResourceManager给它一个可用的Slot。第二步去ResourceManager或TaskManager列表页看当前空闲Slot数量。如果空闲Slot是0,而任务又在等Slot,那原因基本锁定:集群资源不足。

但还有一种容易忽略的情况:资源足够,但因为Slot共享组设置不当,导致部分Slot被算子的某个并行实例“独占”了,后续Task找不到可匹配的共享Slot。这种时候日志不一定直接报资源不足,更可能出现反复重试申请。我建议这时候把作业的并行度、共享组、chain关系列出来,手工算一遍究竟需要几个Slot,再对比集群实际的Slot数。

如果任务卡在DEPLOYING,那又是另一条排查路径,通常是TaskManager和JobManager之间的RPC连接有问题,或者TaskManager本地状态加载太慢。DEPLAYING卡住很少见,但一旦出现,先检查网络和TaskManager心跳。

4.2 Standalone集群slot不足时的Eager调度失败案例

我实际踩过一个很典型的坑。某个作业逻辑不复杂,主链并行度是3,sink并行度是6,集群是4个Slot的Standalone模式。我当时粗略一算,觉得3个Slot肯定够,结果作业一提交就失败。

后来把ExecutionGraph里的ExecutionVertex数拉出来看:主链某JobVertex有3个子任务,sink有6个子任务。因为并行度不一致,同一Slot里不能把主链子任务和sink子任务完全配对,实际需要4到6个Slot。Eager调度器一次性申请了超过4个的资源,直接判定失败。加上我配了固定延迟重启策略,这个作业就在“失败-重试-再失败”里打转,日志看起来像死循环,其实是每一步部署连第一步都跨不过去。

这个案例给我两个教训:第一,估算Slot需求不能只看总并行度,要结合chain和SlotSharingGroup算;第二,在Standalone等无动态扩容的环境里,Eager策略对资源预估的要求比老版本更严格,资源不足时会很快暴露,而不是给你等待的机会。解决方式也简单,要么把sink并行度调回与主链一致,要么提前把集群Slot数提上去,要么换到能动态申请资源的部署模式。

4.3 failover时调度器在做什么

作业运行中Task挂掉之后,调度器不是简单地把同一个Task重新部署一次。它会触发一个完整的Local Recovery流程:先确认受影响的ExecutionGraph区域,取消这些区域内的Task,释放它们的Slot,然后根据用户配置的RestartStrategy决定是否重试、重试几次。

Flink 1.12默认的故障恢复策略是Region-based Failover,也就是按“流水线区域”隔离故障。一个Task失败后,只会重启与它直接连通的pipelined区域,而不是全部作业都重启。这对长链路作业来说非常重要,能大幅减少恢复范围。调度器在恢复时,依然走Eager策略:把受影响区域的全部ExecutionVertex一次性调度起来。

所以你在日志里会看到这样一个模式:区域内的Task几乎同时进入DEPLOYING,然后一起RUNNING。相比老版本“逐个重启”,这个恢复速度体感明显更快。如果想切换策略,可以通过jobmanager.execution.failover-strategy配置,可选full或region,生产环境我建议保留region。同时要注意,恢复时间不只是调度时间,还包括从Checkpoint或Savepoint加载状态的时间,状态大时即使调度再快,整体恢复也快不起来。

5. 基于调度逻辑的调优经验

5.1 并行度与slot数怎么设计

理解了调度逻辑之后,设计并行度和Slot数就不再是拍脑袋了。我的经验是先做减法,再做加法。

先确定瓶颈算子的并行度,比如窗口聚合对CPU和内存要求高,那就以它为基准;然后让主链路算子的并行度尽量一致,这样Operator Chain能最大化合并,Slot共享也能把资源压缩到最低。常规做法是让一个TaskManager的Slot数等于它的CPU核心数,并行度整体接近集群总核心数,这样线程切换最少、数据本地性也好。

对于明显的高负载算子,比如复杂的自定义函数或大状态窗口,我建议不要让它和普通算子挤在同一个默认共享组里。Flink支持通过slotSharingGroup为算子指定独立共享组,把高负载算子隔离到专属Slot,避免它把同组其他任务拖死。但也要注意,组一旦分开,Slot总数很可能会上升,需要重新评估集群容量。

5.2 关键配置项速查

下面这些配置是我在1.12上最常用的调度相关参数,整理成一张速查表:

配置项作用我的推荐
parallelism.default默认并行度按集群规模和瓶颈算子的需求设置
taskmanager.numberOfTaskSlots每个TaskManager的Slot数一般等于CPU核数,不要贪多
jobmanager.scheduler调度器实现默认Default;生产先别动Adaptive
slotmanager.processing.timeoutSlot申请超时时间集群排队场景可适当调大
restart-strategy失败重启策略流作业用fixed-delay,重试次数别太高
jobmanager.execution.failover-strategy故障恢复范围默认region,保持默认即可

单独提醒一下slotmanager.processing.timeout,这个值默认是10秒左右,Eager调度一次性申请大量Slot时,如果资源不足需要等待,超时时间太短会很快报错。在YARN等能动态扩容的环境中,可以谨慎调大一些,给资源申请留出缓冲。

5.3 我踩过的几个坑

最后分享几个真实经历,都是调度逻辑在实操里最容易绊倒人的地方。

第一个坑,就是升级到1.12之后,我一度觉得“重启变快了”是全自动的,其实完全依赖新调度器和Eager策略。如果某个作业显式关闭了chain、并行度参差不齐,调度负担依然会很大,启动慢的问题也不会消失。

第二个坑,Web UI上Task变少了,不是调度器漏调度。默认chain开启时,几个算子合成一个Task是正常现象,不要被“少得可怜”的任务数吓到。想验证是否正确,可以在作业里调用disableOperatorChaining()对比一下启动时间和资源消耗,立刻能感受到chain对调度的影响。

第三个坑,sink或source的并行度和主链不一致,会让Slot占用翻倍。我前面那个sink并行度从3调到6导致作业失败的案例就是典型代表。现在我做并行度调整,一定会先动手算一遍最终Slot需求再改。

第四个坑,暂时不要在生产环境尝试1.12里的Adaptive Scheduler。虽然这个概念很吸引人,但1.12里它还属于实验特性,默认没有启用。我建议先在测试环境跑通,确认它的资源调整行为和你的部署模式匹配,再考虑上生产。等后续版本成熟了,这类调度器才有机会成为默认选项。

回过头看我自己的项目经历,有一次凌晨线上作业挂了,Eager调度器把所有Task几乎同时拉起,配合region failover和checkpoint恢复,整个作业十来分钟就回到了稳定状态。而同样规模的老版本作业,可能还在逐个Task排队恢复。调度逻辑这东西,平时藏在框架底层看不见,但一到故障和资源紧张的时候,它就会用结果告诉你:设计得好不好,差别是天上地下。你们如果也在折腾Flink 1.12,我建议多花点时间把JobManager日志里slot申请记录和Web UI上的Task部署时间线拉出来看一遍,那比对着文档背概念,更能把调度逻辑真正落到脑子里。

相关新闻

技术写作规范:为何宁可不写不可乱写

技术写作规范:为何宁可不写不可乱写

我无法根据当前输入生成符合要求的博文。原因如下&#xff1a;项目标题仅为"rea"&#xff0c;无明确语义&#xff0c;既非常见技术术语&#xff08;如 React、Realm、ReaScript&#xff09;、也非可识别的产品名、缩写或领域关键词&#xff1b;项目正文为空&#xff…

2026/10/21 6:35:37 阅读更多 →
uniapp考试答题小程序源码解析:从题库加载到自动判分

uniapp考试答题小程序源码解析:从题库加载到自动判分

简介&#xff1a;这是一份基于uniapp框架开发的考试答题类小程序源码&#xff0c;面向希望学习跨平台应用开发、了解前端答题逻辑的开发者与在校学生。项目实现了单选、多选、判断三种题型的答题界面&#xff0c;并支持上一题、下一题的自由切换&#xff0c;题目与答案均以静态…

2026/10/21 6:35:37 阅读更多 →
扣子工作流从零到上手:核心节点、搭建方法与避坑指南

扣子工作流从零到上手:核心节点、搭建方法与避坑指南

1. 扣子的工作流到底是什么&#xff0c;能解决什么问题先说结论&#xff1a;扣子&#xff08;Coze&#xff09;里的工作流&#xff0c;就是把“机器人回答问题”这件事&#xff0c;从一句简单的提示词变成一条可视化的流水线。以前你做一个AI机器人&#xff0c;基本套路就是写一…

2026/10/21 6:35:37 阅读更多 →

最新新闻

GEO源码平台部署全攻略:从环境配置到报错排查实战

GEO源码平台部署全攻略:从环境配置到报错排查实战

最近一两年&#xff0c;GEO&#xff08;Generative Engine Optimization&#xff0c;生成引擎优化&#xff09;已经从概念炒作变成了实打实的业务刚需。看着搜索结果里那些AI概览、大模型直接引用品牌信息&#xff0c;不少团队都想搭建一套自己的GEO分析平台&#xff0c;用来监…

2026/10/21 8:00:12 阅读更多 →
HDFS读写链路全拆解:block、packet、chunk与故障排查实战

HDFS读写链路全拆解:block、packet、chunk与故障排查实战

聊到HDFS&#xff0c;很多人张口就能说“块默认128MB、三副本”&#xff0c;但真要解释一次put操作从客户端发起&#xff0c;到数据最终落盘的完整链路&#xff0c;能讲清楚的人真不多。我带新人时发现&#xff0c;大家最容易卡住的是block、packet、chunk这三层概念经常混在一…

2026/10/21 8:00:12 阅读更多 →
从单向脚本到反馈闭环:Loop Engineering 实践入门

从单向脚本到反馈闭环:Loop Engineering 实践入门

1. “Loop Engineering”不是新造词&#xff0c;而是被严重误读的工程实践范式“Loop Engineering”这个短语最近频繁出现在技术社区、招聘JD和项目文档里&#xff0c;常被冠以“前沿”“下一代”“智能闭环”等修饰词&#xff0c;甚至有教程直接把它包装成某种独立的新技术栈。…

2026/10/21 8:00:12 阅读更多 →
锂电池组装设备选型与焊接工艺调试全指南

锂电池组装设备选型与焊接工艺调试全指南

我在华南一家自动化设备厂商做了多年锂电池组装设备相关的工艺和项目落地&#xff0c;从数码软包电池到电动工具方壳PACK都折腾过。很多刚入行的朋友问这行好不好入门&#xff0c;我的回答是&#xff1a;设备本身的门槛不高&#xff0c;真正拉开差距的是对工艺的理解。同样一台…

2026/10/21 8:00:12 阅读更多 →
基于WebSocket的实时对战小游戏Demo:协议、同步与架构解析

基于WebSocket的实时对战小游戏Demo:协议、同步与架构解析

简介&#xff1a;基于WebSocket的在线游戏开发Demo&#xff0c;是一套可直接运行的实时联机游戏示例。它面向希望快速理解全双工通信与游戏状态同步的前后端开发者&#xff0c;帮助解决从连接建立、消息推送到多玩家协同的常见问题。压缩包共包含488个文件&#xff0c;以JavaSc…

2026/10/21 8:00:12 阅读更多 →
Playwright Fixture机制:模拟数据库与API,根治测试数据污染

Playwright Fixture机制:模拟数据库与API,根治测试数据污染

做Web自动化测试这几年&#xff0c;我在实际项目里总结出三怕&#xff1a;一怕测试环境数据库连不上&#xff0c;二怕第三方接口突然限流&#xff0c;三怕测试数据互相污染。前两个问题会让你辛辛苦苦维护的用例在一夜之间全部标红&#xff0c;第三个问题则更隐蔽——明明昨天还…

2026/10/21 7:59:12 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →