概念
指标与评分器
编写评分函数,告诉引擎什么才算更好。
指标告诉 Skynet 一个输出有多好。它是 metric.py 中的一个 Python 函数。每个引擎都调用同一个函数。本页给出契约、返回值和示例。
契约
def score(input, output, expected=None):
...
return score_value, {"feedback": "..."}| 参数 | 包含的内容 |
|---|---|
input | 一个 dict,包含该用例的输入字段 |
output | 你的 run(artifact, input) 为该用例返回的内容 |
expected | 该用例的期望值;如果用例没有期望值,则为 None |
如果你的函数只接受两个参数,Skynet 会调用 score(input, output)。
对于 DSPy 程序,Skynet 会把 DSPy 的指标调用适配到这同一个函数。你只需为每种产物类型编写一个指标。请阅读优化的工作原理。
返回值
score 必须返回一对值(一个包含两个元素的 tuple 或 list):
- 分数。 一个数字。优化器会尽量让它变高。
- 附加信息。 一个 dict。它必须有一个
"feedback"键,其值为字符串。
附加信息还可以有一个 "scores" 键。它把名称映射到数字,例如 {"format": 1.0, "accuracy": 0.5}。
如果返回值的结构不同,契约检查会在 指标返回值 处失败。
| 返回值 | 是否有效 |
|---|---|
(0.8, {"feedback": "Close. Missing the date."}) | 是 |
[1.0, {"feedback": "OK", "scores": {"format": 1.0}}] | 是 |
0.8 | 否。没有附加信息。 |
(0.8, "Close.") | 否。附加信息不是 dict。 |
(0.8, {}) | 否。没有反馈字符串。 |
("0.8", {"feedback": "OK"}) | 否。分数是字符串。 |
(0.8, {"feedback": "OK", "scores": {"a": "high"}}) | 否。某个命名分数不是数字。 |
编写有用的反馈
GEPA、DSPy GEPA 和 Auto 会读取反馈文本来编写下一个版本。请说明哪里错了,以及正确的输出是什么样子。
| 薄弱的反馈 | 有用的反馈 |
|---|---|
| “Wrong.” | “Expected 'billing', got 'other'. The message mentions a charge.” |
| “Bad format.” | “The output is not valid JSON. It has a trailing comma after the last field.” |
示例
精确匹配
def score(input, output, expected=None):
want = expected["label"]
if output == want:
return 1.0, {"feedback": "Correct."}
return 0.0, {"feedback": f"Expected {want!r}, got {output!r}."}expected 是一个以输出列名称为键的 dict。请阅读数据集与划分。
多项检查
import json
def score(input, output, expected=None):
try:
data = json.loads(output)
except ValueError as exc:
return 0.0, {"feedback": f"Output is not valid JSON: {exc}", "scores": {"format": 0.0, "fields": 0.0}}
want = expected["fields"]
found = [key for key in want if key in data]
fields = len(found) / len(want) if want else 1.0
missing = [key for key in want if key not in data]
feedback = "All fields present." if not missing else f"Missing fields: {', '.join(missing)}."
return 0.5 + 0.5 * fields, {"feedback": feedback, "scores": {"format": 1.0, "fields": fields}}用模型作为评判
指标可以调用模型。从 skynet 辅助模块导入 llm。从指标发出的调用会使用指标模型。
from skynet import llm
def score(input, output, expected=None):
verdict = llm(
"Answer PASS or FAIL, then give one sentence of reasons.",
input=f"Question: {input['question']}\nAnswer: {output}",
)
passed = str(verdict).strip().upper().startswith("PASS")
return (1.0 if passed else 0.0), {"feedback": str(verdict)}如果你没有选择 指标模型,指标会使用与反思模型相同的模型。
准确率、速度和成本
指标可以把准确率与测得的运行时间和模型成本结合起来。在模块级定义 REWARD_SPEC,并返回 reward 的值。
from skynet import reward
REWARD_SPEC = {
"weights": {"accuracy": 0.7, "speed": 0.2, "cost": 0.1},
"accuracy": {},
"speed": {"reference": 2000},
"cost": {"reference": 0.01},
}
def score(input, output, expected=None):
ok = output == expected["label"]
return reward(REWARD_SPEC, 1.0 if ok else 0.0, "Correct." if ok else "Wrong label.")weights必须包含accuracy、speed和cost。每个权重都为 0 或更大,且不能全部为 0。Skynet 会把它们缩放到总和为 1。speed.reference的单位是毫秒。cost.reference的单位是美元。两者都必须为正数。- 没有
accuracy.reference时,准确率是 0 到 1 之间的分数。有它时,准确率是一个误差,越低越好。
Skynet 自己测量时间和成本。运行代码无法更改它们。如果 Skynet 无法测量时间或成本,那一部分记为 0 分,反馈中也会说明。
指标也可以用 skynet 中的 observation() 和 service_cost() 直接读取这些测量值。
限制
| 项目 | 值 |
|---|---|
| 指标超时 | 默认 60 秒,最多 600 秒 |
| 契约检查样例 | 3 个用例 |