基于 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
15 KiB
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返回的checkerTypesSHALL 包含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 使用现有
CheckResult、targets、check_results和 Dashboard API 结构,不新增 LLM 专用存储列或 Dashboard 指标字段
Requirement: LLM Provider 与调用模式
LLM checker SHALL 支持 openai、openai-responses、anthropic 三类 provider。mode: http SHALL 调用 AI SDK generateText。mode: 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/openai的openai.chat(model)创建模型调用对象
Scenario: OpenAI Responses provider
- WHEN target 配置
llm.provider: openai-responses - THEN LLM checker SHALL 使用
@ai-sdk/openai的openai.responses(model)创建模型调用对象
Scenario: Anthropic provider
- WHEN target 配置
llm.provider: anthropic - THEN LLM checker SHALL 使用
@ai-sdk/anthropic的anthropic.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.provider、llm.url、llm.model、llm.prompt、llm.mode、llm.key、llm.authToken、llm.headers、llm.ignoreSSL、llm.options 和 llm.providerOptions。llm.options SHALL 支持 maxOutputTokens(默认 16)、temperature(默认 0)、topP、topK、presencePenalty、frequencyPenalty、stopSequences(字符串数组)和 seed。llm.mode 默认值 SHALL 为 http,llm.key 默认值 SHALL 为空字符串,llm.ignoreSSL 默认值 SHALL 为 false。LLM checker MUST NOT 隐式读取 AI SDK 默认环境变量。
Scenario: 最简 LLM target 解析
- WHEN 系统读取只包含
type: llm以及llm.provider、llm.url、llm.model、llm.prompt的 target - THEN 系统 SHALL 解析为 LLM target,并填充
mode=http、key=""、ignoreSSL=false、options.maxOutputTokens=16、options.temperature=0
Scenario: headers 默认值合并
- WHEN
defaults.llm.headers和 targetllm.headers同时配置同名 header - THEN LLM checker SHALL 按原始 header key 浅合并 headers,并由 target
llm.headers覆盖 defaults 中同名 key
Scenario: options 默认值合并
- WHEN
defaults.llm.options和 targetllm.options同时配置同名 option - THEN LLM checker SHALL 浅合并 options,并由 target
llm.options覆盖 defaults 中同名字段
Scenario: providerOptions 默认值合并
- WHEN
defaults.llm.providerOptions和 targetllm.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: true 时,observing fetch SHALL 仅对当前 target 的 provider 请求使用 Bun tls.rejectUnauthorized=false。
Scenario: 捕获 HTTP metadata
- WHEN AI SDK provider 发起模型 HTTP 请求并收到响应
- THEN observing fetch SHALL 记录 status code 和响应 headers,供
expect.status与expect.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: http 的 outputText SHALL 来自 generateText.text。mode: stream 的 outputText SHALL 来自 fullStream 中 text-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.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。
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=true且failure=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 仅包含 equals、contains、regex 或 json 中的一种。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.stream。expect.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=trueSHALL NOT 阻断基于 status 和 headers 的 APICallError 状态探测
Requirement: LLM Failure Phase 与状态摘要
LLM checker SHALL 使用 request、status、headers、stream、output、finishReason、rawFinishReason、usage、duration 作为第一版 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
statusDetailSHALL 使用类似LLM openai http 200 finish=stop, output=2 chars, usage=12/2 tokens的简短格式
Scenario: 流式成功摘要
- WHEN
provider: anthropic的流式检查成功且存在 raw finish reason - THEN
statusDetailSHALL 使用类似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:check、bun run checkSHALL 通过