AI技术文章

AI Agent 工程化,本质是数据库系统设计

作者:奈特在雕花0 次浏览 来源:奈特在雕花
AI Agent 工程化,本质是数据库系统设计

最近半年,我阅读了业界关于 AI Agent 的工程实践:Anthropic 的 Context Engineering 论文、Manus 的工程分享、Cline 的 Memory Bank 设计等。同时自己也一直在做跟 AI Agent 相关的项目,如:Jta[1](开源的翻译 Agent,基于 Agentic Workflow)。

最近半年,我阅读了业界关于 AI Agent 的工程实践:Anthropic 的 Context Engineering 论文、Manus 的工程分享、Cline 的 Memory Bank 设计等。同时自己也一直在做跟 AI Agent 相关的项目,如:Jta[1](开源的翻译 Agent,基于 Agentic Workflow)。

在这个过程中,我越来越强烈地感受到:AI Agent 工程要攻克的那些核心挑战,其实都似曾相识。

往深了想,AI Agent 到底在解决什么?

Deep Research 自动化了"查资料→收集→整理";Code Agent 压缩了"找代码→参考→改写"。它们都在做同一件事:从海量信息中提取、聚类、压缩出你需要的部分

信息密度过载,是这个时代的问题。AI Agent 提供的是自动化的信息压缩方案。本质上,这是个数据处理问题。

比如说:

上下文管理:如何在有限的 context window(比如 200k token)中管理无限增长的对话历史?超过 128k 后性能就开始下降,这是 Anthropic 论文里提到的"context rot"。

工具协调:Agent 集成 MCP 后可能有上百个工具,如何快速选择正确的?工具太多会导致"上下文混淆",Agent 调用错误的工具。

状态管理:长期运行的 Agent 需要跨会话保存状态。是把状态保存在 context 里(有状态),还是外部化到文件系统(无状态)?如何在需要时恢复完整的历史状态?

查询优化:10000 条历史消息里,如何快速检索出和当前任务最相关的 5 条?全量加载不现实,但精确度又不能下降。

资源淘汰:context window 满了怎么办?哪些信息保留、哪些压缩、哪些删除?用 LRU 还是按重要性排序?

性能权衡:推理速度和信息完整性之间的平衡点在哪?Token 越少越快,但会丢失细节;Token 越多越准,但会超时。

看着这些问题,我突然意识到:这不就是数据库系统过去 50 年一直在解决的吗?

业界分享的这些最佳实践,让我想起几年前基于 RocksDB 和 Raft 实现图数据库的经历。当下 AI Agent 工程化的解题思路,跟 RocksDB 的设计实现,其实有着异曲同工之处。

RocksDB 是什么?它是 Facebook 开源的嵌入式键值存储引擎,被广泛用于 TiDB、ClickHouse 等数据库的底层存储。它用的是 LSM 树(Log-Structured Merge Tree)架构,这个架构解决的核心问题是:如何在磁盘上实现高性能的写入和查询。

写入流程很简单。数据先写入 WAL(Write-Ahead Log,预写日志),这是个 append-only 的文件,顺序写入速度极快。然后数据进入 MemTable,这是个内存中的有序结构,通常用跳表实现。当 MemTable 满了(比如 64MB),就把它刷到磁盘,变成一个 SSTable 文件(Sorted String Table)。

这个设计的巧妙之处在于:所有写入都是顺序的。磁盘顺序写比随机写快 100 倍,这是硬件特性决定的。所以 LSM 树的写入性能非常高,时间复杂度是 O(1)。

但问题来了。SSTable 文件会越来越多。L0 层(Level 0)是最新刷下来的文件,可能有几十个;L1、L2、L3...每一层都是上一层的 10 倍大小。如果不做处理,查询性能会崩溃——你需要遍历所有 SSTable 才能找到一个 key。

所以 RocksDB 有个后台线程在做 Compaction(压缩合并)。它把多个小的 SSTable 合并成一个大的,同时删除过期数据、去重、排序。这个过程是渐进的、异步的,不会阻塞前台的读写。

结果就是:- L0 层:数据最新,未排序,查询需要遍历多个文件- L1-L2 层:部分合并,有序性提升- L3-L6 层:高度合并,完全有序,查询只需二分查找

这是个典型的时间和空间的权衡。写入时延迟排序,查询时用空间换时间。

看到这里,你可能会问:这和 AI Agent 有什么关系?

关系大了。AI Agent 的消息历史,本质上也是 append-only 的。

每次对话,新消息追加到 history 数组的末尾。历史消息不会修改(只读),最新消息总是最相关的。这和 RocksDB 的 WAL 完全一样:顺序追加,时间复杂度 O(1),不阻塞主流程。

更深层的相似在于:两者都基于同一个假设——时间局部性(Temporal Locality)

RocksDB 把最新写入的数据放在 L0 层(内存),不只是因为顺序写快,更因为这些数据被查询的概率最高。数据越新,越"热";越旧,越"冷"。这是几十年数据库实践总结出的规律。

AI Agent 的注意力机制,其实也在做类似的事。虽然 Transformer 理论上可以关注任意位置,但在实际推理中,模型对最近消息的注意力权重往往更高。Anthropic 的研究发现,超过 128k token 后会出现"context rot"——模型对远距离信息的注意力明显下降,就像 RocksDB 的 L6 层数据,虽然还在,但访问成本已经很高了。

所以 append-only 不只是实现细节,背后是对"最近的最重要"这个规律的工程化体现。

更关键的是,AI Agent 也需要"分层存储"。

你不能把所有历史消息都塞进 context window。Claude Opus 支持 200k token,但超过 128k 后性能就开始下降——这是 Anthropic 论文里提到的"context rot"(上下文腐烂)。模型的注意力被稀释了,就像人类的工作记忆一样,记住 1 个电话号码容易,记住 100 个几乎不可能。

所以你需要分层:- 工作记忆(Working Memory):最近 10-20 条消息,直接放在 context 里,相当于 RocksDB 的 MemTable- 短期记忆(Short-term Memory):本次会话的完整历史,压缩后的摘要,相当于 L0-L2 的 SSTable- 长期记忆(Long-term Memory):跨会话的知识库,向量数据库存储,语义检索,相当于 L3-L6 的冷数据

Cline 的 Memory Bank 就是这个设计。activeContext.md是工作记忆,projectbrief.md是长期记忆。它们是外部文件,不占用 context window,需要时才加载。

更有意思的是"记忆整理"。Cline 有个 Auto Compact 功能:当对话历史超过 150k token 时,自动压缩旧消息,保留关键决策点。这不就是 Compaction 吗?把多个旧消息合并成一条摘要,删除冗余信息,保持 context 精简。

再说查询优化。

RocksDB 用 Bloom Filter 来加速查询。这是个概率型数据结构:它能确定性地告诉你"某个 key 一定不存在",或者"可能存在"。当你查询一个 key 时,先检查 Bloom Filter;如果返回"一定不存在",就跳过这个 SSTable,不用读磁盘。

Bloom Filter 的空间开销很小,每个 key 只需几个 bit,但能减少 99% 的无效读取。它的原理是用多个哈希函数把 key 映射到位数组,查询时检查这些位是否都为 1。

AI Agent 的语义检索,本质上也是个过滤问题。你有 10000 条历史消息,但只需要加载和当前查询最相关的 5 条。如果把所有消息都向量化,然后用向量相似度排序,就能快速筛选出 Top-K。

这里的 embedding 就像 Bloom Filter:它把高维的语义信息压缩成低维向量(比如 1536 维),然后用余弦相似度或欧氏距离做快速匹配。HNSW(Hierarchical Navigable Small World)索引能把查询复杂度降到 O(log n),非常接近 Bloom Filter 的 O(1)。

Cline 文档里提到的"相关性检索",用的就是这个原理。它不是把所有历史都加载进来,而是根据当前任务,动态检索最相关的片段。这和 RocksDB 的"按需读取"完全一致。

还有淘汰机制。

数据库的缓存淘汰用 LRU(Least Recently Used):淘汰最久未访问的数据。热数据保留在内存,冷数据 swap 到磁盘。这是个经典算法,Redis、Memcached 都在用。

AI Agent 的遗忘策略,也是类似的思路。Anthropic 论文里说"context is a finite resource",你必须决定哪些信息保留、哪些压缩、哪些删除。

最简单的策略是时间衰减:保留最近 N 条消息,更早的逐步压缩。但这不够智能,因为有些旧消息可能很重要——比如用户在第 1 条消息里说的需求,在第 50 条时仍然要记住。

更好的方法是重要性评分。关键决策点永久保留,普通对话可以压缩,重复信息直接淘汰。这就像 LRU-K 算法:不只看最近访问时间,还看访问频率和访问模式。

memobase.io 这个项目(你之前研究过的),就是把这些策略工程化。它针对 AI Agent 的记忆做了专门优化:自动分层、智能压缩、语义检索、重要性排序。本质上,它就是一个专门为 AI 设计的存储引擎。

说到这里,我想讲个更深层的观察。

RocksDB 的设计哲学是:写入优化优先,查询性能用空间和后台处理来换。这背后是对"写多读少"场景的理解——大多数应用是写入密集的,日志、时序数据、消息队列都是这样。

AI Agent 的对话也是写入密集的。每次交互都会产生新消息(用户输入+模型输出),但查询历史的频率相对低。所以 append-only + 后台压缩的模式,完美适配这个场景。

但有个细节值得注意。RocksDB 的 Compaction 是"信息保留"的——合并后的数据和原始数据完全一致,只是存储更紧凑、查询更快。而 AI Agent 的压缩往往是"有损"的:把 50 条消息总结成 1 条摘要,细节会丢失。

这是个权衡。Anthropic 论文里提到两种策略:Compaction(压缩)和 Summarization(摘要)。前者是可恢复的,把数据外部化到文件系统;后者是不可逆的,用 LLM 生成摘要替换原始内容。

我更倾向于 Compaction。因为 AI Agent 的决策是链式的:第 50 步的行动,可能依赖第 3 步的观察。你无法预测哪个历史片段会在未来变得关键。所以能外部化就外部化,能恢复就不要删除。

Cline 的 Memory Bank 就是这个思路。所有历史决策都保存在 markdown 文件里,需要时读取,用完可以从 context 里移除,但数据永远不会丢失。这就像 RocksDB 的 SSTable:数据在磁盘上,内存只保留索引和缓存。

讲了这么多技术细节,抽象一下,其实就是 5 个设计原则:

第一,分层存储。热数据(工作记忆)放在 context,温数据(短期记忆)做压缩,冷数据(长期记忆)用向量检索。每一层有明确的容量限制和迁移条件。

第二,写入优化。消息追加是 O(1) 的,不阻塞主流程。压缩和整理放在后台异步处理,不影响对话体验。

第三,智能索引。用向量相似度做语义检索,用关键词做精确匹配,用时间戳做范围查询。多维度索引,根据查询模式选择最优路径。

第四,可恢复性。压缩优于摘要,外部化优于删除。信息可以被移出 context,但不应该被永久丢失。这是对"你无法预测未来需要什么"的应对。

第五,最小化原则。不是越多越好,而是信噪比最大化。只加载真正相关的信息,每个 token 都有明确作用。Anthropic 说的"smallest possible set of high-signal tokens"。

这 5 个原则,不只适用于 AI Agent,也适用于所有需要管理大规模状态的系统。分布式存储、缓存设计、数据湖、知识图谱,底层逻辑都是一样的。

最后说回 RocksDB 和 AI Agent 的相似性。

它们面对的是同一类问题:在有限资源下管理无限增长的数据。内存是有限的,context window 是有限的,注意力预算是有限的。但数据是无限的,历史消息会不断积累,用户的需求会越来越复杂。

数据库社区花了 50 年解决这个问题。从早期的 B 树、哈希索引,到后来的 LSM 树、列式存储,再到现在的分布式系统、向量数据库。每一代技术都是对"有限资源 vs 无限数据"这个矛盾的新解答。

AI Agent 社区现在面临的,是同样的挑战。好消息是,我们不需要重新发明轮子。RocksDB 的 LSM 树、Bloom Filter、Compaction,这些久经考验的技术,可以直接迁移到 AI Agent 的实践中,包括但不局限于 Context Engineering、Memory Management 等。

当然,AI Agent 有自己的特殊性。语义理解、上下文推理、多轮对话,这些是传统数据库没有的。但在存储和检索这个层面,本质是相通的。

这就是工程的魅力。很多看似全新的问题,背后都能看到 Unix、数据库这些经典系统的设计哲学。识别本质,复用智慧,而不是重新发明轮子。

(全文完)

相关链接

[1] Jta - JSON Translation Agent: https://github.com/hikanner/jta

推荐阅读

出海应用翻译太痛了,我开源了一个 AI Agent 来解决

从 Anthropic 的"令牌经济学"到 Cline 的"遗忘艺术"

Context Engineering 2.0:上下文工程的上下文

文章来自于微信公众号 “奈特在雕花”,作者 “奈特在雕花”

相关主题

读完之后

加入 AINRK 交流群

了解加入交流群的方式,与更多 AI 从业者交流。

查看交流群说明(在新窗口打开)