同一个问题问两遍结果差别不小,排查顺序建议从输入侧开始:先把两次调用送进去的东西原样落盘,逐字比对提示词、上下文条数、生成参数是否真的相同;输入确认相同却仍不一致,再单独只动上下文长度做阶梯对比,看差异是否随上下文变长而出现。只在对话界面里“再问一次”来对比,通常分不清是提示词没定型,还是长上下文带来的漂移,而这两种情况对应的处理动作并不一样。
判断方向是先把变量固定住,再判断差异的性质。建议把提示词全文、上下文条数、字符数、调用时间记进同一张表,把提示词与生成参数抽到配置文件后连续调用三次;再只改上下文长度,做一次全量历史与只带最近若干轮的阶梯对比。如果输入逐字相同仍有差异,那属于模型侧的非确定性,处理重点应转为统一复现口径和输出约束,而不是反复重写一版新提示词。
把两次回答的完整输入原样记录下来并逐字比对
这一步只解决一个问题:两次调用送进去的内容是不是真的相同。人在界面里重问,很容易在没察觉时改了标点、少贴一段材料,或者把上一轮回答又粘回了上下文。
建议按下面这张表记录,每次调用一行。提示词全文可以存成独立的 txt 文件,表里写文件名和首行摘要,避免长文本把表格撑坏;上下文条数和字符数从请求体或调用日志里取,不要凭印象填。
| 编号 | 调用时间 | 提示词全文(文件路径 / 首行摘要) | 上下文条数 | 上下文字符数 | 疑似不一致字段 |
|---|---|---|---|---|---|
| A 第一次 | 以日志时间为准 | prompt_a.txt | 从请求体读取 | 从请求体读取 | — |
| B 第二次 | 以日志时间为准 | prompt_b.txt,与 A 逐字 diff | 从请求体读取 | 从请求体读取 | 比对后填写 |
如果 A、B 两行的提示词文件内容、上下文条数、字符数三项都一致,可以先接受“输入相同”这个前提,进入下一步;任何一列对不上,就把它当作差异来源处理,而不是先怀疑模型。
把提示词和生成参数抽到配置文件后连续跑同一问题
排除手改输入造成的差异,做法是把提示词和生成参数从界面输入框里抽出来放进配置文件,程序只读配置,不读临时拼接的字符串。下面是一段通用骨架,字段名以你所调接口的实际文档为准,不同接口对采样参数、最大输出长度的叫法并不统一,不要照搬别人示例里的字段名。
# 通用调用骨架,字段名以实际接口文档为准
cfg = load_config('call_config.yaml') # prompt_template / context_turns / generation_params
def run_once(cfg, question, history):
payload = {
'prompt': cfg.prompt_template, # 提示词全文,程序不再手改
'context': history[-cfg.context_turns:], # 上下文条数由配置决定
# 生成参数按接口文档字段名展开,不在这里临时改
**cfg.generation_params,
}
log_request(payload) # 落盘:调用时间、上下文条数、字符数
return call_api(payload)
for i in range(3):
save(f'run_{i}.txt', run_once(cfg, QUESTION, HISTORY))
对应的配置片段可以这样写,替换项只有提示词正文和参数名:
# call_config.yaml 示例
prompt_template: |
你是一名技术助手,只依据给定材料回答。
输出字段:结论、依据、待确认项。
context_turns: 8 # 阶梯对比时只改这一行
generation_params: # 具体参数名以接口文档为准
...
验证方式是同一问题连续调用三次,把三份输出逐条比对:先比事实字段,再比结构字段,最后才比措辞。三次的请求体也要一起留存,确认除可预期的随机性外,送进去的内容没有变化。
只改上下文长度、其余不动,做阶梯对比
如果配置文件版本下连续三次仍然不一致,下一步只改一个变量:上下文长度。同一问题准备两份输入,一份带全量历史,一份只带最近若干轮,其余配置一个字都不动,各跑一遍,并记录差异出现的位置——是开头结论就变了,还是中段引用材料出错,或者只是结尾补充说明不同。
| 输入形态 | 上下文条数 | 上下文字符数 | 差异出现位置 | 初判类型 |
|---|---|---|---|---|
| 全量历史 | 从请求体读取 | 从请求体读取 | 结论段 / 依据段 / 结尾段 | 措辞 / 事实 / 结构 |
| 只带最近若干轮 | 从请求体读取 | 从请求体读取 | 结论段 / 依据段 / 结尾段 | 措辞 / 事实 / 结构 |
差异出现的位置往往比差异数量更有用。如果错乱或前后矛盾只在长上下文那组出现,优先怀疑上下文里混进了过期材料、重复段落或相互冲突的指令;如果两组都在同一处出错,提示词或生成参数的可能性更大。
把差异归类成措辞漂移、事实变化、结构变化三类
分类是为了避免看到不一致就去改提示词。三类的判定线索和下一步动作并不相同。
| 类型 | 判定线索 | 下一步动作 |
|---|---|---|
| 措辞漂移 | 实体、数值、结论都能对齐,只是同义改写、语序或称呼不同 | 先不改提示词,可在提示词里固定术语表和输出格式;如果业务方可接受同义改写,按稳定处理 |
| 事实变化 | 两次出现不同的数值、日期、条件或结论 | 检查上下文里是否携带了不同来源片段;把事实来源固定进上下文,并要求只依据给定材料作答 |
| 结构变化 | 字段缺失、段落顺序变了、格式解析失败、长度突变 | 检查输出约束是否写在提示词靠后位置、是否给了格式示例;用校验脚本判定,不靠肉眼 |
写一条可接受的复现标准放进使用备注
给这类调用写一条使用备注,下次判断不一致时按同一口径执行,避免每次凭感觉决定要不要继续调提示词。可以这样写:
同一提示词全文、同一上下文条数、同一生成参数下,连续调用 3 次;
比较维度按“事实字段 → 结构字段 → 措辞”的顺序;
3 次中事实字段与结构字段一致、措辞差异属于可接受改写,记为稳定;
若事实或结构每次不同,先把上下文降到只带最近若干轮再跑一组;
仍不稳定时,在备注中标注为不确定输出,调用方按需增加校验或人工确认。
这条备注建议和应用配置放在一起,改动提示词或上下文策略时同步更新,否则下次再遇到不一致,又会回到“是提示词没固定还是上下文太长”的原点。