A16第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

工具参数与校验

工具参数与校验 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能为课程检索工具定义参数结构,把模型传来的参数当不可信输入层层校验,用结构化错误拒绝无效输入和越权资源,并用四类坏参数完成测试。

问题场景

工具上线第二天,日志里出现这些调用:arguments 是一段不是 JSON 的话;query 缺失;track 传了 "ReactJS" 这种枚举外的值;更糟的是 get_lesson 被要求读一个不存在的课程编号。参数来自模型的「生成」,不是来自你的表单校验——它是和用户输入同级的不可信输入。

核心解释

以课程检索为例(本站 lib/ai/tools.ts 的真实校验,简化版):

const TRACKS = ["foundation", "react", "nextjs", "ai", "python", "go", "engineering"];

function searchCourses(argsJson: string): string {
  // 第一层:解析防御——arguments 可能根本不是合法 JSON
  let args: Record<string, unknown>;
  try {
    args = JSON.parse(argsJson || "{}");
  } catch {
    return JSON.stringify({ error: "参数不是合法 JSON" });
  }
  // 第二层:字段存在性与边界——缺参、空串、超长
  const query = String(args.query ?? "").trim();
  if (!query || query.length > 100) {
    return JSON.stringify({ error: "query 参数缺失或过长" });
  }
  // 第三层:枚举白名单——可选参数也不能放行任意值
  const track = typeof args.track === "string" ? args.track : null;
  if (track && !TRACKS.includes(track)) {
    return JSON.stringify({ error: `track 必须是:${TRACKS.join("/")}` });
  }
  return doSearch(query, track); // 真正执行
}

三条原则:

  1. 错误也返回结构化 JSON,不抛异常。工具的失败要作为结果回传给模型(A15 的「拒绝也是结果」),模型读到 {error: "..."} 能修正参数重试;抛异常只会让整条请求崩掉。
  2. 越权资源用「查无此物」拒绝。get_lesson 收到不存在的课程编号时返回 {error: "课程 XXX 不存在"}——查询永远限定在服务端自己的资源表里,模型报什么名字都只能命中白名单内的东西,这就是资源访问无法越权的根本原因。
  3. 校验失败要可归因。错误信息里写清哪个参数、为什么不行(「缺失或过长」「不在枚举内」),模型的下次修正才有依据——A09 的「带原因重试」在工具侧同样成立。

示例拆解

四类坏参数的测试用例,直接写成数组跑:

const CASES: Array<[string, string, string]> = [
  // [用例名, arguments, 预期]
  ["缺参", "{}", "error"],
  ["超长", JSON.stringify({ query: "长".repeat(200) }), "error"],
  ["非法枚举", JSON.stringify({ query: "状态", track: "ReactJS" }), "error"],
  ["资源不存在", JSON.stringify({ course_code: "Z99" }), "error"],
  ["正常", JSON.stringify({ query: "状态", track: "react" }), "ok"],
];

for (const [name, args, expect] of CASES) {
  const result = searchCourses(args);
  const passed = expect === "error" ? !!JSON.parse(result).error : !JSON.parse(result).error;
  console.log(`${passed ? "PASS" : "FAIL"} [${name}] -> ${result.slice(0, 60)}`);
}

关键点:

  • 每个分支一条用例,校验代码改坏了立刻能发现;这组用例随工具一起提交进仓库。
  • 「正常」用例是对照组:校验收得太紧(把合法输入也拒了)同样是 bug。
  • 参数结构在声明(A15 的 parameters schema)和执行(这里的校验)两侧各写一次——声明是给模型的建议,校验才是给服务的保证,前者挡不住生成错误。

动手任务

  1. 给本站 get_lesson 补上与 search_courses 同级的三层校验(course_code 非空、格式合法、必须存在于课程表)。
  2. 把四类坏参数用例跑通,然后故意删掉枚举校验那一层,观察哪条用例变 FAIL。
  3. 决策题:get_lesson 收到格式合法但课程编号大写的 r08,返回错误还是宽容转换?写出你的选择和理由(本站选择 toUpperCase() 后再查——接口对大小写的宽容度要明确决策并写进文档)。

常见错误

  • 信任声明不写校验:schema 只影响模型的倾向,挡不住它生成缺参或编造值。
  • 校验失败抛异常:整条聊天请求 500,模型没有机会修正,用户只看到报错。
  • 错误信息写成「参数错误」:没有哪个参数、什么原因,模型重试还是错。
  • 只测正常路径:四类坏参数是工具接口的最低测试集,缺一类就埋一类雷。

检查与复盘

自查清单:

  • 能说出解析、字段、枚举三层校验各拦什么
  • 错误返回是结构化 JSON 且带可归因的原因
  • 四类坏参数用例齐备且在仓库里

参考资料

检查题共 3 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1读代码中等

阅读工具参数校验代码。模型传来的 arguments 是 {"query":"状态","track":"ReactJS"}(track 枚举应为小写方向名)。这段代码会在哪一层拦下它、返回什么?

const TRACKS = ["foundation", "react", "nextjs", "ai", "python", "go", "engineering"];
function searchCourses(argsJson) {
  let args;
  try { args = JSON.parse(argsJson || "{}"); }
  catch { return JSON.stringify({ error: "参数不是合法 JSON" }); }
  const query = String(args.query ?? "").trim();
  if (!query || query.length > 100) return JSON.stringify({ error: "query 参数缺失或过长" });
  const track = typeof args.track === "string" ? args.track : null;
  if (track && !TRACKS.includes(track)) return JSON.stringify({ error: "track 必须是:" + TRACKS.join("/") });
  return doSearch(query, track);
}
Q2故障定位较难

bug 现场:工具参数校验失败时开发者写了 throw new Error("参数错误")。结果是整条聊天请求 500,用户看到报错,模型没有机会修正参数重试。而站内其他工具返回 JSON.stringify({error: "..."}) 就没这个问题。为什么?该怎么改?

Q3多选中等(多选,全部选对才算正确)

给工具参数校验设计测试用例集,下列哪些属于「四类坏参数」的最低测试集?正确的有?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...