Agent 循环
octo 的每一个界面——CLI、Web、IM——底层都跑着同一个循环:
- 把对话内容(历史 + 系统提示)发送给 provider。
- 模型回复文本,或者发起一次工具调用。
- 工具调用在真正执行前,先经过权限引擎(allow / ask / deny 规则)。
- 工具执行,结果作为
tool_resultblock 追加进历史。 - 重复第 1 步,直到模型只回复纯文本、不再发起工具调用为止。
两种协议默认都是流式的——文本增量和工具调用参数片段会逐步到达,重新聚合之后再派发。
--stream=false 会把一轮回复整个缓冲下来,只打印最终文本——适合把输出捕获进文件或脚本里。
每一次工具调用都是可见、受门控的——没有任何东西会悄悄执行。规则可以按工具名和参数模式
允许、询问或拒绝;一个 PreToolUse hook 可以在配置好的规则之上
加更严格的门控,但永远不会比配置的规则更宽松。
长会话会被有策略地压缩,而不是盲目截断:octo 会先从过时的工具结果里回收上下文,再考虑真正 清掉对话轮次,并且在一轮对话中途遇到 provider 的上下文长度错误时也能优雅恢复,而不是丢掉 整个会话。这个话题本身够大,单独写了一页——完整机制见历史压缩。
从截断的回复里恢复
Section titled “从截断的回复里恢复”这是另一个问题,和“输入超出上下文”不一样:provider 的回复本身在写到一半时撞上了输出 token 上限。octo 会按需升级,最多两步,再干净地放弃:
- 原样重发这一轮,但上限调高。 两家 provider 默认第一次尝试都是 16,384 token;如果被截断了,
会用升级后的 32,768 token 上限重试一次(
--max-tokens-escalate,设成0可以关掉这个重试), 重放时用的是没变过的历史,而不是把被截断的回复接上去。 - 如果还是被截断,并且这条回复是纯文本(不是卡在工具调用中间),octo 会把已经写出来的部分 接上,加一条固定指令让它从断点接着写,然后继续——每一轮最多这样续 3 次。
一个卡在工具调用中间被截断的回复不会走这条路——工具调用要么完成要么没完成——而是直接优雅地 结束这一轮。
下一步:系统提示本身是怎么拼起来的,见配置分层。