
1. 技能加载全挂先别急着怀疑模型1.1 表象还原一次看起来像“全场翻车”的加载失败前阵子我在终端里启动 opencode准备用刚配置好的一批 skills 来处理手头项目。所谓 skills就是 opencode 里一类可复用的“技能包”一个 SKILL.md 负责描述这个技能怎么用旁边再放一些参考文档、脚本或模板模型在会话里能根据任务自动匹配并加载它们。我本来想着这次能省不少事结果打开会话输入/skills列表里干干净净一个技能都没出现。再试一次模型对我的技能指令完全“失忆”。日志里倒是没有明显的红字可打开调试面板就看到了“spawn rg ENOENT”和“failed to find ripgrep”这类报错。第一反应是 opencode 安装坏了准备重装。可转念一想这类工具出问题往往不是本体坏了而是它依赖的某个底层可执行文件没就位。这里要说的“ripgrep”就是那个底层依赖。ripgrep 是一个用 Rust 写的全文搜索工具作者是 BurntSushi在开发者圈子里通常简写为rg。它最大的特点是快递归搜索几十万行代码只需要几十毫秒而且默认尊重.gitignore不用你手动排除一堆目录。VSCode 的全局搜索、todo-tree 插件、甚至不少 CI 脚本都在用它opencode 的技能加载模块也基于它工作。我的技能全部加载失败偏偏不是模型或者 API 的问题而是它没有找到可以用的系统 rg。1.2 从几条蛛丝马迹定位到 ripgrep排查这类问题最直接的办法不是猜而是看日志。opencode 的日志默认写在用户数据目录下Linux 和 macOS 上一般在~/.local/share/opencode/log/Windows 则在%USERPROFILE%\.local\share\opencode\log\附近。里面会有按日期命名的 log 文件打开后搜索rg、grep、ripgrep、skill这几个关键词基本就能看到完整的调用链。我那次在日志里找到了这样几行2025-XX-XX 10:23:45 ERROR skill_loader: failed to list skills: exec: rg: executable file not found in $PATH 2025-XX-XX 10:23:45 ERROR skill_loader: fallback to builtin rg failed: binary not found第一行说在 PATH 里找不到rg第二行说 opencode 尝试使用内置 ripgrep 二进制结果也没找到。也就是说技能加载器同时依赖“系统 rg”和“内置 rg”只要两者都不可用整个扫描流程就会抛错因为它属于启动阶段的强依赖异常处理又比较保守最终表现为所有技能一起失败而不是跳过某个坏文件。明白这一点之后问题就变成“为什么系统里没有 rg”。我快速执行了which rg输出为空。又看了下dpkg -l | grep ripgrep同样没有。这台机器虽然是日常开发机但之前一直用 VSCode 的远程搜索功能从来没意识到 VSCode 自带的搜索能力依赖的是vscode-ripgrep这个扩展而不是系统级 rg。当 opencode 以 CLI 方式运行时它无法直接复用 VSCode 内部打包的 rg必须依赖自己的调用路径。2. opencode 技能加载机制与 ripgrep 的定位2.1 技能系统的基础逻辑不是“塞给模型”而是“能被找到”opencode 的技能系统不像插件市场那样需要安装“特效包”它更接近一组约定你把自己的工作方法、项目规范、常用命令、检查清单写进 SKILL.md再把相关文件放在同一目录下opencode 在会话启动时会对这些目录做一次索引供模型按需调用。这里有一个关键点技能能不能被模型感知取决于首次加载时 opencode 是否成功扫描到了技能文件。而扫描动作并不是逐个文件夹readdir那么简单它通常会在技能目录里递归查找所有SKILL.md、.md、.txt、.json、.yaml等可能含有上下文的文件然后做关键词切分、文件名匹配、内容检索。文件一多用find或者 Go 标准库的filepath.Walk也能实现但性能会比较难看尤其是在大型 monorepo、多技能包叠加的场景下。所以 opencode 直接把这款搜索任务交给了 ripgrep。rg 能在一棵很大的目录树里快速命中“哪些文件包含某个关键词”也支持--files模式只列出文件路径还能通过.ignore和.gitignore跳过依赖目录。对 opencode 来说技能目录里真正需要加载的业务文件通常不多但如果不做任何忽略node_modules、build、.git 这些目录一旦混进来整个索引过程会非常慢甚至超时。rg 的 ignore 规则正好解决这个问题。2.2 为什么偏偏是 ripgrep而不是别的搜索工具可能有人会问直接用 Go 的regexp加上filepath.Walk不也能扫吗当然能但性能和规则处理差很多。rg 在进入目录时会自动识别.gitignore、.ignore、.rgignore还能根据.git/info/exclude过滤文件这些规则如果自己实现代码会变得很复杂而且很难覆盖所有边界情形。更重要的是rg 的进程退出码设计得很适合被程序调用。正常搜索结果为空时返回 1出错返回 2找到结果返回 0。上层程序可以根据退出码决定流程是继续还是终止非常干净。opencode 内部有一段逻辑就是调起rg --files --hidden -g SKILL.md来枚举技能文件如果这个进程因为可执行文件缺失直接报 ENOENT调用方会把它当成致命错误中断整个加载过程。其实不只是 opencode很多 AI 编码工具都有类似依赖。比如 VSCode 里的 Copilot、Continue、todo-tree底层都依赖 rg 或 vscode-ripgrep。它们之间有区别VSCode 插件可以捆绑 vscode-ripgrep并且自己管理二进制不需要用户单独安装但 opencode 这类命令行工具在独立环境下更倾向直接调用系统可执行文件或者尝试加载自己内置的二进制。这种“双保险”策略看起来没问题可一旦内置二进制在安装时缺失、平台不匹配或者系统 PATH 被精简过就会出现两边都落空的情况。2.3 技能越多越容易“全挂”的真相一开始我也觉得很奇怪技能是独立文件为什么一个 rg 找不到全部技能都会挂而不是只影响某个技能后来看代码逻辑才明白加载器是先把所有技能文件路径都枚举出来再统一做合法性校验、内容解析和清单生成。枚举阶段一旦抛异常后面的循环根本没有执行机会。这就好比吃饭前需要先点菜菜单由“服务员”去后厨拿结果服务员出门发现楼梯断了整桌人只能饿着。哪怕后厨已经准备好了十八道菜只要菜单没拿回来一道菜也上不了桌。opencode 的技能加载器就是这个逻辑rg 就是那个楼梯断了所有技能都一起趴窝。另外还有一种情况会让“全挂”显得异常夸张技能目录里配置了.gitignore但用户系统里 rg 版本过低不支持某些新语法标志。比如 opencode 调用 rg 时会传--no-config、--hidden这类参数rg 11.0 以下的版本对--no-config支持不完整一旦遇到不认识的 flagrg 会直接报错退出。调用方拿到非零退出码又没做精细的错误分级自然就判定整个技能加载失败。3. 三种常见场景下的修复思路3.1 系统级安装 ripgrep让 PATH 里随时有 rg大多数情况下最简单有效的方案就是给系统装上 ripgrep让rg在老位置待命。Linux 发行版的包管理器基本都收录了 ripgrep# Debian / Ubuntu sudo apt update sudo apt install ripgrep # CentOS / RHEL / Fedora sudo dnf install ripgrep # Arch Linux sudo pacman -S ripgrepmacOS 用户如果装了 Homebrew一条命令解决问题brew install ripgrepWindows 下推荐用 winget 或 Chocolateywinget install BurntSushi.ripgrep # 或者 choco install ripgrep装完以后别急着关终端先执行rg --version验证一下。看到类似ripgrep 14.1.1的输出就说明安装成功。接着再确认它所在目录已经加到 PATH 里最稳妥的办法是执行which rg如果返回路径就说明 opencode 下次启动时能找到它。我在实际使用中发现很多“看似已经安装了 rg”的机器其实并不一定满足要求。比如用npm安装了一些自带 rg 依赖的包它们把 rg 二进制放在node_modules/.bin里很容易造成which rg时灵时不灵的情况。这种被工具链“间接提供”的 rg 最好不要依赖最稳的还是通过系统包管理器装一份干净的。3.2 在 opencode 配置里显式指定 rg 路径如果系统已经被你折腾得很乱或者你希望 opencode 始终使用特定版本的 rg可以在 opencode 配置里手动指定路径。我使用的版本里可以通过环境变量或者配置文件来完成这一步。以环境变量方式为例在 shell 配置文件~/.bashrc或~/.zshrc里加一行export OPENCODE_RIPGREP_PATH/usr/local/bin/rg然后source一下使配置生效。这样 opencode 启动时就会优先读取这个变量而不是满 PATH 去找。要是你的 rg 装在了非标准位置比如~/tools/ripgrep/rg也可以写绝对路径进去。配置文件方式也差不多在 opencode 的配置文件目录里找到config.json添加一个字段{ ripgrepPath: /usr/local/bin/rg }具体字段名可能会随版本有些变化建议启动 opencode 后运行/config面板查看当前可配置项或者在官方文档里搜ripgrep。这个方法适合团队内部统一环境时使用比如有人用容器开发容器里故意不装系统级 rg只挂载了宿主机的一个二进制这时候配置文件指定绝对路径比改 PATH 更可靠。3.3 VSCode 场景别让 vscode-ripgrep 和系统 rg 互相打架很多 opencode 用户同时也在 VSCode 里使用 Todo Tree 或 Search 功能这类扩展会报todo-tree: failed to find vscode-ripgrep - please install ripgrep manually。这个报错其实和 opencode 本身没有直接关系但它们之间有一个共通的机制VSCode 扩展通常内置 vscode-ripgrep这个内置版会独立于系统 rg 运行如果你在 VSCode 里安装的扩展版本与内置 rg 不匹配扩展就会找你系统的 rg 兜底。如果你在 VSCode 里碰到了类似报错处理方式有两个思路。第一直接在 VSCode 扩展市场搜索vscode-ripgrep并安装让扩展重新打包一个匹配的内置 rg第二如果你不想安装额外扩展可以手动安装系统 rg然后在 VSCode 设置里找到todo-tree.general.debug打开日志输出看它到底在哪个路径找 rg。通常把系统 rg 装好后这类报错会消失。本质上opencode 和 VSCode 都在做同一件事找到可用的 ripgrep 可执行文件。opencode 在 CLI 环境里更依赖系统 PATH如果你之前为了让 VSCode 正常工作而手动设置过VSCODE_RIPGREP_PATH这类变量反而有可能干扰到 opencode 的子进程环境。我建议把这些环境变量统一整理避免重复设置。4. 实操复盘从“技能全挂”到“秒级加载”4.1 收集报错信息先看日志再动手修直接修复之前我强烈建议先把现场信息留全。因为“技能加载失败”这个描述太模糊可能是 rg 缺失、版本过旧、权限不足甚至技能文件本身损坏。如果没看日志就动手很可能修了半天发现原因完全在另一个方向。我当时的操作顺序是先打开一个新的终端会话执行rg --version确认系统 rg 是否可用。如果可用再看版本号是否足够新。至少是 11.0.0 以上建议 14.x。然后进入 opencode 日志目录用rg -i skill|ripgrep|rg过滤关键日志。最后打开 opencode执行/skills看当前显示同时观察终端是否有报错输出。这套流程非常适合快速定位问题尤其是当你被报错信息吓到的时候先把“系统里有没有 rg”这个事实搞清至少能排除一大半可能。4.2 安装并验证 rg一条龙示例我那次是在 Ubuntu 服务器上踩的坑所以修复命令很简单sudo apt update sudo apt install ripgrep -y安装完成后我确认了版本rg --version输出ripgrep 14.1.1然后看路径which rg输出/usr/bin/rg到这里系统侧已经没问题了。为了让 opencode 一定用它我还在配置里加了显式路径。然后重新启动 opencode技能列表立刻恢复/skills里能看到所有已配置的技能名称加载耗时从原来的“直接失败”变成了 200ms 不到。这个“加载耗时”其实很多人没注意过。opencode 在你输入/skills时会重新扫描一次技能目录如果 rg 可用整个过程几乎无感如果 rg 不可用则需要等待超时然后报错。两者体感差距非常大也是判断问题归属的一个小技巧。4.3 重载技能并测试真实调用修复后不要只看列表恢复了就结束还应该真正触发一次技能调用确认模型能从技能里读到内容。我习惯的做法是随便写一段和技能主题相关的输入比如我的“代码审查”技能里有“检查 SQL 注入风险”的条目我就故意问一句“审查一下这段 SQL 有没有问题”然后观察 opencode 是否自动带上这个技能。opencode 在识别到相关技能后会在上下文中附上 SKILL.md 的内容摘要你可以在会话信息面板或者调试日志里看到类似loaded skill code-review的提示。如果这里能正常显示说明从 rg 枚举到技能解析、再到模型上下文装配整条链路都通了。我修复后的第一件事是把整个过程写成一个检测脚本放进 CI 里每次构建镜像后执行一次rg --version确保基础环境可用。这一步看起来简单但真的能避免很多“部署到新机器就翻车”的问题。5. 高频报错速查表与避坑心得5.1 常见报错和对应处理方式我整理了一份表格基本覆盖了 ripgrep 相关的最常见坑方便你直接对照排查。报错信息或现象大概率原因解决办法exec: rg: executable file not found in $PATH系统没有安装 rg或 PATH 里没有 rg安装系统级 rg确认which rg有输出spawn ripgrep ENOENT找不到内置或外部 rg 可执行文件重装 opencode或显式配置 rg 路径failed to find vscode-ripgrepVSCode 扩展需要内置 rg但没找到在 VSCode 扩展市场安装 vscode-ripgrep或手动安装系统 rgunknown flag: --no-configrg 版本过旧不支持新版参数升级 rg 到最新版或用包管理器重装技能列表偶尔能显示偶尔为空PATH 配置不完整子进程环境不一致在 opencode 配置里指定绝对路径技能目录很大加载特别慢目录里混入 node_modules、build 等在技能目录加.ignore文件排除非必要内容这份表格我建议收藏。尤其第一条几乎占了我遇到问题的一半原因很多人以为系统里“应该有”实际上很多精简环境默认不装 rg。5.2 独家避坑比修复更重要的是环境约定这个坑踩完之后我最大的心得是AI 编码工具越来越依赖外部基础命令环境一致性比想象中重要。以前我们开发只关心 Node.js 版本、Python 版本现在还要关心 rg、git、fd 这些“底层工具链”。看似只是某个工具缺失影响面却可能是整个技能系统。我给团队定了几条约定分享出来可能对你有用统一用系统包管理器安装 ripgrep不要用 npm 或想当然的二进制拷贝方便后续升级和卸载。在项目的.opencode/skills目录下维护一份.ignore文件把.DS_Store、*.tmp、node_modules等明确排除避免 rg 扫描到无用文件拖慢加载。把rg --version写进团队开发环境的初始化脚本新同事入职之后跑一遍就能发现环境缺失不用等问题出现再排查。如果使用容器开发在 Dockerfile 里固定安装一个版本的 rg不要用 base image 里可能过旧的版本否则换镜像就翻车。这些约定看似和 opencode 没关系但实际能节省大量排查时间。工具链这件事靠脑子记不如靠脚本查。最后再分享一个小经验我在自己常用的 opencode 配置里已经把 ripgrep 的路径明确写死了即使未来机器 PATH 被其他工具修改也不会再影响技能加载。这个操作只花了一分钟但换来了长期稳定非常值得。