去年这个时候,我正在做Eloquence——一个数据可视化的编程语言。Claude Code刚发布,我和大多数开发者一样兴奋得不行。一个能写代码、读文件、跑命令的Agent,未来终于触手可及了。

我立刻把它用起来。然后开始等待。十秒、四十秒。Agent正在代码库里搜索——一个函数名、一处调用、一个引用。Find。Grep。Read。循环往复。有时候找到了,更多时候没找到,再试一次。有时候它确信自己找到了,其实没有,然后自信地改了代码。第二天早上我发现烂摊子:一个重命名的函数漏了三处调用,一个变量到处都改了唯独漏了两层嵌套里的回调,一个"修复"引入了比原问题更糟的bug。

打开网易新闻 查看精彩图片

我没怪模型。模型很聪明,它只是在完成被交代的任务:把我的代码库当作一长串无结构的字符流,尽其所能地推理。但越看它工作——越等、越收拾残局——我脑子里有个问题越来越响:为什么我的AI Agent在读我的代码库时,像在读一本小说?

代码不是文本。代码是数据。

眯起眼看任何程序,你看到的是结构。函数嵌在文件里,文件嵌在模块里,模块通过命名空间连接。调用跨越边界,名称解析到定义,类型流过参数。一个文件第412行的符号,与另一个文件第17行的符号有明确关系。这一切都不是暗示,而是显式编码在语法里的——编译器、linter、语言服务器几十年前就懂这些。

代码是图。代码是树。代码本质上是有形状的数据。

你用过的好IDE都知道这一点。所以"重命名符号"不是全局查找替换,而是遍历语法树、更新每一处引用、放过同名但无关的标识符。所以"跳转到定义"毫秒级返回。工具界早就知道源代码有结构。

然后我们把源代码交给LLM——史上最强的语言工具——却要求它像操作小说段落一样操作代码。

这让我很困扰。一直困扰。

有个例子终于让我想通了。想象你在写小说,主角叫Alice。几百页里,你用几十种方式指代她:Alice、she、her、the girl、the heroine、她姐姐起的昵称、第十二章获得的头衔。所有这些都指向同一个人。

现在编辑让你把她改名成Maya。一个以文本为先的LLM会尽力而为:搜索"Alice",替换为"Maya"。但它会漏掉"she",会搞混另一个也叫Alice的配角,会把"Alice in Wonderland"的引用也改掉。它处理的是字符,不是实体。

而结构化工具——编译器、IDE——知道Alice是一个符号,知道它的所有引用形式,知道它的作用域边界。重命名是精确的、完整的、瞬间的。

这就是差距。我们给AI的是文本接口,但代码需要的是数据接口。

过去一年,Coding Agent爆发式增长。Cursor、Windsurf、Devin、GitHub Copilot Workspace——它们都在用同样的基础范式:把代码当作token流,用RAG检索片段,让模型推理上下文。这个范式能工作,但天花板很明显。

看看Agent在代码库里的典型行为:搜索文件名、grep关键词、读取相关文件、猜测关联、尝试修改、验证结果。每一步都是在用文本工具模拟结构感知。就像用望远镜当显微镜——不是不能用,是处处别扭。

更深层的问题是,LLM的上下文窗口再大,也装不下大型代码库的全部结构关系。GPT-4的128K token约等于10万行代码——一个中等项目的几十分之一。而代码的语义关联往往是稀疏且长距离的:一个接口定义在核心模块,实现在服务层,调用散落在十几个前端组件里。这种关系用文本检索很难捕捉。

业界并非没有意识到。GitHub的Copilot正在实验代码图(code graph)集成,Sourcegraph的Cody用LSIF(Language Server Index Format)索引仓库,一些团队在做AST-aware的RAG。但这些是补丁,不是范式转移。

真正的结构性变化需要重新设计AI与代码的交互接口。不是"给我相关文件",而是"这个符号在哪里被定义、被调用、被类型约束";不是"搜索字符串",而是"查询调用图";不是"读取代码",而是"遍历语法树"。

这听起来像回归传统工具,但关键差异在于:传统工具是确定性的、规则的、脆弱的;AI可以是概率性的、灵活的、泛化的。结合两者——用结构索引给AI导航,用生成能力处理模糊需求——可能是更优解。

我现在的做法是混合架构:用tree-sitter解析代码结构,构建轻量级符号索引,让Agent在需要时查询关系而非搜索文本。重命名一个函数时,Agent拿到的是引用列表,不是候选文件。修改一个类型时,它知道哪些调用点需要检查。这减少了猜测,降低了幻觉,加快了响应。

但这也暴露了生态的断裂。每个语言的解析器不同,每个项目的构建配置不同,IDE和CI和Agent各自维护一套理解。没有通用的"代码数据层"让AI接入。

这可能是下一个基础设施机会。就像数据仓库统一了业务数据的查询接口,代码库也需要一个标准化的语义层——不是替代Git,而是让AI能高效地"看见"结构。Language Server Protocol(LSP)是一个起点,但它为交互式设计,非为批量查询优化。LSIF和SCIP是尝试,但采用率有限。

更激进的设想是:如果代码存储本身结构化呢?不是文本文件,而是带有语义标记的AST数据库,diff操作在结构层面而非行层面。这听起来遥远,但Git的底层已经是内容寻址的Merkle树,我们只是在上层用了文本接口。

回到那个困扰我的问题。一年后,Claude Code和它的同类进步了很多——更快、更准、更少幻觉。但底层范式没变。它们仍在读小说,只是读得更快了。

代码是数据。这个基本事实被忽视,不是因为技术不可能,而是因为路径依赖:LLM从NLP诞生,文本是最自然的接口;Git和文件系统统治开发流程,文本是最兼容的格式。但AI Coding的下一个跃迁,可能恰恰需要打破这个惯性。

不是让AI更像人类读代码,而是让代码更像AI能处理的数据。