@aipack-ai/eval 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/index.d.ts +24 -0
  2. package/dist/index.js +26 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/src/cli.d.ts +12 -0
  5. package/dist/src/cli.js +149 -0
  6. package/dist/src/cli.js.map +1 -0
  7. package/dist/src/core/loader.d.ts +21 -0
  8. package/dist/src/core/loader.js +91 -0
  9. package/dist/src/core/loader.js.map +1 -0
  10. package/dist/src/core/mock-stream.d.ts +16 -0
  11. package/dist/src/core/mock-stream.js +71 -0
  12. package/dist/src/core/mock-stream.js.map +1 -0
  13. package/dist/src/core/mock-tools.d.ts +24 -0
  14. package/dist/src/core/mock-tools.js +161 -0
  15. package/dist/src/core/mock-tools.js.map +1 -0
  16. package/dist/src/core/report.d.ts +26 -0
  17. package/dist/src/core/report.js +149 -0
  18. package/dist/src/core/report.js.map +1 -0
  19. package/dist/src/core/runner.d.ts +18 -0
  20. package/dist/src/core/runner.js +213 -0
  21. package/dist/src/core/runner.js.map +1 -0
  22. package/dist/src/core/scorer/index.d.ts +19 -0
  23. package/dist/src/core/scorer/index.js +56 -0
  24. package/dist/src/core/scorer/index.js.map +1 -0
  25. package/dist/src/core/scorer/rule.d.ts +17 -0
  26. package/dist/src/core/scorer/rule.js +231 -0
  27. package/dist/src/core/scorer/rule.js.map +1 -0
  28. package/dist/src/core/trajectory.d.ts +13 -0
  29. package/dist/src/core/trajectory.js +41 -0
  30. package/dist/src/core/trajectory.js.map +1 -0
  31. package/dist/src/core/types.d.ts +251 -0
  32. package/dist/src/core/types.js +16 -0
  33. package/dist/src/core/types.js.map +1 -0
  34. package/dist/src/core/validate.d.ts +7 -0
  35. package/dist/src/core/validate.js +71 -0
  36. package/dist/src/core/validate.js.map +1 -0
  37. package/package.json +53 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * packages/eval/src/core/scorer/rule.ts - 规则评分器
3
+ *
4
+ * M1 范围:8 个确定性评分器(零 LLM 成本):
5
+ * exact / contains / regex —— 最终文本断言
6
+ * json-field —— 结构化输出断言(dot 路径取值)
7
+ * tool-call —— 工具调用轨迹断言(顺序 + 参数部分匹配)
8
+ * tools-used —— 去重工具集合断言(对齐 Result.toolsUsed)
9
+ * success / stop-reason —— 运行状态断言
10
+ */
11
+ // ─── 参数部分匹配(深比较,expected 未列字段忽略)─────────────────
12
+ export function partialMatch(expected, actual) {
13
+ if (expected === undefined)
14
+ return true;
15
+ if (Array.isArray(expected)) {
16
+ return (Array.isArray(actual) &&
17
+ expected.length === actual.length &&
18
+ expected.every((e, i) => partialMatch(e, actual[i])));
19
+ }
20
+ if (expected !== null && typeof expected === 'object') {
21
+ if (actual === null || typeof actual !== 'object' || Array.isArray(actual)) {
22
+ return false;
23
+ }
24
+ return Object.entries(expected).every(([k, v]) => partialMatch(v, actual[k]));
25
+ }
26
+ return expected === actual;
27
+ }
28
+ // ─── dot 路径取值 ─────────────────────────────────────────────────
29
+ function getByPath(obj, path) {
30
+ let cur = obj;
31
+ for (const key of path.split('.').filter(Boolean)) {
32
+ if (cur === null || typeof cur !== 'object')
33
+ return { found: false, value: undefined };
34
+ cur = cur[key];
35
+ }
36
+ return { found: true, value: cur };
37
+ }
38
+ function matchCall(expected, actual) {
39
+ if (expected.tool !== actual.name)
40
+ return false;
41
+ if (expected.isError !== undefined && expected.isError !== actual.isError) {
42
+ return false;
43
+ }
44
+ if (expected.args === undefined)
45
+ return true;
46
+ return partialMatch(expected.args, actual.args);
47
+ }
48
+ /**
49
+ * 有序子序列匹配:expected 序列按顺序出现在 actual 中(允许间隔)。
50
+ */
51
+ function matchSubsequence(expected, actual) {
52
+ let i = 0;
53
+ for (const a of actual) {
54
+ if (i < expected.length && matchCall(expected[i], a))
55
+ i += 1;
56
+ }
57
+ return i === expected.length;
58
+ }
59
+ function str(p, key, required = true) {
60
+ const v = p[key];
61
+ if (typeof v === 'string')
62
+ return v;
63
+ if (required)
64
+ throw new Error(`评分器参数缺少字符串字段 '${key}'`);
65
+ return undefined;
66
+ }
67
+ const scorers = {
68
+ exact: (trace, p) => {
69
+ const value = str(p, 'value');
70
+ const passed = trace.result.content === value;
71
+ return {
72
+ scorer: 'exact',
73
+ score: passed ? 1 : 0,
74
+ passed,
75
+ reason: passed ? '最终文本完全相等' : `期望 ${JSON.stringify(value)},实际 ${JSON.stringify(trace.result.content)}`,
76
+ };
77
+ },
78
+ contains: (trace, p) => {
79
+ const value = str(p, 'value');
80
+ const passed = trace.result.content.includes(value);
81
+ return {
82
+ scorer: 'contains',
83
+ score: passed ? 1 : 0,
84
+ passed,
85
+ reason: passed ? `包含 ${JSON.stringify(value)}` : `未包含 ${JSON.stringify(value)}`,
86
+ evidence: passed ? value : undefined,
87
+ };
88
+ },
89
+ regex: (trace, p) => {
90
+ const value = str(p, 'value');
91
+ const flags = str(p, 'flags', false) ?? '';
92
+ const re = new RegExp(value, flags);
93
+ const passed = re.test(trace.result.content);
94
+ return {
95
+ scorer: 'regex',
96
+ score: passed ? 1 : 0,
97
+ passed,
98
+ reason: passed ? `匹配 /${value}/${flags}` : `不匹配 /${value}/${flags}(实际 ${JSON.stringify(trace.result.content.slice(0, 80))})`,
99
+ };
100
+ },
101
+ 'json-field': (trace, p) => {
102
+ const path = str(p, 'path');
103
+ const expectedValue = 'value' in p ? p.value : undefined;
104
+ let parsed;
105
+ try {
106
+ parsed = JSON.parse(trace.result.content);
107
+ }
108
+ catch (e) {
109
+ return {
110
+ scorer: 'json-field',
111
+ score: 0,
112
+ passed: false,
113
+ reason: `最终文本不是合法 JSON: ${e.message}`,
114
+ };
115
+ }
116
+ const { found, value } = getByPath(parsed, path);
117
+ if (!found) {
118
+ return {
119
+ scorer: 'json-field',
120
+ score: 0,
121
+ passed: false,
122
+ reason: `路径 '${path}' 不存在`,
123
+ };
124
+ }
125
+ const hasExpected = 'value' in p;
126
+ const passed = hasExpected ? partialMatch(expectedValue, value) : true;
127
+ return {
128
+ scorer: 'json-field',
129
+ score: passed ? 1 : 0,
130
+ passed,
131
+ reason: passed
132
+ ? `路径 '${path}' 命中${hasExpected ? '且值匹配' : ''}`
133
+ : `路径 '${path}' 期望 ${JSON.stringify(expectedValue)},实际 ${JSON.stringify(value)}`,
134
+ };
135
+ },
136
+ 'tool-call': (trace, p) => {
137
+ const calls = p.calls;
138
+ if (!Array.isArray(calls))
139
+ throw new Error("评分器参数缺少数组字段 'calls'");
140
+ const order = p.order === 'exact' ? 'exact' : 'subset';
141
+ const actual = trace.trajectory;
142
+ let passed;
143
+ if (order === 'exact') {
144
+ passed =
145
+ actual.length === calls.length &&
146
+ calls.every((c, i) => matchCall(c, actual[i]));
147
+ }
148
+ else {
149
+ passed = matchSubsequence(calls, actual);
150
+ }
151
+ const actualDesc = actual
152
+ .map((a, i) => `${i + 1}. ${a.name}(${JSON.stringify(a.args ?? {})})`)
153
+ .join(' → ') || '(无工具调用)';
154
+ const expectedDesc = calls.map((c) => c.tool).join(' → ') || '(无)';
155
+ return {
156
+ scorer: 'tool-call',
157
+ score: passed ? 1 : 0,
158
+ passed,
159
+ reason: passed
160
+ ? `轨迹匹配(${order}):${expectedDesc}`
161
+ : `轨迹不匹配(${order}):期望 ${expectedDesc},实际 ${actualDesc}`,
162
+ evidence: actualDesc,
163
+ };
164
+ },
165
+ 'tools-used': (trace, p) => {
166
+ const tools = p.tools;
167
+ if (!Array.isArray(tools))
168
+ throw new Error("评分器参数缺少数组字段 'tools'");
169
+ const expectedSet = new Set(tools);
170
+ const actualSet = new Set(trace.result.toolsUsed);
171
+ const passed = expectedSet.size === actualSet.size &&
172
+ [...expectedSet].every((t) => actualSet.has(t));
173
+ return {
174
+ scorer: 'tools-used',
175
+ score: passed ? 1 : 0,
176
+ passed,
177
+ reason: passed
178
+ ? `工具集合匹配: ${[...expectedSet].join(', ')}`
179
+ : `期望 {${[...expectedSet].join(', ')}},实际 {${[...actualSet].join(', ')}}`,
180
+ };
181
+ },
182
+ success: (trace, p) => {
183
+ const expected = typeof p.value === 'boolean' ? p.value : true;
184
+ const passed = trace.result.success === expected;
185
+ return {
186
+ scorer: 'success',
187
+ score: passed ? 1 : 0,
188
+ passed,
189
+ reason: passed
190
+ ? `运行成功标志 = ${expected}`
191
+ : `期望 success=${expected},实际 ${trace.result.success}${trace.result.error ? `(${trace.result.error})` : ''}`,
192
+ };
193
+ },
194
+ 'stop-reason': (trace, p) => {
195
+ const value = str(p, 'value');
196
+ const passed = trace.result.stopReason === value;
197
+ return {
198
+ scorer: 'stop-reason',
199
+ score: passed ? 1 : 0,
200
+ passed,
201
+ reason: passed
202
+ ? `stopReason = ${value}`
203
+ : `期望 stopReason=${value},实际 ${trace.result.stopReason}`,
204
+ };
205
+ },
206
+ };
207
+ export const RULE_SCORER_TYPES = Object.keys(scorers);
208
+ /** 执行单个规则评分器;未知类型 / 参数错误返回 scorer 自身失败而非抛出 */
209
+ export function runRuleScorer(trace, type, params) {
210
+ const fn = scorers[type];
211
+ if (!fn) {
212
+ return {
213
+ scorer: type,
214
+ score: 0,
215
+ passed: false,
216
+ reason: `未知评分器类型 '${type}'(M1 仅支持: ${RULE_SCORER_TYPES.join(', ')})`,
217
+ };
218
+ }
219
+ try {
220
+ return fn(trace, params);
221
+ }
222
+ catch (e) {
223
+ return {
224
+ scorer: type,
225
+ score: 0,
226
+ passed: false,
227
+ reason: `评分器执行错误: ${e.message}`,
228
+ };
229
+ }
230
+ }
231
+ //# sourceMappingURL=rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../../src/core/scorer/rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * packages/eval/src/core/trajectory.ts - 工具调用轨迹提取
3
+ *
4
+ * Result.toolsUsed 只有去重的工具名;完整轨迹(名称 / 参数 / 顺序 /
5
+ * isError)从 runtime.getMessages() 返回的会话消息重建:
6
+ * - assistant 消息的 toolCall 块 → 调用记录(按消息顺序)
7
+ * - toolResult 消息 → 按 toolCallId 回填 isError
8
+ */
9
+ import type { Message } from '@aipack-ai/agent';
10
+ import type { ToolCallRecord } from './types';
11
+ export declare function extractTrajectory(messages: Message[]): ToolCallRecord[];
12
+ /** 统计 assistant 轮数(对话深度,不含 toolResult 消息) */
13
+ export declare function countTurns(messages: Message[]): number;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * packages/eval/src/core/trajectory.ts - 工具调用轨迹提取
3
+ *
4
+ * Result.toolsUsed 只有去重的工具名;完整轨迹(名称 / 参数 / 顺序 /
5
+ * isError)从 runtime.getMessages() 返回的会话消息重建:
6
+ * - assistant 消息的 toolCall 块 → 调用记录(按消息顺序)
7
+ * - toolResult 消息 → 按 toolCallId 回填 isError
8
+ */
9
+ export function extractTrajectory(messages) {
10
+ const records = [];
11
+ const errorById = new Map();
12
+ for (const msg of messages) {
13
+ if (msg.role === 'toolResult') {
14
+ const toolMsg = msg;
15
+ errorById.set(toolMsg.toolCallId, toolMsg.isError);
16
+ }
17
+ }
18
+ for (const msg of messages) {
19
+ if (msg.role !== 'assistant')
20
+ continue;
21
+ if (typeof msg.content === 'string')
22
+ continue;
23
+ for (const block of msg.content) {
24
+ if (block.type !== 'toolCall')
25
+ continue;
26
+ const call = block;
27
+ records.push({
28
+ id: call.id,
29
+ name: call.name,
30
+ args: call.arguments,
31
+ isError: errorById.get(call.id),
32
+ });
33
+ }
34
+ }
35
+ return records;
36
+ }
37
+ /** 统计 assistant 轮数(对话深度,不含 toolResult 消息) */
38
+ export function countTurns(messages) {
39
+ return messages.filter((m) => m.role === 'assistant').length;
40
+ }
41
+ //# sourceMappingURL=trajectory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trajectory.js","sourceRoot":"","sources":["../../../src/core/trajectory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,UAAU,iBAAiB,CAAC,QAAmB;IACnD,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE7C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,GAIf,CAAC;YACF,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QACvC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAAE,SAAS;QAC9C,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;gBAAE,SAAS;YACxC,MAAM,IAAI,GAAG,KAAwB,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,SAAS;gBACpB,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,QAAmB;IAC5C,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * packages/eval/src/core/types.ts - Eval 体系类型定义
3
+ *
4
+ * 四个核心概念:
5
+ * EvalCase —— 一条评测用例(输入 + 期望 + 评分器配置)
6
+ * RunConfig —— 一次评测运行的执行配置
7
+ * RunTrace —— 单个用例运行后的完整轨迹(供评分器消费)
8
+ * EvalReport —— 一次运行的汇总报告
9
+ *
10
+ * 设计原则(对齐 EVAL_PLAN.md):
11
+ * - 轨迹优先:工具调用轨迹是 agent 框架最值得断言的信号
12
+ * - 规则评分覆盖 70%:M1 只实现规则评分器,semantic / llm-judge 留到 M5
13
+ * - origin 标识:报告按数据来源分组,诊断 eval 集与真实分布的漂移
14
+ */
15
+ import type { Message, Result } from '@aipack-ai/agent';
16
+ /** 用例数据来源(报告按 origin 分组诊断分布漂移) */
17
+ export type CaseOrigin = 'handwritten' | 'trace' | 'synthetic' | 'dataset' | 'bugfix';
18
+ /** 脚本化的一次工具调用 */
19
+ export interface MockToolCall {
20
+ /** 缺省自动生成(mock_tc_<n>) */
21
+ id?: string;
22
+ /** 被调用的 mock 工具名 */
23
+ name: string;
24
+ args?: Record<string, unknown>;
25
+ }
26
+ /** 脚本化的一轮 assistant 响应 */
27
+ export interface MockAssistantTurn {
28
+ /** 本轮要发起的工具调用(可多个 = 并行调用) */
29
+ toolCalls?: MockToolCall[];
30
+ /** 本轮 assistant 文本(可与 toolCalls 并存,文本在前) */
31
+ text?: string;
32
+ /** 缺省:有 toolCalls 时 'toolUse',否则 'stop' */
33
+ stopReason?: string;
34
+ /** 本轮 usage(缺省 {input:10, output:5, total:15}) */
35
+ usage?: {
36
+ input?: number;
37
+ output?: number;
38
+ total?: number;
39
+ };
40
+ }
41
+ export interface MockScript {
42
+ /** 依次消费的 assistant 轮次;每次 streamFn 调用取下一条 */
43
+ turns: MockAssistantTurn[];
44
+ /** 轮次耗尽后的兜底文本轮(缺省 'done') */
45
+ fallbackText?: string;
46
+ /**
47
+ * 轮次耗尽后是否持续发起该工具的调用(模拟死循环,
48
+ * 配合 case metadata.maxSteps 断言 max_turns 截断)。
49
+ */
50
+ infiniteTool?: string;
51
+ /** infiniteTool 的参数(缺省 {}) */
52
+ infiniteToolArgs?: Record<string, unknown>;
53
+ }
54
+ export interface AgentInput {
55
+ /** 用户消息 */
56
+ message: string;
57
+ systemPrompt?: string;
58
+ /** 使用的 mock 工具名列表;缺省注册全部标准 mock 工具 */
59
+ tools?: string[];
60
+ /** 预置 mock 文件系统(readFile / writeFile / listDir / search 消费) */
61
+ fs?: Record<string, string>;
62
+ /** mock 模式必填:脚本化 LLM 轮次 */
63
+ mock?: MockScript;
64
+ }
65
+ /** 期望的工具调用(args 为部分匹配:只断言列出的字段) */
66
+ export interface ExpectedToolCall {
67
+ tool: string;
68
+ args?: Record<string, unknown>;
69
+ /** 断言该调用结果是否为错误(undefined = 不断言) */
70
+ isError?: boolean;
71
+ }
72
+ /** 期望结果的声明式写法(loader 会归一化为 scorers) */
73
+ export type ExpectedResult = {
74
+ type: 'exact';
75
+ value: string;
76
+ } | {
77
+ type: 'contains';
78
+ value: string;
79
+ } | {
80
+ type: 'regex';
81
+ value: string;
82
+ flags?: string;
83
+ } | {
84
+ type: 'tool-call';
85
+ calls: ExpectedToolCall[];
86
+ order?: 'exact' | 'subset';
87
+ } | {
88
+ type: 'tools-used';
89
+ tools: string[];
90
+ } | {
91
+ type: 'json-field';
92
+ path: string;
93
+ value?: unknown;
94
+ } | {
95
+ type: 'success';
96
+ value?: boolean;
97
+ } | {
98
+ type: 'stop-reason';
99
+ value: string;
100
+ };
101
+ /** M1 支持的规则评分器类型 */
102
+ export type RuleScorerType = 'exact' | 'contains' | 'regex' | 'json-field' | 'tool-call' | 'tools-used' | 'success' | 'stop-reason';
103
+ /** 全量评分器类型(M5 扩展 semantic / llm-judge) */
104
+ export type ScorerType = RuleScorerType | 'semantic' | 'llm-judge';
105
+ export interface ScorerConfig {
106
+ type: ScorerType;
107
+ /** 评分器参数(各评分器自定义,见 rule.ts) */
108
+ params: Record<string, unknown>;
109
+ /** 加权(缺省 1);M1 规则评分器只看 passed,权重影响加权分 */
110
+ weight?: number;
111
+ }
112
+ export interface EvalCase {
113
+ /** 全局唯一,如 'tool-calling/single-echo' */
114
+ id: string;
115
+ /** 套件名:'agent-e2e' | 'tool-calling' | 'text-output' | ... */
116
+ suite: string;
117
+ /** 一句话描述(报告展示用) */
118
+ description?: string;
119
+ input: AgentInput;
120
+ /** 声明式期望(归一化为 scorers,与 scorers 字段可并存) */
121
+ expected?: ExpectedResult;
122
+ /** 显式评分器配置(与 expected 可并存) */
123
+ scorers?: ScorerConfig[];
124
+ origin: CaseOrigin;
125
+ metadata?: {
126
+ tags?: string[];
127
+ /** 工具调用步数上限(映射 RuntimeOptions.maxTurns,缺省 50) */
128
+ maxSteps?: number;
129
+ /** 单用例墙钟超时 ms(缺省 30000) */
130
+ timeoutMs?: number;
131
+ /** usage.total token 上限(预算熔断) */
132
+ maxTokens?: number;
133
+ };
134
+ }
135
+ /** 完整轨迹中的一条工具调用记录(从会话消息重建) */
136
+ export interface ToolCallRecord {
137
+ id: string;
138
+ name: string;
139
+ args: unknown;
140
+ /** 对应 toolResult 的 isError(结果缺失时 undefined) */
141
+ isError?: boolean;
142
+ }
143
+ /** 单个用例一次运行的完整产物 */
144
+ export interface RunTrace {
145
+ caseId: string;
146
+ suite: string;
147
+ origin: CaseOrigin;
148
+ result: Result;
149
+ /** 按时间序的工具调用轨迹(含参数与顺序) */
150
+ trajectory: ToolCallRecord[];
151
+ messages: Message[];
152
+ durationMs: number;
153
+ usageTotal: number;
154
+ /** 实际对话轮数 */
155
+ turns: number;
156
+ }
157
+ export interface ScoreResult {
158
+ scorer: ScorerType;
159
+ /** 0~1;规则评分器为 0/1 */
160
+ score: number;
161
+ passed: boolean;
162
+ reason: string;
163
+ /** 命中证据(文本片段 / 工具调用序号) */
164
+ evidence?: string;
165
+ }
166
+ export interface CaseResult {
167
+ caseId: string;
168
+ suite: string;
169
+ origin: CaseOrigin;
170
+ passed: boolean;
171
+ /** 加权平均分 0~1 */
172
+ score: number;
173
+ scores: ScoreResult[];
174
+ /** 运行错误(超时 / 熔断 / 框架异常) */
175
+ error?: string;
176
+ durationMs: number;
177
+ /** 工具调用步数 */
178
+ steps: number;
179
+ usageTotal: number;
180
+ }
181
+ export interface SuiteSummary {
182
+ total: number;
183
+ passed: number;
184
+ passRate: number;
185
+ avgScore: number;
186
+ }
187
+ export interface EvalReport {
188
+ runId: string;
189
+ startedAt: string;
190
+ durationMs: number;
191
+ mode: 'mock' | 'live';
192
+ model?: string;
193
+ totals: {
194
+ cases: number;
195
+ passed: number;
196
+ passRate: number;
197
+ avgScore: number;
198
+ usageTotal: number;
199
+ };
200
+ bySuite: Record<string, SuiteSummary>;
201
+ byOrigin: Record<string, SuiteSummary>;
202
+ results: CaseResult[];
203
+ }
204
+ export interface RunConfig {
205
+ mode: 'mock' | 'live';
206
+ /** 只跑指定套件;缺省全部 */
207
+ suites?: string[];
208
+ /** 每用例重复次数(消随机性);mock 模式缺省 1 */
209
+ repeats?: number;
210
+ /** 用例并发数(缺省 8) */
211
+ concurrency?: number;
212
+ /** 单用例墙钟超时 ms(缺省 30000,case metadata 可覆盖) */
213
+ timeoutMs?: number;
214
+ /** maxTurns 全局缺省(缺省 50,case metadata 可覆盖) */
215
+ maxSteps?: number;
216
+ /** 用例目录(loader 用) */
217
+ casesDir?: string;
218
+ /** 报告输出目录(缺省 ./eval-results) */
219
+ reportDir?: string;
220
+ /** baseline 文件路径(门禁对比) */
221
+ baselinePath?: string;
222
+ /** 通过率回归阈值(缺省 0.02) */
223
+ regressionThreshold?: number;
224
+ /** 更新 baseline 文件 */
225
+ updateBaseline?: boolean;
226
+ }
227
+ export interface BaselineFile {
228
+ /** 生成 baseline 的 runId */
229
+ runId: string;
230
+ updatedAt: string;
231
+ totals: {
232
+ cases: number;
233
+ passed: number;
234
+ passRate: number;
235
+ };
236
+ bySuite: Record<string, {
237
+ total: number;
238
+ passed: number;
239
+ passRate: number;
240
+ }>;
241
+ }
242
+ export interface BaselineComparison {
243
+ ok: boolean;
244
+ overallDelta: number;
245
+ regressions: Array<{
246
+ suite: string;
247
+ baseline: number;
248
+ current: number;
249
+ delta: number;
250
+ }>;
251
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * packages/eval/src/core/types.ts - Eval 体系类型定义
3
+ *
4
+ * 四个核心概念:
5
+ * EvalCase —— 一条评测用例(输入 + 期望 + 评分器配置)
6
+ * RunConfig —— 一次评测运行的执行配置
7
+ * RunTrace —— 单个用例运行后的完整轨迹(供评分器消费)
8
+ * EvalReport —— 一次运行的汇总报告
9
+ *
10
+ * 设计原则(对齐 EVAL_PLAN.md):
11
+ * - 轨迹优先:工具调用轨迹是 agent 框架最值得断言的信号
12
+ * - 规则评分覆盖 70%:M1 只实现规则评分器,semantic / llm-judge 留到 M5
13
+ * - origin 标识:报告按数据来源分组,诊断 eval 集与真实分布的漂移
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * packages/eval/src/core/validate.ts - 用例静态校验
3
+ *
4
+ * loader 加载 JSON 后立即校验,坏用例在运行前暴露(fail fast)。
5
+ */
6
+ import type { EvalCase } from './types';
7
+ export declare function validateEvalCase(c: EvalCase): string[];
@@ -0,0 +1,71 @@
1
+ /**
2
+ * packages/eval/src/core/validate.ts - 用例静态校验
3
+ *
4
+ * loader 加载 JSON 后立即校验,坏用例在运行前暴露(fail fast)。
5
+ */
6
+ import { RULE_SCORER_TYPES } from './scorer/rule.js';
7
+ const ORIGINS = [
8
+ 'handwritten',
9
+ 'trace',
10
+ 'synthetic',
11
+ 'dataset',
12
+ 'bugfix',
13
+ ];
14
+ const EXPECTED_TYPES = [
15
+ 'exact',
16
+ 'contains',
17
+ 'regex',
18
+ 'tool-call',
19
+ 'tools-used',
20
+ 'json-field',
21
+ 'success',
22
+ 'stop-reason',
23
+ ];
24
+ export function validateEvalCase(c) {
25
+ const errors = [];
26
+ const at = (msg) => `[${c.id ?? '<missing id>'}] ${msg}`;
27
+ if (!c.id || typeof c.id !== 'string')
28
+ errors.push('缺少 id');
29
+ else if (!/^[a-z0-9][a-z0-9/_-]*$/i.test(c.id))
30
+ errors.push(at('id 含非法字符'));
31
+ if (!c.suite || typeof c.suite !== 'string')
32
+ errors.push(at('缺少 suite'));
33
+ if (!c.input || typeof c.input !== 'object') {
34
+ errors.push(at('缺少 input'));
35
+ return errors;
36
+ }
37
+ if (!c.input.message || typeof c.input.message !== 'string') {
38
+ errors.push(at('input.message 必须为非空字符串'));
39
+ }
40
+ if (c.input.mock) {
41
+ if (!Array.isArray(c.input.mock.turns) || c.input.mock.turns.length === 0) {
42
+ errors.push(at('input.mock.turns 必须为非空数组'));
43
+ }
44
+ else {
45
+ c.input.mock.turns.forEach((t, i) => {
46
+ if (!t.toolCalls && t.text === undefined) {
47
+ errors.push(at(`mock.turns[${i}] 既无 toolCalls 也无 text`));
48
+ }
49
+ });
50
+ }
51
+ }
52
+ if (!c.origin || !ORIGINS.includes(c.origin)) {
53
+ errors.push(at(`origin 必须为: ${ORIGINS.join(' | ')}`));
54
+ }
55
+ if (c.expected) {
56
+ const type = c.expected.type;
57
+ if (!type || !EXPECTED_TYPES.includes(type)) {
58
+ errors.push(at(`expected.type 必须为: ${EXPECTED_TYPES.join(' | ')}`));
59
+ }
60
+ }
61
+ for (const s of c.scorers ?? []) {
62
+ if (!RULE_SCORER_TYPES.includes(s.type)) {
63
+ errors.push(at(`scorers 含未知类型 '${s.type}'(支持: ${RULE_SCORER_TYPES.join(', ')})`));
64
+ }
65
+ }
66
+ const hasScorers = Boolean(c.expected) || (c.scorers?.length ?? 0) > 0;
67
+ if (!hasScorers)
68
+ errors.push(at('expected 与 scorers 至少提供一项'));
69
+ return errors;
70
+ }
71
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/core/validate.ts"],"names":[],"mappings":"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"}