跳到主要内容

第二十节 — 测试体系

这一节回答:FinBayes 用什么测试体系覆盖业务正确性 / 子系统协作 / 端到端场景 / LLM 不确定性?

测试金字塔在 LLM 应用中的调整

传统软件的测试金字塔是 "单元 > 集成 > E2E",但 LLM 应用多一层评估测试(详见 CHAP-21)—— 因为 LLM 输出无确定预期,必须用统计指标而非断言。

这张图表达什么:从下到上数量递减,从下到上跨越的范围递增。评估测试单独一层是 LLM 应用的特殊性,不能用传统 assert 覆盖(详见 CHAP-21)。

这张图特意不表达什么:每层具体覆盖率指标(在工程实现仓 CI 配置);性能基准测试(在 CHAP-21 评估体系)。


单元测试

覆盖范围

类别覆盖对象
业务对象逻辑Session / Watchlist / Judgment Record / Profile / Fin Object / StateCandidate 的方法
状态机转移CHAP-11 的 5 个状态机的每个转移
工具实现每个工具的纯函数部分(不依赖外部 API)
数据契约校验Pydantic schema 的字段级约束
Provider Adapter 内部归一化OpenAI-compatible 转换逻辑(不真调 Provider)
输入边界 hook 规则凭证识别正则的命中率(CHAP-17)
综合层输出 schema 校验含反方 / 风险 / 失效条件字段的强约束
错误处理各种错误码 / 异常路径

关键约束

  • 单元测试不调真 LLM / 不调真外部 API(速度 + 成本 + 稳定性)
  • 单元测试不读真 State Store(用内存 SQLite 或 tmpdir)
  • 单元测试不依赖时间(用 freeze_time 等手段冻结时间)
  • 每个单元测试 <100ms

LLM 相关的单元测试

LLM 相关的纯函数也走单元测试:

测试对象测试内容
Prompt 模板渲染给定输入变量,渲染出预期 Prompt 文本
LLM 响应解析给定 mock 响应(含异常格式),解析逻辑健壮
Function Calling schema 生成给定工具池,生成的 schema 符合 OpenAI 规范
多轮上下文压缩给定长上下文,压缩后保留关键信息 + 长度合规

集成测试

覆盖范围

类别覆盖对象
子系统间协作CHAP-09 各子系统接口 + CHAP-10 各 sequence 流转
State Store 完整 CRUD真 SQLite(tmpdir 隔离)+ 事务原子性
Provider Adapter Poolmock LLM Provider + 4 层降级链触发
Capability Registry工具注册 + LLM Function Calling mock + 工具调用
Cache真 Redis(容器化)或内存 LRU
审计 trail 写入所有事件类型按预期写入
并发任务执行asyncio.TaskGroup + 失败隔离 + 取消(CHAP-12)

Mock 策略

真 / Mock对象
State Store / Cache / 内部子系统
MockLLM Provider(用 fixture 录制响应 + 重放)/ 外部数据 Provider(用 fixture)

LLM Mock 是集成测试的关键工程,下文专门讨论。

关键约束

  • 集成测试单次跑应该 <30 秒(不可被开发者跳过)
  • 每个子系统至少有 3 个跨子系统的协作场景测试

端到端测试

覆盖范围

CHAP-06 列举的 9 个关键业务场景每个对应至少一组端到端测试:

场景端到端测试核心断言
S1 即时认知请求首屏 <X 秒 + 流式输出完整 + 含反方与失效条件
S2 状态确认候选 → 用户确认 → 持久化到 Watchlist
S3 复盘历史 Judgment 引用 + 综合层输出含"信息缺口"
S4 关注流主动信号触发 → Channel 通知投递
S5 长 Session 上下文多轮后压缩 + 关键 Judgment 保留
S6 边界拒收凭证凭证类输入识别 + 不进入 Task / LLM / 状态对象 / 审计 trail
S7-S9(按当前任务类型清单)业务约束按 CHAP-06 描述

LLM 调用策略

端到端测试分两档:

LLM 调用何时跑
快档Mock LLM(录制 fixture)每次 PR / CI
真档真 LLM(用低成本 Provider,如 DeepSeek)nightly / release 前

约束:快档必须每次 CI 跑(覆盖业务流转);真档跑发现的真实 LLM 问题用于评估闭环(CHAP-21)。

数据隔离

  • 端到端测试绝不用用户真实 State Store
  • 每次端到端测试在 tmpdir 起独立 State Store + Cache
  • 测试结束清理(含 OS Keychain 中的测试凭证 —— 用专属测试命名空间)

LLM Mock 工程

LLM Mock 是 FinBayes 测试体系的关键 —— 既要快、便宜、稳定,又要逼真。

三种 Mock 模式

模式描述用途
录制重放真实跑过一次,把请求/响应 fixture 化集成 + 端到端快档
手工 fixture工程写死的固定响应(如"返回工具调用 X")单元测试 / 特定路径覆盖
简化模型 stub用简单规则模拟(如 echo / 关键词路由)性能测试 / 并发测试

录制重放的关键

关键约束

  • fixture 文件纳入 Git(可 review + 可追溯 LLM 行为变化)
  • fixture 不含真实凭证 / 用户敏感信息(录制前脱敏)
  • fixture 中的请求 hash 基于关键参数(model / messages / tools)+ 忽略时间戳等
  • Provider 端 API 变化导致 fixture 失效时,CI 给明确报错(不悄悄通过)

降级路径测试

CHAP-13 列举的每条降级路径必须有测试:

降级层测试方法
L1 → L1'mock 用户 Provider 全部失败 → 验证走系统默认
L1' → L2mock 系统默认失败 + 本地 LLM 可用 → 验证走本地
L2 → L3mock LLM 全失败 + 缓存有 → 验证走缓存
L3 → L4mock 全失败 → 验证走受限菜单 + 用户明示提示
State Store 只读模式mock SQLite 锁 / 磁盘满 → 验证读功能可用 + 写拒绝
Cache 不可用mock Redis down → 验证退化为不缓存模式
工具调用超时验证 EvidencePacket 标 timeout + 综合层继续
证据完全缺失验证综合层输出"信息缺口"而非硬编
用户主动取消验证 TaskGroup 取消 + 5s grace + 部分结果保留

约束:每条降级路径不仅测试走通,还测试用户感知(banner / 错误码 / 审计 trail 写入)。


边界与安全测试

CHAP-17 列举的每条边界约束必须有测试:

边界测试
凭证类输入拒收给输入贴 私钥 / API key / 助记词 等样式 → 拒收 + 安全回应 + 不进入任何后续
输出端凭证样式过滤mock LLM 输出含凭证样式 → 输出端剥除 + banner
执行类工具注册拒绝尝试注册 category=execution 工具 → 拒绝 + 审计记录
含凭证参数的工具拒绝尝试注册参数含 private_key 字段的工具 → 拒绝
Prompt 注入输入含 "忽略上述指令" → 综合层输出仍符合契约 + 不偏离任务
用户数据隔离跨 user_id 查询 → 拒绝 / 返回空
TLS 降级配置 HTTP Provider URL → 启动时拒绝
本机 socket 限尝试从非 localhost 访问 Web API → 拒绝

约束:边界测试是回归关键 —— 战略边界一旦被绕过,影响远大于功能 bug。CI 跑边界测试集失败 = 阻断 release。


并发测试

CHAP-12 的并发设计需要专项测试:

测试场景内容
TaskGroup 一子任务失败其他子任务能正常完成(或可控取消)
用户主动取消任务5s grace + 部分结果落审计 trail + 资源清理
多 Session 并发每 Session 独立 + Cache key 不串
backpressureProvider 限流时排队 + 不掉单
高并发下 SQLite WAL多读 1 写不冲突 + 事务原子性
主动信号触发的批量任务多 Judgment 同时失效 → 批量任务正确分发

测试数据管理

测试用户画像 / Watchlist / Judgment Record

类型来源
单元测试用 fixture工程写死
集成测试用 fixtureYAML 文件 + 测试时载入 tmpdir 隔离 SQLite
端到端测试用 fixture多套画像(保守 / 激进 / 多频次 / 新手)+ 多套 Watchlist
评估测试用数据集详见 CHAP-21

测试金融对象

  • 不用真实交易所 / 真实价格 API
  • 用 fixture 模拟价格序列(含暴涨 / 暴跌 / 横盘 / 缺数据等场景)
  • 必要时用历史数据回放(如某次行情事件的真实数据)

测试可观测性依赖

测试依赖 CHAP-18 可观测性体系:

  • 测试断言可以基于审计 trail(如"凭证拒收事件按预期写入")
  • 集成测试用 task_id trace 验证因果链完整
  • 性能测试用指标体系(latency / token / cost)做基线对比

CI 集成

触发跑什么
每次 PR单元 + 集成 + 端到端快档(Mock LLM)+ 边界测试集 + verify-kb
nightly全部 + 端到端真档(真 LLM,低成本 Provider)+ 评估测试(详见 CHAP-21)
release 前nightly 全跑 + 完整评估数据集 + 性能基线对比

约束

  • 每次 PR 测试不超过 15 分钟(开发者反馈循环)
  • nightly 测试不超过 1 小时(含真 LLM 调用)
  • 测试报告进入审计 trail(哪次测试失败 / 哪条 fixture 过期 / 等)

第一阶段不做的测试能力

明示不做:

  • 跨平台兼容性矩阵测试(仅主流 macOS + Linux + Windows 各一基线)
  • 浏览器 UI 自动化测试(第一阶段 Web UI 仅做最小可用,重点在 CLI / TUI)
  • 性能回归基准跑(评估闭环里有,但不在 CI 跑)
  • 模糊测试(fuzzing)—— 可演化能力

与其他章节的关系

  • 测试覆盖的场景来源 → CHAP-06 关键业务场景
  • 测试覆盖的子系统接口 → CHAP-09 子系统组件
  • 测试覆盖的流转 → CHAP-10 关键场景流转图
  • 测试覆盖的状态机 → CHAP-11 状态对象生命周期
  • 并发测试覆盖的设计 → CHAP-12 并发与异步处理
  • 降级路径测试覆盖 → CHAP-13 故障与降级路径
  • 边界测试覆盖 → CHAP-17 边界与安全
  • 测试依赖的可观测性 → CHAP-18 可观测性
  • 评估测试详见 → CHAP-21 评估闭环