1
0
Files
DiAL/openspec/specs/llm-checker/spec.md
lanyuanxiaoyao cfca03b4d6 refactor: 规范审查与重组,合并细粒度规范,清理过时内容
- 合并 20+ 细粒度 spec 为粗粒度主题规范:dashboard、data-store、probe-engine、probe-api、probe-config 等
- 删除完全冗余规范:data-retention(被 probe-engine+data-store 覆盖)、backend-code-quality(DEVELOPMENT.md 已记录)
- 补充 http-checker 规范至完整标准(配置+执行+expect+校验+observation),匹配代码 440 行实现
- 清理 tcp/udp/llm checker 规范中已废弃 defaults 配置段的残留 Scenario
- 清理 checker-cohesion-structure 中的实现路径引用(src/server/...)
- 统一所有 spec 格式(## Purpose 开头,去除 # Capability/Title 形式)
- 更新 prompt-spec-review.md 审查提示文档
2026-05-22 18:55:18 +08:00

16 KiB
Raw Blame History

Purpose

定义 LLM checker 的配置模型、Provider/Mode 支持、执行观测、Expect 断言、失败 Phase 和测试行为。

Requirements

Requirement: LLM Checker 注册与模块结构

系统 SHALL 提供 type: llm checker用于大模型服务的应用层拨测。LLM checker MUST 位于自包含目录,并通过 checker 注册入口注册到 CheckerRegistry。LLM checker SHALL 复用现有 checker 抽象、配置 schema 组装、启动期语义校验、引擎调度、存储序列化和共享 expect 基础设施。

Scenario: 注册 LLM checker

  • WHEN 系统初始化默认 checker registry
  • THEN registry SHALL 包含 llm 类型,且 /api/meta 返回的 checkerTypes SHALL 包含 llm

Scenario: LLM checker 目录自包含

  • WHEN 开发者查看 LLM checker 目录
  • THEN 该目录 SHALL 包含 LLM checker 的类型、schema、语义校验、provider 创建、observation 构建、expect 断言、执行逻辑和模块入口

Scenario: 不扩展存储和 API 结构

  • WHEN LLM checker 写入检查结果
  • THEN 系统 SHALL 使用现有 CheckResulttargetscheck_results 和 Dashboard API 结构,不新增 LLM 专用存储列或 Dashboard 指标字段

Requirement: LLM Provider 与调用模式

LLM checker SHALL 支持 openaiopenai-responsesanthropic 三类 provider。mode: http SHALL 调用 AI SDK generateTextmode: stream SHALL 调用 AI SDK streamText。所有模型调用 MUST 将 maxRetries 固定为 0,并 MUST 使用引擎注入的 ctx.signal 响应超时和取消。

Scenario: OpenAI Chat Completions provider

  • WHEN target 配置 llm.provider: openai
  • THEN LLM checker SHALL 使用 @ai-sdk/openaiopenai.chat(model) 创建模型调用对象

Scenario: OpenAI Responses provider

  • WHEN target 配置 llm.provider: openai-responses
  • THEN LLM checker SHALL 使用 @ai-sdk/openaiopenai.responses(model) 创建模型调用对象

Scenario: Anthropic provider

  • WHEN target 配置 llm.provider: anthropic
  • THEN LLM checker SHALL 使用 @ai-sdk/anthropicanthropic.messages(model) 创建模型调用对象

Scenario: 非流式调用模式

  • WHEN target 配置 llm.mode: http 或省略 llm.mode
  • THEN LLM checker SHALL 调用 generateText 并从返回结果构建非流式 observation

Scenario: 流式调用模式

  • WHEN target 配置 llm.mode: stream
  • THEN LLM checker SHALL 调用 streamText 并消费 fullStream 构建流式 observation

Scenario: 超时取消传递给 SDK

  • WHEN 引擎注入的 ctx.signal 被 abort
  • THEN LLM checker SHALL 将该 signal 传递给 AI SDK 调用并将取消或超时结果记录为检查失败

Requirement: LLM 配置解析与默认值

LLM checker SHALL 解析 llm.providerllm.urlllm.modelllm.promptllm.modellm.keyllm.authTokenllm.headersllm.ignoreSSLllm.optionsllm.providerOptionsllm.options SHALL 支持 maxOutputTokens(默认 16)、temperature(默认 0)、topPtopKpresencePenaltyfrequencyPenaltystopSequences(字符串数组)和 seedllm.mode 默认值 SHALL 为 httpllm.key 默认值 SHALL 为空字符串,llm.ignoreSSL 默认值 SHALL 为 false。LLM checker MUST NOT 隐式读取 AI SDK 默认环境变量。

Scenario: 最简 LLM target 解析

  • WHEN 系统读取只包含 type: llm 以及 llm.providerllm.urlllm.modelllm.prompt 的 target
  • THEN 系统 SHALL 解析为 LLM target并填充 mode=httpkey=""ignoreSSL=falseoptions.maxOutputTokens=16options.temperature=0

Scenario: Anthropic Bearer token

  • WHEN target 配置 llm.provider: anthropic 和非空 llm.authToken
  • THEN LLM checker SHALL 将 authToken 映射到 Anthropic SDK 的 Bearer token 认证字段

Scenario: key 不隐式读取环境变量

  • WHEN target 未配置 llm.key
  • THEN LLM checker SHALL 将 SDK provider 的 api key 设置为空字符串,而不是隐式读取 SDK 默认环境变量

Requirement: LLM HTTP Metadata 与 TLS

LLM checker SHALL 通过 AI SDK provider 的 custom fetch 注入 observing fetch。observing fetch SHALL 调用 Bun fetch,在不消费 response body 的前提下记录 HTTP status、statusText 和 headers。llm.ignoreSSL: trueobserving fetch SHALL 仅对当前 target 的 provider 请求使用 Bun tls.rejectUnauthorized=false

Scenario: 捕获 HTTP metadata

  • WHEN AI SDK provider 发起模型 HTTP 请求并收到响应
  • THEN observing fetch SHALL 记录 status code 和响应 headersexpect.statusexpect.headers 使用

Scenario: 不消费响应体

  • WHEN observing fetch 捕获 HTTP metadata
  • THEN observing fetch SHALL 返回原始 response 给 AI SDK不提前读取或克隆消费 body

Scenario: 忽略证书校验

  • WHEN target 配置 llm.ignoreSSL: true
  • THEN observing fetch SHALL 对当前 target 的 provider 请求设置 tls.rejectUnauthorized=false

Requirement: LLM Observation

LLM checker SHALL 在 SDK 调用结果和 expect 断言之间构建 LlmCheckObservation。observation SHALL 包含 provider、model、mode、outputText、finishReason、rawFinishReason、usage、stream、http 和 warnings 中可观测的字段。mode: httpoutputText SHALL 来自 generateText.textmode: streamoutputText SHALL 来自 fullStreamtext-delta 的原始文本聚合。

Scenario: 非流式 observation

  • WHEN generateText 调用成功
  • THEN LLM checker SHALL 从 SDK result 中提取 outputText、finishReason、rawFinishReason、usage、response headers 和 HTTP metadata

Scenario: 流式 observation

  • WHEN streamText 调用成功且 stream 正常完成
  • THEN LLM checker SHALL 从 fullStream 聚合 outputText并记录 stream.completed、firstTokenMs、finishReason、rawFinishReason、usage 和 HTTP metadata

Scenario: APICallError observation

  • WHEN AI SDK 抛出带 statusCode 或 responseHeaders 的 APICallError
  • THEN LLM checker SHALL 构建包含可用 HTTP metadata 的 observation并继续执行可执行的 status、headers 和 duration 断言

Scenario: 无 HTTP metadata 的 SDK 错误

  • WHEN AI SDK 抛出不带 statusCode 和 responseHeaders 的错误
  • THEN LLM checker SHALL 返回 phase: "request" 的 error failure

Requirement: LLM Expect 断言

LLM checker SHALL 支持 expect.statusexpect.headersexpect.outputexpect.finishReasonexpect.rawFinishReasonexpect.usage.inputTokensexpect.usage.outputTokensexpect.usage.totalTokensexpect.stream.completedexpect.stream.firstTokenMsexpect.durationMsexpect.status SHALL 保持 HTTP 状态码数组语义并复用共享 status code 断言,未配置时在 Resolved expect 中物化默认 [200]expect.headers SHALL 使用共享 RawKeyedExpectations 输入并在运行期使用 KeyedExpectationsheader key 大小写不敏感。expect.output MUST 使用共享 RawContentExpectations 输入并在运行期使用 ContentExpectationsexpect.finishReasonexpect.rawFinishReason SHALL 使用共享 RawValueExpectation 输入并在运行期使用 ValueExpectationexpect.usage.*expect.stream.firstTokenMsexpect.durationMs SHALL 使用共享 RawValueExpectation 输入并在运行期使用 ValueExpectation。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
  • THEN LLM checker SHALL 在 Resolved expect 中使用默认 status: [200] 语义

Scenario: expect headers 通过

  • WHEN observing fetch 捕获的响应 headers 满足 expect.headers 配置
  • THEN LLM checker SHALL 通过共享 header expectation 包装函数判定 headers 断言通过

Scenario: output ContentExpectations 通过

  • WHEN LLM 输出文本满足 expect.output 中配置的全部 ContentExpectations
  • THEN LLM checker SHALL 判定 output 阶段通过

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 中某个较早顺序的断言失败
  • THEN LLM checker SHALL 立即返回该断言对应的 mismatch failure不继续执行后续断言

Scenario: 期望认证失败状态

  • WHEN AI SDK 抛出带 HTTP status 401 的 APICallError,且 target 仅配置 expect.status: [401]
  • THEN LLM checker SHALL 判定本次检查为 matched=true

Scenario: APICallError 缺失模型输出

  • WHEN AI SDK 抛出带 HTTP status 的 APICallError,且 target 同时配置需要模型结果的 expect.output
  • THEN LLM checker SHALL 因 outputText 缺失返回 phase: "output" 的 mismatch failure

Requirement: LLM Output 规则

LLM checker SHALL 使用共享 ContentRules 校验 expect.output。每个 output rule SHALL 为直接 ValueMatcher,或 jsoncssxpath extractor 规则之一。直接 matcher SHALL 作用于原始输出字符串。equals SHALL 对原始输出字符串做严格相等比较。contains SHALL 判断原始输出是否包含子串。regex SHALL 对原始输出执行无 flags 正则匹配。json SHALL 将原始输出解析为 JSON并用现有 JSONPath 子集和 ValueMatcher 校验提取值。json.equals SHALL 支持任意 JSON value。cssxpath 在 schema 层面可用,但 LLM 输出通常为纯文本或 JSON实际场景中仅 json 提取器有意义。

Scenario: 原始输出严格相等

  • WHEN outputText"OK\n" 且 target 配置 expect.output: [{ equals: "OK" }]
  • THEN LLM checker SHALL 判定 output 断言失败,因为 equals 不自动 trim

Scenario: output contains 通过

  • WHEN outputText 包含配置的子串
  • THEN LLM checker SHALL 判定该 output contains 规则通过

Scenario: output regex 通过

  • WHEN outputText 匹配配置的合法 regex
  • THEN LLM checker SHALL 判定该 output regex 规则通过

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 resolve 阶段 SHALL 将该 expectation 的 matcher 物化为 {exists: true},运行期按存在性语义执行

Scenario: output 规则按顺序快速失败

  • WHEN expect.output 包含多个 expectation 且第一条 expectation 失败
  • THEN LLM checker SHALL 返回第一条失败 expectation 的 mismatch failure不继续校验后续 output expectation

Requirement: LLM Stream 断言

LLM checker SHALL 仅允许 mode: stream 使用 expect.stream。仅当用户配置了 expect.stream 且未配置 expect.stream.completedresolve 阶段 SHALL 在 Resolved expect 中物化默认 completed: trueLLM checker MUST NOT 因为 llm.mode: stream 自动添加 stream.completed 断言。expect.stream.firstTokenMs SHALL 使用共享 RawValueExpectation 输入并在运行期使用 ValueExpectation,且仅统计第一个非空 text-delta 事件耗时,不统计 reasoning、tool call 或 source 事件。

Scenario: stream completed 默认值

  • WHEN target 配置 llm.mode: stream 且配置 expect.stream: {}
  • THEN resolve 阶段 SHALL 在 Resolved expect 中物化 stream.completed: true 并要求 SDK stream 正常完成

Scenario: 未配置 expect.stream 不添加 completed

  • WHEN target 配置 llm.mode: stream 但未配置 expect.stream
  • THEN resolve 阶段 SHALL NOT 自动添加 stream.completed 断言

Scenario: stream error

  • WHEN fullStream 产生 error part
  • THEN LLM checker SHALL 返回 phase: "stream" 的 failure

Scenario: firstTokenMs 达标

  • WHEN target 配置 expect.stream.firstTokenMs: {lte: 1000} 且首个非空 text delta 耗时满足 matcher
  • THEN LLM checker SHALL 判定 firstTokenMs 断言通过

Scenario: firstTokenMs 缺失

  • WHEN target 配置 expect.stream.firstTokenMs 但 stream 未产生非空 text delta
  • THEN LLM checker SHALL 返回 phase: "stream" 的 mismatch failure

Scenario: APICallError 不被默认 completed 阻断

  • WHEN mode: stream 的 SDK 调用在 stream 启动前抛出带 HTTP status 的 APICallError
  • THEN 默认 stream.completed=true SHALL NOT 阻断基于 status 和 headers 的 APICallError 状态探测

Requirement: LLM Failure Phase 与状态摘要

LLM checker SHALL 使用 requeststatusheadersstreamoutputfinishReasonrawFinishReasonusageduration 作为第一版 failure phase。成功结果的 API detail SHALL 从持久化 observation 动态构造,简短描述 provider、mode、HTTP status、finish reason、raw finish reason、first token、输出长度和 token usage 中可用的信息。observation 和 detail MUST NOT 写入完整 prompt、完整输出或 key。

Scenario: request failure

  • WHEN 模型请求因网络错误、认证调用异常、AbortSignal 或无 HTTP metadata 的 SDK 错误失败
  • THEN LLM checker SHALL 返回 phase: "request" 的 error failure

Scenario: output mismatch failure

  • WHEN 模型输出不满足 expect.output
  • THEN LLM checker SHALL 返回 phase: "output" 的 mismatch failure

Scenario: 非流式成功摘要

  • WHEN provider: openai 的非流式检查成功
  • THEN detail SHALL 使用类似 LLM openai http 200 finish=stop, output=2 chars, usage=12/2 tokens 的简短格式

Scenario: 流式成功摘要

  • WHEN provider: anthropic 的流式检查成功且存在 raw finish reason
  • THEN detail SHALL 使用类似 LLM anthropic stream 200 finish=stop raw=end_turn, firstToken=624ms, output=2 chars 的简短格式

Scenario: serialize 展示文本

  • WHEN store 同步 LLM target
  • THEN LLM checker serialize() SHALL 返回类似 openai:gpt-4o-mini @ https://api.openai.com/v1 的 target 展示文本和 resolved config JSON

Requirement: LLM Checker 测试策略

LLM checker 的自动化测试 MUST 不访问真实外部模型服务。测试 SHALL 使用本地 mock HTTP/SSE 服务模拟 OpenAI Chat Completions、OpenAI Responses 和 Anthropic Messages 的成功、错误和流式响应。测试 SHALL 覆盖 schema、语义校验、defaults 合并、变量替换、provider factory、observation、expect、execute、registry 注册、配置加载和 JSON Schema 导出。

Scenario: 本地 mock provider 测试成功路径

  • WHEN 测试运行 LLM checker 的 OpenAI、OpenAI Responses 和 Anthropic 成功路径
  • THEN 测试 SHALL 使用本地 mock 服务返回 provider 响应,不依赖外部网络或真实 API key

Scenario: 本地 mock provider 测试错误路径

  • WHEN 测试运行 401、429、500、超时、stream error、stream abort、缺 usage 或无文本输出路径
  • THEN 测试 SHALL 断言 LLM checker 返回符合 spec 的 matched、failure phase、actual、detail 和 observation

Scenario: 质量检查覆盖 LLM checker

  • WHEN 实现完成后执行质量检查
  • THEN bun run schema:checkbun run check SHALL 通过