Agent Select 行为锚点:Coding Agent 为什么偏爱 grep 而不是 LSP 在 Coding Agent 的工具调用统计里,有一个让工程师困惑的现象:明明 LSP(Language Server Protocol,语言服务器协议)能给出更精确的语义导航,Agent 们在需要找代码的时候,却几乎总是先调 grep、ripgrep、find,而不是去用更高级的语义工具。2026

Agent Select 行为锚点:Coding Agent 为什么偏爱 grep 而不是 LSP

在 Coding Agent 的工具调用统计里,有一个让工程师困惑的现象:明明 LSP(Language Server Protocol,语言服务器协议)能给出更精确的语义导航,Agent 们在需要找代码的时候,却几乎总是先调 grep、ripgrep、find,而不是去用更高级的语义工具。2026 年下半年的一份小规模实证研究,把这件事的数据拍到了桌面上:在最简单的本地化任务上,被对比的三个模型使用语义工具的比例只有 0% 到 6%;哪怕强迫它们先走语义路径,任务成功率反而从 100% 跌到了 89%。换句话说,在大多数 Coding Agent 的工作流里,grep 不仅没被淘汰,反而是默认选择。

这件事不是关于 grep 比 LSP 更聪明,而是关于 Agent 在一个回合制的工作循环里到底需要什么。grep 之所以能持续赢,不是因为它精确,而是因为它廉价、可见、可信、易恢复 —— 这四个属性加在一起,构成了 Agent 在高噪声任务流里偏爱的工具可用度。这个结论对一个看似奇怪的实证结果是必要的补充:同样是 LSP,在清洁仓库里加 16% token 却没带来 F1 收益;在噪声多的仓库里却能提升 0.246 的 F1 同时省 12% 的 token。两份同一语言、相似结构的代码库,完全相反的结论,说明决定 Agent 工具选择的不是编程语言,而是 grep 在那个仓库里的错误匹配率。

数据画像:三个模型、三类任务、四个仓库的发现

这份研究的设计非常克制。它覆盖三个 Claude 系列模型、多个 Python 与 TypeScript 仓库,把任务分成三类:本地化(找一段代码在哪)、引用完整性(找某个函数的所有调用方)、多文件重命名。每个数据点跑两到三次,把数字当成信号而不是定论。研究的对比对象是 grep 这一类词法搜索工具,以及 LSP-backed 的三种子语义能力:references(查找引用)、definitions(查找定义)、document symbols(文档符号)。LSP 还能做诊断、重命名、代码动作,这些在本次研究中没测,所以结论只适用于上述三种语义能力。

在本地化这种简单任务上,所有三个模型的语义工具使用率都在 0% 到 6% 区间。当研究方强行改成"先走语义"的策略,任务成功率从 100% 跌到 89%。这说明对于简单找代码的任务,Agent 自己就会选择最快的路径,任何强行的引导都会降低效率。

在引用完整性任务上,模型选择语义导航的比例跳到了 45% 到 57%。这个差距很重要:它说明 Agent 的工具选择不是盲目的偏爱,而是会根据任务性质调整。当任务需要"找全"而不是"找一个",语义工具的价值才会显现。这里 LSP 路径的优势是精度(precision):1.00 对比 grep 的 0.76,意味着每一个返回的位置都是真实的调用,而不是同名注释或字符串带来的误匹配。

最有意思的是仓库之间的对比。研究选取了两个 TypeScript 仓库和一个 Python 仓库做引用完整性测试。在 remeda 这个干净的 TypeScript 仓库里,grep 的精度已经是 1.00(完美匹配),切到 LSP 之后 F1 没有提升,token 却多花了 16%。在 hono 仓库里,grep 的精度只有 0.51(同一个名字到处出现),LSP 提升了 0.246 的 F1 同时省下 12% 的 token。Python 的 requests 仓库是中间情况:grep 精度 0.76,LSP 提升 0.072 的 F1 但多用 19% 的 token。

两个同语言、同结构的仓库给出完全相反的判断,意味着"静态类型语言适合 LSP"这种泛化是错的预测器。真正决定价值的,是 grep 在那个仓库里已经有多干净 —— 在 grep 已经干净的仓库里,语义导航是纯粹的额外成本;在 encoding 已经爆炸的仓库里,精度提升能自己赚回来。这条结论对于工具设计和企业落地都有直接含义:工具好不好,不能光看它本身的能力,还要看它跑在什么地基上。

输出形状:为什么"位置+内容"胜过"位置"

研究的第二个关键发现是:工具的输出形状会改变 Agent 的行为。最初的语义导航版本只返回位置 —— 文件路径、行号、列号。Agent 知道一个引用在哪,但还得再读一次文件才能看到代码,然后才能决定下一步怎么动。grep 则是直接把匹配行返回:`src/auth.ts:42: return validateToken(token)`。位置和内容是一起到达的,模型无需额外的读动作就能判断相关性。

当研究方改了返回内容,在每个引用位置附带两行源代码之后(后端没变,只是返回给模型的东西变了),多文件重命名任务的 pass@1 从 0.67 跳到了 0.83,后续文件读取次数从每次 15.2 暴跌到 3.2 —— 这比 grep 路径自己的 4.3 还低。同样的工具,同样的后端,只是把"位置"升级成"位置加内容",Agent 的工作量和成功率就发生了质变。

这个结果的解读有两个角度,二者并不互斥。第一个角度是分布性的:模型在训练中见过大量 grep 风格的轨迹,内联格式对它更熟悉,所以它用得更顺。第二个角度是结构性的:工具的输出形状决定了 Agent 还需要多少后续动作来消化结果。一个语义正确但需要 5 次后续动作的工具,不如一个不那么精确但 1 次就够用的工具。Anthropic 在关于 Agent 工具设计的指南里表达了同样的观点:工具是非确定性 Agent 的接口,它们返回的上下文本身是设计的一部分。语义正确但让 Agent 走弯路的工具,和错误但让 Agent 走直路的工具,在工作流意义上是相反的。

这条结论对工具作者来说尤其重要:不要只盯着"我的工具能不能返回精确结果",还要问"我的工具让 Agent 还需要做几次额外动作"。一个能让 Agent 一次完成判断的工具,在工作流意义上胜过精度更高但需要二次消化的工具。

Harness 是 Agent 的一部分

把这两份观察综合起来,会自然推出一个等式:agent capability = model × harness。模型的能力不是孤立存在的,它必须和 harness —— 即围绕模型那一整套定义 —— 一起工作。Harness 包含上下文里的指令、可用的工具、它们的输入 schema、返回结果的形状、错误信息的形态,以及决定模型下一步看什么的循环逻辑。

在这份研究里,LSP 后端始终没变,模型也没变。只是在返回里加了几行源代码,Agent 的成功率和读取次数就发生了巨大变化。Anthropic 关于长任务 Agent 的研究也表达了同样的观点,只是把它放到更长时间尺度上:环境配置、进度产物、验证机制,决定了 Agent 在多会话里能完成什么。在单个工具调用循环里,返回格式、错误信息、回退路径,起着相同的作用。

当模型的训练里包含了 Agent 工具调用轨迹,harness 还定义了这些样例里的 prompt、工具调用、结果、恢复路径。一个习惯了 read、grep、edit、bash 这套流程的模型,可能已经学会了依赖这套接口的策略。把它放到一个不同的工具层里,它能发挥出的能力就会变化。这是为什么模型的基准测试分数不能原样搬到另一个运行时 —— harness 才是版本的一部分。

这条原则对工具评测有根本影响。我们评测一个 Agent 工具的时候,不能只看模型在某个特定 harness 上的分数,而要看它在不同 harness 形状下的弹性。同样一个 LSP,在"只返回位置"的 harness 里显得难用,在"位置加内容"的 harness 里就显得丝滑。同样一个 grep,在干净仓库里已经够用,在命名冲突的仓库里就力不从心。

从 affordance 看工具选择

另一份独立的分析从 affordance(功能可见性)的角度解读了这个现象。它提出的核心问题是:为什么 Agent 会优先选择 grep,是因为训练偏好,还是因为 LSP 被藏在了不可见的 IDE 接口后面?答案大概率是混合的,而且公开证据还不足以确定哪一种解释占主导。但有一个事实很清楚:Shell 搜索具有非常清晰的"动作-结果"循环,而 LSP 功能常常被包装在编辑器 UI、扩展状态、后台索引、项目配置后面,不会干净地出现在 Agent 的调用轨迹里。

这就是为什么 Cursor 这种 IDE 形态的产品,会让人感觉比终端优先的 Agent 更聪明:产品形态改变了"什么是廉价工具"。同样的模型行为,在"查找引用是首类动作"的产品里显得聪明,在"查找引用藏在脆弱桥后面"的产品里就显得笨拙。MCP(Model Context Protocol)在这个问题上是有用的,但不是魔法粉。如果 LSP-backed 的 MCP 工具能返回清晰的符号结果和明确的错误,Agent 就有更好的理由去用它;如果它表现得像个黑盒,Agent 就会退回 grep。MCP 不是让工具自动好用,而是让工具变得可见可调。

这条结论对 Harness 工程有直接含义:工具的可见性是 Agent 偏好的一部分。一个被埋在深层配置里、需要 Agent 先理解整套项目结构才能调用的工具,在工作流意义上等于不存在。把它提到工具列表的顶层、给它清晰的描述、让它返回的内容直接可消费,这才是真正的优化方向 —— 比优化工具本身的精度更有效,因为 Agent 不会去用它不理解的工具。

六条检查:给 Harness 工程的实用清单

第一,做同等准确率下的真实任务测试。token 用得少不是胜利,如果成功率也下降了,那就是失败的优化。研究里 grep 在干净仓库里比 LSP 少用 16% token 的同时没掉 F1,这是有效的优化;但如果为了省 token 把准确率搞掉了,这种优化就是负面的。

第二,观察 Agent 是否真的会调用新工具。一个工具"可用"不等于模型"知道何时该用"。研究里三个模型在本地化任务上对 LSP 的使用率只有 0% 到 6%,说明哪怕工具暴露出来了,模型也未必会主动选它。Harness 工程的优化必须包含"调用率"这个指标,而不仅仅是"可用性"。

第三,工具返回的上下文要足够支撑下一步决策。`路径:行号:内容`通常比一个裸位置对象好用。这个原则在研究的数据里有明确支撑:把 LSP 返回从"位置"升级到"位置加内容",pass@1 从 0.67 升到 0.83,后续读取从 15.2 降到 3.2。

第四,保留原生的回退路径。语义搜索和词法搜索解决不同的问题,二者不是替代关系,而是互补。研究本身虽然证明 LSP 在噪声仓库里有优势,但同时也证明 grep 在文本范围编辑(评论、字符串字面量、配置)里不可替代。把它们都留着,让 Agent 根据任务自动路由,而不是做强行的二选一。

第五,按任务和代码库做路由。一个噪声多的代码库可能从语义导航里受益;一次文本范围编辑仍然需要 grep。研究里 remeda 和 hono 是两个 TypeScript 仓库,但给出了完全相反的判断。Harness 工程应该提供条件路由,而不是一刀切的工具优先级。

第六,引入新轨迹时要做强化。一个 prompt 可以告诉 Agent 有一个工具,但未必能创造一个可靠的策略让 Agent 在合适的时候用上它。要让 Agent 形成新的工具习惯,需要在 harness 层提供比"提示词"更结构化的支持 —— 比如把规则写进项目记忆、或者提供具体的路由条件。这就是 affordance 的工程化表达。

企业 Agent 落地的偏好工程启示

把这些发现合起来,对企业部署 Coding Agent 有三个层次的启示。第一个层次是工具设计:不要再争论哪个工具在绝对意义上更好,而是评估在你的代码库、你的任务分布上,哪个工具的 affordance 更好。评测应该跑在真实的 Agent 循环里,而不是只测模型的某个静态能力。

第二个层次是 harness 设计:把工具的可见性放在和工具的能力同等重要的位置。Harness 应该主动暴露工具的应用场景、典型用法、典型陷阱,而不是让 Agent 通过试错去理解这些。返回结果的形状要和下一步决策匹配,不要让 Agent 走不必要的弯路。

第三个层次是偏好工程:与其靠 prompt 暗示 Agent 用什么工具,不如把偏好编码进 harness 的结构里 —— 路由规则、调用模板、错误处理策略。这些结构化偏好比自然语言提示更稳定,也更容易审计。Harness 工程不是"给 Agent 更多工具",而是"让 Agent 更好地选择工具"。

这份研究的真正贡献,是把 Coding Agent 的工具选择从一个"看模型心情"的黑箱,变成了一个可以测量、可以工程化的对象。Grep 持续赢 LSP 不是 bug,而是 Agent 工作流的 feature —— 它廉价、可见、可信、易恢复。企业要做的不是强行让 Agent 用更花哨的工具,而是让自己的 harness 也能具备这四个属性。Harness 引导偏好,偏好引导行为,行为引导结果。这条链条上的每一环,都比工具本身的能力更值得优化。