质疑智能体堆栈:向量存储、超图与编码任务
三种大型智能体堆栈未能识别真正瓶颈的情况:基于文件的智能体内存优于向量存储、超图RAG以及那些所谓的高级方案。
来源: The Case Against Your Own Agent Stack: What Contrarians Get Right This Year
不断添加更多组件的趋势
通读今年关于AI智能体的相关报道,有一点在各种观点中尤为突出:几乎所有的建议都是要求再添加某些组件——记忆层、图结构、编排框架、包含三次重排步骤的检索流程,以及更多用于监控现有智能体的智能体。其背后隐藏着一种观念,即复杂性意味着进步,而那些功能仍与六个月前相比没有提升的系统就已经落伍了。
读到这篇文章的各位很可能都已经构建并维护过相当规模的这类技术栈。因此,值得花点时间看看那几篇持相反观点的文章:你添加的那些复杂组件很可能并未真正赢得其存在的理由。大多数反对派式的科技文章不过是反向炒作,同样充满自信,却同样缺乏证据支撑。这三种案例有所不同,因为它们各自都有可验证的依据,无论是基准测试结果、结构证明,还是对某人工作日常的如实描述。以下的标准同样适用于任何技术栈,包括你自己的。
每个案例都体现了该领域在寻求更多结构化方案时的尝试,尽管真正的问题往往藏在更为隐蔽的地方。
案例一:智能体内存与向量存储
向量存储中的智能体记忆通常在半分钟内即可完成加载。有人提出智能体应能在不同会话间保留信息,于是自然而然地产生了“嵌入、存储、通过相似度检索”的思路。所有教程都是按这种方式操作的,因此它便成了默认方案,而且也无人用更简单的方法对其进行过测试。
今年有一篇文章进行了这样的测试。在LoCoMo基准测试中,由纯文本文件文件夹和grep工具构成的基础方案,其得分竟高于几款经过资金支持、专门为记忆功能设计的产品。这次对比并非刻意安排——那些具备嵌入技术、相似度搜索功能,有时还叠加了图结构记忆的系统,竟输给了一个需要花费整个下午才能搭建起来的简单方案。
仔细想想,这个解释似乎很清楚。相似度搜索只是进行匹配,而非推理。它擅长找出与查询在含义上相近的文本,但在处理需要记忆的内容时表现不佳,比如无法识别三周前的事实已被昨天的信息取代,或是两个已保存的记忆存在冲突且必须有一个占优。索引没有时间概念或修正能力,因此只会返回最接近的向量,由模型自行发现前五名中的两个结果相互矛盾。
第二个差异在于熟悉程度而非能力。模型是在大量关于文件操作的资料上训练的:打开文件、搜索文件、编辑文件、列出文件夹、追踪文件来源等。这些文件与模型训练数据中的原始内容极为相似,因此直接使用grep命令结合文件读取循环即可完成操作。相比之下,供应商提供的向量查询API则是模型必须在自己的上下文窗口内学习如何有效使用的工具,此前几乎没有任何接触机会。这种选择意味着用模型已有的技能去换取需要在任务进行中才学会的新技能,同时还会增加嵌入处理和检索的延迟。
将这两种选项并列展示的决策指南如下。
WHEN A FILESYSTEM + GREP BASELINE IS ENOUGH WHEN YOU ACTUALLY NEED A VECTOR STORE
------------------------------------------------ ------------------------------------------------
Single-agent or small-team memory Retrieval across a corpus too large to
fit or scan in context at all
Facts that change over time and need Cross-document semantic search where
correction, not just accumulation keyword overlap is genuinely weak
Memory the model itself writes, Centralized memory shared by many agents
manages, and re-reads in its own loop that needs access control and auditing
Debuggable state, plain text you can Multi-hop or relational reasoning across
open, diff, and edit by hand thousands of entities where similarity
search is doing real narrowing work
基于文件的循环结构是怎样的
“直接使用文件”这种说法在变成代码之前听起来很模糊。这个版本无需任何托管服务。
import os
import subprocess
from datetime import datetime
MEMORY_DIR = "agent_memory"
def write_memory(topic: str, content: str) -> str:
os.makedirs(MEMORY_DIR, exist_ok=True)
path = os.path.join(MEMORY_DIR, f"{topic}.md")
timestamp = datetime.utcnow().isoformat()
with open(path, "a", encoding="utf-8") as f:
f.write(f"\n## {timestamp}\n{content}\n")
return path
def recall(query: str) -> str:
# ripgrep if you have it, grep -r works fine too
result = subprocess.run(
["rg", "-i", "-C", "2", query, MEMORY_DIR],
capture_output=True, text=True
)
return result.stdout or "no matches"
def list_topics() -> list[str]:
if not os.path.isdir(MEMORY_DIR):
return []
return [f[:-3] for f in os.listdir(MEMORY_DIR) if f.endswith(".md")]
将那三个函数作为工具交给模型,让模型自行决定何时保存、何时搜索以及何时加载整个简短主题文件。这样做的优势在于无需承担嵌入成本,也不需要托管或支付数据库费用;而且当出现异常时,还可以用任何编辑器打开相关状态信息。一旦这种方法行不通,原因会是具体的,比如语料库过大而无法快速检索,或是多跳问题超出了关键词搜索的解决范围。这种情况下,使用向量存储显然比照搬教程更为合理。
这并不是说向量数据库不应该存在。只是建议在先测试那些基础方法之前不要直接选用向量数据库。先尝试使用完整上下文、文件系统以及grep工具,只有当某种付费内存产品能显著超越这些基础方法的性能,足以弥补其成本及带来的调试难度增加时,才考虑采用它。大多数智能体内存系统其实根本无需达到那样的标准。
第二种情况:RAG中的超图
去年的主流是向量数据库,而今年则是基于图的RAG技术。当团队认为普通的知识图谱无法充分表达某些信息时,就会开始使用超图。这种思路看似合理——普通边仅能连接两个节点,但许多事实涉及更多参与方。以费用审批为例:某人代表财务团队,批准同事的报销申请,这笔费用从季度营销预算中支出,这样同一个事实就涉及五方参与者。如果强行将其拆成二元边对,要么无法体现其作为一个完整事件的特性,要么需要查询系统自行将多个边重新组合。而超边可以同时连接任意数量的节点,因此似乎是存储这类事实的理想方式,各团队在构建超图RAG系统时也期望这种结构能提升信息检索的准确性。
另一篇分析文章将这一预期与超图RAG论文的实际代码进行了对比,而非仅依据其摘要,结果发现了一些近乎滑稽的现象。HyperGraphRAG旨在支持原生超边,但它实际上将所有数据都存储在采用二进制边的普通图数据库中。该论文的作者们证明,将每条超边转化为围绕事件实体节点的几条二进制边并不会造成任何信息损失,因此这两种表示形式完全可以互相精确转换。
这一细节决定了整个论证的走向。假设一个原生超边与一个由二元边构成的实体化簇具有完全相同的关联结构,且两者可以毫不费力地相互转换,那么在二者之间做选择就不会对建模结果产生任何影响,只不过是选择一种存储格式而已。有评论者简洁地总结了其中的数学原理:这两种形式本质上是相同的关联结构,而超树的宽度只有在元数受限时才会发生恒定变化。正是超树宽度决定了查询的复杂度,跳数并非如此,同样,封装在一条边中的参与者数量也不影响查询难度。只要转换表示方式,将该数值调整一个常数,确保每个事实涉及的参与者数量保持在有限范围内(比如五个人审批一笔开支,而非五百人),查询难度就不会改变。
为何这会让人措手不及
这是一个很容易犯的错误。跳数看起来很直观,因为问题和答案之间的节点越多,查询似乎就越复杂。而超树宽度则远没有那么直观:它属于约束满足与查询复杂度理论范畴,其特性与跳数的差异需要仔细观察才能发现。有时候添加某种结构虽然能减少某个特定查询的跳数,但对该查询所属的复杂度类别却可能毫无影响,甚至略有负面影响。论文中的示例虽然看起来很 impressive,却无法揭示控制一般情况的真正衡量标准。
在决定使用普通图、实体化图还是原生超图存储之前,这种对比能提供帮助。
REPRESENTATION WHAT IT ADDS WHAT IT ACTUALLY CHANGES
--------------------- ------------------------------- --------------------------------
Plain binary graph Simplest to build and query Baseline; loses atomicity of
with standard graph tooling multi-participant facts
Reified binary graph Recovers atomicity via an Same incidence structure as a
(event node + roles) explicit "event" node hyperedge; hypertree width
shifts by a constant only
Native hypergraph Hyperedges as first-class No reduction in query
store objects, arguably cleaner complexity class over a
to write against reified graph; new storage
engine to run and maintain
这并不意味着图结构对RAG毫无用处。多跳关系检索相比平面向量搜索确实能从图中获益,这一点无人质疑。争议在于从图到超图的额外转换步骤。一旦有理论依据支撑这一观点,这样的转换就能带来结构更清晰的数据模型,需要新增一种运行基础设施,而不会影响决定查询速度的核心因素。如果检索效果不佳,有依据的解决办法通常是构建更好的图结构或采用更智能的检索策略,而非引入更复杂的边类型。
案例三:高级工程师的实际工作内容
前两个案例都涉及检索架构,而最后一个案例不同,因为它并非工具选择的问题,而是关于高级工程工作由什么构成的问题。
在一家拥有上千名员工的公司中负责管理一个五人团队的技术主管表示,人工智能无法承担他工作中95%的内容。他首先似乎承认了一个事实:编写代码所占据的时间在他总工作量中占比极小,而且差距很大。他的团队遵循“谁开发就谁负责运行”的原则,因此系统的运维工作由他的工程师亲自承担,而非由专门的运维团队负责,后者往往会把生产环境问题视为与自己无关的事。他的早晨从8:30开始,首先是审查拉取请求。这些代码大多由人工智能工具生成,其质量显然已经远超他两年前所看到的水平。他并不怀疑人工智能能够写出优质代码,完全接受这一事实,同时指出这其实几乎没改变他实际要做的工作。
这挑战了在思考代理堆栈时常见的一个前提,即认为能力决定了自动化的速度。按照这种逻辑,如果模型能生成正确的代码,那么编码工作就不再需要人工,任务的自动化程度也就取决于生成代码所占的时间。这位技术主管表示,这种观点从来都不成立,人工智能只是让缺陷显现出来而已。他的工作并非以编码为主,而是以协调为主:决定要开发什么以及开发顺序,与优先级相互冲突的人士协商工作范围,审查并支持他人做出的决策,负责传递消息,指导年轻工程师,以及在利益相关方的需求与系统能够安全支持的范围之间进行协调。这些工作中没有任何一项属于编码任务。
这或许是今年开发者讨论中最容易被忽视的要点,甚至比任何基准指标都重要,因为它解释了为何对于许多高级工程师,甚至是那些热衷于日常使用的开发者来说,更高的编码测试分数与更轻松的工作量并未同步提升。SWE-bench的分数在几年内从个位数上升到70多分,确实是一个巨大的进步。但这并不意味着工作量会减少70%,因为代码编写从来就不是工作的70%,尤其是当工作内容还包括值班轮换、项目规划以及拉取请求处理时更是如此。
有一个需要注意的地方:第三点在其他设置中的适用性不如前两点那么理想。关于向量数据库和超图的说法可以通过基准测试或证明来验证。高级工程师一周内处理协调工作与编写代码的时间比例会因公司规模、团队成熟度、相关工作是造成效率低下的因素还是必要的工作内容,以及个人资历的不同而有所差异。在一家拥有千名员工且采用“构建即运行”策略的公司中,五人团队所承担的工作只是其中一种类型,并不能代表所有的工程岗位。不过,调整的方向总体上仍是正确的。智能体能力决定了代码编写部分能在多大程度上实现自动化,但对于协调工作部分则几乎无法提供任何参考,因为协调工作从来就不取决于打字速度或代码质量。
共同模式
将这三个案例放在一起看,就能发现这个案例并非专门探讨向量数据库、超图或人工智能技术。它讲的是某个领域原本认为的瓶颈所在与实际瓶颈所在之间的差距。
CASE WHERE COMPLEXITY WAS ADDED WHERE THE REAL BOTTLENECK WAS
------------------ ---------------------------------- --------------------------------
Agent memory Vector embeddings, similarity Whether the model can use a
search, sometimes a graph layer retrieval method it already
on top of that has deep fluency with
RAG structure Native hyperedges, a new The complexity class governing
storage engine, more query cost, which the fancier
elaborate graph modeling structure barely touches
"How much of the An assumption that model Whether the job was ever
job gets automated" capability alone predicts mostly about the thing the
the automatable fraction model is good at
这些复杂的解决方案本身并无错误。向量数据库确实有实际用途,超图或许能在目前尚未被发现的领域发挥作用,而人工智能代理确实能减轻工程工作中的繁琐任务,这一点技术负责人也予以认可。问题在于人们忽略了这样一个问题:这些额外的复杂设计究竟是针对真正的瓶颈,还是仅仅针对那些最容易用复杂方案解决的环节。
这种习惯不仅存在于人工智能代理的开发中。添加一层功能总比去思考原有的基础方案是否经过公正对比要容易得多。新的抽象层被视为可见的进步,可以拿来作为升级的依据。而要验证grep已经能完成工作、查询复杂度是否真的有所改善,或者耗费你一周时间的问题是否就是你原本认为的那个,则需要更长的时间,过程也更麻烦,有时甚至会导致人们决定停止开发。
添加新层前的检查清单
在添加新层之前,应对现有技术栈进行核对的简短清单:
1. Have I benchmarked the boring baseline, not just assumed it loses?
(full-context, grep, a plain graph, a human doing the coordination)
2. Does the new structure change the metric that actually governs cost
or quality, or does it just look more sophisticated on a diagram?
3. Am I reaching for this because a benchmark or proof told me to,
or because it's what the tutorials and the funded products default to?
4. If I strip this layer back out, what specifically breaks?
If I can't name it precisely, I probably don't need the layer yet.
5. Am I solving the bottleneck I actually have, or the bottleneck
that's most interesting to build a sophisticated solution for?
这一切并非主张减少工程工作量,而是主张在构建那些假定答案已知的复杂系统之前,先投入工程精力找出瓶颈所在。这三种观点并非出于反对而提出,而是进行了那些并不引人注目的验证,而这些验证结果与常规假设相悖。这比那些带有夸张标题的激进观点要求要高得多,也是任何技术架构都应遵循的标准。
相关阅读
- 模型差异检测代理的发现:风格变化掩盖了细微的微调效果 —— 一位黑箱审计员将基础模型与金融领域的LoRA进行了对比:82%的测试发现了长度和结构的改变,而预期的风险分析变化却未被发现。