fix(agent): 空转回合注入续跑指令,复用空响应重试次数
模型有时一整轮只输出思考、既不给正文也不调工具,harness 视为自然结束 (end_turn 且无 tool_use),直接以 completed + 空总结收场,一条未完成的意图 断在半路,表现为「任务正常结束但没有文字总结」。五层 LLM 重试均够不着它 (非错误路径),SDK 空响应重试也够不着(以是否 yield 过事件判空,而思考增量 本身就是事件)。 work 的 Stop 钩子现识别这种回合并注入一条续跑指令,让模型带着已有思考继续 执行下一步——不选原样重发,因这类空转由提示词与上下文形状决定,是稳定行为 而非随机抽风。次数复用 LLM 页「空响应重试次数」(默认 2、-1 关闭),是一条 意图的总量闸而非连续次数(harness 已限死连续空转只推一次)。
This commit is contained in:
1 parent
eb5290039a
commit
3c2a60fba7
4 files changed
+255
-4
No files matched your search
@@ -78,3 +78,29 @@ func (e *Engine) modelErrorRetryPolicy() (retries int, backoff time.Duration) {
|
||||
}
|
||||
return retries, backoff
|
||||
}
|
||||
|
||||
// emptyTurnNudgeLimit resolves how many empty-turn continuations one work may
|
||||
// inject (see steerHooks.Stop). It deliberately reuses layer ②'s knob —— 「空响应
|
||||
// 重试次数」:两者是同一件事的两种手段。SDK 那层管「一个内容块都没有」,手段是把
|
||||
// 同一个请求原样重发;这里管「只有思考、既无正文也无工具」,手段是追加一条指令让
|
||||
// 模型带着已有思考接着做(原样重发对这种由上下文形状决定的空转没有意义)。判空口径
|
||||
// 不同是因为 SDK 以「有没有 yield 过事件」为准,而思考增量本身就是事件——但用户配
|
||||
// 「空响应重试几次」时想表达的是「模型没产出实质内容就再来一次」,两层共用一个次数
|
||||
// 才对得上这个心智。
|
||||
//
|
||||
// 读全局策略而不是某个 profile 的覆盖:一个 run 中途可能因故障转移换 profile,而这
|
||||
// 是整条意图的总量闸,不该跟着换端点而变。语义与 SDK 的 emptyRetries() 同构:
|
||||
// 0 = 默认 defaultEmptyTurnNudges;-1(负) = 关闭空转续跑;>0 = 用该值。
|
||||
func (e *Engine) emptyTurnNudgeLimit() int {
|
||||
if e == nil || e.m == nil || e.m.pg == nil {
|
||||
return defaultEmptyTurnNudges
|
||||
}
|
||||
switch n := e.m.pg.LLMRetryPolicy().Empty.Attempts; {
|
||||
case n == 0:
|
||||
return defaultEmptyTurnNudges
|
||||
case n < 0:
|
||||
return 0
|
||||
default:
|
||||
return n
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user