智能体如何决定下一步

智能体每走一步,都在三条边界之内做决定:这一轮真实可用的工具、事先给定的预算、当前用户的实时权限。工具出错不会让整轮重来,错误会被整理成结构化信息交还给模型,由它在下一步自己修正。

智能体不是一次性的“检索 + 生成”。面对一个问题,它会先查一路资料,看结果不够再换个方式查,必要时读原文、调用外部工具,甚至把一部分工作交给另一个更专门的智能体。每一步做什么,由模型根据当前掌握的证据决定。

让模型自己决定,难点在于让它可控:不能调用不存在的工具,不能越权读数据,不能无限循环下去,失败了也不能留下半截回答。这篇讲 ApeMind 如何做到这些。

一轮的完整轨迹

下面是一轮问答的真实结构(内容为示意)。点右侧步骤或用方向键逐步查看,留意上方的状态和预算如何变化。

QUEUED RUNNING COMPLETED
工具调用 0 / 60
模型轮次 0 / 30
用时 0s / 600s
›create_turn idempotency_key=7f3a…执行租约 SET NX EX → 已获得
›assemble 模型 · 提示词 · @员工手册 · 12 个可见工具历史 9 轮 → 摘要 + 最近 3 轮,未超上下文预算
›search_knowledge(modes=[vector, fulltext, graph])8 条证据 · 匹配强度 strong
›expand_entity_graph(entity="区域总监")invalid_param:缺少 collection_id → 下一步补全后 成功
›subagent__policy_researcher(handoff_note)独立委派会话 · 自己的模型与工具 → 返回 3 条带出处的结论
›read_evidence_refs([ref_12, ref_31, ref_44])回读原文 3 段,核对措辞与条款号
›answer 流式输出回读过的原文成为依据 [1] [2] [3] · 写入持久消息
  1. 创建并抢占

    同一个幂等键只会产生一轮。轮次先进入排队,抢到执行租约的进程才开始运行,多个进程不会重复执行同一轮。

  2. 装配这一轮

    确定模型、提示词、资料范围和工具清单。历史太长时先摘要再裁剪,保证第一次请求就不会超出模型上下文。

  3. 第一次决策:多路检索

    模型选择 search_knowledge,一次请求同时走向量、全文和图谱三路。

  4. 失败即信息

    某次调用漏了必填参数。服务端返回可自纠的 invalid_param,而不是误导性的“查无结果”,模型下一步补齐参数重试。

  5. 交给子智能体

    需要专门研究的部分,以结构化交接单委派给子智能体。它在独立会话里运行,结果作为一次工具返回回到这一轮。

  6. 回读原文

    检索结果只是摘录。回答前按证据引用读回原文,确认条款号和措辞。

  7. 回答并附上依据

    边生成边推送。本轮回读过的原文作为编号依据附在回答下方,和回答一起写入持久消息,轮次完成。

模型能看见哪些工具

模型只能调用这一轮真实注册的工具。工具清单由每个智能体的工具配置决定:管理员可以按服务器、按工具开关,不在清单里的工具,模型看不见,也调不到。

资料范围不等于权限

用户在输入框里 @ 某个知识库,或者用 # 指定某篇文档,是在告诉智能体“这一轮先看这里”。范围按优先级确定:本条消息里明确提到的最优先,其次是请求里带的,最后是智能体的默认知识库。

但这只是选材提示。每一次真实读取,都会重新校验智能体托管密钥的授权范围、工具范围,以及当前用户对这份资料的实时权限。@ 一个无权访问的知识库不会让它变得可读;被撤销权限的资料,下一次读取就会被拒绝。

一次工具调用要穿过的几层

模型发出的调用不会原样直达工具。中间有一组包装层,各自处理一类常见问题:有的直接修好,有的把问题整理成模型能看懂的提示交回去。选一个场景,看调用在哪一层被处理。

场景

层的排列为示意,表达的是职责而不是精确的调用顺序。

这些机制的共同点是:修复要么精确,要么不做。裸名恢复只在唯一、无歧义时生效,不做模糊匹配;参数修复记录每一次修改。修不了的问题不会被掩盖,而是变成结构化的错误交还给模型,整轮不会因为一次调用失败而重来。

预算:给每一轮划定边界

模型自己决定下一步,就必须有人决定“到此为止”。每个智能体有一个响应预算档位,同时约束三件事:总时限、工具调用次数、包含工具调用的模型轮数。显式设置的数字优先于档位。

总时限
工具调用
工具轮数

轮数在每批工具执行之前检查:一旦超限,这一整批工具都不会执行,智能体转入回退回答,只基于已经收集到的证据作答,并说明证据边界。预算耗尽不会留下一个没有结尾的回答。

把子任务交给子智能体

一个智能体可以挂载其他智能体作为子智能体。在父智能体眼里,每个子智能体就是一个工具,名字形如 subagent__policy_researcher。调用它时必须提交一张结构化的交接单。

HandoffNote 父 → 子
goal
整理华东区差旅超标的完整审批链
context_refs
ref_12、ref_31(已读过的证据引用)
acceptance
每个审批节点都要附上原文出处
constraints
只看 2024 年以后生效的制度
1只传引用,不传历史。交接单里的上下文只能是可追溯的证据或产物引用,父对话的原始历史不会整段塞给子模型。
2独立会话,独立策略。子智能体在自己的委派会话里运行,用自己的模型、提示词、工具和预算。同一次委派按“父轮次 + 调用 ID”幂等。
3权限跟着根走。子智能体读数据时沿用调用链最上层父智能体的身份和实时权限,计量也归到它名下。委派不会扩大可读范围。
4失败也有结构。超时、预算耗尽、权限不足等以明确的原因码返回;已经产出的部分结论会尽量带回,供父智能体有边界地继续。

上下文为什么不会爆

长对话和大段工具返回是上下文溢出的两大来源。运行时在每次请求模型前做三层处理:

  1. 工具返回按类型截断。历史里超长的工具结果按工具类型设上限,并插入截断标记,模型知道这里被省略了。
  2. 历史先摘要再裁剪。有会话摘要时只保留最近几轮原文,其余由摘要代替。
  3. 最终守护。发送前再按模型的上下文窗口检查一次,仍会溢出就直接给出友好提示并正常结束本轮,而不是发出一个注定失败的请求。

写进数据库的工具输出只保存 8 KiB 以内的预览,单个长文档不会把消息记录撑大,刷新页面后也仍能看到摘要。

依据从哪里来

搜索结果只是候选。智能体按证据句柄回读原文后,读取工具的返回会被转换成结构化的来源材料:引用的文字、知识库与文档定位、产生它的工具调用。这些材料按顺序编号,作为回答的依据,和回答一起持久化。

引用回答的是“这句话依据的是哪段原文”,帮你核对,但不等于自动证明答案完全正确。完整机制见 引用如何回到原文。

断线和刷新

执行过程通过 SSE 实时推送,每 5 秒一次心跳防止代理断开空闲连接。网络闪断后,前端带着上次收到的序号重连,从断点继续,重复的片段会被去重。隔了很久再刷新,实时事件可能已过期,这时从持久化的消息里恢复最终答案、工具摘要和引用。

运行这一轮的进程如果意外退出,后台协调器会发现失去租约的轮次,把它标记为失败,界面不会永远停在“运行中”。

边界

出问题时查哪一环

回答提到“证据不足”或比预期短

可能是预算先到了:工具调用次数或轮数达到上限,转入了基于已有证据的回退回答。在执行过程里看调用次数;问题确实复杂时,把智能体切到更大的预算档位。

智能体从不调用某个工具

先确认这个工具在该智能体的工具配置里是开启的。提示词里写了也没用,模型只能看见本轮注册的工具。

@ 了知识库,回答里却没有它的内容

@ 只是选材提示。确认智能体的托管密钥授权范围包含这个知识库,以及当前用户对它有读权限;也检查一下执行过程里检索是否返回了结果。

给想深入的读者:哪些状态是权威的

轮次的生命周期以数据库里的轮次记录为准,Redis 只保存低延迟的状态影子、执行租约、短期事件和取消标记。可刷新恢复的答案、工具摘要和引用以持久化的消息片段为准。历史事件不做长期逐条回放,长期审计依赖持久消息和调用明细账本。

完成一次任务后,可通过执行诊断与上下文用量定位问题,再用版本化问答集评估配置改动。