颠覆Kafka的统治,新一代云原生消息系统Pulsar震撼来袭!

1bac8bfc70efb0f1138ef8129a32296d.png

导语 | 在信息流场景,内容的请求处理、原子模块调度、结果的分发等至关重要,将会直接影响到内容的外显、推荐、排序等。基于消息100%成功的要求,我对Pulsar进行了调研,并采用Pulsar实现消息的可靠处理。本文主要参考Pulsar的官方文档和技术文章,对Pulsar的特性、机制、原理等进行整理总结。

一、Pulsar概述

Apache Pulsar是Apache软件基金会顶级项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计,支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性,被看作是云原生时代实时消息流传输、存储和计算最佳解决方案。

Pulsar是一个pub-sub (发布-订阅)模型的消息队列系统。

(一)Pulsar架构

Pulsar由Producer、Consumer、多个Broker、一个BookKeeper集群、一个Zookeeper集群构成,具体如下图所示。

1f569ebc9bcda94f4bef44636c648aee.png

  • Producer:数据生成者,即发送消息的一方。生产者负责创建消息,将其投递到Pulsar中。

  • Consumer:数据消费者,即接收消息的一方。消费者连接到 Pulsar 并接收消息,进行相应的业务处理。

  • Broker:无状态的服务层,负责接收消息、传递消息、集群负载均衡等操作,Broker不会持久化保存元数据。

  • BookKeeper:有状态的持久层,包含多个Bookie,负责持久化地存储消息。

  • ZooKeeper:存储Pulsar、BookKeeper的元数据,集群配置等信息,负责集群间的协调(例如:Topic与Broker的关系)、服务发现等。

从Pulsar的架构图上可以看出,Pulsar在架构设计上采用了计算与存储分离的模式,发布/订阅相关的计算逻辑在Broker上完成,而数据的持久化存储交由BookKeeper去实现。

  • Broker扩展

在Pulsar中Broker是无状态的,当需要支持更多的消费者或生产者时,可以简单地添加更多的Broker节点来满足业务需求。Pulsar支持自动的分区负载均衡,在Broker节点的资源使用率达到阈值时,会将负载迁移到负载较低的Broker节点,这个过程中分区也将在多个Broker节点中做平衡迁移,一些分区的所有权会转移到新的Broker节点。在后面Bundle小节会具体介绍这部分的实现。

  • Bookie扩展

存储层的扩容,通过增加Bookie节点来实现。在BooKie扩容的阶段,由于分片机制,整个过程不会涉及到不必要的数据搬迁,即不需要将旧数据从现有存储节点重新复制到新存储节点。在后续的Bookkeeper小节中会具体介绍。

(二)Topic

和其他消息队列类似,Pulsar中也有Topic。Topic即在生产者与消费者中传输消息的通道。消息可以以Topic为单位进行归类,生产者负责将消息发送到特定的Topic,而消费者指定特定的Topic进行消费。


  • 分区Topic(Topic-Partition)

Pulsar的Topic可以分为非分区Topic和分区Topic。

普通的Topic仅仅被保存在单个Broker中,这限制了Topic的最大吞吐量。分区Topic是一种特殊类型的主题,支持被多个Broker处理,从而实现更高的吞吐量。

针对一个Topic,可以设置多个Topic分区来提高Topic的吞吐量。每个Topic Partition由Pulsar分配给某个Broker,该Broker称为该Topic Partition的所有者。生成者和消费者会与每个Topic分区的Broker创建链接,发送消息并消费消息。

如下图所示,Topic1有Partition1、Partition2、Partition3、Partition4、Partition5五个分区,Partition1和Partition4由Broker1处理,Partition2和Partition5由Broker2处理,Partition3由Broker3处理。

a546a502a77eaa4cba154cb98ae34fde.png

从Pulsar社区版的golang-sdk可以看出,客户端的Producer和Consumer在初始化的时候,都会与每一个Topic-Partition创建链接,并且会监听是否有新的Partition,以创建新的链接。

  • 非持久Topic

默认情况下,Pulsar会保存所有没确认的消息到BookKeeper中。持久Topic的消息在Broker重启或者Consumer出现问题时保存下来。

除了持久Topic,Pulsar也支持非持久Topic。这些Topic的消息只存在于内存中,不会存储到磁盘。

因为Broker不会对消息进行持久化存储,当Producer将消息发送到Broker时,Broker可以立即将ack返回给Producer,所以非持久Topic的消息传递会比持久Topic的消息传递更快一些。相对的,当Broker因为一些原因宕机、重启后,非持久Topic的消息都会消失,订阅者将无法收到这些消息。

  • 重试Topic

由于业务逻辑处理出现异常,消息一般需要被重新消费。Pulsar支持生产者同时将消息发送到普通的Topic和重试Topic,并指定允许延时和最大重试次数。当配置了允许消费者自动重试时,如果消息没有被消费成功,会被保存到重试Topic中,并在指定延时时间后,重新被消费。

  • 死信Topic

当Consumer消费消息出错时,可以通过配置重试Topic对消息进行重试,但是,如果当消息超过了最大的重试次数仍处理失败时,该怎么办呢?Pulsar提供了死信Topic,通过配置deadLetterTopic,当消息达到最大重试次数的时候,Pulsar会将消息推送到死信Topic中进行保存。

(三)订阅(subscription)

通过订阅的方式,我们可以指定消息如何投递给消费者。

  • 订阅类型(Subscription type)

Pulsar支持独占(Exclusive)、灾备(Failover)、共享(Shared)、Key_Shared这四种订阅类型。

独占(Exclusive)SinglePartition

Exclusive下,只允许Subscription存在一个消费者,如果多个消费者使用同一个订阅名称去订阅同一个Topic,则会报错。如下图,只有Consumer A-0可以消费数据。

3a04c7fd6d87802e2d71694c67e93bab.png

灾备(Failover)

Failover下,一个Subscription中可以有多个消费者,但只有Master Consumer可以消费数据。当Master Consumer断开连接时,消息会由下一个被选中的Consumer进行消费。

  • 分区Topic:Broker会按照消费者的优先级和消费名的顺序对消费者进行排序,将Topic均匀地分配给优先级最高的消费者。

  • 非分区Topic:Broker会根据消费者订阅的非分区Topic的时间顺序选择消费者。

如下图,Consumer-B-0是Master Consumer。当Consumer-B-0发生问题与Broker断开连接时,Consumer-B-1将成为下一个Master Consumer来消费数据。

79d37b1cb1303aa6094b5595b2b6bb6e.png

共享(Shared)

shared中,多个消费者可以绑定到同一个Subscription上。消息通过 round robin即轮询机制分发给不同的消费者,并且每个消息仅会被分发给一个消费者。当消费者断开连接,所有被发送给消费者但没有被确认的消息将被重新处理,分发给其它存活的消费者。

如下图, Consumer-C-1、Consumer-C-2、Consumer-C-3都可以订阅 Topic消费数据。

a386cad2294f751afa684330a15ef0f0.png

Key_Shared

Key_Shared中,多个Consumer可以绑定到同一个Subscription。消息在传递给Consumer时,具有相同键的消息只会传递给同一个Consumer。

0d041583f63fa6d068e4f18ffb3c9540.png

  • 订阅模式(Subscription modes)

订阅模式有持久化和非持久化两种。订阅模式取决于游标(cursor)的类型。

创建订阅时,将创建一个相关的游标来记录最后使用的位置。当订阅的consumer重新启动时,它可以从它所消费的最后一条消息继续消费。

  • Durable(持久订阅):游标是持久性的,会保留消息并保持游标记录的位置。当Broker重新启动时,可以从BookKeeper中恢复游标,消息可以从游标上次记录的位置继续消费。默认情况下,都是持久化订阅。

  • NonDurable(非持久订阅):游标不是持久性的,当Broker宕机时,游标会丢失并无法恢复,所以消息无法继续从上次消费的位置开始继续消费。

一个订阅可以有一个或多个消费者。当使用者订阅主题时,它必须指定订阅名称。持久订阅和非持久订阅可以具有相同的名称,它们彼此独立。如果使用者指定了以前不存在的订阅,则会自动创建订阅。

默认情况下,没有任何持久订阅的Topic的消息将被标记为已删除。如果要防止消息被标记为已删除,可以为此Topic创建持久订阅。在这种情况下,只有被确认的消息才会被标记为已删除。

  • 多主题订阅

当Consumer订阅Topic时,默认指定订阅一个主题。从Pulsar的1.23.0-incubating的版本开始,Pulsar消费者可以同时订阅多个Topic。可以通过两种方式进行订阅:

  • 正则表达式,例如:

    persistent://public/default/finance-.*

  • 明确指定Topic列表。

二、Pulsar生产者(Producer)

Producer是连接topic的程序,它将消息发布到一个Pulsar broker上。

(一)访问模式

消息生成者有多种模式访问Topic ,可以使用以下几种方式将消息发送到Topic。

  • Shared:默认情况下,多个生成者可以将消息发送到同一个Topic。

  • Exclusive:在这种模式下,只有一个生产者可以将消息发送到Topic ,当其他生产者尝试发送消息到这个Topic时,会发生错误。只有独占Topic的生产者发生宕机时(Network Partition)该生产者会被驱逐,新的生产者才能产生并向Topic发送消息。

  • WaitForExclusive:在这种模式下,只有一个生产者可以将消息发送到Topic。当已有生成者和Topic建立连接时,其他生产者的创建会被挂起而不会产生错误。如果想要采用领导者选举机制来选择消费者的话,可以采用这种模式。

(二)路由模式

当将消息发送到分区Topic时,需要指定消息的路由模式,这决定了消息将会被发送到哪个分区Topic。Pulsar有以下三种消息路由模式,RoundRobinPartition为默认路由模式。

  • RoundRobinPartition:如果消息没有指定key,为了达到最大吞吐量,生产者会以round-robin (轮询)方式将消息发布到所有分区。请注意round-robin并不是作用于每条单独的消息,而是作用于延迟处理的批次边界,以确保批处理有效。如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。这是默认的模式。

  • SinglePartition:如果消息没有指定key,生产者将会随机选择一个分区,并发布所有消息到这个分区。如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。

  • CustomPartition:自定义模式,用户可以创建自定义路由模式,通过实现MessageRouter接口实现自定义路由规则。

(三)批量处理

Pulsar支持对消息进行批量处理。批量处理启用后,Producer会在一次请求中累积并发送一批消息。批量处理时的消息数量取决于最大消息数(单次批量处理请求可以发送的最大消息数)和最大发布延迟(单个请求的最大发布延迟时间)决定。开启批量处理后,积压的数量是批量处理的请求总数,而不是消息总数。

  • 索引确认机制

通常情况下,只有Consumer确认了批量请求中的所有消息,这个批量请求才会被认定为已处理。当这批消息没有全部被确认的情况下,发生故障时,会导致一些已确认的消息被重复确认。

为了避免Consumer重复消费已确认的消息,Pulsar从Pulsar 2.6.0开始采用批量索引确认机制。如果启用批量索引确认机制,Consumer将筛选出已被确认的批量索引,并将批量索引确认请求发送给Broker。Broker维护批量索引的确认状态并跟踪每批索引的确认状态,以避免向Consumer发送已确认的消息。当该批信息的所有索引都被确认后,该批信息将被删除。

默认情况下,索引确认机制处于关闭状态。开启索引确认机制将产生导致更多内存开销。

  • key-based batching

key_shared模式下,Broker会根据消息的key来分发消息,但默认的批量处理模式,无法保证将所有的相同的key都打包到同一批中,而且Consumer在接收到批数据时,会默认把第一个消息的key当作这批消息的key,这会导致消息的错乱。因此key_shared模式下,不支持默认的批量处理。

key-based batching能够确保Producer在打包消息时,将相同key的消息打包到同一批中,从而consumer在消费的时候,也能够消费到指定key的批数据。

没有指定key的消息在打包成批后,这一批数据也是没有key的,Broker在分发这批消息时,会使用NON_KEY作为这批消息的key。

(四)消息分块

启用分块后,如果消息大小超过允许发送的最大消息大小时,Producer会将原始消息分割成多个分块消息,并将分块消息与消息的元数据按顺序发送到Broker。

在Broker中,分块消息会和普通消息以相同的方式存储在Ledger中。唯一的区别是,Consumer需要缓存分块消息,并在接收到所有的分块消息后将其合并成真正的消息。如果Producer不能及时发布消息的所有分块,Consumer不能在消息的过期时间内接收到所有的分块,那么Consumer已接收到的分块消息就会过期。

Consumer会将分块的消息拼接在一起,并将它们放入接收器队列中。客户端从接收器队列中消费消息。当Consumer消费到原始的大消息并确认后,Consumer就会发送与该大消息关联的所有分块消息的确认。

  • 处理一个producer和一个订阅consumer的分块消息

如下图所示,当生产者向主题发送一批大的分块消息和普通的非分块消息时。假设生产者发送的消息为M1,M1有三个分块M1-C1,M1-C2和M1-C3。这个Broker在其管理的Ledger里面保存所有的三个块消息,然后以相同的顺序分发给消费者(独占/灾备模式)。消费者将在内存缓存所有的块消息,直到收到所有的消息块。将这些消息合并成为原始的消息M1,发送给处理进程。

23a4399ae9a048db90b04b38fc6384fb.png

  • 多个生产者和一个生产者处理块消息

当多个生产者发布块消息到单个主题,这个Broker在同一个Ledger里面保存来自不同生产者的所有块消息。如下所示,生产者1发布的消息M1,M1 由M1-C1,M1-C2和M1-C3三个块组成。生产者2发布的消息M2,M2由M2-C1,M2-C2和M2-C3三个块组成。这些特定消息的所有分块是顺序排列的,但是其在Ledger里面可能不是连续的。这种方式会给消费者带来一定的内存负担。因为消费者会为每个大消息在内存开辟一块缓冲区,以便将所有的块消息合并为原始的大消息。

03b98aa5dd36bfc7a69ee67715893411.png

三、Pulsar消费者(Consumer)

Consumer是通过订阅关系连接Topic,接收消息的程序。

Consumer向Broker发送flow permit request以获取消息。在 Consumer端有一个队列,用于接收从Broker推送来的消息。

(一)消息确认

Pulsar提供两种确认模式:

  • 累积确认:消费者只需要确认最后一条收到的消息,在此之前的消息,都不会被再次发送给消费者。

  • 单条确认:消费者需要确认每条消息并发送ack给Broker。

45e5818e813e590b63fc9f27a63a7ca2.png

如图,上方为累积确认模式,当消费者发送M12的确认消息给Broker后,Broker会把M12之前的消息和M12一样都标记为已确认。下方为单条确认模式,当消费者发送M7的确认消息给Broker后,Broker会把M7这条消息标记为已确认。当消费者发送M12的确认消息给Broker后,Broker会把M12这条消息标记为已确认。

需要注意的是,订阅模式中的shared模式是不支持累积确认的。因为该订阅模式下的每个消费者都能消费数据,无法保证单个消费者的消费消息的时序和顺序。

  • AcknowledgmentsGroupingTracker

消息的单条确认和累积确认并不是直接发送确认请求给Broker,而是把请求转交给AcknowledgmentsGroupingTracker处理。

为了保证消息确认的性能,并避免Broker接收到非常高并发的ack请求,Tracker默认支持批量确认,即使是单条消息的确认,也会先进入队列,然后再一批发往Broker。在创建consumer的时候,可以设置acknowledgementGroupTimeMicros,默认情况下,每100ms或者堆积超过1000时,AcknowledgmentsGroupingTracker会发送一批确认请求。如果设置为0,则每次确认消息后,Consumer都会立即发送确认请求。

(二)取消确认

当Consumer无法处理一条消息并想重新消费时,Consumer可以发送一个取消确认的消息给Broker,Broker会重新将这条消息发送给Consumer。

如果启用了批量处理,那这一批中的所有消息都会重新发送给消费者。

消息取消确认也有单条取消模式和累积取消模式,取决于消费者使用的订阅模式。

在Exclusive模式和Failover订阅模式中,消费者仅仅只能对收到的最后一条消息进行取消确认。

在Shared和Key_Shared的订阅类型中,消费者可以单独否定确认消息。

如果启用了批量处理,那这一批中的所有消息都会重新发送给消费者。

  • NegativeAcksTracker

取消确认和其他消息确认一样,不会立即请求Broker,而是把请求转交NegativeAcksTracker进行处理。Tracker中记录着每条消息以及需要延迟的时间。Tracker默认是33ms左右一个时间刻度进行检查,默认延迟时间是1分钟,抽取出已经到期的消息并触发重新投递。Tracker存在的意义是为了合并请求。另外如果延迟时间还没到,消息会暂存在内存,如果业务侧有大量的消息需要延迟消费,还是建议使用reconsumeLater接口。NegativeAck唯一的好处是不需要每条消息都指定时间,可以全局设置延迟时间。

(三)redelivery backoff机制

通常情况下可以使用取消确认来达到处理消息失败后重新处理消息的目的,但通过redelivery backoff可以更好的实现这种目的。可以通过指定消息重试的次数、消息重发的延迟来重新消费处理失败的消息。

(四)确认超时

除了取消确认和redelivery backoff机制外,还可以通过开启自动重传递机制来处理未确认的消息。启用自动重传递后,client会在ackTimeout时间内跟踪未确认的消息,并在消息确认超时后自动向代理重新发送未确认的消息请求。

  • 如果开启了批量处理,那这批消息都会重新发送给Consumer。

  • 与确认超时相比,取消确认会更合适。因为取消确认能更精确地控制单个消息的再交付,并避免在消息处理时引起的超过确认超时而导致无效的再重传。

(五)消息预拉取

Consumer客户端SDK会默认预先拉取消息到Consumer本地,Broker侧会把这些已经推送到Consumer本地的消息记录为pendingAck,这些消息既不会再投递给别的消费者,也不会ack超时,除非当前Consumer被关闭,消息才会被重新投递。Broker侧有一个RedeliveryTracker接口,这个Tracker会记录消息到底被重新投递了多少次,每条消息推送给消费者时,会先从Tracker的哈希表中查询一下重投递的次数,和消息一并推送给消费者。

(六)未确认的消息处理

如果消息被消费者消费后一直没有确认怎么办?

unAckedMessageTracker中维护了一个时间轮,时间轮的刻度根据ackTimeout、tickDurationInMs这两个参数生成,每个刻度时间=ackTimeout/tickDurationInMs。新追踪的消息会放入最后一个刻度,每次调度都会移除队列头第一个刻度,并新增一个刻度放入队列尾,保证刻度总数不变。每次调度,队列头刻度里的消息将会被清理,unAckedMessageTracker会自动把这些消息做重投递。

重投递就是客户端发送一个redeliverUnacknowledgedMessages命令给Broker。每一条推送给消费者但是未ack的消息,在Broker侧都会有一个集合来记录(pengdingAck),这是用来避免重复投递的。触发重投递后,Broker会把对应的消息从这个集合里移除,然后这些消息就可以再次被消费了。

四、Pulsar服务端 

Broker是Pulsar的一个无状态组件,主要负责运行以下两个组件:

  • http服务:提供为生产者和消费者管理任务和Topic查找的REST API。Producer通过连接到Broker来发送消息,Consumer通过连接到Broker来接收消息。

  • 调度器:提供异步http服务,用于二进制数据的传输。

(一)消息确认与留存

Pulsar Broker会默认删除已经被所有Consumer确认的消息,并以backlog的方式持久化存储所有未被确认的内消息。

Pulsar的message retention(消息留存) 和message expiry (消息过期)这两个特性可以调整Broker的默认设置。

  • Message retention: 保留Consumer已确认的消息。

通过留存规则的设定,可以保证已经被确认且符合留存规则的消息持久地保存在Pulsar中,而没有被留存规则覆盖、已经被确认的消息会被删除。

617ae3f44fd19b9e4703858f4876e72b.png

  • Message expire(消息过期):设置未确认消息的存活时长(TTL)。

通过设置消息的TTL,有些即使还没有被确认,但已经超过TTL的消息,也会被删除。

7ddb82020744b9304c642f1b172b02da.png

(二)消息去重

实现消息去重的一种方式是确保消息仅生成一次,即生产者幂等。这种方式的缺点是把消息去重的工作交由应用去做。

在Pulsar中,Broker支持配置开启消息去重,用户不需要为了消息去重去调整Producer的代码。启用消息去重后,即使一条消息被多次发送到Topic上,这条消息也只会被持久化到磁盘一次。

如下图,未开启消息去重时,Producer发送消息1到Topic后,Broker会把消息1持久化到BookKeeper,当Producer又发送消息1时,Broker会把消息1再一次持久化到BookKeeper。开启消息去重后,当Producer再次发送消息1时,Broker不会把消息1再一次持久化到磁盘。

619c3b9cdb5341d3216b21dde4fe8db1.png

  • 去重原理

Producer对每一个发送的消息,都会采用递增的方式生成一个唯一的sequenceID,这个消息会放在message的元数据中传递给Broker。同时,Broker也会维护一个PendingMessage队列,当Broker返回发送成功ack后,Producer会将PendingMessage队列中的对于的sequence id删除,表示Producer任务这个消息生产成功。Broker会记录针对每个 Producer接收到的最大Sequence ID和已经处理完的最大Sequence ID。

当Broker开启消息去重后,Broker会对每个消息请求进行是否去重的判断。收到的最新的Sequence ID是否大于Broker端记录的两个维度的最大Sequence ID,如果大于则不重复,如果小于或等于则消息重复。消息重复时,Broker端会直接返回ack,不会继续走后续的存储处理流程。

(三)消息延迟传递

延时消息功能允许Consumer能够在消息发送到Topic后过一段时间才能消费到这条消息。在这种机制中,消息在发布到Broker后,会被存储在BookKeeper中,当到消息特定的延迟时间时,消息就会传递给Consumer。

下图为消息延迟传递的机制。Broker在存储延迟消息的时候不会进行特殊的处理。当Consumer消费消息的时候,如果这条消息设置了延迟时间,则会把这条消息加入DelayedDeliveryTracker中,当到了指定的发送时间时,DelayedDeliveryTracker才会把这条消息推送给消费者。

ed02c21814d9fb422bcc3da1fd1871be.png


  • 延迟投递原理

在Pulsar中,可以通过两种方式实现延迟投递。分别为deliverAfter和deliverAt。

deliverAfter可以指定具体的延迟时间戳,deliverAt可以指定消息在多长时间后消费。两种方式本质时一样的,deliverAt方式下,客户端会计算出具体的延迟时间戳发送给Broker。

DelayedDeliveryTracker会记录所有需要延迟投递的消息的index。index由Timestamp、Ledger ID、Entry ID三部分组成,其中Ledger ID和Entry ID用于定位该消息,Timestamp除了记录需要投递的时间,还用于延迟优先级队列排序。DelayedDeliveryTracker会根据延迟时间对消息进行排序,延迟时间最短的放在前面。当Consumer在消费时,如果有到期的消息需要消费,则根据DelayedDeliveryTracker index的Ledger ID、Entry ID找到对应的消息进行消费。

如下图,Producer依次投递m1、m2、m3、m4、m5这五条消息,m2没有设置延迟时间,所以会被Consumer直接消费。m1、m3、m4、m5在DelayedDeliveryTracker会根据延迟时间进行排序,并在到达延迟时间时,依次被Consumer进行消费。

3ef3b34c286ba4d00f4b37e1e6f6dd16.png

(四)Bundle

我们知道,Topic分区会散落在不同的Broker中,那Topic分区和Broker的关系是如何维护的呢?当某个Broker负载过高时,Pulsar怎么处理呢?

Topic分区与Broker的关联是通过Bundle机制进行管理的。

每个namespace存在一个Bundle列表,在namesapce创建时可以指定Bundle的数量。Bundle其实是一个分片机制,每个Bundle拥有 namespace 整个hash范围的一部分。每个Topic (分区) 通过hash运算落到相应的Bundle区间,进而找到当前区间关联的Broker。每个Bundle绑定唯一的一个Broker,但一个Broker可以有多个Bundle。

如下图,T1、T2这两个Topic的hash结果落在[0x0000000L——0x4000000L]中,这个hash范围的Bundle对应Broker2,Broker2会对T1、T2进行处理。

同理,T4的hash结果落在[0x4000000L——0x8000000L]中,这个hash范围的Bundle对应Broker1,Broker1会对T4进行处理;

T5的hash结果落在[0x8000000L——0xC000000L]中,这个hash范围的Bundle对应Broker3,Broker3会对T5进行处理;

T3的hash结果落在[0xC000000L——0x0000000L]中,这个hash范围的Bundle对应Broker3,Broker3会对T3进行处理。

17dc322ce601f9dee2739ed0a9c9ace7.png

Bundle可以根据绑定的Broker的负载进行动态的调整、绑定。当Bundle绑定的Broker的Topic数过多、负载过高时,都会触发Bundle拆分,将原有的Bundle拆分成2个Bundle,并将其中一个Bundle重新分配给不同的Broker,以降低原Broker的Topic数或负载。

五、Pulsar存储层(Bookkeeper)

BookKeeper是Pulsar的存储组件。

对于Pulsar的每个Topic(分区),其数据并不会固定的分配在某个 Bookie上,具体的逻辑实现我们在Bundle一节已经讨论过,而Topic的物理存储,实际上是通过BookKeeper组件来实现的。

(一)分片存储

概念:

  • Bookie:BookKeeper的一部分,处理需要持久化的数据。

  • Ledger:BookKeeper的存储逻辑单元,可用于追加写数据。

  • Entry:写入BookKeeper的数据实体。当批量生产时,Entry为多条消息,当非批量生产时,Entry为单条数据。

Pulsar在物理上采用分片存储的模式,存储粒度比分区更细化、存储负载更均衡。如图,一个分区Topic-Partition2的数据由多个分片组成。每个分片作为BookKeeper中的一个Ledger,均匀的分布并存储在BookKeeper的多个Bookie节点中。

基于分配存储的机制,使得Bookie的扩容可以即时完成,无需任何数据复制或者迁移。当Bookie扩容时,Broker可以立刻发现并感知新的Bookie,并尝试将新的分片Segment写入新增加的Bookie中。

14499a9ad248a463aadc8e4c57772b57.png

如上图,在Broker中,消息以Entry的形式追加的形式写入Ledger中,每个Topic分区都有多个非连续ID的Ledger,Topic分区的Ledger同一时刻只有一个处于可写状态。

Topic分区在存储消息时,会先找到当前使用的Ledger,生成Entry ID(每个Entry ID在同一个Ledger内是递增的)。当Ledger的长度或Entry个数超过阈值时,新消息会存储到新Ledger中。每个messageID由[Ledger ID,Entry ID,Partition编号,batch-index]组成。(Partition:消息所属的Topic分区,batch-index:是否为批量消息)

一个Ledger会根据Topic指定的副本数量存储到多个Bookie中。一个Bookie可以存放多个不连续的Ledger。

(二)读写数据的流程

  • Journals:Journals文件包含BookKeeper的事务日志信息。在对Ledger更新之前,Bookie会保证更新的事务信息已经写入Journals。当Bookie启动或者旧的Journals大小达到阈值时,就会创建一个新的Journals 。

  • Entry Logs:Entry Logs管理从Bookie收到的Entry数据。来自不同Ledger的Entry会按顺序聚合并写入Entry Logs,这些Entry在Entry Logs中的偏移量会作为指针保存在Ledger Cache中,以便快速查找。当Bookie启动或者旧的Entry Logs大小达到阈值时,就会创建一个新的Entry Logs。当旧的Entry Logs没有与任何活跃的Ledger关联时,就会被垃圾回收器删除。

  • Index Files:每个Ledger都会创建一个Index file,它包括一个头和几个固定长度的Index page,这些Index page记录存储在Entry Logs中的Entry的偏移量。由于更新索引文件会引入随机的磁盘I/O,所以索引文件由后台运行的同步线程延迟更新。这确保了更新的快速性能。在索引页持久化到磁盘之前,将它们聚集在Ledger Cache中以方便查找。

  • Ledger Cache:Ledger Cache存放在内存池中,这样可以更高效地管理磁盘头调度。

f9213ae3203c667494194a50e76aa677.png

  • 消息的写入

将Entry追加写入Ledger中。

将这次Entry的更新操作写入Journal日志中,当由多个数据写入时,可以批量提交,将数据刷到Journal磁盘中。

将Entry数据写入写缓存中。

返回写入成功响应。

到这里,消息写入的同步流程已经完成。

3-A. 内存中的Entry数据会根据Ledger和写入Ledger的时间顺序进行排序,批量写入Entry Log中。

3-B. Entry在Entry log中的偏移量以Index Page的方式写入Ledger Cache中,即iIdex Files。

Entry Log和Ledger Cache中的Index File会Flush到磁盘中。

  • 消息的读取

A.先从写缓存中以尾部读的方式读取。

B.如果写缓存未命中,则从读缓存中读取。

C.如果读缓存未命中,则从磁盘中读取。磁盘读取有三步:

  • C-1.读取Index Disk,获取Entry的偏移量。

  • C-2.根据Entry的偏移量,在Entry Disk中快速找到Entry。

  • C-3.将Entry数据写入读缓存中。

参考文献

1.Pulsar官方文档

2.BookKeeper官方文档

3.Apache Pulsar 技术系列-客户端消息确认

4.Apache Pulsar 技术系列-Message deduplication这里的去重与你想的可能不一样

5.Apache Pulsar 技术系列-Pulsar延迟消息投递解析

6.Apache 系列—Pulsar核心特性解析

 作者简介

a00f5303a6dbc96239ffa70c78b1d85e.png

雷洁彦

腾讯后端开发工程师

腾讯后端开发工程师,目前负责QQ浏览器信息流内容业务的后端开发工作。

 推荐阅读

七年激荡!Serverless的下一站将去往何方?

基于流计算Oceanus和Elasticsearch Service构建百亿级实时监控系统

云原生吞噬世界,是大势所趋还是技术炒作?

带你彻底击溃跳表原理及其Golang实现!(内含图解)

64702f3681ae0aa64bcfb80a826f211a.gif

相关推荐

大模型的Agent Skill功能,在LLM HTTP底层交互流中是怎么承载的?

网络文章文本的 skill,昨天我实现了之后也就开始好奇起 Skills 的底层原理了——相比起各种花里胡哨的 agrnt 应用,我一直以来都很对大模型在纯粹的 HTTP 交互层面是如何交互的更感兴趣。Skill 的全部"魔法"都发生在 system prompt 的措辞和 SKILL.md 文件的编写质量上——本质上是一种 prompt engineering + 文件系统的组合。但是各种天花乱坠的 skills,是如何在底层的 HTTP 中,与大模型——这个只会嘴遁的工具交互的呢?

QcloudCommunity的博客 402

万字入门AI Infra:深入理解大模型中的数学与Infra优化

关注腾讯云开发者,一手技术干货提前解锁👇AI Infra科普、入门或者进阶 ~~ 不到 5 万字,带你融会贯通理解大模型的数学、算法、硬件到系统工程。任何问题欢迎沟通交流。可怕,我竟然有点认同这句话。当然大概率是因为姚顺宇太懂AI了,而我则是因为无知者无畏。不过仔细想想,大模型推理中的数学逻辑确实不复杂,很多原理只需要高中数学知识就能看懂。但是为什么像 vLLM 这种推理系统这么复杂呢?本文将拆解大模型中几个核心操作(RMSNorm、Softmax、Causal Mask、Sampling)背后的数学与

QcloudCommunity的博客 1160

平平无奇的源码,竟藏着Agent的核心秘密?

System Prompt 是剧本,Skill 是剧本的扩展包,Agent Loop 是演员按剧本行动。欢迎留言,我们将邀请作者针对性回复你的评论,我们将选取1则优质的评论,送出腾讯云定制文件袋套装。,让 Agent 能主动"巡逻",而不只是被动等待用户消息。OpenClaw 的 System Prompt 分层结构。的"行为底线",任何 Skill 或用户命令都无法覆盖它。精华 2:Skill 的"按需加载"避免 token 浪费。精华 1:System Prompt 是"分层乐高积木"

QcloudCommunity的博客 424

QQ音乐Harness Engineering实践

一个看似简单的需求,背后可能牵动服务调用链、配置、灰度、埋点、错误码、接口契约和历史兼容性。当 AI 开始快速生成大量代码,真正的瓶颈就不再是"写不出来",而是"看不完、想不清、管不住"。在对话式编码阶段,一种典型的工作模式逐渐流行:开发者把需求丢给 AI,扫一眼输出"看起来对"就直接采纳,不审 diff、不追问逻辑、不验证边界。以音乐商业化业务为例,一个需求可能从 TAPD 单开始,经过需求评审、技术方案、服务影响面分析、IDL 契约变更、业务代码实现、测试验证、CR、灰度上线,最后才进入稳定运行。

QcloudCommunity的博客 712

从Prompt、Context到Harness,工程的三次进化与终局之战

同时,强制要求散落各处的决策记录(Slack、邮件、文档)全部迁移至代码仓库,确保 Agent 的唯一信息来源是可信的、版本受控的仓库。更坑爹的是,研究表明,当上下文过长时,模型会出现"中间遗忘(Lost in the Middle)"现象:它对开头和结尾的内容记忆较好,对中间大段内容的关注度大幅下降。这篇文章,我想带你完整走一遍这三次进化的逻辑:它们分别解决了什么问题,它们之间是什么关系,它们的边界在哪里,以及:当三者融合,AI 工程师的终极形态究竟是什么?大语言模型的本质,就是这位金鱼助理。

QcloudCommunity的博客 714

让Skill自己训练自己:8阶段Loop、3层评测、5维AND门控,从此实现自进化

所以每一轮改动都是有据可查的,不是「感觉这里可以优化」,而是「Case 41 挂了,因为 agent 把离职问题路由到了邮箱分类,但正确答案在通讯录,所以我改 root_index.md 加个易混淆提示」。但让我最后决定把它记录下来的,不是这的个「吸收」过程,而是它真的在替我探索我不想费脑力触达的边界,这看起来更像是一个机器智能应该有的样子。不准跨层,每轮只改一层的东西。但我经过这 19 轮之后觉得,更准确的描述是互补——你在明处看着,AI 在暗处替你试错,你看不见的那一半就是 AI 能贡献的地方。

QcloudCommunity的博客 1121

只加两行代码,为什么要两天?一文深度理解业务系统的复杂性

关注腾讯云开发者,一手技术干货提前解锁👇业务系统复杂性一直是令开发者头痛的问题。复杂的不是增加一个需求需要耗费多少时间,而是在增加一个需求后带来的蝴蝶效应:其它功能会不会受到影响、要如何去找到这些影响,最终如何实现系统正常运行......功能之间隐秘增加的耦合、不可避免的代码腐化在导致业务复杂性增加。大家都在说的软件开发提效到底在提什么?程序员日常工作中应该如何提升开发效率?敏捷开发、瀑布流式开发孰是孰非?欢迎阅读。业务背景与目标我依然非常清晰地记得某个时候,我所在团队 Leader 曾讲过一段话:“我担

QcloudCommunity的博客 709

从零设计生产级 Multi-Agent Harness:架构、评估、记忆、成本与 MCP 工具接入全拆解

在 AI Agent 领域,Multi-Agent Harness 指的是把多个 Agent 的能力、工具、状态、通信、编排、监控统一收束在一个运行时之内的框架。第三,Agent 路由。一个输入框,一个大模型,几个工具,一段写得很漂亮的 System Prompt,再配上一个炫酷的前端,看起来就像一个能干活的“数字员工”。Multi-Agent 完全不一样——它有计划、有中间步骤、有工具调用、有 Agent 协作、有重试、有反思、有最终合成。增加 Budget、权限、重试、压缩、轨迹评估、审计、回归测试。

QcloudCommunity的博客 706

拆完Hermes源码,我发现Agent的“自我进化“根本不需要训练模型

两个月 5.2 万 Star,Hermes Agent 用一个"LLM 审判官"机制实现了 Agent 的自我进化——不改模型权重,只改"怎么用模型"的策略。OpenClaw(社区昵称"龙虾"🦞)是 2025-2026 年最火的开源 Agent 框架,30 万+ Star,插件市场 ClawHub 里有数千个插件,能接入 Telegram、WhatsApp、Slack、飞书、企业微信——几乎是一个"个人 AI 操作系统"。Skill 是英语写的,Memory 是英语写的,审查 Prompt 也是英语写的。

QcloudCommunity的博客 905

AI Infra 其实没有多少新东西

传统 Infra 追求横向扩展,而 AI Infra 呈现 “AI 大型机”特性,是因为传统后台服务的可以容忍毫秒级延迟,但 AI 集群不行,GPU 的算力是 CPU 的数百倍,微秒级的延时等待也会造成很大的算力损耗,需要硬件的高度集成。在可预见的1-3年的未来,这样的专用硬件+网络的集中式架构很难发生比较大的改变。与之相似,模型推理的批处理就是将多个输入样本打包(batch),将原本串行的 N 次轻量的推理计算,合并为 1 次重量的计算,实现单位时间内处理更多的请求,提高了 GPU 利用率。

QcloudCommunity的博客 820

程序员越早想通这些越好

永远没有正确架构,你永远无法偿还完所有的技术债务,如同你永远不会设计出完美的界面,请避开完美主义的陷阱,永远没有完美的代码,能够满足当前需求,又为未来不管是自己还是他人留有扩展空间,就很好了。我们努力的方向就是将现实世界映射到有限的数据结构中,这就需要用户自定义类型,如果你的代码中包含类似 trader portfolio 这样的概念,你就可以使用他们的名称来建模,而不是使用基本数据类型复杂嵌套表述你的目的。从另一个角度看,代码里的注释都是补丁,是碎片化的,局部的注释很难推导模块整体的顺序及耦合关系。

QcloudCommunity的博客 464

RAG已死?不,是Grep回归了!

这样一来可以直接复用已有的计算缓存,只为新增的增量部分付全价,前面累积的大头按约 1/10 的缓存价计费。所以实际的工具组合方式是灵活的:Grep(默认模式)→ Read 是最常见的路径,但 Grep(content 模式)可以独立使用,LLM 也可以直接调 Read(如果已经知道文件路径),或者一次同时发起多个 Grep 并行搜索。它从零开始构建自己的对话历史,不继承主对话的消息,这意味着它搜索过程中产生的大量 grep 结果、代码片段都留在自己的 context 里,主对话只收到一段总结性的文本结论。

QcloudCommunity的博客 571

深入浅出Harness Engineerring之核心模式与理念

在智能体生命周期的关键节点(如会话开始、工具调用后)自动触发预设动作(如代码格式化),由系统确保关键流程被执行,不依赖可能被模型遗忘的指令。简单讲就是利用向量存储这个技术,来实现模糊检索,原理是:即使字面不同,但语义相近的文本,其向量在数学空间中的位置也很接近。根据命令类型、参数和影响,自动评估其风险等级(安全、有风险、危险),并采取自动执行、请求确认或直接拦截等不同策略。关键特性是可隔离、可重建、可扩展。不是一次判断就定终身,允许你改变、允许情况复杂,通过不断观察、思考、调整,越来越懂真实的你。

QcloudCommunity的博客 621

Harness Engineering实践心得:如何高效驾驭AI?

但如果这些经验被拆成一条条 Rule、一个个 Skill、一项项 Script 检查——"XAML 不能有中文"、"编译要用这个路径的 MSBuild"、"SVN 命令必须带认证参数"——AI 就能在秒级时间里"吸收"十年的踩坑经验。然后可以看到下方的图,我的PM会很顺利的调度其他agent分别负责不同的事项,遇到问题的时候也能准确的找到合适的agent。MCP 本质上是在扩展 AI 的"感知范围"——Rule 管"该怎么做",Skill 管"怎么操作",MCP 管"能看到什么、能碰到什么"。

QcloudCommunity的博客 838

从第一性原理思考 Agentic Engineering

需要注意的是,这一结论是有条件的:如果 AI 缺乏领域知识,过早介入反而可能引入 1.2 节所述的"似是而非"损耗——因此上下文供给(实践 1)是 AI 全链条参与的前提,而非结果。通过一个专门的自迭代 Skill 实现——当 AI 在协作过程中识别到自己被纠正(用户否定了 AI 的输出并给出了正确方向),它自动评估这个错误的根因,搜索现有的 Rules 和 Skills 中是否已有针对该类问题的处理规则。事实上,越是复杂的项目,工程师的认知负担越重(公理 3),AI 分担认知负担的潜在边际价值也越大。

QcloudCommunity的博客 756

万字干货!Harness Engineering如何工程化落地?

关注腾讯云开发者,一手技术干货提前解锁👇Harness Engineering 的概念已经火了有一阵了,全网很多文章基本都是在讲理念,讲为什么今天做 AI 开发,不能只靠一段提示词,也不能把模型当一个“更聪明的代码补全”。但与此同时,一个问题也在持续地发酵:这个问题特别重要。因为 Harness 这个词听上去很大,很像一个抽象的方法论,但它如果不能落到工程、目录、文档、脚本、工作流里,那它最后就只是一句漂亮的话。事实上,不同项目落地出来的 Harness,表面上可能完全不一样。有的项目是 CI 很强,有的

QcloudCommunity的博客 1161

全方位对比:Hermes VS OpenClaw

多账号矩阵运营 如果你的业务涉及同时管理多个社交媒体账号、多个平台的内容分发、多渠道的客户互动——OpenClaw是目前市场上的"无可替代的底座"。一个有专业背景的个人,现在可以快速构建一个垂直领域的AI助手:比如"财务分析Hermes"、"法律咨询Hermes"、"技术架构Hermes",直接通过微信提供服务,积累自己的专业IP。跨平台生态融合 如果你的工作流涉及飞书、Slack、Discord等多个平台的深度集成,需要这些平台之间的数据流转和联动——OpenClaw的生态支持是强大的。

QcloudCommunity的博客 1216

一文讲透:Harness Engineering即控制论!

而研发模式的升级我认为将会是一个飞跃的过程,之所以现在我们仍然需要需要参与编码,一个非常大的原因就是缺乏传感器和基建,仍然需要人来串流程,比如需要人来验证UI是否正确、需要人来告诉AI代码不符合代码规范,因此在有些场景我们仍然感觉 AI 完成的很吃力,但这不是 AI 能力不行,而是我们并没有为 AI 提供友好的环境……控制论中的突变理论介绍了矫枉过正和飞跃现象之间的矛盾,如果质变中经历的中间过渡态是不稳定的,那么他就是一个飞跃的过程,如果中间过渡态是稳定的,那么他就是一个渐变的过程。但是这些阶段要解决的。

QcloudCommunity的博客 1202

神级开源插件!一句话让QClaw变3D小镇,Agent工作可视化,赛博云监工

这个架构是翻了几次车才想通的—原有AI的实现机制是用 setTimeout 猜动画时长导致了幽灵 NPC、工位撞车、越改越崩,后面我停下来换了思路,推翻原有的架构,按照“意图+回传”的架构进行重构,之前的问题一次性全部解决。15 天,我和 AI 吵了 185 次架, 4.5 万行代码,开发了一款 QClaw / OpenClaw 的UGC游戏插件,这可能是人类跟 Agents 交互的新范式,插件已完全开源,有需要的欢迎自取~我天天用,用着用着我就在思考,Agent 产品现在的交互体验,基本都一个样。

QcloudCommunity的博客 1195

一文搞懂Hermes:新顶流Agent如何从经验中自我进化

Voyager 是一个能在 Minecraft 中自主探索、学习技能、无限积累能力的 AI Agent,它提出了 "Skill Library"(技能库)的概念——Agent 将成功的行为序列编码为可复用的技能函数,存储在一个持续增长的代码库中。如果 Skill 的名称和描述不够精准,或者用户的任务描述与 Skill 的触发条件有语义差距,Agent 可能会错过相关的 Skill。如果进程在写入过程中崩溃,目标文件要么是旧内容(还没被替换),要么是新内容(替换已完成),绝不会出现写了一半的损坏文件。

QcloudCommunity的博客 2628

自掏腰包一万元,拥抱AI这一年,我的工具、实践和思考

其实也很好理解,过往的每个领域(比如前端、后端、设计、测试等等)都主要是这个领域的从业者在为它添砖加瓦,但是大模型时代的 AI 应用领域却几乎是所有使用 AI 的人类一起在迭代,无分领域,无分文理,无分老少。因为每个时期的 AI 都有着不同的特点,很多时候当我费尽心思去钻研,自以为有点弄明白 AI 应该怎么玩的时候,基座模型和行业范式常常就会迎来颠覆式的变革,很多过往的经验都会倾覆,然后只能从头再来。过去一年多,被热议的 AI 范式大概是沿着四个阶段递进演化,每一个阶段其实都在解决前一个阶段的核心问题。

QcloudCommunity的博客 626

办公Agent的CI/CD时刻到来了

核心观点是:企业给员工配了一堆 AI 工具,试点跑了几百个,但真正拖慢 AI 转型速度的瓶颈不在模型能力,在"最后一公里":AI 输出和实际工作流之间的那道鸿沟。代码写完,打个 tar 包,开 FTP 传到服务器上,SSH 进去解压,改配置文件,重启服务,刷一下页面祈祷别 500。你复制,切窗口,粘贴到腾讯文档里,发现 Markdown 格式全乱了,手动调标题层级,代码块重新标记语言类型,表格散架了重排一遍。模型是越来越聪明了,但"生成"到"落地"之间那段路,还是你自己在走,一步一步,手动搬运。

QcloudCommunity的博客 607

4亿token买来5个教训:让6个AI Agent连写4天代码发生了什么?

Watchdog 检测到崩溃 → 启动诊断 Agent 分析原因 → 诊断 Agent 跑满 20 轮没搞定 → Watchdog 判定“已修复”,重启系统 → 重启的系统因为诊断 Agent 留下的环境变量被继承,连续 3 次拒绝启动 → 触发连续失败保护退出 → Watchdog 第 3 次巡检,发现进程和记录文件都没了,尝试手动重启,自己也停了。启动前我翻了论文、做了架构设计、写了监控+修复异常的脚本。这个发现让我的情绪从沮丧变成了兴奋——不是那种"终于修好了"的释然,是"我找到根因了"的快感。

QcloudCommunity的博客 583

赛博斗蛐蛐:9大模型决战三国志,天命在谁?

我猜是这样的,当Gemini面临决策的时候,一系列关键词比如 守城,劣势,粮草,三国等等,在语义层面的关联性中LLM发现了竖壁清野这个关键词概率非常高,所以以此为策略,然后再根据这个策略完美执行操作。MiMo 的外交是三方中最精明的。Kimi作为国产模型,也同样颠覆了我对他的看法,最后的比赛他甚至一度是优势者,存在很多夺冠的可能性,要知道他的对手可是claude和gemini,而他经常出兵慢,回过头来想想或许正是他的策略,毕竟中后期他的优势都来自于稳健的发展和较少的战斗频率(和claude比).

QcloudCommunity的博客 872

Claude Code是怎么知道你在骂他的?这 12 条发现值得关注

确实存在——5,500+ 行,集成了消息队列、工具装配、MCP 编排、会话状态、权限、分析、文件持久化和优雅关闭,基本上一个文件干了所有事。对抗虚假声明——"不要在输出显示失败时声称所有测试通过,不要压缩或简化失败的检查来制造绿色结果"。完整的 KAIROS 是一个持续驻留的守护进程,能感知你在不在电脑前、主动寻找有用的工作做,更像是住在终端里的全职同事。源码里藏着一个叫 "Buddy" 的电子宠物系统,完整度令人惊讶——这不是一个 TODO,而是一个从数值系统到 ASCII 动画都实现了的完整功能。

QcloudCommunity的博客 701

逆向深扒Claude Code源码,我发现了什么!?

Anthropic 内部用户获得更严格的质量控制 prompt、更好的验证机制(独立 Verification Agent)、以及针对 model 弱点的专门对策(如 Capybara v8 的 29-30% 错误声明率对策)。这不是代码混乱的标志——它是整个 Agent Loop 的心脏,所有的流式处理、工具调度、上下文压缩、子代理管理都在这个文件中协调。这种"胖核心"设计是刻意的:保持核心循环的原子性,避免分散到多个模块导致的协调复杂度。核心价值:Fork 代理的工具输出不会回到主代理的上下文窗口。

QcloudCommunity的博客 885

一文讲透如何构建Harness——六大组件全解析

本文将从"裸模型的四个硬伤"出发,逐一拆解 Harness 的六大组件——文件系统、Bash + 沙箱、记忆(AGENTS.md)、Web Search + MCP、上下文工程、编排 + Hooks——带你看清 AI Agent 真正的竞争壁垒,不在模型层,而在 Harness 层。如果你用过 Agent 做复杂任务,你一定有过这样的体验:一开始 Agent 的表现很好,但随着对话变长,它开始"变笨"——遗忘之前的约定,重复之前犯过的错误,甚至对同一个问题给出前后矛盾的回答。对于简单的问答场景,这无所谓。

QcloudCommunity的博客 759

Harness Engineering 来了,SDD 还有意义吗?

他们的解法是把「品味」编码进规则,然后用一个定期运行的「doc-gardening」Agent 自动扫描漂移、发起修复 PR——「技术债务就像一笔高息贷款,不断地以小额贷款的方式偿还,总比让债务不断累积要好」。代码仓库本地的、已版本化的工件,才是 Agent 所能看到的全部。在 Vibe Coding 模式下(即不做规划、随意向 AI 提需求的开发方式),大量设计决策、跨服务约定、「为什么不选另一个方案」的讨论,全部停留在一次性的 AI 对话里——下一个人,或者下一次 AI 会话,完全无从获取。

QcloudCommunity的博客 417

零废话!一文讲透从0构建AI Agent

每个工具都要写一套"定义→解析→执行"的胶水代码。上下文管理就是在"记住足够多"和"别超标"之间找平衡,常见策略包括滑动窗口、摘要压缩、RAG 检索增强。上下文窗口是 Agent 的"工作记忆",但它有硬性限制。LLM 负责"动脑"决策,代码负责"动手"执行。这是从"聊天机器人"进化到"Agent"的关键一步。每次调 API 都把完整对话历史发过去,LLM 本身是无状态的,"记忆"完全靠客户端的数组模拟。这样 Agent 一开始就知道自己"在哪里"、"手头有什么",不需要先花一轮工具调用去探索环境。

QcloudCommunity的博客 710

距离AI把小编我裁掉还有72小时

这是一个基于 OpenClaw 开源生态打造的本地化 AI Agent 助手,已于上周开启全量公测,无需复杂配置,三步即可上手,自带安全防护,支持微信直连绑定,内置多款优质国产大模型,现阶段也是完全免费使用,基本上前面提到过的痛点都可以靠它解决。对了,这里特别强调一点,上面的截图让产品同事汗流浃背,再三找我确认 QClaw 思考过程中优美的“草”字说法,不会是默认出现的吧?AI 好,AI 得学,但更重要的是,在你的工作和生活里,哪些东西是能结构化地被 AI 所接入的,不要死守着那些脏活累活不放。

QcloudCommunity的博客 673
上一篇: 带你彻底击溃跳表原理及其Golang实现!(内含图解)
下一篇: 代码质量第 3 层 - 可读的代码
腾讯云开发者
腾讯云开发者 企业官方账号 企业官方账号
博客等级 码龄10年 6290粉丝 1962原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值