对于用户的一个 Task ,它和 Turn 以及 step 的关系如下:
Session(一次长期对话)
│
├── Task(用户想完成的一件事情)
│ ├── Turn 1
│ │ ├── Step 1
│ │ ├── Step 2
│ │ └── ...
│ ├── Turn 2
│ │ ├── Step 1
│ │ └── ...
│ └── Turn 3
└── ...
一次 Agent Runtime 运行过程(Turn/Step 生命周期)流程图:
输入进入 inbox
→ turn/start
→ claim(领取)本 Step 要处理的新输入(next-step输入+Turn边界允许领取的一条next-turn输入)
→ 准备这一轮要给模型的候选输入
→ 组装 prompt sections + tool schemas
→ agent/pre-step
├─ 修改本 Step 的新输入
├─ 拒绝进入 Step
└─ 做 compaction 等请求前处理
→ step/start
→ 把获准进入的输入写入 session log
→ 从 session log 派生 model history
→ 构造本次模型请求= system prompt + model history + tool schemas + model call config
→ agent/request
→ llm/stream
→ 得到 assistant/message
→ 如果模型产生 tool call
↓
pre-execute(权限检查、approval、参数修改、参数校验、参数补充)
↓
execute(sandbox 隔离执行、真正调用工具函数)
↓
post-execute(结果加工、格式化、添加上下文)
↓
写入 tool/result
→ step/end
→ 判断:还有没有“现在就能继续做”的工作?
├─ 工具结果要求模型继续 → 进入下一个 Step
├─ 工具结果认为已经完成 → turn/end
├─ inbox 已有 next-step input
└─ 都没有
↓
agent/turn-stopping
↓
再检查一次有没有新的 steering
├─ 有 → 下一 Step
└─ 没有 → turn/end
其中:
Turn是一次连续活跃运行。Turn从 Agent 开始处理输入开始一直持续到当前已经没有可以立即继续执行的工作为止。
Step是Turn里面的一次模型行动循环。- 一次
Step= 调用一次模型+执行这次模型产生的工具调用 Turn:Step= 1 : N
- 一次
inbox不单纯是一个用户消息队列,而是两条队列
inbox ├─ next-turn └─ next-step它们大致对应三种常用投递方式:
followup → next-turn → 唤醒 Agent → 通常用于普通的新一轮用户输入 steer → next-step → 如果 Agent 正在运行,在最近的 Step 边界加入 → 可以理解为“中途补充/纠偏” inject → next-step → 但是不会主动唤醒 Agent → 用于给之后的模型请求注入上下文其中:
a.followup()是普通下一Turn 输入
b.steer()是最近 Step 的 steering
c.inject()是下一次 pre-step 使用的模型上下文prompt sections是由其它很多插件共同贡献的,最终会组成 system prompt,按order排序拼接。tool schema是发给LLM的工具投影,并非Tool本身,它只包含:name description parameterspre-step是模型看到这些内容之前的最后处理阶段,它不是普通hook实际上它控制是否进入模型上下文。session log是事实来源,model history是派生结果。parameters=参数结构定义(JSON Schema),例如:{ "type":"object", "properties":{ "path":{ "type":"string" } } }model call config包含:model temperature max tokens provider 参数
