Skip to content

快速开始:用 SDK 跑一个 Eval(开发中) ​

开发中

这种接法正在开发,暂时不能在平台上选用,现在请连 GitHub 仓库或用本地测试机。已经用它接好的项目照常可用,下面的说明照旧适用。

在你们自己的进程里,让 Agent 跑几条输入,给每条输出打分,把结果作为一个实验传上来。大约 5 分钟。

任务在你们的机器或 CI 上用你们自己的密钥跑,这种接法里平台从不调用你们的 Agent。平台收到的是每一行的输入、输出和分数,以及你们包装过的调用的追踪。

1. 从平台装 SDK ​

TypeScript 包 @preworld/sdk 和 Python 包 preworld 不在 npm 和 PyPI 上:平台自己提供,在 https://<平台地址>/downloads/,每次发版一个新版本。项目的目标 →「SDK」卡片上有下面两行,平台地址和当前版本都已填好,从那里复制就行。也可以自己查当前版本:

bash
curl -fsS https://<平台地址>/downloads/sdk.json     # "version",以及 "npm" 和 "python" 下的文件名

然后在你们项目的目录里:

bash
curl -fLO https://<平台地址>/downloads/preworld-sdk-<版本>.tgz
npm install -D ./preworld-sdk-<版本>.tgz tsx     # tsx 用来跑 *.eval.ts 文件
bash
curl -fLO https://<平台地址>/downloads/preworld-<版本>.tar.gz
pip install ./preworld-<版本>.tar.gz             # 或:uv add ./preworld-<版本>.tar.gz

把下载的文件和代码一起提交:package.json(或你们的依赖清单)指向的就是它,CI 里的 npm ci、pip install 装的是同一个文件,平台出了新版本也装得上。要升级,就下载新文件,照样装一次。每个文件旁边有它的校验值(<文件名>.sha256)。SDK 依赖的开源包(OpenTelemetry)由 npm 和 pip 从各自的仓库下载。

2. 建一个能上传的项目令牌 ​

  1. 建一个项目(侧栏的「新建项目」,选「会调用工具的 Agent」),或者打开一个已有的。
  2. 打开自动化 →「API 令牌」。填名称,比如 laptop,勾上**「允许上传日志与实验」,点「新建令牌」**。
  3. 复制令牌(pj_…)。它只显示这一次。

然后在运行 Eval 的终端里设四个变量:

bash
export PREWORLD_API_KEY=pj_你的令牌           # 刚建的令牌
export PREWORLD_PROJECT=prj_你的项目编号      # 项目地址里的那一段:/p/<这里>/…
export PREWORLD_BASE_URL=https://<平台地址>
export PREWORLD_ENVIRONMENT=dev                # dev、staging、prod 或 ci(设了 CI 时默认 ci)

SDK 自己从不读取 .env 文件。项目的「目标」→ SDK 那张卡片上有这几行,项目的值已经填好。

3. 写一个 Eval ​

ts
import { Eval } from '@preworld/sdk';

// 你们的 Agent:这里用一个查表回答的替身
async function answer(question: string): Promise<string> {
  const known: Record<string, string> = { '退款多久到账?': '退款在 5 个工作日内到账。' };
  return known[question] ?? '我不知道。';
}

Eval('support-bot', {
  data: [
    { input: '退款多久到账?', expected: '退款在 5 个工作日内到账。' },
    { input: '能改收货地址吗?', expected: '可以,发货之前都能改。' },
  ],
  task: answer,
  scores: [
    function exact({ output, expected }) { return output === expected; },   // 在你们这边打分:0 到 1 的数,或 true / false
    'builtin:factuality',                                                   // 在平台上由评审模型打分
  ],
});
python
from preworld import Eval

def answer(question):
    known = {"退款多久到账?": "退款在 5 个工作日内到账。"}
    return known.get(question, "我不知道。")

def exact(output, expected):          # 按参数名拿到 input、output、expected、metadata
    return output == expected

Eval(
    "support-bot",
    data=[
        {"input": "退款多久到账?", "expected": "退款在 5 个工作日内到账。"},
        {"input": "能改收货地址吗?", "expected": "可以,发货之前都能改。"},
    ],
    task=answer,
    scores=[exact, "builtin:factuality"],
)
  • data 是一组行(input,可选 expected、metadata、tags、id)、任意可迭代对象,或者用 dataset("<名字>") 取平台上存的数据集。
  • task 是你们的代码。它拿到这一行的输入(第二个参数是这次的序号、行的键和期望值)。
  • scores 里可以混着放:你们写的函数在你们的进程里跑;平台评分器写编号,内置库的是 builtin:<slug>,项目自己建的是 sc_…。函数请起名,名字就是这个评分器的列名。

4. 运行 ​

bash
npx preworld eval            # TypeScript:这个目录下所有 *.eval.ts / *.eval.js
preworld eval                # Python:这个目录下所有 eval_*.py / *_eval.py

平台用它的评分器(这里是 builtin:factuality)给这些行打分时,它会等着,打完再打印摘要和链接(PREWORLD_LANG=zh 时是中文):

text
preworld: 平台评分器还在打分:打完再读发布门槛…
Preworld · support-bot
  实验:support-bot · 4f2c9e1 (exp_…)
  2 行 · 2 次 · 任务出错 0 · 9.4 秒
  评分器                 平均
  exact               50%
  builtin:factuality  100%
  发布门槛:通过 ✓
    – 相对基线回归 ≤ 0 行 (实得 no baseline) · 仅提示
  https://<平台地址>/p/prj_…/experiments/exp_…

这条规则是项目默认的发布门槛:和基线比,不能有哪一行变差,只提示。第一次跑还没有基线,所以这条不适用(–)。打开链接:实验里有每一行的输入、输出和分数,和终端里一样。改完再跑一次,第二个实验会和第一个逐行比较:它的基线是项目设的基线,没设就是同一个 Eval(Eval('support-bot', …) 里同一个名字)上一次跑完的实验,不管提交和时间是否一样。

--no-send 只在本地跑和评分,什么都不上传;--first 5 每个 Eval 只跑前 5 行。

5. 在 CI 里卡住 ​

这条命令在发布门槛通过或没设规则时退出码是 0,没通过是 1,读不到门槛是 2,出错是 3。在设置 →「发布门槛」里定规则(比如「factuality 平均 ≥ 0.8」),然后在 CI 里跑同样的命令:没过门槛的版本这一步就失败。平台评分器给每一行都打完分才读发布门槛,所以按平台评分器定的规则不会因为分数还没出来就判没过。10 分钟内没打完(PREWORLD_GATE_WAIT,单位分钟),就不读门槛,退出码是 2。在 PR 上评论表格的 GitHub Action 见 CI 与 GitHub Action。

记录任务里的调用 ​

包装一下模型客户端,每一行就能连到它在日志里的追踪:

ts
import OpenAI from 'openai';
import { wrapOpenAI, traced } from '@preworld/sdk';
const openai = wrapOpenAI(new OpenAI());                                  // chat、responses 和 embeddings 调用都记成跨度
const search = traced(async (q: string) => db.search(q), { name: 'search', kind: 'retrieval' });

Python 用 wrap_openai(OpenAI())、wrap_anthropic(Anthropic()) 和 @traced。所有选项见 SDK 参考。

下一步 ​

  • 数据集:把行存在平台上,带版本,失败的行随时加进去。
  • 评分器:评审模型、代码和规则,保存前先拿一行试跑。
  • 实验:基线、对比和并排看。

本站的法律文件都是草稿,待律师审阅,尚未生效。