refactor: 统一 expect 断言体系,引入共享 ValueMatcher/ContentRules/KeyValueExpect 模型
- 引入共享 ValueMatcher(equals/contains/regex/exists/empty/gt/gte/lt/lte) - 引入共享 ContentRules 数组(direct/json/css/xpath 提取器) - 引入共享 KeyValueExpect(动态键值断言,字面量等价 equals) - maxDurationMs → durationMs: ValueMatcher(所有 checker) - match → regex(固定无 flags) - Ping max* → packetLossPercent/avgLatencyMs/maxLatencyMs(ValueMatcher) - LLM finishReason/rawFinishReason → ValueMatcher - DB 新增 result: ContentRules - TCP banner → ContentRules 数组 - 删除旧模块:operator.ts、validate-operator.ts、duration.ts、body.ts、text.ts、output.ts - 更新全部 checker schema/validate/expect/execute - 更新 probe-config.schema.json、probes.example.yaml - 更新 README.md、DEVELOPMENT.md(含 expect 字段选择规范) - 同步 10 个 delta specs 到主 specs,归档 change
This commit is contained in:
@@ -108,7 +108,7 @@ LLM checker SHALL 在 SDK 调用结果和 expect 断言之间构建 `LlmCheckObs
|
||||
- **THEN** LLM checker SHALL 返回 `phase: "request"` 的 error failure
|
||||
|
||||
### Requirement: LLM Expect 断言
|
||||
LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、`expect.finishReason`、`expect.rawFinishReason`、`expect.usage.inputTokens`、`expect.usage.outputTokens`、`expect.usage.totalTokens`、`expect.stream.completed`、`expect.stream.firstTokenMs` 和 `expect.maxDurationMs`。`expect.status` 和 `expect.headers` 的运行期断言 SHALL 复用 `src/server/checker/runner/http/expect.ts` 中的 `checkStatus` 和 `checkHeaders` 函数。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、duration;流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、duration。
|
||||
LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、`expect.finishReason`、`expect.rawFinishReason`、`expect.usage.inputTokens`、`expect.usage.outputTokens`、`expect.usage.totalTokens`、`expect.stream.completed`、`expect.stream.firstTokenMs` 和 `expect.durationMs`。`expect.status` SHALL 保持 HTTP 状态码数组语义并复用 HTTP status 断言。`expect.headers` SHALL 使用共享 `KeyValueExpect` 且 header key 大小写不敏感。`expect.output` MUST 使用共享 `ContentRules`。`expect.finishReason` 和 `expect.rawFinishReason` SHALL 使用共享 `ValueMatcher`。`expect.usage.*`、`expect.stream.firstTokenMs` 和 `expect.durationMs` SHALL 使用共享 `ValueMatcher`。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、durationMs;流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、durationMs。
|
||||
|
||||
#### Scenario: 默认 status 断言
|
||||
- **WHEN** LLM target 未配置 `expect.status`
|
||||
@@ -118,13 +118,25 @@ LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、
|
||||
- **WHEN** observing fetch 捕获的响应 headers 满足 `expect.headers` 配置
|
||||
- **THEN** LLM checker SHALL 判定 headers 断言通过
|
||||
|
||||
#### Scenario: expect headers 不匹配
|
||||
- **WHEN** observing fetch 捕获的响应 headers 不满足 `expect.headers` 中的某项配置
|
||||
- **THEN** LLM checker SHALL 返回 `phase: "headers"` 的 mismatch failure
|
||||
#### Scenario: output ContentRules 通过
|
||||
- **WHEN** LLM 输出文本满足 `expect.output` 中配置的全部 ContentRules
|
||||
- **THEN** LLM checker SHALL 判定 output 阶段通过
|
||||
|
||||
#### Scenario: 全部 expect 通过
|
||||
- **WHEN** LLM checker 构建出的 observation 满足所有已配置 expect
|
||||
- **THEN** 检查结果 SHALL 为 `matched=true` 且 `failure=null`
|
||||
#### Scenario: finishReason ValueMatcher 通过
|
||||
- **WHEN** observation.finishReason 为 `stop` 且 target 配置 `expect.finishReason: {equals: "stop"}`
|
||||
- **THEN** LLM checker SHALL 判定 finishReason 阶段通过
|
||||
|
||||
#### Scenario: rawFinishReason regex 通过
|
||||
- **WHEN** observation.rawFinishReason 为 `end_turn` 且 target 配置 `expect.rawFinishReason: {regex: "^(stop|end_turn)$"}`
|
||||
- **THEN** LLM checker SHALL 判定 rawFinishReason 阶段通过
|
||||
|
||||
#### Scenario: usage matcher 通过
|
||||
- **WHEN** observation.usage.totalTokens 为 14 且 target 配置 `expect.usage.totalTokens: {lte: 20}`
|
||||
- **THEN** LLM checker SHALL 判定 usage 阶段通过
|
||||
|
||||
#### Scenario: durationMs matcher 失败
|
||||
- **WHEN** LLM target 配置 `expect.durationMs: {lte: 1000}` 且实际执行耗时为 1500ms
|
||||
- **THEN** LLM checker SHALL 返回 phase=`duration` 的 mismatch failure
|
||||
|
||||
#### Scenario: 首个 expect 失败
|
||||
- **WHEN** 多个 LLM expect 中某个较早顺序的断言失败
|
||||
@@ -139,7 +151,7 @@ LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、
|
||||
- **THEN** LLM checker SHALL 因 `outputText` 缺失返回 `phase: "output"` 的 mismatch failure
|
||||
|
||||
### Requirement: LLM Output 规则
|
||||
LLM checker SHALL 支持 `expect.output` 有序规则数组,每个规则 MUST 仅包含 `equals`、`contains`、`regex` 或 `json` 中的一种。`equals` SHALL 对原始输出字符串做严格相等比较。`contains` SHALL 判断原始输出是否包含子串。`regex` SHALL 对原始输出执行正则匹配。`json` SHALL 将原始输出解析为 JSON,并用现有 JSONPath 子集和 operator 校验提取值。
|
||||
LLM checker SHALL 使用共享 `ContentRules` 校验 `expect.output`。每个 output rule SHALL 为直接 `ValueMatcher`,或 `json`、`css`、`xpath` extractor 规则之一。直接 matcher SHALL 作用于原始输出字符串。`equals` SHALL 对原始输出字符串做严格相等比较。`contains` SHALL 判断原始输出是否包含子串。`regex` SHALL 对原始输出执行无 flags 正则匹配。`json` SHALL 将原始输出解析为 JSON,并用现有 JSONPath 子集和 `ValueMatcher` 校验提取值。`json.equals` SHALL 支持任意 JSON value。`css` 和 `xpath` 在 schema 层面可用,但 LLM 输出通常为纯文本或 JSON,实际场景中仅 `json` 提取器有意义。
|
||||
|
||||
#### Scenario: 原始输出严格相等
|
||||
- **WHEN** `outputText` 为 `"OK\n"` 且 target 配置 `expect.output: [{ equals: "OK" }]`
|
||||
@@ -150,19 +162,27 @@ LLM checker SHALL 支持 `expect.output` 有序规则数组,每个规则 MUST
|
||||
- **THEN** LLM checker SHALL 判定该 output contains 规则通过
|
||||
|
||||
#### Scenario: output regex 通过
|
||||
- **WHEN** `outputText` 匹配配置的合法正则
|
||||
- **WHEN** `outputText` 匹配配置的合法 regex
|
||||
- **THEN** LLM checker SHALL 判定该 output regex 规则通过
|
||||
|
||||
#### Scenario: output JSONPath 通过
|
||||
- **WHEN** `outputText` 是 JSON 字符串且 JSONPath 提取值满足 operator
|
||||
#### Scenario: output JSONPath 字符串 equals 通过
|
||||
- **WHEN** `outputText` 是 JSON 字符串且 JSONPath 提取字符串值满足 `equals`
|
||||
- **THEN** LLM checker SHALL 判定该 output json 规则通过
|
||||
|
||||
#### Scenario: output JSONPath 对象 equals 通过
|
||||
- **WHEN** `outputText` 是 JSON 字符串且 JSONPath 提取对象值满足 `equals`
|
||||
- **THEN** LLM checker SHALL 使用深度相等判定该 output json 规则通过
|
||||
|
||||
#### Scenario: output JSONPath 存在性默认语义
|
||||
- **WHEN** `outputText` 是 JSON 字符串且 target 配置 `expect.output: [{json: {path: "$.status"}}]`
|
||||
- **THEN** LLM checker SHALL 将该规则按 `exists: true` 语义执行
|
||||
|
||||
#### Scenario: output 规则按顺序快速失败
|
||||
- **WHEN** `expect.output` 包含多个规则且第一条规则失败
|
||||
- **THEN** LLM checker SHALL 返回第一条失败规则的 mismatch failure,不继续校验后续 output 规则
|
||||
|
||||
### Requirement: LLM Stream 断言
|
||||
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`。`expect.stream.completed` 未配置时,LLM checker SHALL 在 stream observation 路径使用默认 `true` 语义。`expect.stream.firstTokenMs` SHALL 仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
|
||||
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`。`expect.stream.completed` 未配置时,LLM checker SHALL 在 stream observation 路径使用默认 `true` 语义。`expect.stream.firstTokenMs` SHALL 使用共享 `ValueMatcher`,并仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
|
||||
|
||||
#### Scenario: stream completed 默认值
|
||||
- **WHEN** target 配置 `llm.mode: stream` 且未配置 `expect.stream.completed`
|
||||
@@ -173,7 +193,7 @@ LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`。`expect.stre
|
||||
- **THEN** LLM checker SHALL 返回 `phase: "stream"` 的 failure
|
||||
|
||||
#### Scenario: firstTokenMs 达标
|
||||
- **WHEN** target 配置 `expect.stream.firstTokenMs` 且首个非空 text delta 耗时满足 operator
|
||||
- **WHEN** target 配置 `expect.stream.firstTokenMs: {lte: 1000}` 且首个非空 text delta 耗时满足 matcher
|
||||
- **THEN** LLM checker SHALL 判定 firstTokenMs 断言通过
|
||||
|
||||
#### Scenario: firstTokenMs 缺失
|
||||
|
||||
Reference in New Issue
Block a user