跳转到内容

Agent 循环

octo 的每一个界面——CLI、Web、IM——底层都跑着同一个循环:

  1. 把对话内容(历史 + 系统提示)发送给 provider。
  2. 模型回复文本,或者发起一次工具调用
  3. 工具调用在真正执行前,先经过权限引擎(allow / ask / deny 规则)。
  4. 工具执行,结果作为 tool_result block 追加进历史。
  5. 重复第 1 步,直到模型只回复纯文本、不再发起工具调用为止。

两种协议默认都是流式的——文本增量和工具调用参数片段会逐步到达,重新聚合之后再派发。 --stream=false 会把一轮回复整个缓冲下来,只打印最终文本——适合把输出捕获进文件或脚本里。

每一次工具调用都是可见、受门控的——没有任何东西会悄悄执行。规则可以按工具名和参数模式 允许、询问或拒绝;一个 PreToolUse hook 可以在配置好的规则之上 加更严格的门控,但永远不会比配置的规则更宽松。

长会话会被有策略地压缩,而不是盲目截断:octo 会先从过时的工具结果里回收上下文,再考虑真正 清掉对话轮次,并且在一轮对话中途遇到 provider 的上下文长度错误时也能优雅恢复,而不是丢掉 整个会话。这个话题本身够大,单独写了一页——完整机制见历史压缩

这是另一个问题,和“输入超出上下文”不一样:provider 的回复本身在写到一半时撞上了输出 token 上限。octo 会按需升级,最多两步,再干净地放弃:

  1. 原样重发这一轮,但上限调高。 两家 provider 默认第一次尝试都是 16,384 token;如果被截断了, 会用升级后的 32,768 token 上限重试一次(--max-tokens-escalate,设成 0 可以关掉这个重试), 重放时用的是没变过的历史,而不是把被截断的回复接上去。
  2. 如果还是被截断,并且这条回复是纯文本(不是卡在工具调用中间),octo 会把已经写出来的部分 接上,加一条固定指令让它从断点接着写,然后继续——每一轮最多这样续 3 次。

一个卡在工具调用中间被截断的回复不会走这条路——工具调用要么完成要么没完成——而是直接优雅地 结束这一轮。

下一步:系统提示本身是怎么拼起来的,见配置分层