From 93adb93b9d6fa4d8e5d279e68bbdbcd31b8b44ab Mon Sep 17 00:00:00 2001 From: weichen Date: Sat, 3 Oct 2026 15:53:42 +0000 Subject: [PATCH] Retry direct model calls before the first delta Why: The direct path never retried provider timeouts, so a single 429 failed the run. Evidence: go test ./internal/settings/ ./internal/runtime/ passed. Default runtime.llmRetries is 2. Impact: Retries happen only when the stream has not forwarded a delta. Injected test streams are not wrapped. Refs: feature/llm-robustness Co-authored-by: Cursor --- deps/agent-native | 2 +- deps/agent-squad | 2 +- internal/runtime/llm.go | 3 ++- internal/settings/settings.go | 2 ++ internal/settings/settings_test.go | 16 ++++++++++++++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/deps/agent-native b/deps/agent-native index d84a67c..9f5751c 160000 --- a/deps/agent-native +++ b/deps/agent-native @@ -1 +1 @@ -Subproject commit d84a67cd240e785291873c19686765c1d1f60c22 +Subproject commit 9f5751c20527ae223323650ab8da877fc7b89601 diff --git a/deps/agent-squad b/deps/agent-squad index 02c0d6a..9efce79 160000 --- a/deps/agent-squad +++ b/deps/agent-squad @@ -1 +1 @@ -Subproject commit 02c0d6aae4f1af3265b94cdc3172eccefa2cb3c9 +Subproject commit 9efce79b86044db0594f8761cd01c42677a90931 diff --git a/internal/runtime/llm.go b/internal/runtime/llm.go index 2a22796..f4a2877 100644 --- a/internal/runtime/llm.go +++ b/internal/runtime/llm.go @@ -200,7 +200,8 @@ func (l *LLMRunner) Execute(rc RunCtx) error { stream := l.Stream if stream == nil { client := openLLMClient(route.Base, route.Key) - stream = client.Stream + retries := int(eff.GetNumber("runtime.llmRetries", 2)) + stream = agentllm.WithRetry(client.Stream, retries, nil) } // 知识注入:当前消息包含 @knowledge 引用时检索注入(最小实现:显式引用)。 diff --git a/internal/settings/settings.go b/internal/settings/settings.go index 2c34ecf..2b6eaa6 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -36,6 +36,7 @@ func SystemDefaults() map[string]any { "rateLimitPerMin": 0, // 租户限流(次/分;0 = 不限) "redisAddr": "", // 限流共享后端(原生 Redis addr;空 = 进程内) "verify": true, // 终轮确定性检查与独立验证 + "llmRetries": 2, // 直连模型在尚未输出时的额外重试次数 }, "storage": map[string]any{ "dataDir": "./data", // 启动注入实际值(--data) @@ -81,6 +82,7 @@ func Schema() []core.SettingDesc { {Key: "runtime.rateLimitPerMin", Group: "runtime", Label: "租户限流(次/分)", Type: core.SettingNumber, Default: 0, Effect: core.EffectHot, Description: "0 = 不限;多用户建议 300–1200"}, {Key: "runtime.redisAddr", Group: "runtime", Label: "Redis 地址(限流共享)", Type: core.SettingString, Default: "", Effect: core.EffectRestart, Description: "原生 Redis(如 127.0.0.1:6379);空 = 进程内限流(单实例)"}, {Key: "runtime.verify", Group: "runtime", Label: "终轮结果检查", Type: core.SettingBool, Default: true, Effect: core.EffectNewRun, Description: "终轮确定性检查与独立验证"}, + {Key: "runtime.llmRetries", Group: "runtime", Label: "模型重试次数", Type: core.SettingNumber, Default: 2, Effect: core.EffectNewRun, Description: "直连模型在还没有任何输出时,遇到超时、限流或服务端错误的额外重试次数。0 = 不重试。已经开始输出后不再重试"}, {Key: "runtime.composeFinal", Group: "runtime", Label: "终轮整理回复", Type: core.SettingBool, Default: true, Effect: core.EffectNewRun, Description: "用过工具的回合结束后,再用主模型写一段直接回答用户的最终回复"}, {Key: "context.maxHistoryMessages", Group: "context", Label: "历史窗口条数", Type: core.SettingNumber, Default: 40, Effect: core.EffectNewRun}, {Key: "context.maxToolResultChars", Group: "context", Label: "工具结果上限(字符)", Type: core.SettingNumber, Default: 20000, Effect: core.EffectNewRun}, diff --git a/internal/settings/settings_test.go b/internal/settings/settings_test.go index 446d501..50a52a7 100644 --- a/internal/settings/settings_test.go +++ b/internal/settings/settings_test.go @@ -2,6 +2,22 @@ package settings import "testing" +func TestLLMRetriesDefault(t *testing.T) { + block := SystemDefaults()["runtime"].(map[string]any) + if block["llmRetries"] != 2 { + t.Fatalf("llmRetries = %#v", block["llmRetries"]) + } + var found bool + for _, desc := range Schema() { + if desc.Key == "runtime.llmRetries" && desc.Default == 2 { + found = true + } + } + if !found { + t.Fatal("schema missing runtime.llmRetries default 2") + } +} + func TestDeliverableSettingsPresent(t *testing.T) { defaults := SystemDefaults() block, ok := defaults["deliverables"].(map[string]any)