摘要
一句话总结
多智能体协同本质上是一个分布式系统问题,为此构建的 kli 协同层通过事件溯源和无冲突复制数据类型(CRDTs),利用本地纯文本 JSONL 日志实现了多并发会话的无锁状态收敛与无消息协同。
核心要点
- 分布式系统本质:多智能体会话具有私有状态和共享可变资源,表现出分布式计算的四大属性(延迟、独立内存、并发、局部故障),无法仅靠检索或提示词解决结构化、时间性和协同查询问题。
kli架构设计:作为一个单二进制文件和 MCP 服务器(暴露 31 个工具和 7 个生命周期钩子),它不依赖外部数据库,仅使用受版本控制的本地 JSONL 文件,唯一的外部依赖是用于嵌入的本地模型(ollama)。- 基于事件的状态计算:系统不直接存储状态,而是通过重放(折叠)包含 19 种事件类型(如
task.create、observation等)的仅追加(append-only)日志来计算当前状态。 - CRDT 收敛合并:任务状态字段采用 LWW-Registers、G-Sets、OR-Sets 和 LWW-Maps 等 CRDT 数据结构,确保并发写入满足交换律、结合律和幂等性,无需锁或协同协议即可自动解决冲突。
- 无消息协同(共识主动性):智能体不通过直接消息传递,而是通过在共享日志中留下行为痕迹(如工具调用、文件读写)进行间接协同,系统据此计算行为指纹以预警文件冲突或接管孤儿任务。
- 专用图查询语言:放弃了 SQL,开发了基于 S-表达式的 TQ(任务查询,用于遍历任务 DAG)和 PQ(剧本查询,用于模式图的激活扩散),以满足智能体特定的访问模式。
- 深度反思机制:结构化的事件日志保留了决策的因果链条和死胡同,允许 LLM 在任务结束后重放完整历史,生成并评估可供未来检索的评分模式。
风险与局限性
- 跨团队功能未充分验证:将会话集群划分为团队的协同信号虽已实现,但尚未在真实的多团队工作流中进行端到端验证。
- 缺乏通用查询能力:TQ 和 PQ 仅针对智能体协同设计,不支持任意的 Join、聚合或即席 SQL 分析。
- 单会话开销过大:对于单兵作战且无并发会话的开发者,CRDT 机制带来了不必要的复杂性。
- 日志无限增长与性能瓶颈:仅追加日志目前缺乏快照或截断机制,重放延迟随事件数量线性增长,且 JSON 反序列化耗时远超 CRDT 计算本身。
- 存储扩展性受限:当前依赖文件系统和 git 进行同步,达到一定规模后将需要真正的网络传输层。
- LWW 策略的语义缺陷:标量字段的“最后写入者胜出”策略可能导致时间戳较晚但上下文较差的写入覆盖掉更有价值的信息。
- 向量时钟未实际应用:事件已收集向量时钟,但当前排序仍依赖挂钟时间戳,这在跨机器运行会话时无法保证因果正确性。
正文
打开 Claude Code 开始工作。昨天的会话已经消失:它理解的架构、建立的约定、探索过的死胡同都不复存在。目前还有另外两个会话正在重叠的文件上运行,而你的会话却无法看到它们。私有上下文,没有共享状态。因此,你只能重新解释、重新发现,并看着智能体(Agents)在彼此的工作上互相踩踏。
在本博客交互功能的开发过程中,一个会话在三个调用点发现了一个编码错误——一个会静默破坏 WebSocket 通信的 alist/plist 不匹配问题。它将诊断结果记录在任务的事件日志中并退出。下一个会话在启动时重放了该日志并继承了修复方案,而它们之间没有任何显式的通信。这就是标准的事件重放(Event replay)。
这就是协同(Coordination),而不是检索、记忆或更好的提示词。多会话 AI 智能体工作流是具有私有状态、独立故障模式和共享可变资源的并发进程,表现出 Waldo 等人指出的区分分布式计算与本地计算的所有四个属性:延迟、独立内存、并发和局部故障。Lamport 最初的论述也明确包含了单台计算机上的进程。协同问题非常具体:现在还有谁在编辑这个文件?凌晨 2 点崩溃的会话是否留下了半成品?哪些保存的模式真正改善了结果?这些在分布式系统文献中都有充分研究的解决方案。事件溯源(Event sourcing)提供了持久化的历史记录;CRDTs(无冲突复制数据类型)提供了收敛合并,其正确性证明是纯代数的,定理陈述中不需要网络模型。
具体的协同问题由此产生:对任务依赖的结构化查询、对跨会话决策传播的时间查询,以及对并发写入的冲突检测。每种问题都需要不同的计算原语(图遍历、事件重放、合并语义),这是相似性搜索无法提供的。
对于单库单兵作战的开发者,简单的方法就足够了。将一组精心维护的 Markdown 文件提交到 git,就能以零新工具的成本获得持久化和版本控制。如果你没有运行并发会话,这确实是正确的选择。但当达到特定阈值时,简单方法就会失效。当智能体共享凭证时,git blame 无法区分会话;为每个智能体分配独立的 git 身份也无法扩展。如果没有原则性的评分机制,扁平的上下文文件会不断膨胀,直到降低上下文质量。图数据库(如 Neo4j)能很好地处理带类型的边遍历,但引入了外部服务依赖;关系型数据库可以用递归 CTE 表达,但查询很快会变得笨拙。我们选择了带有 CRDT 合并语义的 JSONL 事件日志,因为它们是受版本控制的纯文本文件:无需运行外部服务,无需数据库迁移,支持离线工作,且当工具崩溃时 grep 依然可用。
kli 是一个为 AI 智能体会话构建的协同层,基于事件溯源和无冲突复制数据类型(CRDTs)。会话将事件追加到共享日志中,状态通过重放计算得出,并发写入无需锁或协同协议即可收敛。我们将智能体上下文视为不断演进的结构化集合,而不是静态提示词。我们的核心贡献是将观察结果作为事件记录在仅追加(append-only)的日志中,让每个会话都能访问之前会话学到的所有内容,并在并发运行时提供 CRDT 合并语义。基于嵌入(Embedding)的检索处理观察搜索和模式激活;而事件溯源图则处理检索无法解决的结构、时间和协同查询。整个系统只有一个二进制文件,一个暴露 31 个工具和 7 个生命周期钩子的 MCP 服务器,以及一个外部依赖:用于搜索和检索的本地嵌入模型(ollama)。其他一切都是自包含的:与你的代码放在一起的、受版本控制的纯 JSONL 文件。
三大协同问题
每种问题都有独特的计算特征,且都不能简化为相似性搜索。
- 结构化查询:“哪些任务阻塞了当前任务?当前计划的叶子阶段是什么?”这需要遍历带有类型边(
depends-on、phase-of、related-to)的图,即有向无环图(DAG)上的传递可达性。元数据过滤器只能匹配单个字段,无法沿着depends-on边追踪三层来发现真正阻塞你工作的因素。 - 时间查询:“在会话崩溃到现在的这段时间里发生了什么?这些决策是如何跨会话按顺序传播的?”这需要因果排序,而不仅仅是时间戳过滤。元数据过滤器可以给你“过去一小时的观察结果”,但无法重建哪个会话的交接信息指导了后续哪个会话的工作。这是事件序列的属性,而不是单个记录的属性。
- 协同查询:“两个会话是否即将发生冲突?这个会话是否应该申请独占访问权?”这需要活跃度检测和合并语义:了解哪些会话当前处于活跃状态、它们正在编辑哪些文件,以及如何解决对同一字段的并发写入。任何检索系统(无论过滤得多好)都无法提供这种协同。
Halpern 和 Moses 证明,当进程维护私有状态时,在共享内存系统中坍缩的知识层级依然严格存在。无论物理拓扑如何,系统在认知上都是分布式的。智能体会话完全满足这一条件:每个会话都带有一个其他会话无法观察到的私有上下文窗口。正是信息访问的结构(而非网络的存在)创造了分布式特性。分布式系统研究针对这类问题已有成熟的解决方案。
运行机制
基于事件的状态计算 (State from events)
kli 不在数据库中存储任何内容。每个任务都是一个包含 JSONL 事件日志的目录,默认存储在 git 根目录的 .kli/tasks/ 下。状态就是重放日志得到的结果。这是一个折叠(fold)操作:
给定事件日志 和初始状态 :
实际的 apply-task-event 处理 19 种事件类型:task.create、session.join、observation、tool.call 等。任务状态结构体的每个字段本身也是一个 CRDT:状态和声明使用 LWW-Registers(最后写入者胜出寄存器),观察和会话使用 G-Sets(仅增长集合),边和制品使用 OR-Sets(观察-移除集合),元数据使用 LWW-Maps。但核心原理就是折叠:创建空状态,迭代,应用。
在实践中,这意味着任务数据就是受版本控制的文件:git log 是你的任务历史,grep 可以查找观察结果,每个事件都带有时间戳和会话 ID。
事件日志还有第二个用途。在任务结束时,开发者可以调用反思命令,通过 LLM 智能体重放完整的事件日志(每个工具调用、每个观察、每个死胡同)。LLM 会评估发生的事情,提出新模式,并对现有模式进行评分(附带证据)。因为事件日志携带了足够的结构来重建决策原因和结果,这种反思是实质性的而非表面的。生成的模式进入一个评分图中,未来的会话可以通过语义相似度检索它们。
收敛合并 (Convergent merge)
仅追加(Append-only)本身是不够的。当两个会话写入同一个任务时,它们的事件需要合并。考虑一个具体场景:会话 A 移除了一个过期的边,而并发工作的会话 B 向同一个任务添加了一条新边。如果使用朴素集合,A 的移除可能会抹杀 B 的添加。正确的行为是:B 的添加存活下来,因为 A 的移除只应影响 A 所知道的内容。
这就是 OR-Sets(Observed-Remove Sets)解决的问题。每次添加都带有一个唯一标签。移除操作只会墓碑化(tombstone)它已经观察到的标签;来自并发添加的、尚未被观察到的标签将存活下来。
任何基于状态的 CRDT 的合并函数必须满足交换律、结合律和幂等性。这使得系统无需协同协议、无需锁、无需手动解决冲突即可实现收敛。结合律提供了与顺序无关的三方合并;幂等性意味着重放重复事件是无害的。
无消息协同 (Coordination without messages)
大多数智能体协同系统使用消息传递:请求、响应、通过代理共享状态。kli 不需要这些。每次工具调用都会向共享日志写入一个追踪事件:文件路径、会话 ID、时间戳。当新会话启动时,引导程序会读取这些追踪记录,发现其他会话所做的工作,而它们之间没有任何显式消息传递。信息存在于环境中。
Heylighen 将这种模式形式化为共识主动性(Stigmergy):通过在共享媒介中留下的痕迹进行协同,一个智能体行动留下的痕迹会刺激另一个智能体的后续行动。kli 中的“存放并响应”循环完全符合这一定义。kli 进一步计算所有会话追踪的行为指纹并提供相似度分数,这被称为认知共识主动性(Cognitive stigmergy):用理性智能体可以推理的计算制品来增强间接协同。
在实践中,引导程序会为每个先前的会话计算行为指纹:它调用了哪些工具、读取与编辑了哪些文件、其观察结果在嵌入空间中的聚类情况等。会话根据其工具与事件的比例被分类为 :builder(构建者,主要进行编辑)或 :observer(观察者,主要进行读取)。当两个构建者在相同文件上重叠时,系统会发出冲突警告。
除了文件冲突,kli 还利用这些痕迹进行“孤儿任务接管”。当一个会话崩溃或断开连接时,下一个引导相同任务的会话会发现被遗弃的阶段并可以接管它们。
从单会话到团队 (From sessions to teams)
Claude Code 的团队功能会生成多个智能体(如团队负责人、研究员、实现者等),它们共享任务列表并通过直接消息通信。kli 并不重新实现这些。从 kli 的角度来看,团队只是碰巧共享一个 team-name 的会话集群。当智能体加入团队时,kli 会读取环境变量并发出 session.team-join 事件。
会话指纹携带相同的身份信息。当新会话启动时,集群感知功能会根据 team-name 将活跃会话划分为团队和独立智能体,标记跨团队的文件冲突,并将同团队的构建者分组。
需要坦诚的是:这部分已实现但尚未经过充分验证。我们还没有端到端地运行过真实的多团队工作流来确认这些跨团队协同信号在实践中是否真正有效。
图查询
放弃数据库而选择事件溯源的代价是:没有 SQL,无法跨任务进行 Join 操作。为了弥补这一点,我们构建了两种专用的查询语言,Claude 可以通过 MCP 工具使用它们。
- TQ (Task Query):遍历任务图。它是一种管道语言,每一步转换一组节点。例如
(-> (active) :enrich (:sort :obs-count) (:take 5))返回被观察次数最多的五个活跃任务。 - PQ (Playbook Query):在模式图(由反思工作流生成的评分模式)上操作。检索是一个混合管道:首先根据查询嵌入进行语义相似度匹配,然后在共现图上进行激活扩散,提升在成功会话中经常一起应用的模式的权重。
两者都是 S-表达式管道。它们不会取代用于分析的 SQL,但对于智能体协同实际需要的访问模式来说,我们并不觉得缺少 SQL 是个问题。
实际案例:标准工具无法告诉你的事
Git 记录了什么被改变了。GitHub Issues 和 PR 评论捕获了部分讨论内容。CI 日志显示了测试结果。但它们都没有保留将智能体的第一个假设与最终提交连接起来的诊断链、死胡同和设计原理。
我们让一个“图分析智能体”(其工具包括任务查询、观察搜索和时间线遍历)来重建交互功能是如何开发的。
该智能体首先引导任务,在一次调用中加载其完整的计算状态(由 61 个事件折叠成 CRDT 的结果)。然后它按语义(而非文件名)搜索错误。它追踪了知识是如何在会话之间流动的:
第一个会话发现了编码错误,并将其整合修复。当第二个会话在第二天早上加入时,它继承了正确的代码,甚至不需要知道这个错误的存在。结构依赖已经解决。没有协同协议,日志中留下的痕迹就足够了。
智能体还搜索了设计原理——那种无法在源代码中存活的推理。例如,CSRF 修复包含了关于优雅降级的推理,这是任何提交信息都不会包含的;资源管理器小部件被完全构建、验证可用,然后被砍掉,因为它的方向不对,这种判断在只显示删除操作的 diff 中是不可见的。
这种结构化的痕迹也为反思工作流提供了动力。因为事件保留了足够的结构来重建为什么做出决策以及结果如何,反思可以追踪事件序列,从而生成有价值的评分模式。
git log 告诉你改变了什么。事件日志告诉你尝试了什么、失败了什么、学到了什么,以及为什么最终的设计是现在的样子。
权衡与局限性
- 无通用查询:TQ 和 PQ 仅处理智能体协同需要的访问模式。它们不支持任意的 Join、聚合或即席分析。
- 简单工作流的 CRDT 开销:对于单兵作战且一次只运行一个会话的开发者来说,CRDT 是不必要的机制。低于两个并发会话时,纯文本文件同样有效且更容易推理。
- 仅追加导致的增长:事件日志在没有压缩的情况下不断增长。我们尚未实现快照或日志截断,重放延迟随事件数量线性增长。JSON 反序列化占据了折叠操作约 100 倍的时间,CRDT 计算本身并不是瓶颈。
- 基于文件的存储:系统依赖文件系统和 git 进行同步。在达到一定规模时,文件系统级别的协同将不再足够,你可能需要一个真正的传输层。
- 标量字段的最后写入者胜出 (LWW):LWW-Registers 确定性地解决冲突,但不一定总是正确的。如果会话 A 将任务标记为“阻塞”,而会话 B 在一秒后将其标记为“活跃”,较晚的时间戳将获胜,无论谁拥有更多上下文。这是一个已知的局限性。
- 向量时钟已收集但尚未应用:每个事件都携带一个向量时钟用于未来的因果分析,但当前实现仍按挂钟时间戳对事件进行排序。对于共享文件系统的同机运行会话,这足够了;但对于跨机器运行的会话则不然。
现有技术 (Prior Art)
现有工具分为三类,每类优化协同问题的不同部分:
- 观察存储 (Observation stores):如 MemGPT、Mem0 和 Zep。它们将上下文视为分层存储问题,并添加了数据库支持的持久化和跨会话检索。它们擅长召回,但不提供协同原语。两个使用相同存储的会话无法检测到它们正在处理同一个文件。
- 基于图的工作流框架:如 LangGraph、Microsoft 的 AutoGen 和 ChatDev。它们将智能体工作流表示为状态机或消息传递图。它们擅长工作流编排,但拓扑结构是在设计时预先定义的。智能体工作的新兴结构(在工作完成前图的形状是未知的)难以提前指定。
- 持久化工作流引擎:如 Temporal 和 Prefect。它们通过事件溯源解决持久化问题,但假设你可以提前定义工作流。这适用于 CI 管道,但智能体会话不遵循预定义的 DAG。
kli 占据了设计空间中的一个不同点:具有 CRDT 合并语义的事件溯源状态,专为彼此预先不知情的并发会话而优化。最接近的先前工作是 Kleppmann 等人的“本地优先软件”宣言,以及 Automerge 和 Yjs 等协作文档编辑实现。kli 将类似的收敛保证应用于任务级协同状态。
kli 在 MIT 许可证下开源。
参考文献
- M. Shapiro, N. Preguiça, C. Baquero, M. Zawirski. “Conflict-Free Replicated Data Types.” SSS 2011.
- Q. Zhang, et al. “Agentic Context Engineering: Evolving Contexts for Self-Improving Language Models.” arXiv:2510.04618, 2025.
- C. J. Fidge. “Timestamps in Message-Passing Systems That Preserve the Partial Ordering.” 1988.
- P.-P. Grassé. “La reconstruction du nid et les coordinations interindividuelles…” Insectes Sociaux, 1959.
- C. Packer, et al. “MemGPT: Towards LLMs as Operating Systems.” arXiv:2310.08560, 2023.
- M. Kleppmann, et al. “Local-First Software: You Own Your Data, in Spite of the Cloud.” Onward! 2019.
- G. Theraulaz and E. Bonabeau. “A Brief History of Stigmergy.” Artificial Life, 1999.
- D. Horthy. “Advanced Context Engineering for Coding Agents.” HumanLayer, 2025.
- J. Waldo, et al. “A Note on Distributed Computing.” Sun Microsystems Laboratories, 1994.
- L. Lamport. “Time, Clocks, and the Ordering of Events in a Distributed System.” CACM, 1978.
- V. B. F. Gomes, et al. “Verifying Strong Eventual Consistency in Distributed Systems.” arXiv:1707.01747, 2017.
- J. Y. Halpern, Y. Moses. “Knowledge and Common Knowledge in a Distributed Environment.” JACM, 1990.
- F. Heylighen. “Stigmergy as a Universal Coordination Mechanism I: Definition and Components.” Cognitive Systems Research, 2016.
- A. Ricci, et al. “Cognitive Stigmergy: Towards a Framework Based on Agents and Artifacts.” E4MAS 2006.