关于 speedread
speedread 是供编码 Agent 使用的 MCP 服务和命令行工具,可按符号和令牌预算读取代码。它整合仓库概览、带上下文的搜索、关系追踪及变更感知读取,减少重复的代码阅读调用。
主要功能
- 01按预算生成仓库概览
- 02按符号归组搜索结果
- 03追踪调用者、被调用者、引用和实现
- 04批量读取符号相关代码
- 05通过 etag 读取变更
- 06命令行与 JSON Lines 输出
适用场景
- 01查找编码任务相关代码
- 02追踪调用关系和实现
- 03仅查看上次读取后的变更
- 04在令牌预算内检查大型文件
MCP 相关
向 MCP 客户端提供 `map`、`search`、`trace` 和 `read` 四种代码阅读工具。命令行工具也提供对应能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

speedread
为 AI 编码 Agent 提供高效利用令牌的代码搜索与导航。 speedread 是一个 MCP 服务和 CLI,可在令牌预算内,为 Claude Code、GitHub Copilot、Codex、Cursor、Gemini CLI 等 Agent 提供回答问题所需的代码库片段:每个搜索结果所在的函数、大型文件的结构骨架、符号的调用方和实现,或自上次读取以来的变更。它不返回整个文件或裸 grep 结果。
Read 并不适合编码 Agent。 Agent 阅读代码应当具有自适应性、状态性、符号感知能力,并受令牌预算限制,而不是以字节为单位。每次模型调用都会重新发送系统提示词、工具定义和此前的对话(在我们的评测中,仅这些内容就有 18–21k 个令牌,还未包含代码),因此 Agent 的成本更多取决于往返次数,而非字节数。ripgrep 返回匹配项,cat 返回字节,因此 Agent 只能再次询问:打开文件、查找函数、搜索下一个调用点。speedread 返回针对问题最小且有用的代码单元,并提供足够的结构信息,通常无需再次调用。
| Agent 需要什么 | 内置工具返回什么 | speedread 返回什么 |
|---|---|---|
| 某项内容的位置 | 文件名或裸匹配行 | 每个命中项及其所属函数或类,并标出行范围(search) |
| 大型文件中的一个函数 | 按每页 2,000 行返回整个文件,或返回猜测的范围 | 该符号的完整源码(read path#Symbol),或文件结构骨架 |
| 调用方、被调用方、实现 | 每次跳转都要搜索,然后继续读取 | 一次调用即可查看关系,最多深入三层(trace) |
| 编辑后再次查看文件 | 再次返回整个文件 | 仅返回变更内容,并按符号标注(read path@etag) |
十项代码问题任务之一,按记录的速度重放了其评测对话记录;每种条件下的三次试验表现完全一致。内置的 grep 只返回文件名,因此 Agent 必须再问两次。speedread 的 search 则会返回匹配行及其所属声明。这是十项任务中节省量第二大的任务;有两项任务的结果约差 1%,而十项任务的输入令牌总量减少了 35%。交互式完整报告:brennengreen.github.io/speedread(也可在 demo/index.html 中独立查看),由 demo/build.py 根据 evals/results/ 生成。
用实测说话,而非空谈
在真实仓库中运行真实 Agent,两组使用相同的模型(claude-sonnet-5)和运行环境(GitHub Copilot CLI):一组使用内置工具,另一组使用 speedread 作为阅读工具。每次试验、转录记录、评分器和差异内容都已提交,包括 speedread 没有带来帮助的工作负载。
| 工作负载(真实 Agent,模型和运行环境相同) | 每组试验次数 | 输入 token | 模型耗时(中位数) | 质量 |
|---|---|---|---|---|
| 代码问题:查找、阅读、回答 | 30 | −35%(95% CI −45 至 −23%) | −47% | pass^3 90% → 100% |
| 关系问题:调用方、被调用方、实现 | 8 | −57%(CI −74 至 −20%) | −34%(不显著) | 100% → 100% |
| 修复 bug:查找、编辑、运行测试套件(有指导 · 独占) | 16 | −3% · −1%(不显著) | −24% · −27%(不显著) | 100% → 100%;压缩从未掩盖 bug |
| 已安装但未作为阅读工具使用(问答 · 修复 bug) | 10 · 16 | +31% · +46%(10 项中有 9 项 · 8 项中有 8 项更高) | — | 26 次试验中 0 次使用 |
- 往返调用决定节省幅度。 speedread 通过减少模型调用来节省开销:返回带有外围上下文的答案、批量读取,以及单次跳转完成
trace。修复 bug 时,编辑和测试占据了大部分轮次,而读取结果只占输入的约 1%,因此 token 几乎没有变化。 - 是否采用决定一切。 未使用的 MCP 服务并非零成本:每次调用都会附带其工具定义(实测每次调用 +2.2k token)。将其配置为阅读工具。
- 适用范围。 仅测试了一个模型和一种运行环境,且关系问题和 bug 修复测试套件规模较小。其他客户端和模型未经测试;这些数据仅能证明此配置下的效果,并不保证你的场景也如此。
pass^3 表示三次试验全部通过的任务占比。区间是对均值比率进行 bootstrap 得出的 95% 置信区间(evals/stats.py)。各套件详情:结果 · 方法:evals/README.md · 所有表格:evals/RESULTS.md · 原始试验和转录记录:evals/results/
快速开始
1. 安装(适用于 Apple Silicon 上的 macOS,Rust 1.90+;在 M4 上,干净构建耗时 80 秒,另需下载时间):
cargo install --locked --git https://github.com/brennengreen/speedread
# or: brew install brennengreen/tap/speedread
预编译二进制文件、一键式 Claude Desktop 安装包、其他平台,以及 tap 名称的由来:安装。
2. 将其添加到 Agent 中作为阅读工具,而不是再添一个普通工具。 在没有指导的情况下将其与内置工具一同安装后,它未被使用,反而增加了运行开销(见上文)。
# GitHub Copilot CLI: add the server, then remove the built-in readers (edit and bash stay)
copilot mcp add speedread -- speedread mcp
copilot --excluded-tools view grep glob
# Claude Code: keep Read, because Edit requires it
claude mcp add --scope user speedread -- speedread mcp
claude --disallowedTools Grep Glob
VS Code、Cursor、Codex、Gemini CLI、Zed 和 Claude Desktop:配置。如果无法移除内置工具,请将阅读说明添加到 AGENTS.md、CLAUDE.md 或 .github/copilot-instructions.md。
3. 或者在任意仓库中手动试用:
speedread map --symbols # structure, with each file's top-level definitions
speedread search 'handleRequest' # hits grouped under their enclosing function
speedread trace '#handleRequest' --depth 2 # callers of callers; --direction callees|refs|impls
speedread read 'src/app.ts#Server.start' src/util.ts:40-80 # several targets, one call, one budget
基本操作
通过 MCP 提供的四种工具,CLI 中也有对应功能:
| 基本操作 | 用途 | 返回内容 |
|---|---|---|
| map | 定位结构 | 按重要性加权、受预算限制的仓库树,包含行数;可按需返回顶层符号 |
| search | 定位文本 | 使用 ripgrep 引擎;每个命中项按其所属函数或类分组,并附带行范围 |
| trace | 定位关系 | 调用方、被调用方、引用、实现:支持语法分析并感知接收者 |
| read | 获取确切证据 | 在单个 token 预算内批量处理目标和 path#Symbol;path@etag 仅返回变更内容 |
read:批量读取、预算控制、感知符号
一次调用可接收任意组合的目标。它们共享一个 token 预算(默认 8,000)。
| 目标 | 返回内容 |
|---|---|
src/app.ts | 整个文件。如果超出预算,则返回骨架:签名、类型和文档,函数体折叠为 A-B ⋯。如果仍然太大,则返回大纲。绝不会盲目截断。 |
src/app.ts:120-180、src/app.ts:120 | 指定行;单行(或编译器错误中的 file:line:col)会返回所在的函数或类。如果不在任何符号内(顶层代码,或没有语法解析器的语言),则返回所在的代码块,根据缩进和 end、} 等闭合行来确定。 |
src/app.ts#handleRequest、#Server.start | 该符号的完整源代码,包括文档和装饰器。单独使用 #Name 可查找任意位置的定义。 |
README.md#Install、package.json#scripts | Markdown 章节,或 JSON、YAML、TOML 键。 |
src/**/*.test.ts | glob(遵循 .gitignore);结果集较大时,优先缩减最大的文件。 |
src/app.ts@<etag> | 仅返回自标头中 etag 对应版本以来发生的更改。 |
以下是 flask 的 1,628 行 app.py 的真实骨架(节选),耗费 3.4k tokens,而整个文件需要 21k:
==> src/flask/app.py @… (1,628 lines) [skeleton]
110 class Flask(App):
111 """The flask object implements a WSGI application and acts as the central
112-205 ⋯
366 def get_send_file_max_age(self, filename: str | None) -> int | None:
367 """Used by :func:`send_file` to determine the ``max_age`` cache
368-391 ⋯
感知符号的重新读取。 编辑后,path@etag 会返回 unchanged、增长中的日志新增的尾部内容,或指出具体更改的差异。差异块带有 git 风格的函数上下文,mode=outline 则只返回符号摘要。摘自 tests/mcp.rs:
==> src/lib.rs @… (was @…): 1 hunk, +1 -1, now 131 lines
symbols:
add [1-7]: body changed, signature unchanged
@@ -2,5 +2,5 @@ add
pub fn add(a: i32, b: i32) -> i32 {
let c = a + b;
- let d = c;
+ let d = c * 2;
let e = d;
签名编辑会读取 f3 [25-27]: signature changed: `pub fn f3() -> u32` → `pub fn f3(k: u32) -> u32` ;新增函数会读取 g [133-135]: added `pub fn g() -> u8` 。
Etags 为 64 位。 etag 是内容完整的 64 位 xxh3 哈希,以 16 位十六进制数字打印;Agent 已读取的内容快照存储在以 etag 为键的 256 MB LRU 缓存中。两个不同的内容必须在 64 位上发生碰撞,才会被视为相同。在单个会话中的 100,000 个快照里,发生这种情况的概率约为 3 × 10⁻¹⁰。相同内容共享 etag,这是正确的。较短的标签会被拒绝,不会按前缀匹配。
search:按所在符号分组的匹配结果
$ speedread search 'def url_for|current_app.url_for\(' src/flask/helpers.py
3 matches in 1 file for /def url_for|current_app.url_for\(/
==> src/flask/helpers.py @5dc322f9c9cf99f2 (3 matches)
[200-251] def url_for(endpoint: str, *, _anchor: str | None = None, _method: str | None = None, _scheme: str | None = None, _external: bool | None = None, **values: t.Any) -> str
200 def url_for(
212 :meth:`current_app.url_for() <flask.Flask.url_for>`. See that method
244 return current_app.url_for(
output=symbols 会在同一次调用中返回每个匹配所在函数的完整源代码。output=files 会返回带计数的路径。
trace:追踪关系,而非文本
Agent 最耗费资源的循环是 search → open → 再次 search,以跟踪调用链。trace 一次调用即可完成。以下是 gin 的真实输出(节选):
$ speedread trace '#AbortWithStatus' --depth 2
==> callers of Context.AbortWithStatus (context.go:221-227)
func (c *Context) AbortWithStatus(code int)
14 call sites in 14 functions · syntactic: comments, strings and the definition excluded
context.go
[245-251] func (c *Context) AbortWithError(code int, err error) *Error
249 c.AbortWithStatus(code)
← [820-828] func (c *Context) BindUri(obj any) error
824 c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
…
$ speedread trace Render.Render --direction impls
==> implementations of Render.Render (render/render.go:11-12)
Render(http.ResponseWriter) error
19 implementations of Render · syntactic: types whose method sets cover the interface
render/bson.go [13-16] type BSON
[20-29] func (r BSON) Render(w http.ResponseWriter) error
render/data.go [12-16] type Data
[18-26] func (r Data) Render(w http.ResponseWriter) (err error)
…
它支持四个方向:
callers:按调用函数分组的调用点;depth设为 2–3 可构建调用树。callees:函数体中的每个调用,并解析到其定义。refs:每处使用,包括导入和类型提及。impls:子类以及 trait、协议和接口实现。Go 接口按方法集进行结构匹配。方法目标会列出每个重写实现。
tree-sitter 会排除注释和字符串。系统会通过接收者、所在类、Go 包和文件区分同名定义。仍然存在歧义的调用点会标记为 ?,绝不会被悄悄合并。它基于语法,不进行类型推断。请参阅限制与路线图。
map:按预算生成概览
遵循 .gitignore 并包含行数的目录树。目录会按重要性逐步展开,直到耗尽预算(默认 3,000):优先显示源码,其次是隐藏目录、测试目录或供应商目录。symbols=true 会添加每个文件的顶层定义。符号链接会列为 name → target,绝不会跟随。
代码执行与 Skills
Anthropic 的通过 MCP 执行代码主张在数据到达模型之前先进行过滤。四个工具定义总计约需 1.4k tokens。CLI 与它们保持一致,并为需要编写脚本的 Agent 添加了 JSON Lines:
speedread symbols src --json | jq -r 'select(.kind=="function" and .end-.start>80) | "\(.path):\(.start) \(.qualified)"'
speedread search 'TODO' --json | jq -r .path | sort | uniq -c | sort -rn | head
speedread map --json | jq -s 'map(select(.lines != null)) | sort_by(-.lines) | .[:10]'
skills/speedread/SKILL.md 将这一工作流打包为 Agent Skill。
结果
这里的每个数字都来自 evals/ 中的一项评测,该评测依据 Anthropic 的 Demystifying evals for AI agents 构建:明确的任务、重复试验、确定性的结果评分器、pass@k 和 pass^k、包含对照组的均衡任务集、相互隔离的试验,以及经过阅读的转录记录。原始数据(包括每份转录记录)均已提交。所有 Agent 试验都通过 GitHub Copilot CLI 使用 claude-sonnet-5 进行,令牌数量取自运行框架的使用日志,精确无误。汇总表涵盖了全部四种真实 Agent 工作负载,而不仅仅是表现最好的一种。
代码问题
Suite 3:10 个带有版本特定答案的问题,涉及 6 个代码库和 5 种语言,每个问题试验 3 次。10 项任务的成本都有所下降,两种条件下工具结果的大小大致相同:节省来自每个答案少了两次往返。作为唯一的读取工具,speedread 在 30 次试验中使用了 27 次;另外 3 次,Agent 使用 cat 读取了一个 41 行的 go.mod 文件。本套评测开展时,trace、符号差异和内容感知估算器都尚不存在。
关系问题
Suite 3b提出了两跳调用者、已解析的被调用者、Go 接口实现(结构性)和 Rust trait 实现等问题。其中两项用一次好的 grep 就能回答,作为对照组。Agent 未经提示,在 8 次试验中有 7 次选择了 trace。对于两跳问题,内置工具用了 7–14 次工具调用和 186k–277k 个令牌。使用 speedread 时,Agent 以 depth: 2 调用了 trace,又检查了一个函数后便给出答案:2 次调用,61k 个令牌。样本量较小(每组 8 次试验),因此影响幅度仅供参考。
转录记录审查促使我们调整了本套评测的评分器。两跳任务的两次基线试验都排除了 BasicAuth,理由是它的 AbortWithStatus 调用位于 BasicAuthForRealm 返回的闭包内部,而路由器会将该闭包作为值调用。这种解读有其合理之处,因此评分器现在接受这两个答案。trace 会将闭包内的调用归到外层的具名函数中;此项已列入限制。
Bug 修复(SWE 风格)
套件 4 在 gin(Go)和 flask(Python)中注入 8 个真实回归问题,向 Agent 提供仅描述症状的 Bug 报告,并依据仓库的**完整测试套件(测试未作任何修改)**评分。每项任务都经过验证:注入问题后测试会失败,应用参考修复后则会通过。四种条件如下:
- 基线:仅使用内置工具
- 可用:安装 speedread,不提供使用指导
- 优先使用:用一句话要求 Agent 使用它进行阅读
- 独占使用:移除内置的 view/grep/glob;保留 edit 和 bash
共进行 64 次试验:
- **质量没有损失。**64 次试验全部通过。压缩从未掩盖 Bug:在使用 speedread 的 32 次试验中,有 0 次先将含 Bug 的文件显示为骨架或大纲,再显示 Bug 所在的代码行。Agent 先搜索,再阅读准确范围。
- **是否采用呈现两极分化。**在 16 次试验中,可用条件下 speedread 一次也没被选用,但 8 项任务的输入 token 都比基线更多(+46%;成本 +30%)。一句话指导就让采用率升至 16/16,78% 的读取通过 speedread 完成。
- **调用轮次更少、耗时更短;token 数持平。**在 8 项任务中,优先使用条件有 7 项调用模型次数更少(中位数为 5 次,而非 6 次),模型耗时减少 24%。独占使用条件的模型耗时减少 27%,在 8 项任务中有 7 项耗时更短。成本下降 4–6%,但输入 token 数仍处于噪声范围内(−3%、−1%)。每种条件只有 16 次试验,因此这些区间均未排除零;应将其视为趋势(统计数据)。
- **未观察到:**评审者期待的“成功率不变,同时上下文减少 50–90%”。当阅读占据大部分调用轮次时,这一结果成立;在短暂的编辑与测试循环中则不成立。
- 在这 32 次试验中,从未调用
trace:根据症状修复 Bug 需要搜索和阅读,而非调用图。
工具级别
套件 1 涵盖真实代码仓库中的 35 个阅读场景。每个场景都按信息充分性评分:如果精简后的回答遗漏了任务所需信息,就会判定失败。它展示了这些基本工具能将内容压缩到什么程度,也包含若干结构上有利的对比:整文件读取和每页 2,000 行。公平的对比是最佳情况基线:搜索名称并精确读取对应函数,结果为 −46%,且只需一次调用,而非两次。上文真实 Agent 测试套件的结果才是评估 Agent 表现的依据。
预算契约
预算以 token 计,但没有客户端会告知服务端其分词器。Suite 2 使用 SVG 路径数据、JSON、锁文件、压缩后的 JS、真实的 CJK 文档、表情符号、base64、十六进制转储和数字表格来攻击估算器。在 o200k、cl100k 和旧版 Claude 分词器下,固定采用每 token 2.6 字节的估算方式,会导致 9.5% 的读取超出预算,最严重时达到预算的 1.81 倍。感知内容的估算器(src/tokens.rs)根据字符类别对 token 数量建模,并以 o200k 和旧版 Claude 中更严格的一方为基准进行拟合。它使 525 次读取中超出预算的次数降至 0(最严重时为预算的 0.99 倍)。
使用生产环境分词器校准。 离线分词器只是近似值。测试工具会记录每次模型调用的准确输入 token 数,因此可以通过连续调用还原每个工具结果的实际成本(Suite 2b)。在 claude-sonnet-5 上,实际 token 数中位数是估算值的 1.22 倍;speedread 自身的输出则是 1.36 倍,这与 Anthropic 关于 Claude 4.7+ 分词器产生的 token 数增加约 30% 的说明一致。因此,自称为 Claude 的客户端会使用 Claude 配置,将预算扩大 1.4 倍。可在任何位置通过 SPEEDREAD_TOKENIZER=claude 强制使用;其他配置为 openai 和 legacy。
速度
速度不是重点,减少返回内容才是。但每次调用执行更多工作(例如解析、分组和预算控制)不会增加延迟,这一点仍然很重要。以下数据在 Apple M4(4 个性能核心 + 6 个能效核心)、缓存预热状态下测得,数值为中位数:
| 任务 | speedread | ripgrep 默认设置 | ripgrep -j4 |
|---|---|---|---|
| 遍历 vscode(19,167 个文件),包含大小和修改时间 | 26 ms | 29 ms(仅文件名) | 30 ms |
| 在 vscode 中搜索字面值 | 103 ms | 294 ms | 109 ms |
读取 742 KB、2.1 万行的 .d.ts → 骨架(冷启动进程) | 17 ms | ||
在 vscode 中查找 #createDecorator 定义 | 332 ms |
当 ripgrep 被限制为只使用性能核心时,搜索速度与其相当。与 ripgrep 默认设置相比慢 2.9 倍,是因为线程在这款芯片上被分配到了能效核心:内核时间增加了 6.6 倍。speedread 根据 hw.perflevel0.logicalcpu 设置线程池大小。
对比
其他 MCP 服务也涵盖了其中一些功能。官方文件系统服务会批量读取整个文件,并按行数限制读取范围。Serena通过语言服务器感知符号。ast-grep MCP支持结构化搜索,claude-context通过嵌入向量进行搜索,repomix则会将整个代码库打包到一个文件中。speedread 将批量读取、符号感知读取整合在同一 token 预算下,并支持仅重新读取差异和查询关联关系,而且无需嵌入向量或语言服务器。包含来源的功能表见 docs/RESEARCH.md。
安装
从源代码安装(需要 Rust 1.90+,可通过 brew install rust 或 rustup 安装;tree-sitter 语法也需要 C 编译器,Xcode 的 Command Line Tools 提供了该编译器):
cargo install --locked --git https://github.com/brennengreen/speedread # or, in a clone: cargo install --locked --path .
这会安装一个原生二进制文件 ~/.cargo/bin/speedread,无需运行时依赖。--locked 会构建 Cargo.lock 中锁定的依赖版本,CI 会测试这些版本。
**预编译版本,适用于 Apple Silicon 上的 macOS:**每个版本发布都会附上二进制文件及其 SHA-256 校验和。
mkdir -p ~/.local/bin # or any directory on your PATH
curl -fsSL https://github.com/brennengreen/speedread/releases/latest/download/speedread-aarch64-apple-darwin.tar.gz | tar -xz -C ~/.local/bin
该二进制文件未经公证。curl 不会设置 macOS 的隔离标记;如果你改用浏览器下载,请运行 xattr -d com.apple.quarantine speedread 清除该标记。
**Claude Desktop,一键安装:**下载 speedread-aarch64-apple-darwin.mcpb 并打开。Claude Desktop 会询问允许 speedread 读取哪个文件夹;读取该文件夹之外的内容会被拒绝。
Homebrew:brew install brennengreen/tap/speedread 会从源代码构建。请使用完整名称:普通的 brew install speedread 安装的是另一个程序,即 homebrew-core 中的 RSVP 快速阅读工具,它也会安装一个 speedread 二进制文件。
**MCP Registry:**该项目列为 io.github.brennengreen/speedread(mcp-name: io.github.brennengreen/speedread),因此支持注册表的客户端可以找到并安装该软件包。为你安装 speedread 的 Agent 可以参考 llms-install.md。
新版本会作为版本发布并附带说明;如需接收通知,请使用 Watch → Custom → Releases。
平台支持
| 平台 | 状态 |
|---|---|
| Apple Silicon 上的 macOS | 已构建、调优并测试:CI 会在 macOS 15 上使用两种目录遍历器运行测试套件。 |
| Intel 上的 macOS | 使用相同代码。测试套件已在 Rosetta 2 下作为 x86_64 构建通过;尚未在 Intel 硬件或 CI 中测试。 |
| Linux | 测试套件已在 Ubuntu 24.04(x86_64)上通过,CI 会在每次推送时运行测试。macOS 专用代码会被排除编译,并使用可移植的遍历器(ignore crate)。尚未针对该平台进行调优或基准测试,也暂无预编译二进制文件:请使用 cargo 安装。欢迎反馈其他发行版和 arm64 的测试结果。 |
| 其他 Unix 系统 | 未测试;适用 Linux 代码路径。 |
| Windows | 不支持:代码使用了仅限 Unix 的 API。WSL2 与 Linux 状态相同。 |
结果中的速度数据来自 Apple M4。
让它成为读者
可用不等于采用。如果 speedread 只是和内置工具一起安装,却没有任何引导,那么在两个测试套件的 26 次试验中,它一次也没有被使用。由于它的工具定义会随每次模型调用一并发送,这些运行的成本甚至高于不安装 speedread(输入 token 分别增加 31% 和 46%)。一句引导就让采用率达到 16 次中的 16 次。因此,应将 speedread 配置为读者,而不是众多选项之一。
**GitHub Copilot CLI:**添加服务,然后移除内置读取工具。保留 Edit 和 bash。
copilot mcp add speedread -- speedread mcp
copilot --excluded-tools view grep glob
Claude Code:
claude mcp add --scope user speedread -- speedread mcp
claude --disallowedTools Grep Glob # keep Read: Edit requires it
**Claude Code 的注意事项,已量化。**Claude Code 的
Edit/Write要求先用原生Read读取文件;MCP 读取不算 (claude-code#32214,已关闭,且不计划修复)。speedread 无法移除这次读取。在 bug 修复测试套件中,speedread 的 Agent 编辑了此前只通过 speedread 查看过的文件。对每个文件执行完整的默认Read会花费 2k–21k token(中位数为 9.8k),而且之后每一轮都会重新发送这些内容。加上这部分(作为上限),相较基线,preferred 的输入 token 会从 −3% 变为 +10%,exclusive 则会从 −1% 变为 +19%。基线不会变化,因为它本来就通过原生方式读取了这些文件。对编辑位置执行范围Read可能就足以满足检查,但尚未验证。在 Claude Code 中,预期 speedread 会在编辑密集型工作中减少轮次和耗时,而非减少 token;上述探索和关系梳理方面的节省仍然适用。
**VS Code、Cursor、Codex、Gemini CLI、Zed、Claude Desktop:**参见配置。将以下内容添加到 AGENTS.md、CLAUDE.md 或 .github/copilot-instructions.md:
## Reading code
Use the speedread MCP tools to read, search and navigate code; use built-in tools only to edit and run commands.
- `read` everything you need (files, `path:A-B`, `path#Name`, `#Name`) in ONE call; expand skeletons with `path#Name`.
- `search` finds text (hits show their enclosing function); `trace` follows callers, callees and implementations.
- After editing, `read path@etag` to see only what changed.
配置
speedread 通过 stdio 提供 MCP(speedread mcp)。工作区根目录依次来自 --root <dir>(可重复指定)、$CLAUDE_PROJECT_DIR、客户端的 MCP 根目录(VS Code/Cursor 文件夹、Claude Code --add-dir),然后是当前目录。通常无需任何标志。
| 客户端 | 配置 |
|---|---|
VS Code(.vscode/mcp.json) | { "servers": { "speedread": { "type": "stdio", "command": "speedread", "args": ["mcp"] } } } |
Cursor(~/.cursor/mcp.json) | { "mcpServers": { "speedread": { "command": "speedread", "args": ["mcp"] } } } |
Codex CLI(~/.codex/config.toml) | [mcp_servers.speedread] · command = "speedread" · args = ["mcp"] |
Gemini CLI(~/.gemini/settings.json) | { "mcpServers": { "speedread": { "command": "speedread", "args": ["mcp"] } } } |
Zed(settings.json) | { "context_servers": { "speedread": { "source": "custom", "command": "speedread", "args": ["mcp"] } } } |
| Claude Desktop | .mcpb bundle(一键安装),或绝对二进制路径加上 "args": ["mcp", "--root", "/path/to/project"] |
GUI 应用可能无法继承 shell 的 PATH;请使用 which speedread 返回的绝对路径。
环境变量:
SPEEDREAD_TOKENIZER=claude|openai|legacy固定预算校准方式(默认:从客户端检测,否则使用legacy)。SPEEDREAD_THREADS设置工作线程数(默认:性能核心数)。SPEEDREAD_WALKER=portable使用便携式 walker。
局限性与路线图
trace是基于语法的。 它使用 tree-sitter,并根据接收者、类和包进行名称解析,但不进行类型推断,因此对未知接收者调用x.f()时,会匹配所有f,并标记为?。闭包和 lambda 中的调用会归入外层的具名函数,作为值传递的函数不算调用。下一步是在相同的trace接口后加入可选的 LSP/SCIP 层,以支持精确引用、重写和调用层级。- 定义带来的开销与采用率。 无论是否使用,该服务都会为每次模型调用增加约 2.2k 个 token(若替代 view/grep/glob,则净增 0.9k)。下一步包括精简描述、对工具名称和描述进行 A/B 测试以提高用户主动采用率,以及提供一个自行选择 map、search、trace 或 read 的高层
context工具。 - 样本量。 Bug 修复和关系测试集每组分别有 16 次和 8 次试验,且 Bug 修复的 token 和耗时差异都在噪声范围内。下一步包括增加任务和试验次数,并使用其他测试框架(Claude Code、Codex)。
- 预算是估算值,而非分词器计数。预算根据离线分词器进行校准,并结合生产计数中的 Claude 配置。
- 针对搭载 Apple Silicon 的 macOS 进行构建和调优。Linux 在 CI 中通过测试套件,但尚未调优;Intel macOS 未纳入 CI。不支持 Windows。参见平台支持。
这些事项各自在 ROADMAP.md 中说明了范围、所需技能和建议的第一步。
安全
从设计上只读:不提供写入工具。路径会被规范化,且必须位于某个根目录内。依赖缓存(~/.cargo/registry、~/go/pkg/mod、SwiftPM 检出目录、SDK)可读取;--no-deps 会关闭此功能,--unrestricted 则会取消所有路径限制。macOS 的隐私保护仍然适用。请私下报告漏洞;参见 SECURITY.md。
工作原理
- 预算阶梯。 每个目标有四种视图:完整视图、骨架、紧凑骨架(折叠注释、文档字符串和导入内容块)以及大纲。条目会从最大项开始逐步降级,直到批次符合预算:先处理探索性目标,再处理请求的符号,显式范围则始终保留。最终大纲会保留所有顶层符号,并按广度优先顺序填充成员。响应会根据内容感知估算值确定大小,发送前还会再检查一次。最高预算(10k)可确保结果在所有客户端中以内联方式显示:Copilot CLI 会将超过 30 KB 的结果写入文件,Claude Code 则会在超过 10k 个 token 时发出警告。
- 大纲。 tree-sitter 支持 Rust、Python、JavaScript、TypeScript/TSX、Go、Java、C、C++、C#、Ruby、PHP、Bash、Swift、Kotlin、Scala、Lua、Luau(
.luau,以及使用 Luau 语法的.lua文件)和 Objective-C。Markdown、JSON、YAML 和 TOML 则由单遍扫描器处理。解析 Objective-C 和 C 头文件前,会先清空 Apple SDK 宏;否则 tree-sitter 的错误恢复会丢弃文件的其余部分。语法定义早于 Luau 属性的语法,因此也会以同样方式清空@native等 Luau 属性。 - 缓存。 每次访问时,都会根据(大小、mtime ns、inode、设备)验证源文件。大纲按内容哈希缓存,长期运行的 MCP 进程会让这两类缓存保持热状态。
- macOS。
getattrlistbulk(2)遍历器可在一次系统调用中批量获取条目的名称、类型、大小、mtime 和标志:比 readdir+lstat 快 1.8 倍。- 工作线程池按性能核心数量设置,优先级为
QOS_CLASS_USER_INITIATED。 - iCloud 按需文件会快速失败,不会触发下载(
IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES)。 - 会提高 launchd 为 GUI 启动进程设置的 256 个描述符软限制。
- 缓存文件不使用 mmap:编辑器原地截断文件时可能触发 SIGBUS。
- 使用 NEON
memchr、simdutf8、xxh3和mimalloc。
每项设计选择背后的研究及其来源均见 docs/RESEARCH.md。
贡献
欢迎提交问题、评估结果和拉取请求,包括 speedread 没有帮助的情况。请在 Discussions 中提出设置问题和想法。以下是一些有用的入门方向:
- **Linux:**测试在 CI 中通过;欢迎分享真实使用情况。一个简单的首批修复:有 五个仅适用于 macOS 的辅助函数 会导致 clippy 在 Linux 上失败。
- **其他运行框架和模型:**Claude Code、Codex、Cursor 和 Gemini CLI 已在上文配置,但尚未进行评估。分享评估结果。
- **语言和客户端:**为解析效果不佳的语言提供大纲测试样例,或提供经过验证的客户端设置。
CONTRIBUTING.md 介绍了设置、测试、添加语言,以及本项目的一条特定规则:模型会读取工具描述和服务说明,因此修改它们可能影响采用情况,而且需要进行评估。ROADMAP.md 列出了范围明确的工作,包括哪些项目适合作为首次贡献。
开发
cargo test # unit, MCP protocol and outline snapshot tests
cargo fmt --check && cargo clippy --all-targets -- -D warnings # CI's other checks
UPDATE_SNAPSHOTS=1 cargo test --test outlines # after an intentional outline change
python3 evals/tool_eval.py <bench> --speedread target/release/speedread --rg rg
python3 demo/build.py # regenerate the demo and README charts from evals/results
许可证
MIT © 2026 Brennen Green
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。