site logo

Marico's space

今日记录 LLM 输出,明日以新模型重演。

编程技术 2026-05-29 20:55:56 3

最近折腾了 prompt-replay,这是一个帮你记录 LLM 调用结果、在升级模型后回放对比的库。踩了几个坑,这篇把核心用法说清楚。

你升级了模型,通义千问 2.1 到通义千问 2.5。新模型更快、更便宜、更聪明。你在周五下午上线。

周一来了一个 bug。用户反馈:负责汇总周报的智能体输出的 JSON 字段名变了,下游解析器静默出错。仪表盘直接空白了。

你没有任何测试覆盖这种情况。你根本不知道输出格式会漂移。模型没有报错,它只是"换了个想法"给字段起了不同的名字。

这正是 prompt-replay 要解决的问题。

The shape of the fix

这个库有两个核心组件:Recorder 和 Replayer。你在录制阶段用 @capture 装饰器包装 LLM 调用,然后换上新模型重放这些历史 prompt,对比结果差异。

from prompt_replay import Recorder, Replayer, capture
import anthropic client = anthropic.Anthropic() # Step 1: record a session
recorder = Recorder(session_path="sessions/weekly_summary.jsonl") @capture(recorder)
def call_llm(prompt: str) -> str: response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": prompt}] ) return response.content[0].text # Run your normal workflow. @capture records every call.
with recorder: result = call_llm("Summarize this week's activity: ...")

录制完会话之后,换上新的模型配置,然后重放:

# Step 2: replay with a new model config
def new_llm(prompt: str) -> str: response = client.messages.create( model="claude-sonnet-4-6", # new model max_tokens=1024, messages=[{"role": "user", "content": prompt}] ) return response.content[0].text replayer = Replayer( session_path="sessions/weekly_summary.jsonl", fn=new_llm, diff_mode="json_diff" # or "exact" or "semantic"
) report = replayer.run() for diff in report.diffs: print(diff.summary())

三种 diff 模式:

  • exact:纯字符串相等。适用于必须返回固定字符串的 prompt。
  • json_diff:把两个响应都解析成 JSON 后对比结构。适用于输出结构化数据的智能体。
  • semantic:计算 embedding 之间的余弦距离。需要你自己提供 embedder。适用于自然语言摘要类场景,逐字对比太严格了。

semantic 模式接受任意可调用对象,签名是接收字符串返回浮点数列表:

replayer = Replayer( session_path="sessions/weekly_summary.jsonl", fn=new_llm, diff_mode="semantic", embedder=my_embed_fn, # any function: str -> list[float] threshold=0.05 # flag if cosine distance exceeds this
)

What it does NOT do

  • 它不会重新执行工具调用。如果你的智能体在录制期间调用了 get_weather("北京"),重放时不会再次调用那个工具。它只是重放 prompt,把新 LLM 的响应和录制的响应做对比。原因后面会详细说。
  • 它不会 mock 你的 LLM 客户端。你传入一个真实的可调用对象。这样库就与模型提供商无关,不用绑定任何 SDK。
  • 它不会自动断言。replayer.run() 返回一个报告,怎么处理由你决定:打印出来、写文件、或者在 diff 超过阈值时抛异常。
  • 它不管理 prompt 版本。它只记录你发送的内容。如果你想单独管理 system prompt 的版本,可以配合 prompt-template-version 一起用。

Inside the lib: why tool calls are out of scope

这是经过深思熟虑的设计决策,也是我花最多时间考虑的部分。

录制会话时,工具调用是对话历史的一部分。它们会出现在录制的消息里。重放时,这些工具调用结果会作为上下文存在,和原始运行时一样。Replayer 把同样的对话历史发给新模型,然后等待响应。

它不会重新调用工具。

原因很简单:在测试重放过程中重新执行工具可能会调用生产系统,这很危险。send_emailcreate_ticketupdate_databasecharge_card。这些操作执行两次可不是闹着玩的。如果重放测试工具包自动重新触发有副作用的工具,会造成真实的损失。

所以契约是:工具结果是冻结历史的一部分。模型根据这个历史给出响应。你在测试的是模型的响应有没有变化,而不是工具是否正常工作。

如果你的工作流重度依赖工具调用,而且真正关心的输出是工具调用序列而不是最终文本,那这个库不是你的菜。但如果真正关心的输出是模型在相同上下文和工具结果下的最终响应,这就是为你量身定做的。

# Internally, the Replayer reconstructs the conversation like this:
# [system, user_msg, tool_use, tool_result, user_msg, ...]
# It sends that full history to the new model and captures the response.
# The tool_result entries are from the original session. No tools are called.

When this is useful

  • 你正在升级模型版本,想在上线前知道哪些智能体输出变了。
  • 你准备重写 prompt,想要一个基准来对比新版本。
  • 你在给 system prompt 加新指令,想看看下游 JSON 结构有没有漂移。
  • 你想在 CI 里跑回归测试,但又不想每次 PR 都付全价 LLM 调用费。录一次,重放便宜得多。

When this is NOT what you want

  • 你的智能体正确性完全取决于工具调用序列,而不是最终的文本响应。库比较的是文本响应,不是行为序列。
  • 你需要测试多轮对话,其中每轮用户输入依赖助手上一轮说的话。重放会原封不动发送录制的历史,不会模拟实时的来回交互。
  • 你想要完整的集成测试,让真实工具真正触发。用 staging 环境做这个。这个库是用来对比模型输出的,不是验证工具执行的。

Install

pip install prompt-replay

零依赖。没有捆绑任何 LLM SDK。自己带客户端。

GitHub: MukundaKatta/prompt-replay

50 个测试用例,全部通过。

Sibling libraries

边界 仓库
agentsnap 智能体工具调用轨迹的快照测试 MukundaKatta/agentsnap
cachebench Prompt 缓存命中率的监控 MukundaKatta/cachebench
agent-decision-log WHY 层:记录选了哪个选项以及为什么 MukundaKatta/agent-decision-log
agenttrace 每次智能体运行的成本和延迟追踪 MukundaKatta/agenttrace

agentsnap 是最相近的兄弟库。它快照工具调用序列。prompt-replay 快照模型产生的文本。它们覆盖智能体输出的不同部分。

What is next

计划里有这么几件事:

  • 一个 CLI 用于快速单次重放,不用写 Python:prompt-replay replay sessions/foo.jsonl --fn my_fn.py --diff json_diff
  • 一个基于阈值的断言辅助函数,让 CI 能自动失败:report.assert_no_regressions(threshold=0.1)
  • 支持用 Pydantic 模型做结构化输出对比,不只是原始 JSON diff

核心循环——录、重放、对比——是稳定的。50 个测试覆盖了三种 diff 模式、@capture 装饰器的边界情况,以及对话历史重建逻辑。

原文链接:https://dev.to/mukundakattamada/prompt-replay-record-llm-outputs-today-replay-against-a-new-model-tomorrow-3l78