基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
简历
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-07 00:32

用模型调用录制回放稳定 AI 测试

dev.to · AI#自动化测试#大模型#成本优化
Editor brief · 编辑速览

文章提出将模型调用保存为本地 JSON,以模型、提示词和温度等输入的哈希匹配回放,减少测试中的随机波动与重复调用成本。方案包含 Pytest fixture、CI 命令和缓存未命中排查,但示例代码尚未运行验证。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

改一行提示词,跑出几十次模型调用,失败的测试却每次不同:到底是代码坏了,还是模型换了个回答?作者给出的办法是把模型响应录成 JSON,日常测试直接回放,让排查过程有一份固定证据。

这套方案适合验证 AI 应用如何处理回答,不能证明模型今天仍然可靠。更需要先说明:原文代码只是草图,作者明确表示没有连接真实接口验证,也没有提供性能或稳定性对照数据。

为什么要把模型回答录下来

模型调用混进单元测试后,一次失败可能有两种来源:程序逻辑出了问题,或者外部服务返回了不同内容。重新运行也未必能帮助定位,还会继续消耗 token 和时间。

作者借用的是录制文件(cassette)的思路:首次请求真实模型,把响应存进 cassettes/,之后遇到相同输入便读取文件。它像给外部依赖留下一份样本,方便反复检查自己的代码。

回放固定的是测试输入,不能固定线上模型的行为。

整个周末项目只有一条范围约束:不能缩短「录制、回放」循环的功能,先不做。因此没有 embeddings、仪表盘或自动重新录制,只有 JSON 文件和一个默认拒绝联网的调用入口。

缓存键决定了哪些调用算同一次

录制能否可信,先看键怎么生成。作者要求只纳入会改变模型行为的输入,草图选择了模型、提示词和温度:

def key_for(model: str, prompt: str, temperature: float) -> str:
    payload = json.dumps(
        {"model": model, "prompt": prompt, "temperature": temperature},
        sort_keys=True,
    )
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]

要看的是 sort_keys=True 和最后的 [:16]:前者固定 JSON 字段顺序,后者截取 SHA-256 的前 16 个十六进制字符。实际请求若还有影响输出的参数,也必须进入键,否则不同请求可能错误复用同一份回答。

作者建议哈希前规范化提示词空白,并锁定精确的模型字符串。不过,代码没有实现空白规范化;这一步也应谨慎,代码缩进等空白可能具有语义。

模型别名还有另一层风险:别名字符串不变,背后的模型却可能变化。此时键不会改变,旧录制仍会命中,回放更无法发现服务端变化。原文把别名漂移与录制失效联系起来,具体机制说得不够准确。

怎样让测试彻底停止偷偷联网

作者把调用分成 record、replay、off 三种模式,CASSETTE_MODE 默认是 replay,CASSETTE_DIR 默认是 cassettes。下面的入口负责决定读文件还是调用模型:

def call(model: str, prompt: str, temperature: float = 0.0) -> str:
    key = key_for(model, prompt, temperature)
    path = CASSETTE_DIR / f"{key}.json"

    if MODE == "replay":
        if not path.exists():
            raise CassetteMiss(key, prompt)
        return json.loads(path.read_text(encoding="utf-8"))["text"]

    text, usage = live_call(model, prompt, temperature)  # your existing client

    if MODE == "record":
        CASSETTE_DIR.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps({
            "key": key,
            "model": model,
            "prompt_sha": hashlib.sha256(prompt.encode()).hexdigest(),
            "text": text,
            "usage": usage,
            "recorded_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        }, indent=2), encoding="utf-8")

    return text

最重要的是 raise CassetteMiss(key, prompt):文件不存在就报错,不能自动退回真实请求。live_call 则是需要接入的现有客户端,并非完整实现。

录制使用 temperature 0,保存响应及 usage,便于之后查看 token 用量;温度设为零也不等于服务端输出绝对可复现。作者给出的录制命令是 CASSETTE_MODE=record pytest tests/test_prompts.py -x,随后用 git add cassettes/ && git commit -m "record prompt cassettes" 提交样本。

CI 使用 CASSETTE_MODE=replay pytest -q,运行器不配置 API key。真正禁止网络还需要运行环境约束,代码里的模式判断并不是网络隔离措施。

原文还用 pytest 的自动 fixture,通过 monkeypatch.setenv 设置回放模式。但 MODE 在模块导入时就读取环境变量,模块若提前加载,fixture 未必有效。把模式放在启动命令里设置,更符合这份草图的实际行为。

遇到缺失录制,按提示词、模型字符串、温度及其他参数依次比较。若三类输入都一致仍未命中,就检查键是否漏了输入。重新录制后要像代码一样审查 JSON 差异,原文用一个 200 行差异的例子提醒:回答变化也可能暴露此前没被发现的提示词修改。

我的判断

我赞成把应用回归测试和模型在线检查分开。前者需要稳定证据,后者需要接触真实服务。固定回答尤其适合检查解析、字段处理和提示词组装;相比手写 mock,录制能保留真实回答的形态,但也需要人工审查样本。

边界不能省。开放式生成宜断言结构、字段是否存在及长度范围,不宜绑定整段措辞。作者建议每晚保留 5 次真实调用的小型冒烟测试,用来发现服务端漂移;这个数量是经验建议,没有覆盖率依据。代码也没有展示如何录制超时、限流等错误,不能据此认定异常路径已经覆盖。

录制前还要在写文件的边界脱敏,仓库会被克隆,JSON 样本同样会传播。需要真实延迟数据、主要担心供应商回归,或数据规则禁止第三方推理的产品,都不能靠这套方案解决核心问题。只有 3 个提示词的小项目,作者认为直接在线跑更省事。

原文也披露了 MonkeyCode 产品推广合作,并提及其免费模型访问和免费服务器选项。作者没有实测,额度、条款和硬件条件也未明确,不能当作已验证的 CI 方案。先试 10 次调用而非 100 次,是控制试验规模的建议,不是免费能力的保证。

能拿来做什么

  • 如果你的 Node 服务要把模型回答解析成业务字段,可以录制少量真实样本,反复验证字段缺失、短回答等情况下的处理逻辑,并优先断言数据结构。
  • 把更新录制设为明确的维护动作,CI 默认回放且缺失即失败;审查提示词修改时,一并检查对应 JSON 的变化。
  • 为模型升级或供应商变化保留独立的在线冒烟任务,同时在录制写盘前脱敏。作者还建议后续增加 --strict,遇到未提交的录制变化就失败,但原文尚未实现这个参数。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
给智能体设上限:一次最多改多少
下一篇
Spring AI 用类型化接口处理分类决策

资讯速览

dev.to · AI · 2026-11-07 00:32

AI 评分 8.0 / 10

相关标签

#自动化测试#大模型#成本优化

继续阅读

  • 智能体评测何时停?先定规则再跑分
  • 给智能体设上限:一次最多改多少
  • Spring AI 用类型化接口处理分类决策
  • 多窗格编程智能体终端的架构与难点
  • 为编程助手添加跨会话偏好记忆