Skip to content

feat(pipeline): add keep-first-think-only toggle for multi-round CoT#2331

Open
MIKAZE3 wants to merge 3 commits into
langbot-app:masterfrom
MIKAZE3:feat/keep-first-think-only
Open

feat(pipeline): add keep-first-think-only toggle for multi-round CoT#2331
MIKAZE3 wants to merge 3 commits into
langbot-app:masterfrom
MIKAZE3:feat/keep-first-think-only

Conversation

@MIKAZE3

@MIKAZE3 MIKAZE3 commented Jul 12, 2026

Copy link
Copy Markdown

概述 / Overview

新增「仅保留第一条思维链」(keep-first-think-only) 配置项,仅在启用「删除思维链」时生效。开启后,多轮工具调用中第一轮保留思维链(用户可见推理过程),后续轮次自动删除思维链。

实现内容:

  1. output.yaml 新增配置字段:在 remove-think 下新增 keep-first-think-only 布尔字段,含中英文 label 和 description。

  2. default-pipeline-config.json 新增默认值"keep-first-think-only": false

  3. localagent.py 分轮次控制:读取 keep_first_think_only 配置,计算 _strip_think_first_round = remove_think and not keep_first_think_only。pre-loop(第一轮)使用 _strip_think_first_round(=False,保留思维链),tool-loop(后续轮次)使用原始 remove_think(=True,删除思维链)。

  4. ws_client.py 流式复用:新增 fallback_after_round 属性和 _stream_rounds 计数器。当 fallback_after_round >= 1 时,第一轮 is_final 不清除 stream_id,使第二轮能复用同一个流式会话,所有内容在同一个消息气泡中展示。

  5. respback.py 配置传递:在调用 reply_message_chunk 前,将 query.pipeline_config 挂载到 query.message_event._langbot_pipeline_config,使平台适配器能读取 misc 配置。

  6. wecombot.py 配置读取:从 _langbot_pipeline_config 读取 remove-thinkkeep-first-think-only,当两者都为 True 时设置 self.bot.fallback_after_round = 1

同时包含两个前置修复提交(think-strip 基础):

  • _ThinkStripState 状态机:处理跨 chunk 边界的 `` 标签剥离
  • _strip_think 方法:正则匹配 `` 和 CRETIRE_* 标签

更改前后对比截图 / Screenshots

修改前 / Before:

  • 开启「删除思维链」后,所有轮次的思维链都被删除,用户完全看不到推理过程
  • 或关闭「删除思维链」后,每轮都显示思维链,信息冗余
image

修改后 / After:

  • 开启「删除思维链」+「仅保留第一条思维链」后,第一轮显示思维链(推理过程可见),后续轮次仅显示实际回复内容
  • 多轮内容在同一个流式消息气泡中展示
image

检查清单 / Checklist

PR 作者完成 / For PR author

请在方括号间写x以打勾 / Please tick the box with x

  • 阅读仓库贡献指引了吗? / Have you read the contribution guide?
  • 我已签署或将在机器人提示后签署 CLA。 / I have signed, or will sign when prompted by the bot, the CLA.
  • 与项目所有者沟通过了吗? / Have you communicated with the project maintainer?
  • 我确定已自行测试所作的更改,确保功能符合预期。 / I have tested the changes and ensured they work as expected.

项目维护者完成 / For project maintainer

  • 相关 issues 链接了吗? / Have you linked the related issues?
  • 配置项写好了吗?迁移写好了吗?生效了吗? / Have you written the configuration items? Have you written the migration? Has it taken effect?
  • 依赖加到 pyproject.toml 和 core/bootutils/deps.py 了吗 / Have you added the dependencies to pyproject.toml and core/bootutils/deps.py?
  • 文档编写了吗? / Have you written the documentation?

MIKAZE3 added 3 commits July 12, 2026 11:57
…tible models

MiniMax-M3 (and other OpenAI-compatible providers) emit chain-of-thought
reasoning directly in the content field wrapped in  tags, instead
of using a separate reasoning_content field or the legacy CRETIRE_REASONING
markers. The existing remove_think logic only handled CRETIRE_* tags, so
think blocks leaked into user-visible output even when remove_think was enabled.

- Add _ThinkStripState: a stateful filter that correctly handles  tags
  split across streaming chunk boundaries.
- Add _strip_think classmethod with regex patterns for both  and
  CRETIRE_* tags.
- Wire think_state into invoke_llm_stream so deltas are filtered before
  reaching the accumulator.
- Add remove_think safety net in _StreamAccumulator so the final message
  from tool-call rounds also gets stripped.
- Fix remove_think resolution to use defensive nested .get() so
  pipelines missing output.misc don't raise AttributeError.
New "仅保留第一条思维链" option under output.misc, effective only when
remove-think is enabled. When on, the first LLM round keeps its
chain-of-thought (visible to the user) while subsequent tool-call
rounds strip CoT.

- output.yaml: add keep-first-think-only boolean field
- default-pipeline-config.json: add default false
- localagent.py: pre-loop uses _strip_think_first_round (=False when
  keep-first), tool-loop uses raw remove_think (=True)
- ws_client.py: fallback_after_round + round counter to keep stream_id
  alive across rounds so the next round reuses the same stream
- respback.py: stash pipeline_config on message_event for adapter access
- wecombot.py: read config and set fallback_after_round=1 when enabled
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. eh: Feature enhance: 新功能添加 / add new features IM: wecom 企业微信 适配器相关 / WeCom and WeComCS adapter related m: Provider LLM 模型相关 / LLMs management pd: Need testing pending: 待测试的PR / PR waiting to be tested labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

eh: Feature enhance: 新功能添加 / add new features IM: wecom 企业微信 适配器相关 / WeCom and WeComCS adapter related m: Provider LLM 模型相关 / LLMs management pd: Need testing pending: 待测试的PR / PR waiting to be tested size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant