先记住这个答案
少量独立参数可以直接放在根对象;一组共同描述某个实体、可以整体复用或整体省略的参数,适合放入一个语义明确的子对象。嵌套不是越少越好,也没有适用于所有模型的固定层数答案。应避免只有单个孩子的空壳层级、把简单字段包进通用配置树,以及为了扁平化而产生大量难懂前缀。最终选择应通过真实调用样本比较漏字段、放错层级、错误修复轮次和阅读成本,并满足所用工具接口的结构限制。
- 按共同实体或生命周期分组,避免没有含义的容器
- required 与额外属性限制需要逐层声明
- 结构复杂到需要猜分支时考虑拆分工具
有意义的分组能够减少字段混淆
代码搜索通常同时出现检索词、过滤范围和分页位置。把目录与文件后缀放入 filters,可以表明它们一起缩小候选集合;分页参数则描述结果读取过程。若把所有参数放入 data.options.config 等容器,层级增加却没有增加解释力。
示例只演示子对象的完整约束。根层要求 filters 存在,并不自动要求它内部的目录字段;内部 required 才承担这项责任。空对象、对象缺失与字段为 null 也不是同一种输入,设计时应分别决定能否接受。
{
"type": "object",
"properties": {
"query": { "type": "string", "minLength": 1 },
"filters": {
"type": "object",
"properties": {
"directory": { "type": "string", "minLength": 1 },
"extension": { "type": "string", "enum": ["ts", "tsx"] }
},
"required": ["directory"],
"additionalProperties": false
}
},
"required": ["query", "filters"],
"additionalProperties": false
}filters 必须提供目录,但可省略后缀限制。根层与子对象分别拒绝未知属性;这里没有自动补默认值,也没有把目录存在性和授权范围交给 Schema 证明。
什么时候扁平化反而损失表达能力
如果工具接收多个地址,每个地址都有城市和邮编,保留地址对象或对象数组通常比 address1City、address2City 更容易扩展,也更能表达字段之间的归属。把所有数组元素拆成带编号的字段,还会让数量上限混进参数名称。
反过来,只有两个独立标量的小工具不必先创建 request、payload 和 options 三层外壳。保留结构应当有共同语义、独立验证或复用价值。不能因为内部服务使用复杂 DTO,就直接把整棵内部对象树暴露给模型。
通过错误样本判断是否需要重构
记录模型是否把子字段放到根层、忘记中间对象,或在互斥分支中同时填入多组参数。若这些错误集中出现,先检查结构是否清楚,再决定改描述、压平一层或把不同动作拆开。不要只看一次成功调用就宣布结构可靠。
更改层级会影响历史调用和客户端生成类型。可以通过版本化工具名或受控适配层迁移,但适配逻辑必须明确旧字段与新字段的映射,拒绝同时给出且互相矛盾的两套输入。错误信息最好给出完整字段路径,让 Agent 能精确修改。
容易答错的地方
- 嵌套最多两层是所有模型通用的标准
- 深度限制可能来自具体接口,可靠性也依赖字段数量与业务关系;应区分平台硬限制和团队经过样本验证的设计选择。
- 根对象禁止额外属性就会递归生效
- 每个子对象有自己的属性规则,未明确限制的内部对象仍可能接受额外字段;需要逐层查看实际校验结果。
面试官还会怎么问?
可选子对象里面可以有必填字段吗?
可以,子对象省略时不触发内部字段要求,但一旦提供它,就必须符合内部 required;若业务要求整组存在,还要在父层声明。
是否应该把复杂 JSON 改成字符串参数?
通常会失去结构检查和错误定位,模型还需要自行处理转义;只有下游本来就接受某种文本语言且有专用解析器时才值得这样设计。
数组会不会天然比对象更难使用?
关键是元素关系是否明确。固定位置含义的长数组容易填错,带字段名的对象数组通常更清楚,但仍需限制长度并定义每个元素的契约。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。