工具粒度怎么定?单点检测工具为什么会让调用次数爆炸?
简化版
粒度太细和太粗都有代价。太细:一个工具一次只能回答一个组合(「这位老师周一第 3 节能不能排」),模型要做全局决策时只能拿它一个个试,调用次数按组合数增长,几十个资源就能试出几百次调用。太粗:一个工具一次返回全部明细,占满上下文、稀释注意力。常用的平衡是**「批量读、单点确认、集中写」**:用一个资源包工具把决策需要的资源和全局约束一次给模型,让它先在上下文里排除明显不可行的方案;单点检测工具只在写入前做最后确认;列表工具只给决策字段,细节交给详情工具一次查一个;写入集中在一个工具里。
详细版
同一个排课任务,两种工具设计的调用次数对比(11 间教室、5 个工作日、13 个节次):
只有单点检测工具:
checkClassroomAvailable(教室, 星期, 节次) → 可用 / 不可用
模型要找可用组合,最坏 11 × 5 × 13 = 715 次调用,这还只是教室开放这一项
资源包 + 单点确认:
queryResources() → 课程、教师、班级、教室、节次、各类不可用时间、已占用时间点(1 次)
模型在上下文里筛出候选 → 每节课写入前做 3~4 次确认 → 写入 1 次
| 粒度 | 优点 | 代价 | 适合 |
|---|---|---|---|
| 单点检测 | 返回小、语义清楚 | 做全局决策时调用次数爆炸 | 写入前的最后确认 |
| 资源包 | 一次拿到全局约束,模型能整体规划 | 返回较大,字段要精选 | 规划开始时建立全局视图 |
| 列表 | 用来选,字段少 | 细节不够,不能据此写入 | 从候选里挑 |
| 详情 | 字段全 | 一次只查一个 | 写入前确认某一项 |
| 集中写入 | 所有校验集中一处 | 写入工具要做完整校验 | 所有落库动作 |
完整版教学
一、为什么粒度会影响调用次数
工具调用的每一次往返,都要把完整的对话历史和工具清单重新发给模型。调用次数决定了三件事:
耗时:每次往返几百毫秒到几秒,300 次就是几分钟
成本:每次往返都要重新计费输入 token,历史越长越贵
稳定性:轮数越多,越容易触发轮数上限、越容易在长上下文里遗忘前面的结果
而调用次数很大程度上由工具粒度决定。模型面对一个「在很多组合里找可行解」的问题时,如果手里只有「检查一个组合」的工具,它就只能逐个试。
二、单点工具的组合爆炸
排课需要同时满足很多约束:教师有空、教室开放、教室装得下、这个时间点没被占。假设每个约束都只有单点检测工具:
教室开放检测:11 间 × 5 天 × 13 节 = 715 种组合
教师可用检测:假设有 20 位教师,每人 5 × 13 = 65 个时间点,就是 1300 种组合
模型不会真的全部试完(会被轮数上限拦下),但只要它把检测工具当成搜索手段,调用次数就会迅速涨到几百次,任务大概率在上限处失败。问题不在模型笨,而在它缺少全局信息:不知道哪些教室哪天关着,只能一个个问。
记忆钩子:单点工具回答「这一个行不行」,规划需要的是「哪些不行」。给模型全局约束,它才不会逐个去问。
三、资源包:一次给全局视图
解决办法是提供一个资源包工具,在规划开始时调用一次,返回:
可排的资源:课程(周课时、默认教师)、教师、班级(人数)、教室(容量、类型)、节次
全局约束: 教师不可用时间、教室不可用时间、已经被别的任务占用的时间点
执行建议: 不要用单点检测工具穷举,先在已知约束里筛出候选
模型拿到这些后,可以在上下文里直接排除「这间教室周三关闭」「这个班 45 人坐不进 40 人的教室」这类明显不可行的方案。资源包的字段要精选:每类资源只给决策需要的几列(ID、名称、容量、人数、周课时),创建时间、备注这类字段不要带,否则一张表几十列会迅速撑大返回。
四、单点检测不是没用,而是换了位置
资源包给的是规划时刻的快照,写入前还是要确认:规划过程中别的写入可能已经占用了这个时间点,模型也可能记错了资源包里的信息。所以单点检测的正确位置是写入前的最后确认:
| 阶段 | 用什么工具 | 次数 |
|---|---|---|
| 建立全局视图 | 资源包工具 | 1 次 |
| 选出候选 | 不调工具,模型在上下文里筛 | 0 次 |
| 写入前确认 | 单点检测工具 | 每项 3~4 次 |
| 落库 | 写入工具(内部再校验一次) | 每项 1 次 |
排 30 节课,大约是 1 + 30 × (4 + 1) ≈ 151 次调用,比逐个试的几百次少很多,而且次数和要排的课数成正比,是可预期的。
五、列表与详情:同一份数据拆两个粒度
另一种粒度设计是把「选」和「确认」拆开:
列表工具:返回 ID、名称、类型、价格、时长、热度、标签等决策字段,每次最多 N 条
详情工具:返回开放时间、闭馆日、最晚入场、经纬度、简介,一次只查一个
列表阶段模型只需要比较候选,用不上简介和开放时间;到了真正要写入某一项时,才花一次调用确认细节。如果列表直接带全部字段,60 条候选就会膨胀回几万字符。详情工具的描述要写明「只在准备写入之前调用,一次查一个」,防止模型拿它批量查。
六、写入要集中
读可以拆多个工具,写最好只有一个入口:
| 做法 | 结果 |
|---|---|
| 每类数据一个写入工具 | 校验逻辑分散,某一个漏了校验就是漏洞 |
| 一个写入工具,内部按类型处理 | 候选范围检查、重复检查、冲突检查集中在一处 |
集中写入还方便统计「写了多少条」,这正是判断任务成败的客观依据。
七、粒度设计的检查清单
1. 模型要做全局决策吗?要 → 提供一次返回全局约束的工具
2. 某个工具会被拿来穷举吗?会 → 描述里写禁止用法,并把它挪到写入前确认
3. 列表工具返回的字段都是决策需要的吗?不是 → 拆出详情工具
4. 写入入口有几个?多于一个 → 考虑合并,集中校验
5. 一次任务的调用次数和任务规模成正比吗?不是 → 粒度可能有问题
八、常见误区与追问
- 误区:工具越细越灵活。 细粒度工具在需要全局决策时会让调用次数按组合数增长,最后撞上轮数上限。
- 误区:把所有数据一次返回最省事。 返回太大会占满上下文,字段要精选,细节拆到详情工具。
- 误区:有了资源包就不需要检测工具。 资源包是快照,写入前仍要确认,检测工具换到了写入前这个位置。
- 误区:调用次数多说明 Agent 在认真工作。 调用次数应该和任务规模成正比,远超预期多半是在用工具穷举。
- 误区:每类数据都要有自己的写入工具。 写入入口越多,校验越容易遗漏,集中写入更可控。
- 追问:资源包太大怎么办? 先按任务范围预先筛选(本学期、本次涉及的课程),每类资源只给决策字段,必要时分城市或分组返回。
- 追问:怎么发现粒度设计有问题? 看调用日志里各工具的调用次数分布,某个检测工具的次数远超写入次数,就是在被拿来穷举。
九、加强记忆
粒度太细,全局决策只能逐个试,11 × 5 × 13 就是 715 次;粒度太粗,返回撑满上下文。平衡做法是批量读、单点确认、集中写:规划开始调一次资源包,拿到资源和全局约束,在上下文里筛候选;单点检测挪到写入前确认;列表只给决策字段,详情一次查一个;写入只留一个入口集中校验。调用次数应和任务规模成正比,某个检测工具次数远超写入次数,就是在被拿来穷举。
项目实战落地
项目里怎么做的
《AI Agent智能教务排课与教学质量分析系统》的排课 Agent 有 8 个工具:3 个资源查询、4 个约束检测、1 个写入。粒度上的关键设计在资源查询工具 queryScheduleResourcesTool:
- 一次返回排课资源包:启用的课程(含周课时和默认教师)、教师、班级(含人数)、教室(含容量和类型)、节次,外加教师可用时间、教室开放时间、同学期其他任务已经占用的时间点,以及一条执行建议,明确告诉模型不要用单点检测工具穷举;
- 字段精选:每类资源只挑排课需要的几列,例如教室只给 ID、名称、容量、类型、楼栋,创建时间和备注不进上下文;
- 检测工具只做最后确认:教师可用、教室开放、教室容量、时间点占用四个检测工具一次只查一个组合,教程算过账:11 间教室、5 个工作日、13 个节次,只靠教室开放检测去试就可能调 715 次;
- 写入只有一个入口:
saveScheduleLessonTool写入前再查一次占用,重复占用一律拒绝。
《AI Agent旅游行程智能规划平台》用的是列表和详情拆分:点位列表工具每行只返回 ID、名称、类型、城区、门票、建议时长、热度、标签等决策字段;开放星期、开放时段、最晚入场、闭园季、经纬度放在详情工具里,一次查一个。
为什么这样取舍
- 排课先给全局约束:排课是在大量组合里找可行解,模型没有全局约束只能逐个问,调用次数会撞上上限。
- 旅游先选后确认:选点位时只需要比较,到写入某一项时才需要开放时间,把细节放进详情工具,60 条候选才不会膨胀。
面试官还会追问
- 资源包里为什么要带上同学期其他排课任务已经占用的时间点?
- 排课 Agent 的指令为什么只写任务和步骤,不把排课规则和知识库内容直接塞进 Prompt?
学完《AI Agent智能教务排课与教学质量分析系统》,上面这些追问你都会迎刃而解。