Skip to content

编写评测配置与用例 ​

本文档介绍如何为你的 Skill 编写完整的评测配置。你将学会如何定义运行环境、编写评测用例、以及配置评估策略。


目录结构 ​

评测文件放在 Skill 目录下的 evals/ 文件夹中:

plain
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、怎么评估」。

最小配置 ​

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/my-test.yaml

完整配置详解 ​

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 中追加:

    yaml
    collect_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 服务)均已支持。

yaml
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:把 SessionInput POST 到远程(或本地)HTTP agent 服务,从响应体读取 SessionResult。相关字段配置在 custom.http 下(见下文)。
  • response_format(可选,默认 session_result)—— skill-up 如何解析 agent 输出。
    • session_result:从 local.output_file(若配置)或 stdout 读出完整的 SessionResult JSON,包含 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):

yaml
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 要点:

  • 请求体默认是 SessionInput JSON,其中不含 custom.kwargs。request_body 中某个值若恰好是 ${session_input} / ${messages} / ${kwargs},会以 JSON 结构注入(而非字符串)——需要整份 kwargs 时用 ${kwargs} 显式注入。
  • 配置了 http.files 后请求变为 multipart/form-data:JSON 体移到 payload 字段,每个命中的文件作为独立 part 上传到固定表单字段 files,其 workspace 相对路径放在该 part 的 filename 里(服务端应读取每个 files part 的 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:

yaml
mcp:
  servers:
    - name: agent-sandbox
      mode: real
      transport: http
      config_ref: evals/fixtures/mcp/agent-sandbox.yaml

config_ref 文件支持以下字段:

yaml
transport: http
endpoint: https://mcp.example.com/mcp?token=${MCP_TOKEN}
required_env:
  - MCP_TOKEN
headers:
  PRIVATE-TOKEN: ${PRIVATE_TOKEN}

stdio MCP 可以配置 command 和 args:

yaml
mcp:
  servers:
    - name: marker
      mode: real
      transport: stdio
      command: /usr/bin/python3
      args: [evals/fixtures/mcp/marker_server.py]

mocked MCP 可以直接使用内置的 filesystem Mock Server:

yaml
mcp:
  servers:
    - name: filesystem
      mode: mocked

也可以通过 config_ref 定义工具响应:

yaml
mcp:
  servers:
    - name: project-mgmt
      mode: mocked
      config_ref: evals/fixtures/mcp/project-mgmt.yaml
yaml
tool_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 级配置,行为不变。

yaml
# eval.yaml —— 所有用例共享的默认 fixture
mcp:
  servers:
    - name: project-mgmt
      mode: mocked
      config_ref: evals/fixtures/mcp/project-default.yaml
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。

yaml
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_mountruntime 工作区路径,默认 /workspace
env注入沙箱命令执行环境的变量;如确需扩展 PATH,可使用 PATH: $CUSTOM_BIN:$PATH,runtime 会在沙箱内展开
setup_steps沙箱创建后在 runtime 工作区内执行的初始化命令
kwargs.base_urlOpenSandbox 服务地址,也可用 OPENSANDBOX_BASE_URL 设置
kwargs.extensionsJSON 字符串形式的 OpenSandbox 扩展配置
kwargs.request_timeout_secondsOpenSandbox SDK 请求超时时间
kwargs.file_transfer_parallelism目录下载并发度

Docker 配置 ​

使用 environment.type: docker 时,Agent 在本地 Docker 容器中运行,提供容器级别的隔离(文件系统、进程、网络),无需任何远程服务。

前置条件: 需要 docker CLI 在 PATH 中,且 Docker daemon 正在运行。runtime 不会自动拉取镜像,请提前执行 docker pull <image>。

yaml
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_policydeny_all 以 --network=none 创建容器(无网络访问);allow_declared 暂不支持,需要 FQDN 级别出口过滤请使用 opensandbox

建议: Docker runtime 适合需要自定义系统包、特定语言运行时、或离线/隔离环境的评测。如需远程托管沙箱,请使用 opensandbox。


case.yaml — 评测用例 ​

每个 .yaml 文件定义一个评测用例,包含「发什么 prompt」和「怎么验证结果」。

单轮对话用例 ​

大多数场景使用单轮 prompt 即可:

yaml
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 用于为用例准备初始环境:

加载仓库模板 ​

yaml
context:
  repo_fixture: evals/fixtures/repos/my-project    # 将目录内容复制到工作区

Git 操作 ​

yaml
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

内联文件 ​

yaml
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

基本多轮用例 ​

yaml
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: fail

post_condition — 轮间门控 ​

post_condition 在每轮结束后检查 Agent 的响应。它是门控而非 judge 的替代品, 用于确保对话在发送下一轮之前保持正轨。

字段说明:

  • must_contain_all:所有字符串必须出现在响应中。
  • must_contain_any:至少一个字符串必须出现。
  • must_not_contain:所列字符串均不能出现。
  • on_fail:条件失败时的行为:
    • fail(默认):立即将用例标记为 FAIL。
    • skip_remaining:跳过后续所有轮次;最终结果取决于 judge 评判。

capture — 模板变量 ​

从轮次响应中捕获值,在后续轮次中使用:

yaml
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 支持按轮次断言:

yaml
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 会被跳过。

yaml
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 检查,而不是在每个用例中重复:

yaml
# 在 eval.yaml 中
cases:
  defaults:
    expect:
      exit_code: 0
      must_not_contain:
        - "TODO"
        - "I cannot"
      files_exist:
        - "result.json"

每个用例可以添加或覆盖这些默认值:

yaml
# 在 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 — 确定性规则评估 ​

基于声明式规则判定用例是否通过,结果完全确定、可重复:

yaml
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 — 自定义脚本评估 ​

用你自己的脚本(任何语言)来评估结果:

yaml
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:

yaml
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:

bash
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,然后运行:

bash
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 路由回归:

bash
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 或原生模型服务的可达性:

bash
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 按你定义的标准来评分,适合需要语义理解的场景:

yaml
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:

yaml
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 依赖显式附件或脚本 输出,而不是完整对话历史:

yaml
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,可按字段指定模式:

yaml
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 后,每个用例会运行两次:

  1. with_skill — 安装 Skill 后执行(实验组)
  2. without_skill — 不安装 Skill 执行(基线组)

对比结果会体现 Skill 带来的增量价值(通过率提升、时间和 token 消耗差异)。 临时对比时,也可以运行 skill-up run ./evals/eval.yaml --baseline, 无需修改 eval.yaml。

yaml
benchmark:
  enabled: true

注意:基线对比会使评测时间和 token 消耗翻倍,默认关闭。


凭据配置 ​

评测需要调用 Agent Engine,并且可能调用模型 API。你既可以通过 skill-up 注入凭据,也可以在所选 Engine 支持时沿用本地已有登录态。凭据解析优先级从高到低:

1. 命令行参数(临时覆盖) ​

bash
skill-up run ./evals/eval.yaml --api-key sk-xxx

2. 环境变量(推荐) ​

bash
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:

ProviderAPI KeyBase URLModel
anthropicANTHROPIC_API_KEYANTHROPIC_BASE_URLANTHROPIC_MODEL
openaiOPENAI_API_KEYOPENAI_BASE_URLOPENAI_MODEL
其他<PROVIDER>_API_KEY<PROVIDER>_BASE_URL<PROVIDER>_MODEL

你也可以在项目根目录创建 .env 文件,skill-up 启动时会自动加载其中的变量。

3. 配置文件(持久化) ​

创建 ~/.skill-up/credentials.yaml:

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 的请求;登录态会在 第一个真实用例执行时得到验证。

yaml
environment:
  type: none
engine:
  name: codex                 # claude_code、qodercli、qwen_code 同样适用
  model: {}

Agent 自身的配置文件和登录数据库仍由对应 CLI 管理。宿主机登录态不会自动复制到 Docker 或 OpenSandbox runtime。

注入指定 provider 的连接 ​

当模型 ID 不应该决定凭据命名空间时,应显式指定 provider。使用 provider 作用域的 环境变量可避免将密钥写入 eval 文件:

bash
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-plus

Engine 决定协议,provider 决定凭据和端点命名空间。这个例子中 Codex 使用 DashScope 的 OpenAI 兼容端点。

为同一个 gateway 配置多种协议 ​

一个 provider 可以声明不同协议的端点。所选 Engine 决定使用哪个端点;skill-up 不会从 provider 反推 Engine:

yaml
# ~/.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
yaml
# eval.yaml
engine:
  name: codex
  model:
    provider: company_gateway
    name: team/model-v2

将 Engine 改为 claude_code 后,会选择同一 provider 命名空间下的 Anthropic 兼容端点。

不透明模型 ID 与历史斜杠写法 ​

显式指定 --provider 后,完整的 --model(包括 /)都会作为不透明值透传:

bash
skill-up run ./evals/eval.yaml \
  --engine codex \
  --provider company_gateway \
  --model team/model-v2

未指定 --provider 时,如果前缀是已知或已配置的 provider,历史 provider/model 写法仍然兼容:

bash
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 内部管理,用户无需关心

配置方式:

bash
# 方式 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 文件:

yaml
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 的参数,不表示独立验证了服务端的模型选择。

例如,按准确名称选择一个可用的具体模型:

yaml
engine:
  name: qodercli
  model:
    name: Qwen3.7-Plus

对于含 / 的不透明 ID,使用 YAML 的 engine.model.name,或者在 CLI 中显式指定 provider,避免被按历史 provider/model 格式解析:

bash
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 和端点:

yaml
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 后运行:

bash
skill-up run ./evals/eval.yaml

skill-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_KEYOpenAI 兼容端点的 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 只是自动确认工具调用,并不做隔离——因此在 none runtime 上 qwen_code 会以宿主机权限执行 shell/write 等工具,这一点与 claude_code、qodercli 一致。qwen_code 故意不强制开启 qwen 自带的 -s 沙箱(它在 Linux 上依赖 docker/podman,其他环境并不可靠);如需运行不受信任的 skill,请改用带隔离的 runtime(environment.type: docker 或 opensandbox),它会统一隔离所有引擎。在 none runtime 上会保留 qwen 的“未启用沙箱”提示作为风险提醒;在隔离 runtime 下该提示会被静默(容器本身即沙箱)。

协议说明:Qwen Code 只支持 OpenAI 兼容协议(外加 Qwen OAuth),没有原生的 Anthropic Messages API 模式。如果要评测 Anthropic 协议的端点,请改用 claude_code 引擎(它读取 ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL);或在端点前挂一层 Anthropic→OpenAI 兼容的转换代理,把代理地址填入 base_url。

最小本地运行示例:

bash
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 是否正确路由命令:

yaml
# 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
yaml
# 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 工具:

yaml
# 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。

基于 Apache 2.0 协议发布。