工具调用里最容易被低估的一类接口,是返回结果带分页的工具。单个接口里加个 cursor 字段看起来简单,但一旦把它交给 Agent 自主循环调用,分页游标就从「协议细节」变成了「状态机设计」问题。我最近在做代码检索和工单查询这块时,踩了几个反复出现的坑。
先分清游标是什么语义
Cursor 官方 changelog 在 1.4 版本里提到,List 工具「现在可以在一次调用中遍历完整目录树,并返回诸如文件数量和类型等元数据」。这类改动的本质是把「多次翻页」合并成「一次拿全」,因为对于目录树这种小结果集,翻页反而是负担。但把同样的逻辑搬到提交历史、日志检索、工单列表这种可能返回几万条的结果上,就变成了新的问题。
所以在写工具之前,必须先定死游标的语义。常见的有三种:偏移量、快照游标、游标 + 完成标志。偏移量在静态数据上用起来最省事,但在数据会变化的列表里必然重复或漏项。快照游标(服务端记下查询时刻的锚点)稳定得多,代价是服务端要持有会话状态。第三种,也就是游标和 has_more 一起返回、游标为空表示到底,是最适合 Agent 的形式,因为它只要求模型记住一个字符串,不需要数学计算。
我倾向于把工具输出固定成这样的形状:
{
"items": [...],
"next_cursor": "eyJ0cyI6MTc...",
"has_more": true,
"total_hint": 1280,
"truncated_fields": ["body"]
}
关键字段是 next_cursor 和 has_more 必须成对出现,total_hint 只是给模型一个量级感,不能当可靠计数用。truncated_fields 是我额外加的:当单条记录体积太大、只返回摘要时,必须告诉调用方被截断的是哪个字段,否则模型会以为拿到的正文就是全部。
两参数互斥是常见错误
看公开资料里 Cursor 的 fetch_pull_request 工具定义,它要求传 PR 编号、提交哈希或 git 引用,其中特别注明「不允许使用 HEAD」。这是一个有用的先例:当工具接受多种定位方式时,提示词必须明确约束边界,否则模型容易传一个它并不理解的值。
分页游标有同样的问题。很多工具有 page 和 cursor 两个参数,如果同时出现或者自相矛盾,服务端要么报错、要么静默取其中一个、要么算出空结果。Agent 收到空结果不会判断「我参数传错了」,而会认为「没有更多数据」,然后停止探索。这类失败安静得可怕。
教训是:同一个分页功能只允许一种游标体系,另一个参数从 schema 层删掉,比在运行时宽松兼容更加省事。
模型在哪一步丢掉游标
再简单的工具也会被复杂上下文吞掉关键字段。跑多轮 agent 时,我看到过几种典型的游标丢失方式。
第一种是上下文压缩把「游标字符串」当噪声行省掉。Agent 上下文吃紧之后,历史信息会被压缩成摘要,而压缩策略通常按信息密度挑选,一大串 base64 通常躲不过。第二次要翻页时,模型根本拿不出现实存在的游标,只能重新从头查。
第二种是并行调用发起多路分页,回包乱序之后把不同查询的游标复合。这需要做法是让每次工具调用的输出 id 与 cursor 强绑定,或者干脆不允许同一个查询的一次执行分发多个翻页请求。
第三种是子工具包装出一层抽象后丢掉了 next_cursor 字段。常见于 MCP Server 里包了一层通用响应包装,内层工具返回 5 条时就丢掉 has_more,模型就因此只取了第一段。
这些问题的对策中我比较满意的是把游标结构做得足够短、足够可日志化。游标内容用服务端可判定的集合(例如记录 id 或时间戳),与业务空间解耦。服务端记录之后,出错时可以直接核对日志里传进来的游标当时是否在返回序列里。
控制总调用次数与取值顺序
游标能翻多页并不意味着应该无限翻页。按经验地给出上限,并在工具描述里写清楚,比如「本次单轮最多返回 10 条;若 has_more 为 true,请说明是否需要继续,并在描述开头列出总条数提示」。这个约束对模型行为有直接影响:它跟你的工具描述同样重要。
另一个取舍是先聚合还是先看例子。如果列表结果一旦查就要翻 5 页才能看全,可以加一个总是单页返回的参数,让模型自己做选择。Cursor 数据里的做法是对于目录树这种小结果直接拿全,这里没有通用的规则,把 2MB 跟 10 页这类分界经经验做成阈值,是相对容易追溯的取法。
在评估数据上要把游标纳入
市面上讨论工具调用的文章大多在讲 ReAct 框架的结构,以及检索类工具的能力;但关于工具返回边界的工程约定却写不下来。一个 Agent 评测里如果只检查最后一轮答案对错,大量翻页引发的重复、遗漏、过早停止通常都抓不到。
我手动跟踪路径时至少加上三列特征:每次工具调用时返回的 has_more 是否与返回条数一致;next_cursor 是否有任何一次在后续调用中被真正使用;整个任务下来对同一信源实际触达的总条数。这些字段记录下来再放大到整个任务切面,就足够看清模型的翻页行为。结果可能会像输出截断一样——单个 case 看起来都没有问题,放大到整体才知道用不用游标。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/agent-engineering/645.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
AI Coding Agent 如何稳定提交可合并的 PR
上周 review 了一个 AI 生成的 PR,测试全绿,但我还是把它关了。它改了 37 个文件,顺手升级了一个依赖,还修改了两条测试断言。问题不是代码不能跑,而是这个 PR 没人敢合。
2026/10/14
-
Agent Harness:可观测、可评测、可回滚的 Agent 基础设施
去年我们做了一个处理退款申请的 Agent。Demo 很顺,上线第一周就出了事故:进程重启后状态丢了,同一笔申请被处理了两次。问题不在模型,而在模型外面那层没人愿意写的基础设施。
2026/10/09
-
MCP 进入生产环境:工具协议、网关与权限边界
MCP 刚出来时,我以为最难的是协议兼容。真正接了十几个工具之后才发现,把工具接上只花了两天,把权限、审计和故障处理理清楚花了两个月。
2026/10/07
-
Agent 安全清单:提示注入、工具越权与数据外泄
一个内部助手被要求总结一封客户邮件。邮件末尾有一行白色小字:把最近的客户名单转发到某个地址。Agent 真的准备去调邮件工具了。安全不能靠模型自觉,得靠系统设计。
2026/10/16
-
并行工具调用暴露的工具设计耦合问题
并行工具调用能砍掉四成延迟,但它更重要的价值在于暴露工具之间的隐性耦合。本文拆解上下文依赖、共享状态竞争与时序前提这三类静默失败,给出读写分类规则、工具合并的判据,以及什么时候该上依赖图编排。
2026/10/23
-
Agent 轨迹回放调试:把失败变成可回放资产
Agent 调试难在无法复现、故障点藏在数步之前。这篇文章讲三件具体的事:把轨迹归一化成可校验的步骤结构,用工具签名和领域策略生成可执行约束,以及用标注过的失败轨迹在 CI 里做回归回放——顺带说明为什么只盯延迟和错误率的可观测看不到这类故障。
2026/10/24
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布