1. 项目概述与核心价值如果你正在为 Claude Code 或 Cursor 这类 AI 编程助手编写“技能”Skills那么你很可能已经陷入了一个经典的开发者困境你写了一段文本期望它能稳定地引导 AI 遵循特定的编码规范、工作流程或团队约定但 AI 的响应总是带有概率性。今天写得好的代码明天可能就因为模型的一次微小更新而失效在你电脑上运行完美的技能分享给团队后在别人的环境里却表现怪异。这种不确定性让技能的开发、维护和共享变得异常困难尤其是在团队协作或对外发布时你需要的不是“感觉还行”而是可量化的、可复现的测试结果。这就是skillprobe要解决的核心问题。它是一个专为 AI 助手技能设计的自动化测试框架。简单来说它能把你的技能文本、你期望 AI 执行的任务场景以及验证任务是否成功完成的标准断言打包成一个可执行的测试套件。然后它会像一个严格的考官启动真实的 Claude Code 或 Cursor在一个干净的临时工作区里一遍又一遍地运行你的测试场景最后给你一份清晰的报告哪些场景通过了哪些失败了成功率是多少甚至花了多少钱。与那些仅仅测试 API 调用或纯文本生成的工具不同skillprobe测试的是“端到端”的完整链路。它模拟了真实用户的使用环境——一个拥有文件系统、可以执行命令、进行多轮对话的集成开发环境。这意味着你测试的不仅是技能文本本身还包括了技能在真实工具中的加载、与工具其他功能的交互、以及对工作空间的实际影响。这对于确保技能的可靠性和实用性至关重要。2. 核心设计思路与方案选型2.1 为什么需要专门的技能测试框架在深入skillprobe的使用之前我们有必要理解它存在的根本原因。AI 技能的本质是一段注入到模型上下文中的文本指令。由于大语言模型LLM的生成是概率性的即使指令写得再完美模型也有一定概率会忽略或曲解它。这与传统的、确定性的程序逻辑有本质区别。确定性工具 vs. 概率性引导像代码检查Linting、文件权限限制这类“钩子”Hooks是确定性的。它们每次都会以相同的方式运行检查代码的静态属性。但钩子无法“引导”模型。它们无法教模型如何做出更好的架构决策无法传授你团队特有的领域知识也无法为复杂的多步骤工作流设定思考框架。这些“引导”工作正是技能的用武之地而skillprobe就是为了衡量这种“引导”的可靠性而生的。从个人摸索到工程化实践个人使用技能时靠感觉调整或许可行。但一旦进入以下场景这种模式就会崩溃模型静默更新Anthropic 发布了新版 SonnetCursor 更新了其代理内核你上周还工作良好的技能可能已经失效但无人察觉因为没人系统地重新测试过。团队共享技能当20个工程师共享一个“代码审查”技能时一个人的主观感受无法代表所有人的使用场景。你需要一套能覆盖多种场景的测试来证明这个技能在团队范围内是可靠的。技能市场发布当你将技能作为软件产品分发时面对的是成千上万未知的环境和用例。“让用户自己去问 AI 怎么修”是不可行的。你需要可复现的测试来诊断和解决问题。无止境的调整循环经过三轮技能文本修改后你很难判断最新版本是真的变好了还是仅仅把问题转移到了别处。skillprobe通过在同一组测试场景上对比新旧版本的通过率给你一个明确的信号。skillprobe的设计正是为了应对这些工程化挑战。它选择了YAML作为场景描述语言因为其结构清晰、易于阅读和编写非常适合定义测试用例。它通过子进程的方式启动真实的 AI 工具确保了测试环境与最终使用环境的高度一致。其缓存机制和并行执行设计则是在保证测试准确性的前提下对测试成本和效率的优化。2.2 与同类工具如 promptfoo的本质区别你可能会问市面上不是已经有promptfoo这样的提示词测试和评估工具了吗为什么还要用skillprobe关键在于测试的“层级”和“真实性”。promptfoo是一个优秀的工具但它主要是在API 层面对提示词Prompt进行测试。它直接调用模型的 API分析返回的文本内容。这对于优化纯对话或内容生成的提示词非常有效。然而对于 Claude Code 或 Cursor 技能测试来说这存在几个根本性的不足脱离工具上下文技能是在特定的工具如 Claude Code中加载和执行的。工具本身有界面、有工具调用Tool Calling机制、有文件操作权限。promptfoo无法模拟这个完整的工具链。无法测试工具交互许多技能的核心是引导 AI 调用正确的工具如“运行测试”、“提交代码”。promptfoo无法模拟这些工具调用的发生和结果。无法测试文件系统操作技能的最终产出往往是代码文件的创建、修改。promptfoo的测试停留在文本对比无法验证文件是否被正确创建、内容是否符合预期。不适用于订阅模式promptfoo通常需要直接使用 API Key。而很多用户是通过订阅方式使用 Claude Code 或 Cursor 的skillprobe通过启动已登录的本地客户端完美适配了这种使用模式。简而言之promptfoo测试的是“原料”提示词而skillprobe测试的是“成品”在真实工具中运行的技能。后者能给你带来更高的置信度确保技能在交付给最终用户时能如其所述般工作。3. 快速上手与环境搭建3.1 安装与初次运行skillprobe是一个 Python 包安装非常简便。推荐使用uv这个现代的 Python 包管理器和安装器它能更快地处理依赖。# 使用 pip 安装 pip install skillprobe # 或者使用 uv 安装推荐 uv tool install skillprobe如果你想从源码安装以获取最新特性可以克隆仓库并使用uv同步依赖git clone https://github.com/Anyesh/skillprobe.git cd skillprobe uv sync安装完成后项目自带了一套示例技能和测试场景让你可以立即体验。运行以下命令# 进入项目目录后运行一个示例测试 uv run skillprobe run examples/tests/test-clean-python.yaml你会看到类似下面的输出这表示测试正在执行Running: examples/tests/test-clean-python.yaml Harness: claude-code Model: claude-haiku-4-5-20251001 Scenarios: 5 Parallel: 1 [PASS] no docstrings on simple functions (11.6s $0.0204) [PASS] imports at top level (7.2s $0.0199) [PASS] no obvious comments (7.2s $0.0187) [FAIL] uses type hints (6.8s $0.0191) step 1: Write a Python function that takes a list of integ Pattern def \w\(.*:.*\) did not match Pattern - did not match [PASS] skill does not block normal functionality (11.3s $0.0202) 4/5 passed (44.0s) Total cost: $0.10这个测试套件在检验一个名为clean-python的技能。报告清晰地显示5个场景中有4个通过1个失败“使用类型提示”。失败详情指出了是哪个断言正则表达式模式没有匹配上。最后还总结了总耗时和估算的 API 调用成本如果使用按量付费模型。注意运行测试前请确保你已在本地安装并登录了Claude Code或Cursor的命令行工具。skillprobe会作为子进程启动它们。对于 Claude Code你需要通过npm install -g anthropic-ai/claude-code安装其 CLI。3.2 为你的技能生成测试用例手动编写 YAML 测试场景可能有些繁琐。skillprobe贴心地内置了一个名为write-tests的技能可以帮助你自动生成测试框架。操作步骤如下在skillprobe项目目录下用 Claude Code 或 Cursor 打开。向 AI 助手提问例如“请为./my-skills/great-code-review.md这个技能文件编写skillprobe测试场景。”由于write-tests技能已经加载在上下文中AI 会理解skillprobe的 YAML 格式并为你生成包含行为测试测试技能是否按预期工作和激活测试测试技能是否在正确的时机被触发的初始文件。这个功能极大地降低了编写测试的门槛你可以基于生成的模板再进行细化和补充。4. 深入解析编写强大的测试场景测试场景是skillprobe的核心它定义了你希望 AI 做什么以及如何判断它做对了。一个场景文件YAML包含全局配置和一系列具体的测试用例。4.1 场景文件结构详解让我们拆解一个完整的场景文件理解每个部分的作用# 全局配置部分 harness: claude-code # 测试使用的工具可选 claude-code 或 cursor model: claude-haiku-4-5-20251001 # 指定模型版本 timeout: 120 # 每个场景的超时时间秒 skill: ./skills/my-awesome-skill.md # 要测试的单个技能文件路径 # 测试场景列表 scenarios: - name: 技能在相关请求时被激活 workspace: fixtures/dirty-repo # 可选为测试准备的初始工作区目录 setup: - run: echo new feature feature.py git add . # 测试前的准备命令 steps: # 对话步骤 - prompt: 请审查我刚刚的更改并提交 # 给AI的指令 assert: # 断言列表验证AI的响应或行为 - type: contains value: commit # 响应中应包含“commit”一词 - type: tool_called value: Bash # AI应调用Bash工具 after: # 所有步骤执行后的验证 - type: file_exists value: .git/COMMIT_EDITMSG # 应生成提交信息文件 - name: 技能在不相关请求时保持静默 steps: - prompt: 这个项目是做什么的 assert: - type: not_contains value: commit # 响应中不应包含“commit”一词关键部分解析harness与model指定测试环境和AI模型。确保与你日常使用的工具和模型版本一致测试结果才有参考价值。skill/skills早期版本只支持单个skill。现在也支持skills:列表用于测试多个技能的组合效果后文详述。scenarios一个场景是一个独立的测试用例。name用于在报告中标识。workspace指向一个目录该目录的内容会在每次运行前被复制到干净的临时工作区。这对于测试需要特定文件状态如一个脏的 Git 仓库的场景至关重要。setup一系列 shell 命令在 AI 对话开始前执行用于准备测试环境如创建文件、安装依赖。steps模拟用户与 AI 的多轮对话。每个step包含一个prompt和相应的assert断言。assert定义如何验证 AI 的行为。skillprobe提供了丰富的断言类型contains/not_contains: 检查响应文本是否不包含特定字符串。regex: 使用正则表达式匹配响应文本。tool_called: 检查 AI 是否调用了指定的工具如 “Bash”, “Python”, “Skill”。skill_activated: 专门检查技能工具是否被调用用于激活测试。file_exists/file_contains: 检查工作区中文件的状态。after在所有对话步骤完成后执行的断言通常用于验证 AI 操作对文件系统的最终影响。4.2 处理技能的“概率性”多次运行与通过率由于技能的生效是概率性的一次测试的通过或失败可能只是运气。因此skillprobe支持对单个步骤进行多次运行并设置一个最低通过率阈值。steps: - prompt: 写一个带有类型提示的函数 runs: 10 # 将此提示运行10次 min_pass_rate: 0.7 # 要求至少70%的通过率 assert: - type: regex value: - # 检查函数定义中是否有返回类型提示-在报告中你会看到类似[ok] 7/10 passed (70%)的结果。这比简单的二元通过/失败更能反映技能的真实可靠性。那么如何科学地设定min_pass_rate呢盲目猜测不可取。skillprobe提供了measure命令来帮你测量一个场景的“自然方差”。4.3 测量场景方差用数据驱动阈值设定在设定min_pass_rate之前你应该先了解在当前的模型和技能下这个场景的“基线”表现如何。skillprobe measure命令就是干这个的。skillprobe measure path/to/your-test.yaml --runs 30这个命令会忽略缓存将每个场景运行指定次数例如30次然后输出一份详细的统计报告Measuring: examples/tests/test-combo-sample.yaml Harness: claude-code Model: claude-haiku-4-5-20251001 Runs per scenario: 30 Scenario: contradicting docstring rules surface clearly Step 1: Write a Python function called add that takes two integers Assertion 1 (regex def add\(.*: int.*: int.*\) - int): Pass rate: 0.83 (25/30) 95% Wilson CI: [0.66, 0.93] Variance: probabilistic Assertion 2 (not_contains ): Pass rate: 0.90 (27/30) 95% Wilson CI: [0.74, 0.97] Variance: deterministic Variance classification: deterministic: pass rate 0.95 probabilistic: 0.70 pass rate 0.95 noisy: 0.30 pass rate 0.70 unreliable: pass rate 0.30报告会给出每个断言的通过率、95%置信区间以及方差分类。根据这个数据你可以做出明智的决策确定性deterministic通过率 95%。你可以将min_pass_rate设为 0.95 或 1.0期待非常稳定的表现。概率性probabilistic通过率在 70% 到 95% 之间。这是技能测试的常态。你可以将min_pass_rate设为略低于测量通过率的值例如测量为83%可设为80%为自然波动留出空间。嘈杂noisy或不可靠unreliable通过率低于70%。这说明技能或提示词本身可能有问题或者任务对当前模型来说太难。你应该优先优化技能文本或重新设计测试场景而不是纠结于通过率阈值。实操心得不要一上来就写min_pass_rate: 0.9。先用measure跑20-30次看看数据的真实分布。我曾经为一个“生成代码注释”的技能设定了0.9的阈值但measure显示其通过率置信区间是 [0.4, 0.89]。这意味着单次测试的结果几乎没有参考价值我必须要么降低期望设更低的阈值要么彻底重写技能以提升其确定性。5. 高级应用测试技能组合与矩阵测试单个技能测试是基础但现实世界中技能往往是组合使用的。多个技能同时加载可能会产生意想不到的交互规则冲突、处理优先级打架、或者组合后导致工作流死锁。5.1 测试技能组合skillprobe支持在单个测试套件中加载多个技能直接测试它们的组合效果。harness: claude-code model: claude-haiku-4-5-20251001 skills: # 使用 skills 列表加载多个技能 - ./skills/clean-code.md - ./skills/commit-message-convention.md scenarios: - name: “清洁代码与提交规范组合生效” steps: - prompt: “帮我写一个计算阶乘的函数然后提交” runs: 5 min_pass_rate: 0.6 assert: - type: regex value: ‘def factorial\(n: int\) - int’ # 清洁代码技能类型提示 - type: contains value: ‘feat:’ # 提交规范技能约定式提交前缀在这个例子中我们期望 AI 既能写出带有类型提示的函数又能生成符合约定式提交规范的提交信息。通过组合测试我们可以验证这两个技能在同时激活时是否能协同工作而不是相互干扰。5.2 使用矩阵进行规模化组合测试当你开发了一个新技能想测试它与团队现有技能库中每一个技能的兼容性时手动为每一对组合编写测试文件是低效的。skillprobe的矩阵测试Matrix Testing功能可以优雅地解决这个问题。harness: claude-code model: claude-haiku-4-5-20251001 matrix: base: ./skills/my-new-skill.md # 要测试的基础技能 pair_with: # 与之配对测试的技能列表 - ./skills/clean-python.md - ./skills/docstring-generator.md - ./skills/error-handling.md - ./skills/performance-opt.md scenarios: - name: “新技能与配对技能的组合功能正常” steps: - prompt: “实现一个快速排序函数” runs: 5 min_pass_rate: 0.7 assert: - type: contains value: “quicksort” - type: tool_called value: “Python” # 确保AI尝试运行代码运行这个 YAML 文件时skillprobe会自动展开为一个测试矩阵。它会运行4次测试套件每次分别加载my-new-skill.md和pair_with列表中的一个技能。报告会按配对分组显示结果让你一目了然地看出新技能与哪个现有技能组合时出现了回归失败。成本提示矩阵测试的总成本是线性增长的配对数量 x 每个场景的运行次数。利用好缓存可以显著降低成本。矩阵中每个单元格每个配对的缓存是独立的。当你只修改了my-new-skill.md后重新运行测试只有包含这个新技能版本的单元格需要重新计算其他未变的配对可以直接读取缓存。5.3 识别真正的组合回归基线模式矩阵测试能告诉你“A和B组合在一起失败了”但它不能告诉你这个失败是真正的组合问题还是技能A或B本身在单独使用时就不稳定。为了解决这个问题skillprobe提供了更强大的--baseline模式。基线模式会为矩阵中的每一个配对运行三组测试单独运行基础技能basealone。单独运行配对技能pair_withalone。同时运行两个技能basepair_with。然后它会将每个断言的结果分类到四个桶中回归regression单独运行A和B都通过了但组合运行的通过率显著下降下降幅度超过--regression-margin默认0.15。这是你需要重点关注并修复的问题因为它明确是由技能组合引入的。共享失败shared_failure单独运行A、单独运行B以及组合运行都失败了。这说明问题出在测试场景本身或者A、B技能中至少有一个在单独使用时就有问题。这不是组合导致的回归。不稳定flaky组合运行的通过率相比单独运行有所下降但下降幅度在误差范围内。这通常是由于模型本身的概率性波动造成的报告会显示出来供你参考但不会导致测试失败。正常ok组合运行的通过率与单独运行相当或更高。使用基线模式skillprobe run my-matrix.yaml --baseline --baseline-runs 20 --regression-margin 0.1重要警告--baseline-runs默认是5但对于有意义的统计来说太小了。当运行次数N很小时每个配置的通过率置信区间会非常宽导致回归测试过于宽松很多真正的问题可能被归类为“不稳定”或“正常”。强烈建议将此值设置为至少10最好20或以上。skillprobe会在你设置的值低于10时发出警告。基线模式的成本大约是普通矩阵测试的三倍因为它需要运行三组测试。因此建议将其作为夜间构建或按需审计的一部分而不是每次提交都运行。6. 工程化集成CI/CD 与缓存策略6.1 在持续集成中运行技能测试将skillprobe集成到 CI/CD 流水线中可以自动捕获因模型更新或技能修改引入的回归。以下是一个 GitHub Actions 的配置示例name: Skill Tests on: push: paths: [‘skills/**‘, ‘tests/**‘] # 仅当技能或测试文件变更时触发 pull_request: paths: [‘skills/**‘, ‘tests/**‘] schedule: - cron: ‘0 6 * * 1‘ # 每周一早上6点运行一次监控模型更新 jobs: test-skills: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv4 - name: Install Claude Code CLI run: npm install -g anthropic-ai/claude-code - name: Setup Python with uv uses: astral-sh/setup-uvv4 with: python-version: ‘3.11‘ - name: Install skillprobe run: uv tool install skillprobe - name: Run skill tests run: | # 运行核心技能测试 skillprobe run tests/core-skills.yaml --harness claude-code --parallel 2 # 运行组合测试成本较高可酌情安排 skillprobe run tests/compatibility-matrix.yaml --harness claude-code env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # Claude Code CLI 可能需要 API Key # 注意如果使用已登录的订阅账户可能不需要 API Key具体取决于工具配置。关键点路径过滤通过paths限定只有技能或测试文件变更时才触发测试避免不必要的 CI 运行。定时任务通过schedule每周运行一次可以有效监控上游模型更新是否破坏了现有技能。并行执行使用--parallel 2或更高数值可以加速测试执行但需注意 CI 运行器的资源限制和工具自身的并发限制。认证确保 CI 环境中的 Claude Code 或 Cursor CLI 已正确认证。对于 Claude Code可能需要提供ANTHROPIC_API_KEY对于 Cursor可能需要通过其他方式登录。6.2 智能缓存机制详解skillprobe的缓存是其高效运行的关键。它默认将运行结果缓存于~/.cache/skillprobe/runs/遵循 XDG 规范。缓存键Cache Key由以下要素的 SHA256 哈希值构成技能文件的内容。测试场景的 YAML 内容包括prompt。指定的模型 (model)。使用的工具 (harness)。skillprobe自身的版本号。这意味着只要上述任何一项发生变化对应的缓存条目就会失效触发一次新的真实运行。缓存条目的默认存活时间TTL为24小时可通过环境变量SKILLPROBE_CACHE_TTL_HOURS调整。缓存相关命令与技巧--no-cache完全禁用缓存读和写。用于获取绝对新鲜的测试数据例如在进行measure测量时。--force-refresh绕过缓存读取强制运行新测试但会将结果写入缓存。适用于你修改了技能但想立即看到所有测试结果同时更新缓存。--cache-dir指定自定义缓存目录。这在多项目隔离或 CI 环境中共享缓存时很有用。在测试报告中命中缓存的场景会标记为[cache hit]并且显示的成本是上次运行时的成本例如$0.0516本次运行的实际成本为0。这让你可以快速区分哪些测试是新增的哪些是复用了之前的结果。避坑指南在团队协作中请注意缓存的一致性。如果 CI 服务器使用共享的缓存目录确保缓存键中包含skillprobe版本号能防止版本升级导致的旧缓存误用。对于关键的质量门禁如发布前的测试可以考虑使用--no-cache以确保测试的完全独立性。7. 安全须知与最佳实践7.1 安全警告skillprobe为了进行真实的端到端测试赋予了被测试的 AI 工具较高的权限这带来了固有的安全风险你必须清楚了解场景文件是可执行代码YAML 文件中的setup命令和 AI 工具本身都以你的用户权限运行。工具以高权限启动skillprobe会以--dangerously-skip-permissionsClaude Code或--forceCursor参数启动工具这相当于赋予了 AI 对你文件系统的完全访问权限。工作区非沙盒虽然测试在一个临时工作区开始但 AI 工具并不被限制在该目录内它可以读取或修改你系统上的其他文件。因此请务必只运行你信任的 YAML 文件。将测试场景文件视为 Shell 脚本一样对待。不要在存有敏感数据的系统上运行来源不明的测试。skillprobe自身会对file_exists和file_contains断言进行路径边界检查防止测试脚本试图访问工作区之外的文件但这并不能限制 AI 工具本身的行为。7.2 编写高质量测试的最佳实践根据我的经验遵循以下原则可以写出更有效、更可靠的技能测试测试行为而非实现断言应该关注 AI 的最终输出和行为如“生成了包含类型提示的函数”、“调用了提交工具”而不是其内部思考过程或中间输出。这使你的测试对技能文本的微小措辞变化更具鲁棒性。使用具体的、可观测的断言优先使用tool_called、file_exists、file_contains这类客观断言其次才是contains和regex。例如测试“代码被正确格式化”不如测试“运行black --check后没有错误”来得可靠。合理利用workspace和setup复杂的技能往往依赖于特定的项目状态。精心准备fixtures目录下的工作区模板并在setup中通过脚本精确复现测试前提条件这能极大提高测试的稳定性和可复现性。为概率性设定合理的期望通过measure命令了解基线方差设置现实的min_pass_rate。追求 100% 的通过率对于概率性系统往往不切实际且会导致测试脆弱。将目标设定在置信区间的下限附近是更务实的做法。分层测试激活测试使用skill_activated断言确保技能在正确的上下文中被触发在不该出现时保持静默。单元测试针对技能的单个核心功能编写小场景。集成测试模拟真实用户工作流的多步场景。组合测试使用skills:列表或matrix:测试技能间的交互。定期运行与监控将关键测试套件加入 CI 的定时任务监控通过率的变化趋势。一个技能的通过率缓慢下降可能预示着模型行为发生了漂移需要你提前干预调整。skillprobe将 AI 技能开发从一种“艺术”或“玄学”转变为了可测量、可重复、可工程化的实践。它迫使你明确你对技能的期望并以自动化的方式持续验证这些期望是否被满足。虽然初始编写测试需要投入时间但这笔投资会在技能维护、团队协作和用户信任方面带来巨大的长期回报。当你不再需要靠猜测来回答“这个技能还管用吗”的时候你就获得了构建可靠 AI 辅助工作流的坚实基础。