gogo-agent

为什么要关闭模型的深度思考?

ReAct 的 Reason→Act→Observe 循环,本质上已经把"思考"拆成了多轮显式的 LLM 调用。每一轮 call 中,模型看到完整的 history(包括之前的推理轨迹、工具返回结果),然后决定下一步。这意味着模型不…

TL;DR

ReAct 的 Reason→Act→Observe 循环,本质上已经把"思考"拆成了多轮显式的 LLM 调用。每一轮 call 中,模型看到完整的 history(包括之前的推理轨迹、工具返回结果),然后决定下一步。这意味着模型不…

ReAct 的 Reason→Act→Observe 循环,本质上已经把"思考"拆成了多轮显式的 LLM 调用。每一轮 call 中,模型看到完整的 history(包括之前的推理轨迹、工具返回结果),然后决定下一步。这意味着模型不需要在单次调用内部做太深的多步推理——框架替它做了"分步思考"的调度。 如果你同时开启 thinking mode,等于在每一轮 ReAct iteration 内部又嵌套了一层隐式 CoT,形成"思考中套思考"的结构。这带来几个实际问题: 延迟叠加。一个垂直 agent 完成一个用户请求通常需要 3-8 轮 tool call。thinking mode 每轮额外增加 2-10 秒(取决于 thinking budget),总延迟可能从 10 秒膨胀到 40-60 秒,对交互体验是致命的。 成本倍增。thinking tokens 虽然通常半价计费,但量大。每轮多 1000-3000 tokens 的 thinking,8 轮下来就是上万额外 tokens。 收益递减。ReAct 的每轮决策通常是"看当前状态→选一个 tool→填参数",这是一个相对浅的单步决策,thinking mode 擅长的深度多步推理在这里用不上。 但也不是完全不能开。关键判断标准是:这一轮调用中,模型是否需要做"不可分解的复合推理"。 典型该开的场景: Planning 阶段。如果你的 agent 有一个独立的 planner 节点(比如先让用户描述需求,一次性生成完整的多步计划),这个调用内部确实需要深度推理——分析约束、处理冲突、排列步骤。这里开 thinking 收益明显。 复杂结果解读。工具返回了一大段非结构化数据(比如一份合同文本、一段报错日志),需要模型做深度理解和判断后再决定下一步。这种"观察→深度理解→决策"在单轮内完成,thinking 有帮助。 最终回答生成。所有工具调用结束后,最后一轮需要综合所有观察结果给用户一个高质量回答。这里开 thinking 可以提升回答质量,且只发生一次,延迟可接受。 典型不该开的场景: 纯路由/分发。意图识别、选择调哪个 tool、填参数——这些是浅层决策,thinking 纯粹浪费。 简单工具链。如果 tool 的 schema 清晰、参数映射直接,模型不需要"想"就知道该调什么。 高频循环。比如 agent 在一个 loop 里反复调同一个 API 翻页取数据,每轮都开 thinking 是灾难。

gogo做法

百炼上的 qwen3.7-max 等模型支持 enableThinking,默认是开启的,开启时模型会在正式输出前,先生成一段内部推理链(thinking tokens)。这段推理不返回给用户,但它帮助模型「想清楚再说」,类似人类做题前在草稿纸上列算式。 在 gogo-agent 里的我们定义了两个model:

// ModelConfig.java — 同一个模型,分两个 Bean 注册,唯一区别是 thinking 开关
@Bean("strongModel")
public Model strongModel() { ... .enableThinking(false).build(); }

@Bean("strongModelWithThinking")
public Model strongModelWithThinking() { ... .enableThinking(true).build(); }

各个Agent针对thinking的开关情况 | Agent | 模型 | enableThinking | thinkingBudget | 关/开理由 | | --- | --- | --- | --- | --- | | MasterAgent | strongModel | 关 | — | 纯路由分发,不需要推理 | | ItineraryManageAgent | strongModel | 关 | — | 结构化 CRUD(差旅单申请/查询/修改),流程明确无歧义 | | InfoAgent | stableModel | 关 | — | RAG 检索 + 格式化,简单问答 | | ItineraryReviewAgent | stableModel | 关 | — | 调审核工具 → 格式化输出,机械性 | | ItineraryPlanAgent | strongModelWithThinking | 开 | 2048 | 多方案比价、排序、审核循环,需要全局权衡 | | BookingAgent | strongModelWithThinking | 开 | 2048 | 多步下单编排 + 审批门禁 + 错误恢复,决策链复杂 |

我们只有针对「需要在多个候选里做权衡」或「多步有状态编排、错一步不可逆」的 Agent 才开思考,比如我们的形成规划和预定两个智能体。 路由型、检索型、CRUD 型全部关掉。 但是,就算是开启了thinking,我们也不是说就无限用的,我们做了两个限制。

thinkingBudget

thinkingBudget是百炼上支持的一个参数。这告诉模型「你最多想 2048 个 token 就必须开始输出」,避免模型陷入冗长的自我论证循环。

// BaseSubAgent.java
public GenerateOptions getEnableThinkingGenerateOptions(Integer thinkingBudget) {
    return GenerateOptions.builder()
            .cacheControl(true)
            // 深度思考模型有时会生成冗长的推理过程,增加等待时间并消耗更多 Token。
            // 通过 thinking_budget 参数可设置推理过程的最大 Token 数。
            .thinkingBudget(thinkingBudget)
            .build();
}

为什么是 2048?这是一个经验值权衡: - 太小(如 512):模型可能还没想清楚就被截断,反而产出更差结果。 - 太大(如 8192):每轮多 8K token 的延迟和费用,对一个 Agent 任务不划算。 - 2048 token 大约等于 1000~1500 汉字,够把一个中等复杂决策的推理链走完,同时把单轮额外开销控制在约 0.024 元以内。

规划前思考,规划后不思考

即使是开了思考的 ItineraryPlanAgent,也不是每一轮都在烧 thinking tokens。我们给PlanAgent提供了一个PlanAwareThinkingHook 实现了阶段感知的动态切换:

public class PlanAwareThinkingHook implements Hook {
    private final PlanNotebook planNotebook;
    private final int thinkingBudget;         // 2048

    private void handleReasoning(PreReasoningEvent event) {
        if (isPlanning()) {
            // Planning 阶段(还没创建计划):保持思考模式。
            // 模型需要从零开始设计方案,这是最需要深度推理的时刻。
        } else {
            // Execute 阶段(计划已创建,正在逐步执行):关闭思考,快速执行。
            disableThinking(event);
        }
    }

    private boolean isPlanning() {
        return planNotebook.getCurrentPlan() == null;  // 没有 plan = 还在规划
    }

    private void disableThinking(PreReasoningEvent event) {
        event.setGenerateOptions(GenerateOptions.builder()
                .cacheControl(true)                    // 保住百炼隐式缓存
                .additionalBodyParam("enable_thinking", false)  // 动态关闭思考
                .build());
    }

    private void enableThinking(PreSummaryEvent event) {
        // 最终总结阶段:重新开启思考,让模型综合全部结果做一次深度整理
        event.setGenerateOptions(GenerateOptions.builder()
                .cacheControl(true)
                .thinkingBudget(thinkingBudget)
                .build());
    }
}

逻辑分三阶段: | 阶段 | 判定条件 | 思考 | 理由 | | --- | --- | --- | --- | | Planning | planNotebook.getCurrentPlan() == null | 开 | 从零设计方案,需全局权衡 | | Execute | plan 已存在,逐步执行子任务 | 关 | 按计划调工具即可,不需要再重新想 | | Summary | PreSummaryEvent | 开 | 综合所有结果做一次最终整理 |

这意味着一个 15 轮的规划任务里,只有开头 2~3 轮(制定计划)和最后 1 轮(总结)在思考,中间 10+ 轮的工具执行全部关闭思考。省了 60~70% 的 thinking tokens,而推理质量不降——因为执行阶段只是按既定计划照做,不需要全局推理。

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。