Skip to content

是否需要修复 s11/code.py的 agent_loop 中 fallback 模型切换不生效的问题(lambda 默认参数在定义时冻结 model_id) #438

Description

@qicaijie99

背景

with_retry 负责临时错误(429/529)的指数退避,并在连续 529 达到 MAX_CONSECUTIVE_529 后切换到 FALLBACK_MODELwith_retry 这一侧的切换逻辑(state.current_model = FALLBACK_MODEL)本身是对的。
但 LLM 调用是通过 lambda 传入 with_retry 的,其中 model 用默认参数 mdl=state.current_model 捕获。该写法在单模型、未配置 fallback 的常规路径下工作正常,但一旦触发 fallback,切换不会生效:虽然state.current_model 已被改为 FALLBACK_MODEL,实际请求却仍发往主模型,因为被lambda默认参数在定义时按值冻结。

改动一览

lambda 默认参数 mdl=state.current_model → 调用时读取 state.current_model

问题:默认参数 mdl=state.current_model 只在 lambda 定义时求值一次,被冻结为 PRIMARY_MODELwith_retry 在 retry 循环里反复调用的是同一个 lambda 对象,默认参数不会随调用重算。因此即便 fallback 分支修改了 state.current_model,callback 内 model=mdl 读到的仍是被冻结住的旧值。

ps:该 lambda 在 while True 每轮迭代会重建,理论上下一轮可读到新模型;但 with_retry 在切换后仍于当轮 retry 循环内用旧模型继续重试,直至 MAX_RETRIES 耗尽抛 RuntimeError,被外层 except 判为 unrecoverable 并 return——loop 往往在回到循环顶部、有机会用上新模型之前就已退出。

修复:让 callback 在调用时读取 state.current_model,不再用默认参数冻结。

影响范围

对单模型 / 未配置 FALLBACK_MODEL 的运行:行为完全一致,无 regression。
对持续 529 触发 fallback 的场景:从"切换无效 → 耗尽重试抛 RuntimeError → 被判 unrecoverable 退出"改为"真正切到 FALLBACK_MODEL 继续请求"。

代码

call site 建议如下改动(with_retry 内部无需改动,已经是正确的):

# before:mdl 在 lambda 定义时被冻结,fallback 切换读不到
response = with_retry(
    lambda mt=max_tokens, mdl=state.current_model:
        client.messages.create(
            model=mdl, system=system, messages=messages,
            tools=TOOLS, max_tokens=mt),
    state)

# after:改为具名 def,model 在调用时读取,fallback 生效
def call_model():
    return client.messages.create(
        model=state.current_model,        # 调用时读取,fallback 才能生效
        system=system, messages=messages,
        tools=TOOLS, max_tokens=max_tokens)

response = with_retry(call_model, state)

或者如果保留 lambda,可以仅删除 mdl 默认参数

response = with_retry(
    lambda mt=max_tokens:                 # 只冻结 max_tokens,不冻结 model
        client.messages.create(
            model=state.current_model,    # 关键:改为调用时读取
            system=system, messages=messages,
            tools=TOOLS, max_tokens=mt),
    state)

注:目的是max_tokens 在重试期间不被 with_retry 修改,保留 mt=max_tokens 冻结无影响;但 model 必须改为调用时读取。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions