漏掉空值、零值、超长输入这类边界,通常不是单一原因:提示词只说了“实现这个函数”而没要求列分支,模型就会按主路径写;上下文里的调用方、校验逻辑、已有工具函数如果没有真正进入这次请求体,模型也看不到约束。可行的判断顺序是:先从会话记录里定位那一次生成,确认到底缺了哪些分支;再用测试用例把“缺”变成可复现的失败;然后把提示词和上下文代码固定成一份请求体,分别用两种提示词模板各跑一次,比较返回代码里的分支数量。这样能区分是提示词颗粒度不够,还是上下文根本没对齐。
适用场景:用 Claude Opus 5.5 生成或补全函数,返回结果主路径能跑但边界分支缺失。操作动作:复制那次生成的函数代码,对照需求列出空值、零值、超长输入的缺失分支,再固定请求体分别测试“先列边界再写”与“直接写”两种提示词。验证方式:用测试用例跑一遍,看边界断言是否通过,并记录返回代码中的分支数量变化。风险边界:模型仍可能漏项,提示词调整只提高覆盖概率,最终要以测试结果为准,并保留人工复核。
在会话记录里找出漏掉边界条件的那次生成
先别急着改提示词,回到对话历史里找到那次具体生成。把返回的函数代码整段复制出来,放到一个临时文件里,把需求描述也一并贴在旁边。很多“模型不行”的判断,其实是因为那次请求里根本没带上调用方的约束。
操作上可以按下面几步走,每一步都能在会话页面里看到证据:
- 在会话历史里定位返回该函数的那条消息,复制完整代码块,不要只复制片段,避免丢掉前置的参数校验或提前 return。
- 在同一条会话里往上翻,找出你当时写的那句需求,把它单独摘出来,确认它是否提到过空值、零值、超长输入、异常输入。
- 把需求里的输入类型逐个对照函数签名:数组参数看空数组,字符串参数看空串和超长串,数值参数看 0 和负数,可选参数看未传值。
- 在函数体里逐个搜索这些分支:是否有 null 判断、是否有长度上限处理、是否有默认值兜底,缺哪个就记一条。
记下来的缺失项就是后面的测试目标。如果发现需求里本来就没写这些约束,那更可能是提示词的问题;如果需求写了、上下文里也有校验函数,但生成结果没用上,就更偏向上下文没有对齐。
写出覆盖边界条件的测试用例骨架
凭感觉判断“好像漏了”不可复现,先把四类边界写成断言。下面的骨架与语言无关,按你实际使用的测试框架改写即可,关键是替换函数名和参数,让断言贴合真实签名。
// 把 fn 替换为被测函数名,参数按真实签名补齐
// 每个用例只验证一个边界,失败信息要能指出是哪一类输入
test("空集合输入", () => {
// 期望:返回空结果 / 抛错 / 走默认分支,按需求约定写死
expect(fn([])).toEqual(结果按需求约定的空集行为);
});
test("空字符串输入", () => {
// 常见漏项:trim 后为空、长度 0 时的分支
expect(fn("")).toEqual(结果按需求约定的空串行为);
});
test("零值输入", () => {
// 覆盖 0、0.0 这类会被 if (x) 误判为假的值
expect(fn(0)).toEqual(结果按需求约定的零值行为);
});
test("超长输入", () => {
// 用重复拼接生成,长度上限按业务约定填,不要照抄示例值
const long = "a".repeat(超长阈值);
expect(() => fn(long)).not.toThrow();
});
断言里写“按需求约定”不是偷懒,而是提醒你先确认契约:空串是返回 null、返回空串还是抛错,必须在写测试前定下来,否则测试本身也会变成猜测。四类输入至少各留一个用例,参数类型不同时按签名再加一组。
用固定请求体提交代码补全任务
要让对比有意义,两次测试除了提示词不同,其它都要一致。把需求、上下文代码、系统提示写进同一份请求结构,用占位符标出需要替换的字段,字段名以你实际接入的服务文档为准。
{
"model": "<按接入文档填写模型标识>",
"system": "<系统提示占位:例如要求先列出边界条件再实现>",
"messages": [
{
"role": "user",
"content": "<需求描述占位:函数用途、输入输出契约>"
},
{
"role": "user",
"content": "<上下文代码占位:调用方、已有校验函数、相关类型定义>"
},
{
"role": "user",
"content": "<待修改或待补全的函数代码占位>"
}
],
"max_tokens": "<按接入文档取值>",
"temperature": "<按接入文档取值,对比实验建议保持一致>"
}
关键点是上下文代码要真的放进 messages,而不是只在你本地开着文件。如果接入方式支持单独的系统提示字段,就把“先列边界”这类全局要求放进去;如果不支持,就并到第一条 user 消息里,位置保持一致,方便两次对比。
分别测试“直接写代码”和“先列边界再写代码”两种提示词
用同一份固定请求体跑两轮,只改提示词。两种模板可以这样写:
模板 A(先列边界再实现):
请先列出这个函数需要处理的边界条件,包括空值、零值、
超长输入和异常输入,逐条说明处理方式;
然后再给出完整实现,实现中每个边界都要有对应分支。
模板 B(直接实现):
请根据下面的需求和上下文代码,给出该函数的完整实现。
拿到两份返回后,不要只看“能不能跑”,统计返回代码里出现的边界分支数量:null 或 undefined 判断、长度为 0 判断、数值为 0 判断、长度上限判断各算一条。把结果记成一张简单的验证记录表,方便回看哪次改动有效。
验证记录表(示例表头,按实际填写)
轮次 | 提示词模板 | 上下文是否随请求提交 | 返回分支数 | 缺失的边界 | 备注
1 | A 先列边界 | 是 | 待统计 | 待填写 |
2 | B 直接实现 | 是 | 待统计 | 待填写 |
3 | A 先列边界 | 否(只发需求) | 待统计 | 待填写 | 隔离上下文影响
如果 A 明显比 B 多出边界分支,说明主要是提示词颗粒度问题;如果 A 和 B 都漏同一类分支,而上下文里其实有现成的校验函数没被用上,更可能是上下文没对齐,需要检查 messages 里是否真的带上了那段代码。
运行测试用例验证补全结果
把返回的实现替换进项目,跑第 2 节那组用例。命令按你的项目实际入口写,下面是占位形式:
# 按项目实际测试入口替换,只作占位
# 单文件运行
npm test -- path/to/目标测试文件
# 或
pytest path/to/test_目标函数.py -v
运行后逐个记录:空集合、空字符串、零值、超长输入四类里,哪些通过、哪些失败。失败的用例先别急着再问一次模型,先判断失败原因:是分支缺失,还是分支存在但逻辑写错。前者回到提示词模板 A,后者把失败断言和实际输出一起贴回上下文再重试。
如果加上完整上下文后模型表现反而变差,或者关键约束没有进入请求,可以回退到更小的上下文重试:只保留目标函数、直接调用方和必要类型定义,去掉无关文件,再跑一次同样的测试。小上下文不一定更好,但能更快判断某段代码到底有没有被正确读取,需要结合你当前接入方式确认。整个流程结束后,保留验证记录表和测试文件,下次遇到同类漏项时可以直接复用。