编写评测配置与用例
本文档介绍如何为你的 Skill 编写完整的评测配置。你将学会如何定义运行环境、编写评测用例、以及配置评估策略。
目录结构
评测文件放在 Skill 目录下的 evals/ 文件夹中:
my-skill/
SKILL.md # Skill 定义文档
evals/
eval.yaml # 评测入口配置(必须)
cases/ # 用例目录
basic-success.yaml # 每个文件是一个用例
edge-case-null.yaml
regression-001.yaml
fixtures/ # 测试资源(可选)
repos/ # 仓库模板
sample-project/
diffs/ # 补丁文件
null-check.patch
scripts/ # 评估脚本
check-output.sh
mcp/ # MCP 工具配置
github.json命名约定:用例文件名即为用例 ID(去掉
.yaml后缀)。例如basic-success.yaml的用例 ID 是basic-success。
eval.yaml — 评测入口配置
eval.yaml 是评测的全局配置,定义了「在什么环境中、用什么 Engine、怎么评估」。
最小配置
schema_version: v1alpha1
environment:
type: none
engine:
name: opencode
model:
provider: dashscope
name: qwen3.8-max
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
cases:
files:
- evals/cases/my-test.yaml完整配置详解
# ========== 1. 版本声明 ==========
schema_version: v1alpha1 # 固定值,不可省略
# ========== 2. 运行环境 ==========
environment:
type: none # 支持 none / opensandbox / docker
# ========== 3. MCP 工具 ==========
mcp:
servers:
- name: github # MCP Server 名称
mode: real # real / mocked
transport: http # http / stdio;未指定时按 endpoint/command 推断
config_ref: evals/fixtures/mcp/github.yaml # 配置文件路径
# ========== 4. Skill 安装 ==========
skills:
- source: local_path # local_path(本地目录)
path: . # Skill 所在路径
include: # 可选:相对 path 的 doublestar glob;不配置表示包含全部文件
- SKILL.md
- references/**
- scripts/**
exclude: # 可选:在 include 后应用,因此 exclude 优先
- references/drafts/**
- .qoder/repowiki/**
# ========== 5. Agent Engine ==========
engine:
name: opencode # claude_code / codex / opencode / qodercli / qwen_code
version: 1.14.24 # 可选的 CLI 具体版本;语义见下文
# kwargs:
# edition: cn # qodercli 可选:global(默认)/ cn
model:
provider: dashscope # 模型供应商
name: qwen3.8-max # 模型名称
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# ========== 6. 用例配置 ==========
cases:
files: # 用例文件列表(相对于 Skill 根目录)
- evals/cases/basic-success.yaml
- evals/cases/edge-case.yaml
defaults: # 用例默认值
timeout_seconds: 300 # 超时时间(秒),默认 300
max_turns: 12 # 最大交互轮数,默认 12
collect_artifacts: # 用 glob 指定要下载的 workspace 产物文件(见下文)
- "**/*.json"
- "report/**"
expect: # 默认 expect 检查,应用于所有用例(见 Expect 章节)
exit_code: 0
must_not_contain:
- "TODO"
- "I cannot"
parallelism: 2 # 用例并行数,默认 1
retry_policy: # 重试策略
max_retries: 1
retry_on: [timeout, error]
# ========== 7. 基线对比(可选) ==========
benchmark:
enabled: false # 启用后会运行 with_skill 和 without_skill 两组对比
# ========== 8. 报告输出 ==========
report:
formats: [json, html] # json / junit / html
artifacts: [transcript] # 报告中包含的产物skills[].include 和 skills[].exclude 使用 doublestar 语法(* 匹配单层路径, ** 可跨目录)。pattern 相对 skills[].path,统一使用 / 分隔; exclude 优先于 include。未配置 include 时默认选择全部文件。 评测框架目录 evals/ 始终排除。配置 include 列表时,应显式包含 SKILL.md,确保安装后的目录仍是有效 Skill。judge.skills 同样支持这两个字段。
cases.parallelism 是配置文件中的默认用例并行数;临时运行时可以用 skill-up run --parallelism N 覆盖它,不需要修改 eval.yaml。命令行覆盖值必须在 1 到 256 之间。
Engine 版本生命周期
每个用例执行前,skill-up 会静态检查 CLI 是否安装,并通过 --version 读取版本;该过程不会验证登录状态,也不会发起模型请求。使用 environment.type: none 时,skill-up 不会修改宿主机安装,配置的具体 engine.version 必须与现有 CLI 匹配。在隔离 runtime 中,Claude Code、 Codex 和 Qwen Code 会先安装指定版本再校验。QoderCLI 的实际版本仍会记录到 报告,但其安装器不支持选择 engine.version,显式配置会被警告并忽略。 对支持版本选择的 adapter,只有完整、精确的语义化版本(例如 2.1.0 或 2.1.0-beta.1)才会实际应用。为保持向后兼容,latest、^2.1.0 等 tag 或范围仍可通过配置加载,但会被警告并忽略。报告会将原始值保留为 requested configuration,并将 applied version 留空。
历史字段 engine.entry 和 engine.model.params 仍会被 v1alpha1 配置加载, 但不会传递给 adapter,并会输出迁移警告。自定义可执行命令请使用 engine.custom.local.command 和 args;adapter 专用参数请使用 engine.kwargs 或 engine.custom.kwargs。
采集 workspace 产物(collect_artifacts)
collect_artifacts 用 glob 声明要从用例 workspace 采集的文件。每次 Agent 运行后——无论成功、失败还是超时——命中的文件都会被下载到:
<output-dir>/<case-id>/<configuration>/outputs/workspace/<相对路径>文件相对 workspace 根目录的路径结构会被保留,例如 report/run-1/summary.json 会落到 outputs/workspace/report/run-1/summary.json。
Glob 语法 采用 doublestar:
*仅匹配单层路径段,**跨目录递归匹配。示例:*.md、src/**/*.go、report/**、**/*.json。两层配置,按并集合并。
cases.defaults.collect_artifacts对所有用例生效;单个用例可在case.yaml中追加:yamlcollect_artifacts: - "out/**"per-case 列表会追加到默认值之后并去重(默认值在前)。
始终采集,与 judge 类型、workspace 是否为 git 仓库无关;采集过程只读,不会修改 workspace。
workspace 下的
.git/目录会被排除(agent_judge会在其中提交一份 baseline),因此**这类宽模式不会把 VCS 内部文件扫进产物。
当用例 deadline 在自定义引擎写出 session result 之前将其杀掉时,skill-up 会合成一份最小的 session-result.json(exit_code 124),直接归档到 <output-dir>/<case-id>/<configuration>/outputs/agent/run/session-result.json。该文件不会写入 workspace,因此 collect_artifacts 的 glob 不会(也不应)命中它。
请勿与
report.artifacts混淆(后者选择产物类型,如transcript/logs),也不同于agent_judge使用的 git workspace diff(那是喂给 judge 的 diff 字符串,不落盘成文件)。collect_artifacts下载的是文件实体,与二者正交。
自定义 Engine(Custom Engine)
当 engine.name 不是内置引擎(claude_code / codex / qodercli / qwen_code)时,必须再写一个 engine.custom 段来告诉 skill-up 怎么调用你的 Agent。transport: local(在 runtime 内执行命令)和 transport: http(调用 HTTP agent 服务)均已支持。
engine:
name: my-agent
model:
provider: dashscope
name: qwen3.8-max
custom:
transport: local # local | http
response_format: session_result # session_result(默认)| text
timeout_seconds: 300
env: # 凭据 / 敏感参数 —— 不要在 command/args 里引用这些
MY_AGENT_TOKEN: ${MY_AGENT_TOKEN}
kwargs: # 非敏感开关,模板里以 ${kwargs.<key>} 暴露
profile: production
local:
command: /opt/my-agent/bin/run
args:
- --input
- ${input_file} # skill-up 写入的 SessionInput JSON 路径
- --output
- ${output_file} # 你的 Agent 应写入 SessionResult JSON 的路径
cwd: ${workspace} # 可选;被限制在 runtime workspace 内
input_file: inputs/messages.json # 可选覆盖(相对 workspace)
output_file: outputs/session-result.json关键字段说明(完整契约见 docs/design/custom-engine.md):
transport(必填)—— skill-up 调用 agent 的方式。local:通过runtime.Exec在当前 runtime 内执行local.command,agent 进程可访问 runtime workspace、已安装的 skill、fixture、MCP 配置以及进程环境变量。http:把SessionInputPOST 到远程(或本地)HTTP agent 服务,从响应体读取SessionResult。相关字段配置在custom.http下(见下文)。
response_format(可选,默认session_result)—— skill-up 如何解析 agent 输出。session_result:从local.output_file(若配置)或 stdout 读出完整的SessionResultJSON,包含exit_code/final_message/transcript/turns/input_tokens/output_tokens/artifacts。推荐保留默认,可以让 judge 和报告拿到完整上下文。text:把 stdout 整体当作final_message,skill-up 自动按输入消息 + 助手回复合成 minimal transcript,使 judge 仍能拿到一段对话。仅适合不输出结构化结果的简易脚本。
timeout_seconds(可选)—— 单次调用的超时时间。未设时回退到 case 级 timeout;两者都设置时 skill-up 取较小值,保证传给 agent 的预算与真实墙钟一致。env(可选)—— 凭据 / 敏感参数通道。值会以进程环境变量形式注入到 agent。这是唯一允许携带凭据的字段:command/args/cwd/input_file/output_file在配置加载阶段会拒绝凭据形态的值。kwargs(可选)—— 非敏感开关,模板里以${kwargs.<key>}暴露。与env不同,kwargs 也走严格凭据检查,不允许携带凭据值或凭据形态的 key(如token/api_key/bearerToken等)。kwargs 不会被写进SessionInput载荷:local 传输会把该载荷落到工作区,被测 agent 可直接读取,因此需要哪个 kwarg,就在args/cwd/env(或http.headers/http.request_body)里用${kwargs.<key>}显式引用。
command / args / cwd / env / input_file / output_file 中可用的模板变量: ${workspace}、${input_file}、${output_file}、${model}、${model_provider}、${model_name}、${case_id}、${variant}、${max_turns}、${timeout_seconds}、${kwargs.<key>},以及环境变量形式 ${VAR} / ${VAR:-default} / ${VAR?error message}。
凭据收敛规则(配置加载期强校验):
${api_key}以及任何看起来像凭据的 kwarg key(token/secret/api_key/apiKey/bearerToken等)都不允许出现在command/args/cwd/input_file/output_file中,必须经由engine.custom.env注入到子进程环境变量里。${SOMEVAR:-...}默认值如果匹配常见凭据特征(sk-...、sk-ant-...、ghp_...、AIza...、AKIA...、JWT 等),同样会在命令行场景被拒。
transport: http 时,把调用配置写在 custom.http 下(替代 custom.local):
engine:
name: remote-review-agent
custom:
transport: http
response_format: session_result
timeout_seconds: 300
kwargs:
profile: production
http:
url: ${CUSTOM_AGENT_ENDPOINT}/v1/run # 必填
method: POST # 仅支持 POST
headers:
Authorization: Bearer ${api_key} # 凭据写在 header,不要写进 URL
files: # 可选:以 multipart 上传 workspace 文件
- path: diff.patch
required: true
- path: "src/**/*.go"
required: false
request_body: ${session_input} # 可选;默认即 ${session_input}HTTP 要点:
- 请求体默认是
SessionInputJSON,其中不含custom.kwargs。request_body中某个值若恰好是${session_input}/${messages}/${kwargs},会以 JSON 结构注入(而非字符串)——需要整份 kwargs 时用${kwargs}显式注入。 - 配置了
http.files后请求变为multipart/form-data:JSON 体移到payload字段,每个命中的文件作为独立 part 上传到固定表单字段files,其 workspace 相对路径放在该 part 的filename里(服务端应读取每个filespart 的filename,而不是按路径找表单 key)。path为 workspace 相对文件或 glob;required: false时缺失/未命中会跳过。 - 凭据必须从
headers(或request_body)引用,不能写进http.url—— URL 渲染出${api_key}会被拒绝,避免泄漏到请求日志。 - 非 2xx 响应按调用错误处理。Agent 在
artifacts.files[].url返回的产物会被 GET 下载(仅 http/https、不跟随重定向、有大小与时间上限)到报告目录。
SessionInput / SessionResult 的完整 JSON 契约见 docs/design/custom-engine.md。
MCP 配置说明
MCP 支持 mode: real 和 mode: mocked。real 用于把真实 MCP Server 安装到 claude_code、qodercli、codex 或 qwen_code 等 Agent;mocked 会由 internal/mcp 生成本地 stdio Mock Server,并按普通 MCP 配置安装到 Agent。
HTTP MCP 可以直接在 eval.yaml 中声明,也可以用 config_ref 引用 evals/fixtures/mcp/*.yaml:
mcp:
servers:
- name: agent-sandbox
mode: real
transport: http
config_ref: evals/fixtures/mcp/agent-sandbox.yamlconfig_ref 文件支持以下字段:
transport: http
endpoint: https://mcp.example.com/mcp?token=${MCP_TOKEN}
required_env:
- MCP_TOKEN
headers:
PRIVATE-TOKEN: ${PRIVATE_TOKEN}stdio MCP 可以配置 command 和 args:
mcp:
servers:
- name: marker
mode: real
transport: stdio
command: /usr/bin/python3
args: [evals/fixtures/mcp/marker_server.py]mocked MCP 可以直接使用内置的 filesystem Mock Server:
mcp:
servers:
- name: filesystem
mode: mocked也可以通过 config_ref 定义工具响应:
mcp:
servers:
- name: project-mgmt
mode: mocked
config_ref: evals/fixtures/mcp/project-mgmt.yamltool_responses:
create_publish_plan_simple:
default:
id: 999
name: "{{params.name}}"
status: ONGOING基于 tool_responses 的 Mock Server 行为如下:
- 仅支持 stdio。
mode: mocked时,skill-up 会拒绝transport: http、endpoint、command和args。 - skill-up 使用
node启动 Mock Server,因此运行环境的PATH中必须有node。只有当 skill-up 自行安装 Claude Code、Codex、Qwen Code 或 OpenCode 时才会准备 Node.js;预先安装的引擎 CLI 或 Qoder CLI 不会附带 Node.js。 - 每次调用某个工具都返回该工具的
default响应;若没有default键,则整个条目即为响应。字符串值可以像示例中的name字段那样用params模板引用调用参数,并可用| default:'<value>'指定缺省值。 - Agent 看到的每个工具描述均为
Mocked MCP tool <name>,输入 schema 接受任意对象,而不是真实 Server 的描述和参数 schema。 - 调用未在
tool_responses中声明的工具会返回 MCP 错误。
环境变量引用支持 ${VAR} 和完整值 $VAR 两种形式,变量名必须匹配 [A-Za-z_][A-Za-z0-9_]*。required_env 会把变量注入 Agent 运行环境;headers 中完整的环境变量引用会额外记录变量名,供 Agent 安装 MCP 时选择合适的传递方式。
用例级 mocked MCP 覆盖
eval 级的 mcp.servers 是所有用例的默认配置。用例(case)可以声明自己的 mcp.servers,在保持相同 MCP Server 名称和工具名称的前提下切换 mocked fixture(由 SUP-0003 引入)。Server 按 name 合并:用例级中与 eval 级同名的条目会整体替换该 eval 级 Server,未出现的新名称则追加到末尾。未声明 mcp 的用例继承 eval 级配置,行为不变。
# eval.yaml —— 所有用例共享的默认 fixture
mcp:
servers:
- name: project-mgmt
mode: mocked
config_ref: evals/fixtures/mcp/project-default.yaml# cases/project-open.yaml —— 相同的 Server/工具,不同的 fixture
id: project-open
input:
prompt: 检查项目状态并继续发布计划。
mcp:
servers:
- name: project-mgmt
mode: mocked
config_ref: evals/fixtures/mcp/project-open.yaml规则与约束:
- 当前 MVP 下用例级 Server 必须使用
mode: mocked;暂不支持用例级 real MCP 覆盖。 - 替换为整条替换,而非字段级合并:需提供完整的 Server 条目(
name、mode、config_ref)。 config_ref相对于 Skill 目录(SKILL.md所在目录)解析,不是相对于用例文件所在目录 —— 与 eval 级 MCP 规则一致。- 每个用例独立生成并安装自己的 mocked Server,因此即使
cases.parallelism > 1,各用例的 fixture 也彼此隔离。
运行环境选择指南
| 环境类型 | 适用场景 | 示例 Skill |
|---|---|---|
none | 纯文本 I/O,无文件系统依赖 | 命令路由、知识问答、文本生成 |
opensandbox | 需要远程沙箱服务 | 代码审查、项目生成、脚本执行 |
docker | 本地容器隔离,无需远程服务 | 自定义工具链、可复现 CI、离线环境 |
建议:如果你的 Skill 不涉及文件操作,使用
none可以省去沙箱准备时间。
OpenSandbox 配置
使用 environment.type: opensandbox 时,沙箱鉴权从进程环境变量 OPENSANDBOX_API_KEY 读取。服务地址、扩展参数等非敏感配置建议放在 environment.kwargs 中;Agent 自动安装后的可执行文件路径由对应 Agent 实现处理,通常不需要在评测配置中手动补 PATH。
environment:
type: opensandbox
image: registry.example.com/your-org/sandbox-base:latest
workspace_mount: /workspace
ready_timeout_seconds: 300
kwargs:
base_url: https://agent-sandbox.example.com
extensions: '{"profile":"ci"}'
request_timeout_seconds: "900"
file_transfer_parallelism: "8"常用配置项:
| 字段 | 说明 |
|---|---|
image | 沙箱镜像;未配置时使用 OpenSandbox runtime 默认镜像 |
workspace_mount | runtime 工作区路径,默认 /workspace |
env | 注入沙箱命令执行环境的变量;如确需扩展 PATH,可使用 PATH: $CUSTOM_BIN:$PATH,runtime 会在沙箱内展开 |
setup_steps | 沙箱创建后在 runtime 工作区内执行的初始化命令 |
kwargs.base_url | OpenSandbox 服务地址,也可用 OPENSANDBOX_BASE_URL 设置 |
kwargs.extensions | JSON 字符串形式的 OpenSandbox 扩展配置 |
kwargs.request_timeout_seconds | OpenSandbox SDK 请求超时时间 |
kwargs.file_transfer_parallelism | 目录下载并发度 |
Docker 配置
使用 environment.type: docker 时,Agent 在本地 Docker 容器中运行,提供容器级别的隔离(文件系统、进程、网络),无需任何远程服务。
前置条件: 需要 docker CLI 在 PATH 中,且 Docker daemon 正在运行。runtime 不会自动拉取镜像,请提前执行 docker pull <image>。
environment:
type: docker
image: node:22 # 必填 — 需提前拉取到本地
workspace_mount: /workspace # 默认:/workspace
env:
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
setup_steps:
- run: npm install -g typescript
entrypoint: ["sleep", "infinity"] # 覆盖容器 entrypoint(默认:sleep infinity)常用配置项:
| 字段 | 说明 |
|---|---|
image | 必填。 Docker 镜像名称,需在本地可用(使用 docker pull 预拉取) |
workspace_mount | 容器内工作区路径,默认 /workspace,必须为绝对路径 |
env | 注入容器命令执行环境的变量 |
setup_steps | 容器启动后执行的初始化命令 |
entrypoint | 覆盖容器的 ENTRYPOINT,默认 ["sleep", "infinity"] |
network_policy | deny_all 以 --network=none 创建容器(无网络访问);allow_declared 暂不支持,需要 FQDN 级别出口过滤请使用 opensandbox |
建议: Docker runtime 适合需要自定义系统包、特定语言运行时、或离线/隔离环境的评测。如需远程托管沙箱,请使用
opensandbox。
case.yaml — 评测用例
每个 .yaml 文件定义一个评测用例,包含「发什么 prompt」和「怎么验证结果」。
单轮对话用例
大多数场景使用单轮 prompt 即可:
id: find-null-bug
title: 应该识别出空指针 bug
description: 验证 Skill 能在代码审查中发现 null 解引用问题
input:
prompt: |
Review the current diff and report findings.
context:
repo_fixture: evals/fixtures/repos/null-check-bug # 加载仓库模板
git:
init: true
checkout: main
apply_diff: evals/fixtures/diffs/null-check.patch # 应用补丁
constraints:
timeout_seconds: 180
max_turns: 8
expect: # 基本门槛检查
must_contain:
- "null"
- "bug"
must_not_contain:
- "LGTM"
exit_code: 0
judge: # 质量评估
type: rule_based
success:
- output_contains:
all: ["null", "bug"]
- exit_code: 0用例上下文 — context
context 用于为用例准备初始环境:
加载仓库模板
context:
repo_fixture: evals/fixtures/repos/my-project # 将目录内容复制到工作区Git 操作
context:
repo_fixture: evals/fixtures/repos/my-project
git:
init: true # 初始化 git 仓库
checkout: feature-branch # 切换分支
apply_diff: evals/fixtures/diffs/my.patch # 应用补丁文件
remotes: # 配置 git remote
- name: origin
url: https://github.com/user/repo内联文件
context:
files:
"src/main.py": |
def hello():
print("Hello World")
"config.json": |
{"debug": true}多轮对话
当评测需要多次顺序交互时(例如迭代优化、阶段门控工作流、澄清循环),使用 input.turns 代替 input.prompt。
何时使用 input.prompt vs input.turns
| 场景 | 使用 |
|---|---|
| 单条指令,评判最终输出 | input.prompt |
| 多步骤工作流,含轮间门控 | input.turns |
| 迭代优化(如"改善命名") | input.turns |
| Agent 需在对话中拒绝无效请求 | input.turns |
基本多轮用例
input:
turns:
- role: user
content: "用 Go 实现一个二分查找函数。"
post_condition:
must_contain_all: ["func", "binary"]
on_fail: fail
- role: user
content: "为刚才写的函数添加单元测试。"
post_condition:
must_contain_any: ["Test", "t.Run", "testing"]
on_fail: failpost_condition — 轮间门控
post_condition 在每轮结束后检查 Agent 的响应。它是门控而非 judge 的替代品, 用于确保对话在发送下一轮之前保持正轨。
字段说明:
must_contain_all:所有字符串必须出现在响应中。must_contain_any:至少一个字符串必须出现。must_not_contain:所列字符串均不能出现。on_fail:条件失败时的行为:fail(默认):立即将用例标记为 FAIL。skip_remaining:跳过后续所有轮次;最终结果取决于 judge 评判。
capture — 模板变量
从轮次响应中捕获值,在后续轮次中使用:
input:
turns:
- role: user
content: "生成一个会话令牌。"
capture:
- variable: token
pattern: "token[=: ]+(?P<value>[A-Za-z0-9]+)"
- role: user
content: "验证令牌 {{token}} 是否有效。"捕获语义:
- 优先使用命名分组
(?P<value>...);若无命名组,则允许恰好一个匿名捕获组。 - 提取器类型:
pattern(正则)或jsonpath— 必须指定且仅指定一个。 - 未匹配或空值 → 用例进入 ERROR 状态。
- 变量作用域仅限当前用例执行(不跨用例、重试或基线变体共享)。
- 引用未知变量会在调用 Agent 之前使轮次失败。
Agent 支持矩阵
| 引擎 | 多轮支持 | 机制 |
|---|---|---|
claude_code | 是 | --resume 标志 + 会话 ID |
qodercli | 是 | -r <session-id> 标志 |
codex | 是 | codex resume <thread-id> 命令 |
opencode | 是 | opencode run --session <session-id> |
qwen_code | 尚不支持 | 回退为批量模式 |
custom | 尚不支持 | 回退为批量模式 |
当 Agent 不支持会话恢复时,所有轮次内容会拼接为单条 prompt 发送,并记录警告日志。
按轮次 Judge 断言
规则型 judge 支持按轮次断言:
judge:
type: rule_based
success:
- turn_response_contains:
turn: 1
contains_all: ["binary_search"]
- turn_response_not_contains:
turn: 2
not_contains: ["TODO", "FIXME"]
- tool_called_in_turn:
turn: 1
name: write_file
args:
path: "search.go"
- tool_not_called_in_turn:
turn: 2
name: delete_file失败行为:
- 不存在的轮次(未执行) → 断言失败。
- 被跳过/失败/出错的轮次 → 断言失败(仅 "completed" 状态的轮次可被断言)。
评估策略
skill-up 的评估分为两层:expect(门槛检查)和 judge(质量评估)。
expect — 快速门槛检查
expect 是零成本的本地检查。如果 expect 不通过,judge 会被跳过。
expect:
must_contain: # 输出必须包含这些关键词(全部匹配)
- "review"
- "bug"
must_not_contain: # 输出不能包含这些关键词
- "LGTM"
- "error"
exit_code: 0 # 期望退出码
files_exist: # 期望这些文件存在
- "review.md"
- "output.json"
files_not_exist: # 期望这些文件不存在
- "temp.log"
file_contains: # 文件必须包含特定文本
- path: "review.md"
content: "security"
golden_file: "expected.txt" # 与黄金文件比对输出默认 expect 检查
在 cases.defaults.expect 中一次性定义通用的 expect 检查,而不是在每个用例中重复:
# 在 eval.yaml 中
cases:
defaults:
expect:
exit_code: 0
must_not_contain:
- "TODO"
- "I cannot"
files_exist:
- "result.json"每个用例可以添加或覆盖这些默认值:
# 在 case.yaml 中
expect:
must_contain:
- "SPECIAL_OK" # 添加到默认检查
exit_code: 1 # 覆盖默认的 exit_code合并语义:
- 切片字段(
must_contain、must_not_contain、files_exist、files_not_exist、file_contains)会被追加并去重(默认值在前)。 - 标量字段(
exit_code、golden_file)在用例层面设置时会覆盖默认值。 - 如果用例未定义
expect,则直接使用默认值。 - 合并后的 expect 在 judge 之前运行;如果合并后的 expect 失败,会跳过 judge 执行。
judge: rule_based — 确定性规则评估
基于声明式规则判定用例是否通过,结果完全确定、可重复:
judge:
type: rule_based
success: # 所有条件必须满足
- output_contains:
all: ["bug", "null"] # 输出必须同时包含
any: ["建议修复", "推荐更改"] # 输出至少包含一个
not: ["LGTM"] # 输出不能包含
- output_matches: # 使用 Go regexp 匹配最终输出
all: ["(?m)^## Status$", "(?m)^## Evidence$"]
any: ["(?i)pass", "(?i)success"]
not: ["(?i)api[_-]?key\\s*="]
- exit_code: 0 # 退出码必须为 0
- tool_called: # Agent 必须调用了某个工具
name: "mcp__github__create_pull_request"
args: # 工具参数部分匹配
title: "Fix null check"
failure: # 任一条件匹配则立即失败
- output_contains:
any: ["无需修改", "代码正确"]
- output_matches:
any: ["BEGIN PRIVATE KEY"]评估逻辑:
failure优先于success。任何一条failure规则匹配即立即失败;否则所有success规则必须全部满足才算通过。
MCP 工具名:
tool_called、tool_called_in_turn和tool_not_called_in_turn会把name与 Agent Engine 记录的工具名做精确匹配。Claude Code、Codex 和 Qoder CLI 记录的 MCP 工具名格式为mcp__<server>__<tool>,其中<server>是mcp.servers中的name。
output_contains 执行字面 substring 检查。需要正则表达式时使用 output_matches;pattern 使用 Go regexp 语法,非法 pattern 会在配置校验阶段失败。
judge: script — 自定义脚本评估
用你自己的脚本(任何语言)来评估结果:
judge:
type: script
script_path: evals/fixtures/scripts/check-quality.sh
timeout_seconds: 30脚本规则:
- 退出码 0 表示通过,非 0 表示失败
- 工作目录为用例的工作区根目录
- 可用环境变量:
$EVAL_FINAL_MESSAGE(最终消息)、$EVAL_EXIT_CODE(退出码) $EVAL_TRANSCRIPT_PATH仅在运行时生成了可用 transcript 路径时才会设置;若未设置,脚本中会收到空值- 脚本的标准输出会作为评估理由记录到报告中
多个独立 judge
同一次 Agent 执行需要同时检查确定性结果和语义质量时,使用 judges:
judges:
- id: functional
type: script
script_path: evals/fixtures/scripts/check-quality.sh
timeout_seconds: 30
- id: semantic
type: agent_judge
model: dashscope/qwen3.8-max
criteria:
- "结果满足用户意图"
pass_threshold: 0.7被测 Agent 只执行一次。每个 judge 独立记录状态、断言、诊断和产物。expect 仍是前置门控:失败时所有 judge 都跳过;通过后即使前一个 judge 失败或出错,后面的 judge 仍会执行。全部 judge 通过时用例才通过。列表顺序决定执行顺序,不表示 judge 之间有数据依赖。
上面的模型示例配合 OpenCode/DashScope engine 配置使用。Agent judge 继承 engine 和 endpoint;只改 model 不会切换 engine。已实际验证的配置见 code-stats 示例。
eval 和 case 都可配置 judges。case 级列表整体替换 eval 级列表;case 级单数 judge 也可覆盖 eval 级列表。同一文档不能同时配置 judge 和 judges;成员 ID 必须唯一,仅使用小写字母、数字、_ 和 -。
多个 judge 在一次被测 Agent 执行后,按配置顺序复用同一个 case runtime(none、Docker 或 OpenSandbox)。每个 Agent judge 新开自己的会话,不 resume 被测 Agent 或前一个 judge。已安装依赖、后台服务、挂载、环境变量和网络策略继续可用,不需要文件系统 Snapshot 或恢复接口。
文件系统由所有 judge 共享:前一个 judge 的修改会被后面的 judge 看到。需要评审原始输出时,请使用只读检查。各 judge 的判定、超时、上下文材料和会话产物仍按 ID 单独记录;这不代表文件系统或外部服务隔离。所有 judge 结束后再按配置清理 case runtime。
OpenSandbox 共享环境验证
OpenSandbox judge 直接复用 case 已启动的 sandbox,不调用原生快照、恢复、pause 或 resume API。各 Agent judge 新开评审会话,工具、文件修改及 case 网络策略留在 同一个 sandbox;结束后按普通 case 生命周期清理。
通过环境提供 OPENSANDBOX_API_KEY、OPENSANDBOX_BASE_URL 和 OPENSANDBOX_IMAGE:
SKILL_UP_OPENSANDBOX_JUDGE_E2E=1 go test -tags e2e -run TestOpenSandboxSharedJudgeEnvironment -v ./e2e测试创建带测试 metadata 和五分钟 TTL 的临时 sandbox,一次 Agent fixture 执行后, 顺序执行一个 script judge 和两个新开的 Agent judge fixture 会话。覆盖共享文件修改、 setup/Agent 安装到工作区外的依赖、环境配置和各 judge 材料读回,以及全通过和 script FAIL / 两个 Agent judge PASS / 总体 FAIL。测试走真实评分链路但不调用模型服务。 实际服务的 egress 执行效果需单独网络测试,不声称外部服务隔离。
若要验证真实 OpenCode provider 链路,还需注入 JUDGE_LIVE_API_KEY,并设置 OpenAI 兼容服务的 SKILL_UP_OPENCODE_JUDGE_BASE_URL 和 SKILL_UP_OPENCODE_JUDGE_MODEL,然后运行:
SKILL_UP_OPENCODE_JUDGE_E2E=1 go test -count=1 -tags e2e -run '^TestOpenSandboxOpenCodeJudgeProvider$' -v ./e2e该测试启动真实 OpenCode CLI 并请求模型服务。runner fixture 预置冲突的 provider 端点和 key,两个独立 judge 必须覆盖它们、评分通过并返回不同 session ID。 临时 sandbox 使用十五分钟 TTL 并正常删除。可用 SKILL_UP_OPENCODE_JUDGE_VERSION 固定 OpenCode 版本(已验证 1.14.24)。
若要覆盖省略 judge.engine.model.base_url 的场景,设置 SKILL_UP_OPENCODE_JUDGE_PROVIDER=openai(或 anthropic),不设置 SKILL_UP_OPENCODE_JUDGE_BASE_URL,并通过相同变量提供原生服务的模型和 key。 测试仍预置冲突的 runner provider,独立 judge 必须使用原生默认端点。 初始化只移除所选 provider 的遗留 inline 配置,保留 case MCP、其他 provider 及 OpenCode 的其他设置。
本地已安装 OpenCode 时,可运行真实 HTTP 路由回归:
SKILL_UP_OPENCODE_ORDER_E2E=1 go test -count=1 -tags e2e -run '^TestOpenCodeJudgeConnectionOrder$' -v ./e2e测试使用两个 loopback 记录端点、fixture key 和固定 HTTP 400 响应,核对独立 judge 与旧式 judge.model 不同顺序下的真实 CLI 请求去向,不调用模型服务,也不验证模型评分成功。
另有配置读回 smoke test,在真实 OpenSandbox 安装并预检 OpenCode,重置冲突的 OpenAI provider,再读回 MCP 和其他 provider 的保留状态。只需上述 sandbox 变量, 不需要模型 key 或原生模型服务的可达性:
SKILL_UP_OPENCODE_CONFIG_E2E=1 go test -count=1 -tags e2e -run '^TestOpenSandboxOpenCodeDefaultRouting$' -v ./e2e报告分别展示门控、各 judge 和总体结果。evaluation.json 保存完整分组结果;grading.json 为兼容格式,每个 judge 对应一个顶层断言。增加 Agent 评审标准不会改变脚本 judge 的权重。多个 case 现在即可引用同一个 script_path;连同辅助文件一起打包脚本属于后续功能。
judge: agent_judge — LLM 评审
让 LLM 按你定义的标准来评分,适合需要语义理解的场景:
judge:
type: agent_judge
model: dashscope/qwen3.8-max # 评审使用的模型
skills: # 可选:仅供 judge 使用的 Skills
- source: local_path
path: evals/fixtures/judge-rubric
criteria: # 评估标准(自然语言描述)
- "输出中识别了真实存在的 bug,并给出了准确位置"
- "没有将正确代码误报为 bug"
- "建议具有可操作性,不是泛泛而谈"
pass_threshold: 0.7 # 通过率阈值,默认 0.7
timeout_seconds: 60 # 可选:限制单次 judge 调用时长(0 = 不加 judge 级 deadline,仍受 case timeout 约束)如果评审需要独立于被测 agent 选择 engine,请用完整的 judge.engine 替换 judge.model:
judge:
type: agent_judge
engine:
name: claude_code
model:
provider: anthropic
name: claude-sonnet-4-6
criteria:
- "结果满足任务要求"judge.engine 和 judge.model 互斥。独立配置要求填写 name、model.provider 和 model.name;支持主运行 engine 的 version、kwargs、model.base_url、custom 字段及环境变量展开。它不会继承 runner 的模型、CLI 覆盖、kwargs、custom 配置或已解析 连接;凭据和 endpoint 按 judge 自己的 provider 与 adapter 协议,使用现有凭据配置解析。 不会根据 provider 自动推断 engine。
旧 judge.model 保留 runner engine 及兼容的 provider 范围内回退。已知原生 OpenAI/Anthropic 协议冲突,以及 adapter 会忽略的评分模型,会在被测 agent 执行前报错。 网关若使用原生 provider 名称提供另一种协议,需要在 credentials.yaml 中显式配置 对应协议的 endpoint。
单个 case 的 judge.engine 会整体替换 eval 的 judge engine;显式 judge.model 切换为旧模式;两者都未指定时继承 eval 的 engine/model 选择。judges 列表仍整体替换, 成员不会继承单个 judge 的默认配置。
Judge 继续使用 case 的 runtime,不新增 judge.environment。独立 judge 的 CLI 在 Docker/OpenSandbox 的评分阶段安装和检查,发生在被测 agent 完成之后。 none 环境需要预先安装所需 CLI,skill-up 只检查可用性;版本选择遵循现有 adapter 规则。单 judge 保留原有共享 runtime 行为;多 judge 同样按顺序复用 case runtime, 各自新开会话,文件系统状态共享。
judge.skills 仅支持 agent_judge。这些 Skills 会安装到 judge agent, 不会安装到主运行 agent;顶层 skills 也不会自动安装到 judge。开启 benchmark 时,with_skill 和 without_skill 都会安装 judge Skills,因为它们是 评分工具,不是被测 Skill。安装过程使用各 Agent adapter 原生的 Skill 机制; skill-up 不会把 Skill 文件内容拼接进 judge prompt。
agent_judge 会将评审上下文物化为文件,并在 judge prompt 中注入一个很小的 材料表。未配置 judge.context 时,默认使用 standard profile: final_message 内联,transcript 和 workspace_diff 以文件引用形式提供, 避免超长 prompt/argv 导致执行失败。
长时间运行的仓库变更类评测通常适合使用 minimal,让 judge 依赖显式附件或脚本 输出,而不是完整对话历史:
judge:
type: agent_judge
model: dashscope/qwen3.8-max
context:
profile: minimal # 省略 transcript/diff,截断 final_message
attachments:
- path: evals/fixtures/diff-result.json
label: diff_result
criteria:
- "判断报告中的代码变更是否满足预期规则。"如需微调 profile,可按字段指定模式:
judge:
type: agent_judge
context:
profile: standard
final_message: include # include | truncate | file_ref | omit
transcript: file_ref # include 超过 limits.max_bytes 会自动降级为 file_ref
workspace_diff: file_ref
generated_files: index # index | include | omit
limits:
max_bytes: 65536成本提示:
agent_judge会消耗额外的 token。建议对关键断言先用expect或rule_based做确定性检查,只对需要语义理解的部分使用agent_judge。
基线对比
启用 benchmark.enabled: true 后,每个用例会运行两次:
- with_skill — 安装 Skill 后执行(实验组)
- without_skill — 不安装 Skill 执行(基线组)
对比结果会体现 Skill 带来的增量价值(通过率提升、时间和 token 消耗差异)。 临时对比时,也可以运行 skill-up run ./evals/eval.yaml --baseline, 无需修改 eval.yaml。
benchmark:
enabled: true注意:基线对比会使评测时间和 token 消耗翻倍,默认关闭。
凭据配置
评测需要调用 Agent Engine,并且可能调用模型 API。你既可以通过 skill-up 注入凭据,也可以在所选 Engine 支持时沿用本地已有登录态。凭据解析优先级从高到低:
1. 命令行参数(临时覆盖)
skill-up run ./evals/eval.yaml --api-key sk-xxx2. 环境变量(推荐)
export ANTHROPIC_API_KEY=sk-ant-xxx
export OPENAI_API_KEY=sk-xxx
skill-up run ./evals/eval.yaml环境变量按 <PROVIDER>_<FIELD> 格式自动解析,支持的字段有 API_KEY、BASE_URL、MODEL:
| Provider | API Key | Base URL | Model |
|---|---|---|---|
| anthropic | ANTHROPIC_API_KEY | ANTHROPIC_BASE_URL | ANTHROPIC_MODEL |
| openai | OPENAI_API_KEY | OPENAI_BASE_URL | OPENAI_MODEL |
| 其他 | <PROVIDER>_API_KEY | <PROVIDER>_BASE_URL | <PROVIDER>_MODEL |
你也可以在项目根目录创建 .env 文件,skill-up 启动时会自动加载其中的变量。
3. 配置文件(持久化)
创建 ~/.skill-up/credentials.yaml:
providers:
anthropic:
api_key: sk-ant-xxx
openai:
api_key: sk-xxx
base_url: https://api.openai.com/v1 # 可选,支持代理常见连接方式
沿用本地登录态
让 provider、model 和 API key 保持为空,并使用宿主机 runtime。skill-up 只会检查 CLI 是否安装,不会为了验证登录态而发起消耗 token 的请求;登录态会在 第一个真实用例执行时得到验证。
environment:
type: none
engine:
name: codex # claude_code、qodercli、qwen_code 同样适用
model: {}Agent 自身的配置文件和登录数据库仍由对应 CLI 管理。宿主机登录态不会自动复制到 Docker 或 OpenSandbox runtime。
注入指定 provider 的连接
当模型 ID 不应该决定凭据命名空间时,应显式指定 provider。使用 provider 作用域的 环境变量可避免将密钥写入 eval 文件:
export DASHSCOPE_API_KEY=your-key
export DASHSCOPE_BASE_URL=https://dashscope.example.com/compatible-mode/v1
skill-up run ./evals/eval.yaml \
--engine codex \
--provider dashscope \
--model qwen3-coder-plusEngine 决定协议,provider 决定凭据和端点命名空间。这个例子中 Codex 使用 DashScope 的 OpenAI 兼容端点。
为同一个 gateway 配置多种协议
一个 provider 可以声明不同协议的端点。所选 Engine 决定使用哪个端点;skill-up 不会从 provider 反推 Engine:
# ~/.skill-up/credentials.yaml
providers:
company_gateway:
api_key: <gateway-api-key>
openai:
base_url: https://gateway.example.com/openai/v1
anthropic:
base_url: https://gateway.example.com/anthropic# eval.yaml
engine:
name: codex
model:
provider: company_gateway
name: team/model-v2将 Engine 改为 claude_code 后,会选择同一 provider 命名空间下的 Anthropic 兼容端点。
不透明模型 ID 与历史斜杠写法
显式指定 --provider 后,完整的 --model(包括 /)都会作为不透明值透传:
skill-up run ./evals/eval.yaml \
--engine codex \
--provider company_gateway \
--model team/model-v2未指定 --provider 时,如果前缀是已知或已配置的 provider,历史 provider/model 写法仍然兼容:
skill-up run ./evals/eval.yaml --engine codex --model openai/gpt-5.4未知前缀会作为模型 ID 的一部分完整保留,不会被猜测成 provider。
qodercli 凭据说明
qodercli 的认证凭证与模型层凭证(如 ANTHROPIC_API_KEY)完全独立,两者不能混用。
| 层级 | 环境变量 | 用途 |
|---|---|---|
| qodercli 服务认证 | QODER_PERSONAL_ACCESS_TOKEN | 向 qodercli 服务验证身份 |
| qodercli CN 服务认证 | QODERCN_PERSONAL_ACCESS_TOKEN | 向 Qoder CLI CN 服务验证身份 |
| 模型层认证 | ANTHROPIC_API_KEY 等 | 由 qodercli 内部管理,用户无需关心 |
配置方式:
# 方式 1:直接设置环境变量
export QODER_PERSONAL_ACCESS_TOKEN=your_token_here
# 方式 2:写入项目根目录 .env 文件
echo 'QODER_PERSONAL_ACCESS_TOKEN=your_token_here' >> .env通过 engine.kwargs.edition: cn 选择 Qoder CLI CN。CN 官方凭据变量是 QODERCN_PERSONAL_ACCESS_TOKEN;skill-up 也接受本地输入别名 QODER_CN_ACCESS_TOKEN,并在运行时以官方变量名转发。这样可以由 macOS Keychain 等密钥管理器注入凭据,而不需要把 Token 写进 eval 文件:
engine:
name: qodercli
kwargs:
edition: cn提示:所选版本的 PAT 不是必须的。未设置时,Global 使用
~/.qoder/,CN 使用~/.qoder-cn/中的本地登录态;项目级配置和 Skill 仍共同使用项目内的.qoder/目录。注意:
--api-key参数和eval.yaml中的 provider API key 不会被用作 qodercli 的认证 token。鉴权来自所选版本对应的 PAT 或本地登录态。
qodercli 的模型选择:
- 非空的
engine.model.name会去除首尾空白后传给 Qoder 的--model,保留大小写和完整 ID。普通调用、stdin 提示词和恢复会话均遵循此规则。 - 历史档位(
lite、efficient、auto、performance、ultimate)、具体模型名和自定义模型 ID 均原样透传,不再使用本地白名单。是否可用取决于安装的 Qoder CLI 和账号,可通过 Qoder 模型选择器或支持该参数的版本中的--list-models查询。 - 不指定模型时使用 Qoder 默认值。显式模型无效或不可用时,返回包含模型上下文和 Qoder 诊断的执行错误;skill-up 不会静默改用默认模型重试。
base_url对 qodercli 不生效,provider API key 也不会作为 Qoder PAT 使用。- 报告中的 requested/applied model 分别表示请求值和实际传入 CLI 的参数,不表示独立验证了服务端的模型选择。
例如,按准确名称选择一个可用的具体模型:
engine:
name: qodercli
model:
name: Qwen3.7-Plus对于含 / 的不透明 ID,使用 YAML 的 engine.model.name,或者在 CLI 中显式指定 provider,避免被按历史 provider/model 格式解析:
skill-up run ./evals/eval.yaml --engine qodercli --provider qoder --model 'team/custom-model-id'这里的 provider 仅用于消除 CLI 解析歧义,路由和认证仍由 Qoder 管理。自定义模型需先在 Qoder 中配置。
OpenCode 引擎
配置 engine.name: opencode。environment.type: none 调用本地已安装的 opencode 并沿用其登录态;docker 或 opensandbox 会在隔离环境内安装 opencode-ai CLI。下方已验证的 DashScope 配置适用于本地和沙箱模式。
模型以 provider/model 传给 OpenCode。显式 API key 经运行时环境变量传入; 未配置时使用 OpenCode 自身的登录态。engine.model.base_url 会通过 OpenCode 的进程内配置指定端点。自定义 OpenAI 兼容供应商需填写 provider ID、模型名和 base URL。真实与模拟 MCP Server 也通过进程内配置传入,不改项目的 opencode.json。JSON 事件用于提取会话 ID、工具调用、回答与 token 用量; 多轮用例通过该会话 ID 继续。
对 OpenCode 而言,不指定 provider 或 base URL 时,--model org/model 会原样传入, skill-up 不会从中推断 provider。显式指定 --provider gateway --model org/model 时,会传入 gateway/org/model;YAML 中的 engine.model.provider 规则相同。 只有 base URL 时,适配器使用 openai/org/model,保留完整的上游模型 ID。
非交互运行会自动批准工具权限。none 模式下工具使用本机用户权限;需要隔离时 使用 docker 或 opensandbox,并确保模型端点与 MCP Server 可从沙箱访问。
DashScope OpenAI 兼容接口
在 evals/eval.yaml 中配置 provider、上游模型 ID 和端点:
environment:
type: none # 隔离运行改为 opensandbox 或 docker
engine:
name: opencode
model:
provider: dashscope
name: qwen3.8-max
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1通过 DASHSCOPE_API_KEY 环境变量或 --api-key 提供密钥,不要写入评测文件。 设置好进程环境中的 DASHSCOPE_API_KEY 后运行:
skill-up run ./evals/eval.yamlskill-up 向 OpenCode 传入 --model dashscope/qwen3.8-max,并通过进程内配置注入 dashscope provider、@ai-sdk/openai-compatible、baseURL 和模型 ID;密钥 由运行时环境变量引用。示例使用官方模型调用 ID qwen3.8-max。 请按地域或业务空间替换端点,并选择当前密钥有权限调用的模型。 如果模型 ID 含 /,将完整 ID 填在 name 或 --model 中,skill-up 不会拆分。
qwen_code 凭据说明
Qwen Code 是面向 Qwen 模型优化的开源终端编码 Agent。引擎名为 qwen_code(别名 qwen-code、qwen),底层使用 @qwen-code/qwen-code CLI。skill-up 会按需通过 npm install -g @qwen-code/qwen-code 自动安装(并自动引导 Node.js 20+),因此无需手动安装。
Qwen Code 对接任意 OpenAI 兼容端点,复用标准 OpenAI 环境变量——与 codex 同一套机制:
| 环境变量 | 用途 |
|---|---|
OPENAI_API_KEY | OpenAI 兼容端点的 API key |
OPENAI_BASE_URL | 端点地址(如 DashScope 的 OpenAI 兼容模式) |
OPENAI_MODEL | 模型 id;由 engine.model.name 自动写入 |
provider: openai(或留空 provider)配合 base_url 即可指向自定义网关;engine.model.name / --model 会同时作为 -m 参数和 OPENAI_MODEL 传入。OPENAI_API_KEY 缺失只是提示信息——Qwen Code 可回退到 ~/.qwen/ 下的本地登录态(如 Qwen OAuth)。每个 case 通过把指令用管道喂给 qwen --yolo 非交互执行,自动确认工具操作。
沙箱说明:
--yolo只是自动确认工具调用,并不做隔离——因此在noneruntime 上qwen_code会以宿主机权限执行 shell/write 等工具,这一点与claude_code、qodercli一致。qwen_code故意不强制开启 qwen 自带的-s沙箱(它在 Linux 上依赖 docker/podman,其他环境并不可靠);如需运行不受信任的 skill,请改用带隔离的 runtime(environment.type: docker或opensandbox),它会统一隔离所有引擎。在noneruntime 上会保留 qwen 的“未启用沙箱”提示作为风险提醒;在隔离 runtime 下该提示会被静默(容器本身即沙箱)。
协议说明:Qwen Code 只支持 OpenAI 兼容协议(外加 Qwen OAuth),没有原生的 Anthropic Messages API 模式。如果要评测 Anthropic 协议的端点,请改用
claude_code引擎(它读取ANTHROPIC_API_KEY/ANTHROPIC_BASE_URL);或在端点前挂一层 Anthropic→OpenAI 兼容的转换代理,把代理地址填入base_url。
最小本地运行示例:
export OPENAI_API_KEY=sk-xxx
export OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
skill-up run ./evals/eval.yaml --engine qwen_code --model qwen3-coder-plus实战示例
示例 A:纯文本路由 Skill
无文件依赖的轻量场景,验证 Skill 是否正确路由命令:
# eval.yaml
schema_version: v1alpha1
environment:
type: none
engine:
name: opencode
model:
provider: dashscope
name: qwen3.8-max
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
cases:
files:
- evals/cases/route-to-summary.yaml
parallelism: 4 # 无状态,可以完全并行
judge:
type: rule_based# cases/route-to-summary.yaml
id: route-to-summary
title: 资源概况查询应路由到 app summary
input:
prompt: |
查一下 my-app 的资源概况,有多少机器
expect:
must_contain:
- "app summary"
must_not_contain:
- "app get"
judge:
type: rule_based
success:
- output_contains:
all: ["app summary", "--name"]示例 B:MCP 工具调用 Skill
验证 Skill 是否正确调用了指定的 MCP 工具:
# cases/create-plan.yaml
id: create-plan
title: 应正确调用创建发布计划工具
input:
prompt: |
帮我创建一个叫「Q1发布」的发布计划,计划 2026-04-03 发布
judge:
type: rule_based
success:
- tool_called:
name: "mcp__project-mgmt__create_publish_plan_simple"
args:
name: "Q1发布"
planReleaseDate: "2026-04-03"常见问题
Q: eval.yaml 中的路径怎么填?
所有路径(包括 cases.files 和 fixture 路径)都是相对于 Skill 根目录(即 SKILL.md 所在目录)的。例如 evals/fixtures/repos/my-project 指的是 <skill-root>/evals/fixtures/repos/my-project。
Q: 什么时候用 expect,什么时候用 judge?
expect 用于快速的、零成本的门槛检查(文件是否存在、关键词是否出现)。judge 用于更复杂的质量评估。两者可以同时使用——expect 不通过时 judge 会被跳过,节省时间和 token。
