v2 接口
/v2 API 的全部接口:数据集、评分器、实验、发布门槛、追踪、在线评分、告警、Playground 和人工评审。令牌、权限、错误和 CI 用的 /v1 接口见 API:令牌与错误;OTLP 和 Gateway 的地址见 OpenTelemetry 与 AI Gateway。
所有接口都在 https://<平台地址>/v2/ 下,收发 JSON,要 Authorization: Bearer <令牌>(或浏览器的登录状态)。
谁能调
| 标记 | 谁 |
|---|---|
| read | 组织成员(浏览器登录或 CLI 令牌 rt_…),或项目的令牌 pj_… |
| ingest | 组织成员,或有上传权限的项目令牌(「允许上传日志与实验」) |
| member | 只有组织成员;项目令牌得到 403 project_token_not_allowed |
| person | 在浏览器里登录、亲手点的成员;CLI 令牌、项目令牌、助手或编码 AI 得到 403 session_required |
项目令牌只能用在它自己的项目上。别的组织的对象,或已删除项目的对象,一律 404 not_found,从不是 403。
翻页和筛选
列表接口收 limit(默认 50,最多 500)和 cursor,回答 next_cursor:把它作为 cursor 传回去取下一页,直到它是 null。带 filter 时,一页最多读 5000 条来找,所以可能回来不满一页(甚至是空的)但带着 next_cursor:只要 next_cursor 不是 null 就接着翻。不是页给出的 cursor 是 400 bad_cursor。filter 用筛选语法(最多 2000 个字符);写错是 400 bad_filter,带出错的 position。
数据集
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/datasets | read | limit、cursor | {datasets, next_cursor},新的在前;每个带 last_experiment |
POST /v2/projects/:p/datasets | ingest | {name, description?} | 201 数据集 |
GET /v2/datasets/:id | read | 数据集和它的 versions | |
PATCH /v2/datasets/:id | member | {name?, description?} | 数据集 |
GET /v2/datasets/:id/rows | read | version、filter、limit、cursor | {rows, next_cursor};404 version_not_found |
POST /v2/datasets/:id/rows | ingest | {rows: [...]},1 到 1000 行:没有 id 的是新增,带 id 的写新修订。字段:input、expected、metadata、tags(≤ 50)、source、conditions(≤ 20)、turns(≤ 50)、simulated_user、env | {rows: [{id, revision, created}]};场景的行是 409 edit_in_scenario_editor |
DELETE /v2/datasets/:id/rows/:row | member | {id, revision, deleted: true} | |
POST /v2/datasets/:id/versions | ingest | {note?} | 201 {dataset_id, version, created_at, created_by, note?, row_count} |
GET /v2/datasets/:id/scores | read | 按行编号列出每一行最近的分数 | |
POST /v2/datasets/:id/import/preview | member | {text, format: "auto" | "promptfoo_yaml"},文字最多 500 万个字符 | {format, total, columns, rows}(前几行) |
POST /v2/datasets/:id/import | member | 同上,再加 mapping: {header, columns: ["input" | "expected" | "tags" | "metadata" | "skip", …]} | {added, rows, problems, problem_count};最多 5000 行 |
POST /v2/datasets/:id/rows/from | member | {experiment_id, row_keys} 或 {trace_ids},1 到 500 | {rows} |
POST /v2/datasets/:id/generate | person | {mode: "describe" | "repo" | "failures", description?, count}(1 到 20,默认 8) | {rows, model, usd};当天额度用完是 429 assistant_allowance |
一行:{id, dataset_id, revision, input, expected?, metadata?, tags, source: {kind, ref?}, conditions?, turns?, simulated_user?, env?, scenario_ref?, question_ref?, created_at, created_by}。条件是 {kind: "normal"}、{kind: "fault", tool, fault} 或 {kind: "attack", vector, plugin?}。
评分器
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/scorers/builtin | read | {scorers}:每个内置评分器的名称、种类、方向、读什么和设置的 schema | |
GET /v2/projects/:p/scorers | read | archived=0|1、limit、cursor | {scorers, next_cursor},包括组织共用的 |
POST /v2/projects/:p/scorers | member | {name, description?, direction?, shared?, kind, config} | 201 评分器;422 invalid_scorer |
GET /v2/scorers/:id | read | version | 评分器(最新版本,或指定的版本) |
PATCH /v2/scorers/:id | member | {name?, description?, config?, direction?, archived?} | 评分器的新版本 |
POST /v2/scorers/:id/try | member,助手不行 | {version?} 加一行:{input?, output?, expected?, metadata?}、{experiment_id, row_key, trial?} 或 {trace_id} | {result, row} |
POST /v2/projects/:p/scorers/try | member,助手不行 | {scorer: {kind, config, direction?} | {id: "builtin:…", config?}} 加一行 | 同上,给还没保存的评分器 |
GET /v2/scorers/:id/calibration | read | version | {scorer_id, version, set, status, results} |
PUT /v2/scorers/:id/calibration | member | {rows: [{input?, output?, expected?, metadata?, human_value}]},1 到 500 | 校准 |
POST /v2/scorers/:id/calibration/run | member,助手不行 | 202,校准开始 | |
GET /v2/projects/:p/scorers/stats | read | 每个评分器:用过它的实验、门槛有没有用它、最近 7 天、校准 |
kind 和 config:llm {prompt, output: "score" | "pass_fail" | {choices: [{label, value}]}, model} · code {language: "js", source} · heuristic {fn, params} · trajectory {mode} · agent {check} · human {key, scale: {min, max} | {choices}}。见评分器。
实验
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/experiments | read | filter、limit、cursor | {experiments, next_cursor},新的在前 |
GET /v2/experiments/:id | read | 实验 | |
POST /v2/projects/:p/experiments | ingest | {name, source?: "sdk" | "playground", target?, dataset?: {id, version?}, scorers?, baseline_id?, environment?, git?: {sha, branch, pr}, metadata?} | 201 实验,带 url 和它要比较的 baseline_id |
POST /v2/experiments/:id/rows | ingest | {rows},1 到 500 行;每行 {row_key?, dataset_row?: {id, revision?}, input, expected?, condition?, tags?, trials: [{trial?, output?, turns?, scores, trace_id?, duration_ms?, cost_usd?}]}。带 Idempotency-Key 请求头,重试就安全。 | {rows: [{row_key, trials}]};409 experiment_finished、409 derived_experiment |
POST /v2/experiments/:id/finish | ingest | {status?: "done" | "cancelled" | "failed"} | 实验 |
GET /v2/experiments/:id/rows | read | baseline、filter、limit、cursor | {rows, next_cursor, baseline_id};每行带基线那一行的分数和 delta |
GET /v2/experiments/:id/compare | read | baseline | {experiment_id, baseline_id, comparable, why, judge, scorers, rows: {improved, regressed, same, new, dropped}} |
POST /v2/experiments/:id/scorers | member,助手不行 | {scorers},1 到 50 个平台评分器 | 202 {experiment_id, scorers, pending};沙盒或 runner 的实验是 409 derived_experiment |
GET /v2/projects/:p/experiment-changes | read | ids(逗号隔开,最多 60 个) | 每个实验对它的基线改善和回归的行数 |
GET /v2/runs/:run_id/experiment | read | 一次运行属于哪个实验;预跑是 404 no_experiment | |
GET /v2/projects/:p/baseline | read | {project_id, baseline_id} | |
PUT /v2/projects/:p/baseline | person | {experiment_id}(或 null) | {project_id, baseline_id};没跑完的是 422 baseline_not_usable |
一次执行里的一个分数:{scorer, value, label?, undecided?: {code, reason, reason_en?}, rationale?, judge_model?, cost_usd?}。从运行得出的行里,平台用中文写的原因另有英文的 reason_en。scorer 是在你们那边打的分写 local:<名字>,内置的写 builtin:<slug>,项目的写 sc_…@<版本>;value 是 0 到 1,越低越好的检查是次数,未判定是 null。scorers 里写的平台评分器会在后台给这些行打分。
发布门槛
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/gate | read | {project_id, rules, updated_by, updated_at, by_person} | |
PUT /v2/projects/:p/gate | person | {rules},最多 50 条,id 不重复 | 门槛;最近的实验重新判一遍 |
GET /v2/experiments/:id/gate | read | wait(秒,最多 60) | 门槛的结果(见下面) |
POST /v2/experiments/:id/gate/preview | read | {rules} | 按这些规则会怎样,不保存 |
一条规则:{id, kind: "scorer", scorer, stat: "mean" | "nonzero", op: ">=" | "<=" | "=", value, blocking, low_risk?: "warn"}(low_risk:低危的得逞只提示,不算进这条)、{id, kind: "release_verdict", blocking: true}、{id, kind: "no_regression", max_rows, blocking}、{id, kind: "undecided_max", fraction, blocking} 或 {id, kind: "required_tags", tags, blocking}。
GET /v2/experiments/:id/gate 回答 {experiment_id, status: "pass" | "blocked" | "none", rules: [{rule, ok, actual, rows?, skipped?, low?: {rows, what}}], baseline_id?, computed_at, experiment_status, final, conclusion: "success" | "failure" | "neutral"}。low 是带 low_risk: "warn" 的规则没算进去的低危行和它们是什么:门槛通过时有它,就是「通过,但有 N 个低危问题」,结论仍是 success。带 ?wait=(即使是 0)时,平台把请求最多挂那么多秒,有变化或门槛定下来就回答,并加上 report:每个评分器的平均、基线、变化、改善和回归的行数、未判定的行和要先处理的问题。一直问到 final 为 true。CI 发起时拿到的编号,在第一次运行进来之前就能读(experiment_status: "pending")。SDK 或 Playground 的实验,平台评分器还在给它的行打分时,门槛带 pending: true,final 是 false,结论是 neutral。见 CI。
追踪
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
POST /v2/projects/:p/traces | ingest | {environment?, spans},1 到 1000 个跨度 | 202 {accepted, traces};422 unknown_experiment |
GET /v2/projects/:p/traces | read | since、until(ISO 时间)、env(逗号隔开:dev、staging、prod、ci、experiment)、filter、limit、cursor | {traces, next_cursor},新的在前 |
GET /v2/projects/:p/traces/facets | read | filter、since、until(默认最近 7 天,最多 31 天)、env | 按状态、环境、来源、模型、工具、标签、版本和分数区间的计数;每类最多 50 个值 |
GET /v2/traces/:trace_id | read | 追踪和它按树的顺序排好的跨度,每个带 depth | |
GET /v2/traces/:trace_id/spans/:span_id/payload | read | part=input|output | 存在存储里的大内容,原样的 JSON |
一个跨度:{trace_id(32 位十六进制), span_id(16 位十六进制), parent_span_id?, name, kind?: "agent" | "llm" | "tool" | "retrieval" | "embedding" | "chain" | "other", start, end(带时区的 ISO 时间), status?: "ok" | "error" | "cancelled"(调用方取消), model?, tokens_in?, tokens_out?, tokens_cached?, cost_usd?, attributes?, input?, output?, environment?, experiment_id?, row_key?, metadata?}。超过 32 KB 的输入输出放进存储,追踪里回答成 {$blob, size, url}。
在线评分
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/online-rules | read | {rules},每条带 today: {day, spent_usd, scored, capped_at} | |
POST /v2/projects/:p/online-rules | person | {name, filter?, environments?, sampling, scorers(1 到 10 个), daily_cap_usd?, enabled?} | 201 规则;超过 20 条是 409 too_many_rules |
PATCH /v2/online-rules/:id | person | 上面任意几项 | 规则 |
DELETE /v2/online-rules/:id | person | {id, deleted: true} | |
POST /v2/online-rules/:id/backfill | person | {limit?}(1 到 1000,默认 100) | 202 {rule_id, queued};409 rule_off、409 rule_capped |
GET /v2/projects/:p/online-rules/preview | read | filter、environments、sampling、scorers | 最近 24 小时:追踪数、符合的、抽中的、每条和每个评分器每天的费用 |
GET /v2/projects/:p/online-stats | read | 最近 7 天对比之前 7 天:追踪数、错误率、每 1000 次的成本、p95 延迟、每个评分器的平均、14 天的日平均 |
告警
只有成员能用:告警里写着人和地址。
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/alerts | member | {alerts} | |
POST /v2/projects/:p/alerts | person | {name, condition, channels(1 到 3 个,每种一个), enabled?} | 201 {alert, webhook_secret?}:签名密钥,只这一次 |
PATCH /v2/alerts/:id | person | 上面任意几项 | {alert, webhook_secret?} |
DELETE /v2/alerts/:id | person | {id, deleted: true} | |
POST /v2/alerts/:id/test | person | 发出的测试消息和每个渠道的送达情况 | |
GET /v2/alerts/:id/events | member | {events}:最近 50 条 |
条件:{kind: "score_mean", scorer, window: "1h" | "24h", below}(0 到 1)、{kind: "error_rate", window, above}(比例,0 到 1)、{kind: "cost_day", above_usd}、{kind: "gate_blocked"} 或 {kind: "rule_capped", rule_id?}。渠道:{kind: "email", user_ids}、{kind: "slack", url}(或 {kind: "slack", keep: true} 沿用存好的地址)或 {kind: "webhook", url, keep_secret?};回答里 Slack 地址和 webhook 密钥只给提示。每个项目最多 50 条告警。webhook 的正文和签名见告警 webhook。
Playground
只有成员能用,项目令牌不行。
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/playground/sessions | member | {sessions, target}(最多 100 个) | |
POST /v2/projects/:p/playground/sessions | member | {name?, columns?, rows?, sample?, scorers?},或 {from: {experiment_id, row_key} | {trace_id}} | 201 会话,带格子和估价 |
GET /v2/playground/sessions/:id | member | 会话 | |
PATCH /v2/playground/sessions/:id | member | {name?, columns?(1 到 6), rows?(≤ 100)或 sample?: {dataset_id, version, n, seed}, scorers?(≤ 20)} | 会话 |
POST /v2/playground/sessions/:id/run | person | 202;409 run_running、422 no_rows、422 model_unavailable、422 target_needs_scenarios | |
POST /v2/playground/sessions/:id/experiments | member | 201 {experiments, baseline_id}(已经存过是 200);409 not_run |
人工评审
只有成员能用,项目令牌不行。
| 接口 | 谁 | 收 | 回答 |
|---|---|---|---|
GET /v2/projects/:p/review-queues | member | archived=1 | {queues} |
POST /v2/projects/:p/review-queues | member | {name, description?, scorers(人工评分器,≤ 20), required, blind, assignees, problem_tags, dataset_id, calibrate: [{scorer, from}], feed: {source: "logs" | "experiments", filter, per_day} | null} | 201 队列 |
GET /v2/review-queues/:id | member | 队列 | |
PATCH /v2/review-queues/:id | member | 上面任意几项 | 队列 |
DELETE /v2/review-queues/:id | member | {id, archived: true} | |
POST /v2/review-queues/:id/items | member | {trace_ids} 或 {experiment_id, row_keys}(1 到 500),assignee? | {added, already, items} |
GET /v2/review-queues/:id/items | member | status: pending | done | skipped、mine=1、limit(默认 100,最多 500)、cursor | {items, next_cursor} |
GET /v2/review-queues/:id/next | member | {item}:你的下一条,或 null | |
GET /v2/review-items/:id | member | 条目,和它要显示的内容 | |
POST /v2/review-items/:id/submit | person | {answers: {"sc_…": 值}, tags, note} | 条目;422 required_missing |
POST /v2/review-items/:id/skip | person | 条目;409 already_reviewed | |
POST /v2/review-items/:id/reassign | member | {assignee}(或 null) | 条目 |