1
0
Files
DiAL/openspec/specs/llm-checker/spec.md
lanyuanxiaoyao 349896bd02 feat: 新增 LLM checker 支持大模型服务应用层拨测
基于 AI SDK v6 实现 openai/openai-responses/anthropic 三类 provider 的 http/stream 模式调用
支持 output/finishReason/usage/stream 等完整 expect 断言链路
新增 9 个源文件和 5 个测试文件共 78 个测试
更新 README/DEVELOPMENT/probes.example.yaml 和 probe-config.schema.json
2026-05-19 00:06:53 +08:00

15 KiB
Raw Blame History

Purpose

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

Requirements

Requirement: LLM Checker 注册与模块结构

系统 SHALL 提供 type: llm checker用于大模型服务的应用层拨测。LLM checker MUST 位于 src/server/checker/runner/llm/ 自包含目录,并通过 src/server/checker/runner/index.ts 注册到 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 开发者查看 src/server/checker/runner/llm/ 目录
  • 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: headers 默认值合并

  • WHEN defaults.llm.headers 和 target llm.headers 同时配置同名 header
  • THEN LLM checker SHALL 按原始 header key 浅合并 headers并由 target llm.headers 覆盖 defaults 中同名 key

Scenario: options 默认值合并

  • WHEN defaults.llm.options 和 target llm.options 同时配置同名 option
  • THEN LLM checker SHALL 浅合并 options并由 target llm.options 覆盖 defaults 中同名字段

Scenario: providerOptions 默认值合并

  • WHEN defaults.llm.providerOptions 和 target llm.providerOptions 同时配置同名 provider namespace
  • THEN LLM checker SHALL 按 provider namespace 浅合并 providerOptions并由 target namespace 覆盖 defaults 中同名 namespace

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.maxDurationMsexpect.statusexpect.headers 的运行期断言 SHALL 复用 src/server/checker/runner/http/expect.ts 中的 checkStatuscheckHeaders 函数。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、duration流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、duration。

Scenario: 默认 status 断言

  • WHEN LLM target 未配置 expect.status
  • THEN LLM checker SHALL 使用默认 status: [200] 语义

Scenario: expect headers 通过

  • 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: 全部 expect 通过

  • WHEN LLM checker 构建出的 observation 满足所有已配置 expect
  • THEN 检查结果 SHALL 为 matched=truefailure=null

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 支持 expect.output 有序规则数组,每个规则 MUST 仅包含 equalscontainsregexjson 中的一种。equals SHALL 对原始输出字符串做严格相等比较。contains SHALL 判断原始输出是否包含子串。regex SHALL 对原始输出执行正则匹配。json SHALL 将原始输出解析为 JSON并用现有 JSONPath 子集和 operator 校验提取值。

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 匹配配置的合法正则
  • THEN LLM checker SHALL 判定该 output regex 规则通过

Scenario: output JSONPath 通过

  • WHEN outputText 是 JSON 字符串且 JSONPath 提取值满足 operator
  • THEN LLM checker SHALL 判定该 output json 规则通过

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

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

Requirement: LLM Stream 断言

LLM checker SHALL 仅允许 mode: stream 使用 expect.streamexpect.stream.completed 未配置时LLM checker SHALL 在 stream observation 路径使用默认 true 语义。expect.stream.firstTokenMs SHALL 仅统计第一个非空 text-delta 事件耗时,不统计 reasoning、tool call 或 source 事件。

Scenario: stream completed 默认值

  • WHEN target 配置 llm.mode: stream 且未配置 expect.stream.completed
  • THEN LLM checker SHALL 要求 SDK stream 正常完成

Scenario: stream error

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

Scenario: firstTokenMs 达标

  • WHEN target 配置 expect.stream.firstTokenMs 且首个非空 text delta 耗时满足 operator
  • 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。成功结果的 statusDetail SHALL 简短描述 provider、mode、HTTP status、finish reason、raw finish reason、first token、输出长度和 token usage 中可用的信息。statusDetail 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 statusDetail SHALL 使用类似 LLM openai http 200 finish=stop, output=2 chars, usage=12/2 tokens 的简短格式

Scenario: 流式成功摘要

  • WHEN provider: anthropic 的流式检查成功且存在 raw finish reason
  • THEN statusDetail 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 和 statusDetail

Scenario: 质量检查覆盖 LLM checker

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