:一次 Turn 为什么会跑多个 Step)
第一次看 Agent Loop,我下意识把“一条用户消息”理解成“一次模型请求”。顺着packages/core/agent-loop/src/agent.ts走完以后,才发现这两个边界并不重合。用户消息先进入 Inbox。驱动器打开 Turn,再根据当时欠下的工作创建 Step。模型如果要求调用工具,工具结果写回 Session 后,同一个 Turn 会继续开下一个 Step;请求失败后选择重试,甚至不会增加 Step 编号。这篇只做一件事:沿当前源码把一条输入从 Inbox 追到turn/end。项目地址:deepseek-ai/deepseek-harness源码基线:0.1.0-rc.5输入先进入 Inbox,不会直接调用模型ReactLoopAgent暴露了三种投递方式:followup(input:UserMessage):void{this.send(input,'next-turn',true)}steer(input:UserMessage):void{this.send(input,'next-step',true)}inject(input:UserMessage):void{this.send(input,'next-step',false)}followup()把消息放进next-turn队列,并唤醒驱动器;steer()把消息放进next-step队列,驱动器正在运行时会并入当前 Turn;inject()同样准备下一 Step 的输入,但不会单独把空闲 Agent 唤醒。这三个方法的差异不是命名上的细节。它们决定一条消息应该开启新 Turn,还是在当前 Turn 的下一个模型边界生效。Agent 从 idle 进入 running 后,只启动一条驱动链:privateasynckick():Promisevoid{try{while(awaitthis.turn()){}}catch(_error){// 已上报的失败和取消在驱动器边界收口}finally{// 恢复 idle;如果还有待处理输入,再次唤醒}}所以同一个 Agent 不会因为连续收到几条消息就并发跑几套主循环。Inbox 可以继续收消息,kick()负责按 Turn 串行消化。Turn 在认领输入之前就已经开始turn()先追加turn/start,然后才尝试生成第一个 Step:constturn=phase.turn+1this.session.append('turn/start',{turn})phase.turn=turnletturnEnds:TurnEndReason|null=nulllettarget:InboxTarget='next-turn'while(true){conststep=phase.step+1constdecision