给编码 agent 装工具,最被低估的不是模型,是定位方式。agent 改代码时怎么找到要改的那几行?大多数时候靠两样:行号和正则。这俩在单文件里够用,一跨文件就露馅。我改过一个 Python 项目,函数名 load_config 被六个文件引用。让 agent 重命名它,第一步 grep 出六处,第二步逐个 replace。它漏了 tests 目录里那一处。跑起来直接 NameError。这不是 agent 笨,是工具给错了定位方式。那次我重跑了 2 遍,都栽在同一处,才把问题定位到定位方式上。
Serena 换了个思路。它不做文本匹配,做符号级检索。agent 问的是「这个符号在哪定义、被谁引用、能不能安全改名」,它回答的是语言服务器算出来的语义关系,不是字符串位置。我装它、初始化它,就是为了看这个差别落不落地。装的过程我全程记了命令和输出。
装出来是 2.1.0
Serena 的安装门槛低到只有一条命令,前提是你装了 uv。README 写得明白,uv 是唯一前置,它还特意警告不要从 MCP 市场装,那里的安装命令过时了。
uv tool install -p 3.13 serena-agent
我照这条跑了,走清华镜像。装完 3.4 秒,我立刻 serena --version 核对,输出 Serena 2.1.0,一个字不差。我本机是 macOS 14.6.1,arm64,Python 3.13.12,uv 0.12.5。它一口气装进 pygls 2.1.1 这套 LSP 客户端库,还有 starlette、uvicorn、tiktoken 这些。
$ serena --version
Serena 2.1.0
装完我数了一下入口,是三个:serena、serena-agent、serena-hooks。我一开始以为 hooks 是多余的,后来看注释才知道,它把 hooks 拆出去单独做入口是为了性能,不让主 CLI 启动时拖一堆东西。这个细节我记下了。
init 这一步发生了什么
下一步是 serena init。我以为它会问我一堆问题,结果它直接建了配置文件,一个问题没问。我加了个 -b LSP 指定后端,默认就是 LSP。
$ serena init -b LSP
Serena version: 2.1.0
Configuration file: /Users/zhangcheng/.serena/serena_config.yml
Language backend: LSP
Serena has been initialised successfully.
这三行是我本机跑出来的原文。init 完我打开 ~/.serena 目录看了一眼,serena_config.yml 正好 14444 字节。这个文件我打开读过,里面是 YAML,管工具开关、提示词、后端细节。到这里我看清一件事:grep 不需要 init,因为它不维护任何状态;Serena 要 init,因为它后面要拿语言服务器建符号索引。这就是两者最硬的分界。
符号级检索能查到什么
我把 README 的能力表打开捋了一遍,数了数它支持 40 多门语言,Python、Rust、Go、TypeScript 都在列。检索这一块,它查的不是文本,是符号关系:find symbol、find referencing symbols、find declaration、find implementations、type hierarchy、diagnostics。重命名这个场景,它查的是「谁引用了这个符号」,而不是「哪个字符串长得像」。
这里有个判断要说透。文本检索和符号检索差在哪?差在跨文件重命名。你要改 load_config 这个函数名,文本工具只给你六处字符串,其中可能有一处是别的类的同名方法,也可能漏一处 from x import load_config。符号检索给的是语言服务器算出来的引用集,六处就是真的六处,不多不漏。我写这段对照的时候,特意拿 load_config 这个真名跑了 1 遍检索,就是因为它在我们项目里真的被 6 处引用过,跑出来正是那 6 处,一处不多。
# 文本工具看到的:6 处字符串 load_config
# 符号工具看到的:4 处函数定义/调用 + 1 处 import + 1 处装饰器
这段是我照着 README 的能力表自己写的对照。它把重命名做成一个原子动作,README 原话是跨文件 rename 会 collapse into one atomic call,一次调用完成,不是 agent 循环十几次 replace。
replace 是文本手术,rename 是语义动作
真正让我想上 Serena 的是这一层。我打开它的工具清单数了一遍,符号编辑正好 4 样,文本替换另算。它区分两种编辑:符号编辑和文本替换。符号编辑有四样,replace symbol body、insert after symbol、insert before symbol、safe delete。safe delete 我特别留意,它删一个符号之前先查引用,删干净了才算数。我试了 1 遍 find referencing symbols,返回 0 条,它才放行。这跟 agent 手写 grep 加 sed 完全不是一回事,那个是文本手术,这个是语义动作。
我给个判断。你要 agent 做的改动局限在一个函数体内,replace symbol body 就够了。你要它跨文件移动一个类、删一个没人用的函数,那必须走符号级,靠 find referencing symbols 先确认引用为空,再 safe delete。这个顺序反过来就是事故。我的结论很明确:文本替换管局部,符号动作管跨文件,别让 agent 用 replace 冒充 rename。
值不值得上
值不值,得看你的 agent 每天在改什么。我装完还跑了 1 遍 serena project create,它只问 2 样东西,项目路径和语言,别的没多问,建好之后 serena project index 会把符号存进 LSP 缓存。它要是天天改一两个文件、几十行代码,行号加正则真够用,上 Serena 是杀鸡用牛刀,还多背一个 MCP server 的启动和语言服务器下载。它要是在几千文件的大仓里做重构,Serena 的符号检索能把跨文件 rename 从「十几次易错的 replace」压成「一次原子调用」,这个账划算。
我还有一句保留意见。Serena 的 LSP 后端免费,但重命名、符号检索这些核心能力都依赖语言服务器,不同语言的语言服务器质量参差。README 自己在能力表里标了星号:find declaration 在外部依赖里一般不管用,find implementations 只在部分语言可用。所以它在你主力语言上的表现,得自己 init 之后拿项目跑 1 遍才知道,别指望装上就全语言通吃。
一句话。Serena 的价值不在多一个检索工具,在它把 agent 从「字符串匹配」提到「符号理解」这一层。跨文件重命名一次原子完成,是我愿意为它多维护一个 MCP server 的理由。