之前整理过的 ZCode 扩展机制、DeepSeek Harness、Unity 官方 Skill,其实都在回答同一个问题:怎么给编程 Agent 补能力。Serena 给出的答案是「把 IDE 的语义分析能力做成 MCP 服务器」——让任何 LLM 客户端都能像资深开发者用 IDE 那样,按符号而不是按行号去理解和修改代码。我把整个仓库读了一遍,这一页记下我认为值得记住的点。
一、Serena 是什么:一句话与三条理念
官方口号是「The IDE for Your Coding Agent」:一个基于 MCP 协议的语义代码工具包,通过语言服务器(LSP)或 JetBrains 插件,为 Claude Code、Codex、Cursor 等任何客户端提供符号级的代码检索、编辑与重构能力。三条设计理念贯穿全库:
- 符号级操作,拒绝行号:LLM 用
name_path(如MyClass/my_method)定位代码,行号只作为返回元数据,从不作为输入定位手段——因为对模型来说行号既费 token 又容易错位; - Agent-first 的工具设计:工具的 docstring 就是提示词,参数描述里写满行为指令(「优先传 relative_path 加速」「include_body 要节制」);
- 类比 IDE 而非类比编辑器:跨文件重命名、找引用这类原本要 Agent 走 8–12 步易错操作的任务,收敛为一次原子调用。
仓库自带的评测里,不同厂商的 Agent 在不同代码库上独立给出了几乎相同的结论:Serena 最大的价值就是把多文件语义操作压成单次原子调用。
二、整体架构:四层堆栈
| 层 | 关键代码 | 职责 |
|---|---|---|
| MCP 服务器 | mcp.py(FastMCP) | 协议暴露层,stdio/HTTP 两种传输 |
| Agent 装配 | agent.py | 组装配置、上下文、模式、工具、后端、提示词 |
| 工具薄壳 | tools/ + facade API | 工具只是壳,逻辑在可复用的 facade 方法里 |
| 语义引擎 | code_editor.py / symbol.py / ls_manager.py | 符号检索、符号编辑、多语言服务器管理 |
| SolidLSP | solidlsp/(独立 MIT 库) | 统一各语言服务器的方言,JSON-RPC 通信 |
两个容易忽略的工程细节:MCP 走 stdio 时日志绝不能写 stdout(协议通道被占用),只能走 stderr、文件和内存;基础工具集在启动时定死(MCP 客户端看到的工具列表固定),之后项目与模式切换只影响运行时的启用集合。
三、工具体系
| 类别 | 代表工具 |
|---|---|
| 符号检索 | get_symbols_overview(读新文件第一步)、find_symbol、find_referencing_symbols、find_implementations |
| 符号编辑 | replace_symbol_body、insert_after_symbol、rename_symbol(LSP WorkspaceEdit,引用感知)、safe_delete_symbol |
| 文本兜底 | search_for_pattern(正则)、replace_content、read_file、execute_shell_command |
| 记忆 | write_memory / read_memory / list_memories 等五个 |
| 工作流 | activate_project、initial_instructions、onboarding |
- 工具用 marker 类声明能力(可编辑、可选、beta 等),marker 名还会进系统提示供模板条件渲染;
- 每个工具有
max_answer_chars(默认 150k)做渐进截断,超时默认 240 秒,语言服务器崩溃会自动重启并重试; - 在 Claude Code 这类自带文件/shell 工具的宿主里,基础工具默认禁用,避免与内置能力重复——这是「上下文即配置」思路的体现。
四、SolidLSP:语言服务器抽象层
SolidLSP 是 Serena 的底座,单独以 MIT 许可发布(其余主体是 GPL-3.0)。值得记的点:
- 统一符号模型:把原始 LSP 类型转成带 parent 链、重载索引、懒加载正文的
UnifiedSymbolInformation,各家语言服务器的怪癖在子类覆写钩子里就地吸收; - 两级符号缓存:按「文件相对路径 + 内容 MD5」做键持久化到
.serena/cache/,带版本号常量做失效管理,空结果刻意不缓存; - 健壮性靠针对性特化:pyright 用 uvx 固定版本、jdtls 下载带 SHA256 校验、TypeScript 监听
$/progress判断索引完成、ContentModified错误只对声明了重试支持的服务器重试; - 支持 70+ 语言;Angular 是三服务器编排(ngserver + TS + HTML),Godot 通过 TCP 6008 连正在运行的编辑器;
- 多语言服务器并行启动且 fail-fast:任何一个启动失败就整体报错,绝不悄悄用残缺能力降级。
五、双后端:LSP 与 JetBrains 插件
- LSP 后端(默认,免费):经 SolidLSP 启动各语言的开源语言服务器;
- JetBrains 后端(付费插件):复用 IDE 的分析能力,多语言索引、依赖库跳转,还有独家的交互式调试——Agent 能设断点、看变量、控制执行流;
- 切换后端的机制很巧:后端声明一张工具类替换表,JetBrains 模式自动激活一个内部 mode,把 7 个核心 LSP 工具换成
jet_brains_*版本,提示词里的工具名同步替换。第三发方扩展也走注册表 + entry point。
六、五层可组合配置
这是 Serena 适配几十种宿主客户端的关键,从粗到细依次覆盖:
- 全局配置
~/.serena/serena_config.yml:后端、超时、忽略路径、基础模式等; - CLI 参数:启动时覆盖或追加;
- Context(单选,会话期固定):运行环境。内置 15 个——claude-code、codex、ide、chatgpt 等,各自排除与宿主重复的工具,并写针对性提示词对抗宿主偏置;
- Mode(可多选,可运行时叠加):行为修饰。内置 9 个——
interactive/editing默认、planning只读、onboarding、one-shot等; - 项目配置
.serena/project.yml:语言、编码、忽略规则、初始提示,另有 gitignore 掉的project.local.yml做本机覆盖。
每层都能增删工具、注入 Jinja2 提示词片段。「同一套工具包,靠配置片段拼出几十种形态」——这个分层思路对我写任何带配置的工具都有参考价值。
七、记忆系统:Markdown + 引用图
- 存储就是纯 Markdown:项目级
.serena/memories/**.md,全局级~/.serena/memories/global/,支持/分主题; - 亮点是
mem:交叉引用体系:重命名记忆时全库自动重写引用,还带引用完整性校验命令; - Onboarding 引导 Agent 把项目知识写进约定记忆:
mem:core、mem:tech_stack、mem:suggested_commands、mem:conventions、mem:task_completion,写前必读mem:memory_maintenance(风格规范本身也是一条记忆); - 设计准则:人类可读、可版本化、渐进披露(Agent 先拿名单按需读,而不是全部塞进上下文)、有防路径逃逸校验。
没有向量库、没有嵌入模型,就靠 Markdown 和引用把跨会话知识管起来了——简单,但把「人机共读」这件事做对了。
八、提示词系统
- 所有提示词是 Jinja2 沙箱模板,用户放
~/.serena/prompt_templates即可覆盖,工厂类由代码生成器产出类型化访问器; - 系统提示组装顺序:资源效率宣言(先符号概览、按需读正文)→ 按工具 marker 条件渲染的区块 → 记忆清单 → context 提示 → 各 mode 提示 → 项目激活消息 → 会话 id;
- 针对 Claude Code 有专门的提示词覆盖,声明 Serena 工具优先于内置 Read/Edit(原话是「Read 对探索性任务是 FORBIDDEN 的」),再配合提醒钩子对抗「Agent 漂移」——承认宿主有偏置,并主动纠正它,这个自觉很难得。
九、REPL Agent 接口(v2 的新玩法)
- 把
agent_interface设为 REPL 后,只暴露serena_repl一个工具:Agent 写 Python 代码访问 facade(s.lsp、s.edit、s.mem、s.shell……),像 notebook 一样跨调用持久变量; - 经典工具与 REPL 方法是同一实现的两张皮:装饰器双向关联,一份逻辑两种暴露;
- 返回类型文档渐进披露:LLM 用
s.info("Type")按需查文档,不用预先塞满上下文。
十、评估方法论:让 Agent 自己当评测者
- 不用 SWE-bench——文档明确说基准测试不适合评「增强层」;改用Agent 自评:一个一次性会话做约 20 个日常任务(五大类),每个任务同时用两套工具各做一遍,用 git diff 验证、统计调用数与负载大小,并被要求强制报告负面与中性发现;
- 任务只给类别不给固定题目,避免挑题目美化结果;任何人都能在自己的代码库上复现;
- 收敛结论值得记住:Serena 赢在多文件语义操作,微小文本编辑和 shell 任务内置工具仍然更好——它不试图取代宿主工具,只补语义缺口。
十一、值得借鉴的工程实践
- 分层配置 + 组合片段:context/mode 两个正交维度,解决「一套工具适配几十种宿主」;
- fail-fast 优于静默降级:语言服务器启动失败直接报错,不悄悄带病运行;
- 内容哈希做缓存键 + 版本号失效:解析类缓存的干净做法;
- 记忆即 Markdown + 引用图:比向量库简单,但跨会话、跨项目、人机共读全占了;
- 安全边界诚实声明:文档明说工具限制只是「引导」不是安全边界,真隔离要靠 Docker 沙箱;
- 许可分层:SolidLSP MIT、应用层 GPL-3.0,既方便生态集成又保护主体。
十二、我的整理理由
读完这个仓库,我最大的感受是:Serena 的成功不在某个算法,而在一连串克制的取舍——符号优先但保留文本兜底、记忆系统简单到只用 Markdown、评测方法把「自夸」的路全部堵死。做工具做到「知道自己的边界在哪,并且把这个边界诚实地写进文档」,大概就是它能让各家 Agent 都「主动要求装上」的原因。之后我会先在自己的项目里试用 find_referencing_symbols 和 rename_symbol,验证一下「8–12 步收敛为 1 步」到底有多真。
相关笔记:上一篇《DeepSeek Harness 学习笔记》里的 MCP 接入、《ZCode 知识点整理》里的扩展机制,与 Serena 是同一条「给 Agent 补能力」主线的不同侧面。
oraios/serena(本地克隆通读:README、docs/ 全部指南、src/serena 与 src/solidlsp 源码)。主体现为 GPL-3.0-or-later 许可,SolidLSP 子库为 MIT。本页为个人要点整理,功能细节以官方仓库为准。