AI自动化测试中,“预期结果”本身可能是隐藏的bug

小Max爱学习 高级 2026/9/7 196 浏览 13 点赞 约 2 分钟

在公司落地AI自动化测试的过程中,我踩过最隐蔽的坑,就是盲目笃信自己写的预期结果。

之前跑集成测试时我搭了个格外严格的校验规则:模型启动前,测试框架会先生成一份预期的exec调用对象并冻结,要求模型执行时原封不动发送对应JSON,只要参数和冻结对象有半点出入,测试直接判定为EXEC_ARGUMENTS_MISMATCH失败。

跑RUN_004这个用例时模型直接被标记为异常,我头一个反应是模型又擅自发挥,没遵守指令。

但拆解问题根源后我发现,出问题的根本不是模型,是我定义的“预期”本身就有误。

当时测试代码是这样写的:

const expectedExecArguments = validateExecArguments({
 command: `node ${artifactPath('verifier')} ${artifactPath('payload')} ${artifactPath('manifest')}`,
});

这段逻辑里,我的预期对象只有一个command键。但模型实际发出来的JSON是这样的:

{"intent": "Run candidate verification", "command": "node /opt/tf/uploads/candidate-verifier.cjs /opt/tf/uploads/candidate-payload.json /opt/tf/uploads/candidate-command-manifest.json"}

它多了一个intent字段,按我的校验逻辑这肯定是mismatch。但我一开始还惯性把这归结为“模型不听话”,直到翻开@truefoundry/[email protected]的源码,看了dist/core/sandbox/Sandbox.mjs里的sandboxExecSchema定义:

var sandboxExecSchema = z.object({
 intent: z.string().describe(
 "A brief, user-facing explanation of the purpose of this command. Avoid technical jargon and code details."
 ),
 command: z.string().describe("The shell command to execute in the sandbox."),
 cwd: z.string().optional().describe("Working directory for command execution."),
 env: z.record(z.string(), z.string()).optional().describe("Additional environment variables to set.")
});

真相很讽刃:在这个运行环境下,intent和command都是必填项。这意味着如果模型真的按我那个只有command的JSON发送,根本过不了沙箱的Schema校验,调用会直接报错。

模型面对“我的指令(只要command)”和“运行时的强制要求(必须有intent)”这两个冲突的规则时,聪明地选择了后者。它根本没有偏离要求,是我的测试框架拿错了校验标准。

当时有人建议走“放宽校验”的捷径,比如只比对command字段,忽略其他多出来的键。但我认为这种做法太偷懒,宽松的校验根本抓不住Agent偷偷塞的奇怪参数,会让测试彻底失去控制力。

所以我最后把预期结果修正为符合实际Schema的状态,直接在代码里加了常量定义:

export const CANDIDATE_VERIFICATION_INTENT = 'Run candidate verification';

## 盲目笃信预期结果会导致验证失败吗?

const expectedExecArguments = {
 command,
 intent: CANDIDATE_VERIFICATION_INTENT,
};

这次踩坑最大的启发是:做AI自动化验证时,Mismatch(不匹配)只能证明两者不同,没法直接判定谁才是正确的。我们总习惯把模型当成“被测试者”,潜意识里觉得结果不对全是模型的问题。但实际上很多所谓的“模型幻觉”或“不遵循指令”,本质都是测试预期和实际运行时的Schema发生了脱节。

以后写验证逻辑时,得先确认运行时的真实Schema到底是什么,不能凭感觉瞎写expected对象。

工作流ZodLLM EvaluationTrueFoundryIntegration Testing

全部回复 (3)

想当场把话说完?进全球 AI 聊天室,登录就能开口。

副
副业中创业者 初级 2026/9/7

JSON Schema 校验太死板,字段一换序直接抛 TypeError,建议加上 order-agnostic 检查。比如在公司落地AI自动化测试的过程中,我踩过最隐蔽的坑,就是盲目笃信自己写的预期结果。之前跑集成测试时我搭了个格外严格的校验规则:模型启动前,测试框架会先生成一份预期的exec调用对象并冻结,要求模型执行时原封不动发送对应JSON,只要参数和冻结对象有半点出入,测试直接判定为EXEC_ARGUMENTS_MISMATCH失败。 跑RUN_004这个用例时模型直接被标记为异常,我头一个反应是模型又擅自发挥,没遵守指令。 但拆解问题根源后我发现,出问题的根本不是模型,是我定义的“预期”本身就有误。当时测试代码是这样写的:

 const expectedExecArguments = validateExecArguments({ command: `node ${artifactPath('verifier')} ${artifactPath('payload')} ${artifactPath('manifest')}`, });

这段逻辑里,我的预期对象只有一个command键。但模型实际发出来的JSON是这样的:

 {"intent": "Run candidate verification", "command": "node /opt/tf/uploads/candidate-verifier.cjs /opt/tf/uploads/candidate-payload.json /opt/tf/uploads/candidate-command-manifest.json"}

它多了一个intent字段,按我的校验逻辑这肯定是mismatch。但我一开始还惯性把这归结为“模型不听话”,直到翻开@truefoundry/trueforge-core@0.1.4的源码,看了dist/core/sandbox/Sandbox.mjs里的sandboxExecSchema定义:

 var sandboxExecSchema = z.object({ intent: z.string().describe( "A brief, user-facing explanation of the purpose of this command. Avoid technical jargon and code details." ), command: z.string().describe("The shell command to execute in the sandbox."), cwd: z.string().optional().describe("Working directory for command execution."), env: z.record(z.string(), z.string()).optional().describe("Additional environment variables to set.") });

真相很讽刺:在这个运行环境下,intent和command都是必填项,这意味着如果模型真的按我那个只有command的JSON发送,根本过不了沙箱的Schema校验,调用会直接报错。模型面对“我的指令(只要command)”和“运行时的强制要求(必须有intent)”这两个冲突的规则时,聪明地选择了后者。它根本没有偏离要求,是我的测试框架拿错了校验标准。当时有人建议走“放宽校验”的捷径,比如只比对command字段,忽略其他多出来的键。但我认为这种做法太偷懒,宽松的校验根本抓不住Agent偷偷塞的奇怪参数,会让测试彻底失去控制力。所以我最后把预期结果修正为符合实际Schema的状态,直接在代码里

0 回复
阿
阿海爱学习 高级 2026/9/7

最烦这种死磕校验,模型多出个空格就报错,简直是浪费生命。在公司落地AI自动化测试的过程中,我踩过最隐蔽的坑,就是盲目笃信自己写的预期结果,这种死磕校验让我深受其害。

0 回复
阿
阿杰在路上 中级 2026/9/7

可选字段没返回就报错,上次 debug 到凌晨才发现是 schema 里少了个问号。 在公司落地AI自动化测试的过程中,我踩过最隐蔽的坑,就是盲目笃信自己写的预期结果。 之前跑集成测试时我搭了个格外严格的校验规则:模型启动前,测试框架会先生成一份预期的exec调用对象并冻结,要求模型执行时原封不动发送对应JSON,只要参数和冻结对象有半点出入,测试直接判定为EXEC_ARGUMENTS_MISMATCH失败。 跑RUN_004这个用例时模型直接被标记为异常,我头一个反应是模型又擅自发挥,没遵守指令。 但拆解问题根源后我发现,出问题的根本不是模型,是我定义的“预期”本身就有误。 当时测试代码是这样写的:

 const expectedExecArguments = validateExecArguments({ command: `node ${artifactPath('verifier')} ${artifactPath('payload')} ${artifactPath('manifest')}`, });

这段逻辑里,我的预期对象只有一个command键。但模型实际发出来的JSON是这样的:

 {"intent": "Run candidate verification", "command": "node /opt/tf/uploads/candidate-verifier.cjs /opt/tf/uploads/candidate-payload.json /opt/tf/uploads/candidate-command-manifest.json"}

它多了一个intent字段,按我的校验逻辑这肯定是mismatch。但我一开始还惯性把这归结为“模型不听话”,直到翻开@truefoundry/trueforge-core@0.1.4的源码,看了dist/core/sandbox/Sandbox.mjs里的sandboxExecSchema定义:

 var sandboxExecSchema = z.object({ intent: z.string().describe( "A brief, user-facing explanation of the purpose of this command. Avoid technical jargon and code details." ), command: z.string().describe("The shell command to execute in the sandbox."), cwd: z.string().optional().describe("Working directory for command execution."), env: z.record(z.string(), z.string()).optional().describe("Additional environment variables to set.") });

真相很讽刺:在这个运行环境下,intent和command都是必填项。这意味着如果模型真的按我那个只有command的JSON发送,根本过不了沙箱的Schema校验,调用会直接报错。 模型面对“我的指令(只要command)”和“运行时的强制要求(必须有intent)”这两个冲突的规则时,聪明地选择了后者。它根本没有偏离要求,是我的测试框架拿错了校验标准。 当时有人建议走“放宽校验”的捷径,比如只比对command字段,忽略其他多出来的键。但我认为这种做法太偷懒,宽松的校验根本抓不住Agent偷偷塞的奇怪参数,会让测试彻底失去控制力。 所以我最后把预期结果修正为符合实际Schema的状态,直接在代码里加上了intent字段:

 const expectedExecArguments = validateExecArguments({ intent: "Run candidate verification", command: `node ${artifactPath('verifier')} ${artifactPath('payload')} ${artifactPath('manifest')}`, });

这样一来,测试才能准确地检测出模型的执行结果。

0 回复

发表回复

支持 Markdown 格式