首页
/ DeepSeek-R1 终端输出满屏 `<think>` 乱码?一行正则修复 Hermes 过滤 Bug

DeepSeek-R1 终端输出满屏 `<think>` 乱码?一行正则修复 Hermes 过滤 Bug

2026-04-15 16:37:46作者:董灵辛Dennis

接入 DeepSeek-R1 后的灾难现场:满屏 think 标签闪烁

满怀期待地看着官方 README 里那句牛逼哄哄的“支持所有兼容 OpenAI 格式的模型”,你兴冲冲地改了 config.yaml,把底层的 LLM 引擎换成了当下最火的 DeepSeek-R1 或者 Qwen3 系列。

你点了一根烟,敲下回车,以为接下来迎接你的是如同原生 IDE 插件般丝滑的智能体多轮交互。别做梦了。

当大模型开始输出的那一瞬间,迎面扑来的是一场 UI 灾难。你的终端并没有优雅地流式打印最终答案,而是像呕吐一样,疯狂地往外喷射带有 <think></think> 包裹的思维链(Chain of Thought)原文本。原本整洁的 TUI 界面瞬间被几千字的内心戏撑爆,光标四处乱闪,排版稀烂,如果你开了多线程 Agent,甚至会看到各个子 Agent 的 think 标签 串词混流。

你跑去翻 GitHub Issues 才发现,只要是带有推理过程(Reasoning)的国内大模型,无一例外全在这个坑里翻了车。

报错现象总结: 在 Hermes-Agent 中接入 DeepSeek-R1、Qwen3 或 MiniMax 等具有深度思考能力的大模型时,由于底层渲染函数 _clean_for_display 无法正确拦截处于流式输出传输中(Mid-stream)的未闭合 <think> 标签,导致思维链的冗余文本直接绕过过滤机制,写入 delta.content,最终完全污染终端 UI 的渲染区域。

官方文档对此避重就轻,这就导致了只要你不用 Claude,你的终端基本就在裸奔。问题究竟出在哪?今天我们直接扒开源码,看看这个低级 Bug 是怎么产生的。

扒开 _clean_for_display 源码:为什么官方的流式正则过滤会彻底失效?

要搞清楚为什么屏幕上会飙乱码,我们得先认清大模型 API 流式输出(SSE, Server-Sent Events)在底层协议上的割裂。

官方在适配 Claude 时,由于 Anthropic 的 API 极其规范,思维链的数据是走专属通道的(通过 delta.thinking_delta 字段传输)。Hermes-Agent 的底层拦截器会把这些数据优雅地交给 reasoning_callback 去处理,根本不会混入最终呈现给用户的 content 里。

但绝大多数兼容 OpenAI 格式的推理模型(如 DeepSeek-R1),简单粗暴到了极点——它们直接把带有 <think>...</think> 的推理过程,硬生生地塞进了标准文本输出的 delta.content 字段里!

为了对比更直观,我们可以看下主流大模型在 Hermes 下的底层链路差异:

接入的模型类型 推理数据传输字段 Hermes-Agent 底层处理逻辑 终端实际表现
Claude 3.5 Sonnet delta.thinking_delta reasoning_callback 独立渲染 完美、纯净、无乱码
DeepSeek-R1 delta.content 丢给 _clean_for_display 擦屁股 <think> 原码满屏飞
Qwen3-Max / MiniMax delta.content 丢给 _clean_for_display 擦屁股 <think> 原码满屏飞

官方为了给这些“不守规矩”的模型擦屁股,在终端渲染层搞了一个名叫 _clean_for_display 的过滤函数。初衷是好的,想用正则表达式把这些标签洗掉。但写这段代码的老哥,显然在处理流式(Streaming)数据时缺乏基本的工程常识。

官方原生的正则逻辑大概是这样的:查找 <think> 开头,</think> 结尾的完整字符串,然后将其替换为空。

这里的致命漏洞在于:流式输出是一块一块(chunk)吐出来的!

我们推演一下这个灾难性的时序逻辑:

  1. Chunk 1 到达:包含 <think>我需要先分析一下这个问题...。注意,此时 </think> 闭合标签根本还没发送过来
  2. 正则匹配失败_clean_for_display 拿着匹配完整闭合标签的正则去扫 Chunk 1,发现没有 </think>,于是判定这是“合法正文文本”。
  3. 脏数据上屏:这半截带着 <think> 的残缺内容,直接绕过防线,被无情地写入了 UI 缓冲区。
  4. Chunk N 到达:包含最后的 </think>。此时再去过滤已经毫无意义,因为前面的垃圾思维链字符早就打印在你的屏幕上了。

处理流式数据的正则,只防“闭合块”而不防“未闭合的半截块”,纯属掩耳盗铃。

手撕 Hermes 虚拟环境:兼顾完整与未闭合 think 标签的临时修法

病因找到了:流式数据截断导致正则失效。那我们要做的,就是对 _clean_for_display 函数进行降维重构。我们必须让它具备处理**“完整块(Complete blocks)”、“流传输中的未闭合块(Incomplete blocks)”以及“孤儿闭合标签”**的三重拦截能力。

如果你头铁,想自己动手丰衣足食,你需要经历以下繁琐的抢修过程:

首先,钻进深不见底的 Python 虚拟环境。如果你是用官方推荐的 uv 工具链安装的,去找类似 venv/lib/python3.11/site-packages/... 下的终端渲染脚本,死磕并定位到 _clean_for_display 函数,将它原有的弱智正则替换为以下三段式暴力清洗逻辑:

import re

def _clean_for_display(cleaned: str) -> str:
    # 阶段 1:先干掉已经完整闭合的块(处理速度最快,防范非流式或极快吐出的短块)
    cleaned = re.sub(
        r'<(think|thinking|reasoning|thought|REASONING_SCRATCHPAD)\b[^>]*>.*?</\1>',
        '', cleaned, flags=re.DOTALL | re.IGNORECASE,
    )
    
    # 阶段 2:核心修复!干掉还在流中、只有开头没有结尾的未闭合块(Mid-stream)
    # 注意:这里使用贪婪匹配到末尾,只要碰到 <think> 起手,后面的流数据全部静默吞噬
    cleaned = re.sub(
        r'<(think|thinking|reasoning|thought|REASONING_SCRATCHPAD)\b[^>]*>.*',
        '', cleaned, flags=re.DOTALL | re.IGNORECASE,
    )
    
    # 阶段 3:兜底清理上一轮或网络抖动遗留的孤儿闭合标签
    cleaned = re.sub(
        r'</(think|thinking|reasoning|thought|REASONING_SCRATCHPAD)>\s*',
        '', cleaned, flags=re.IGNORECASE,
    )
    
    return cleaned

这看起来简单对吧?但实操起来极其恶心。改完源码后,你需要保存、清空缓存、重启守护进程。万一你缩进敲错了一个空格,整个 Agent 的终端模块会直接抛栈然后挂起。

更痛的是,一旦 Hermes-Agent 官方发布个次要版本更新,你执行了 git pull 和重新构建,这段手敲的代码又会被原封不动地覆盖掉。在国内网络环境下,如果你稍微搞乱了依赖树,试图重新 uv pip install,又得面对茫茫多的 Github Raw 域名 Timeout 和 PyPI 连接重置。

拒绝无效折腾:直接获取重构版 display_cleaner.py 覆盖包

手动改虚拟环境源码这种事,既反直觉又极难维护。为了修一个本来就不该存在的输出 Bug,搭进去一个原本可以愉快 Coding 的下午,性价比极低。

为了拯救各位开发者的头发,我已经把这段针对 DeepSeek、Qwen3 等国内推理模型量身定制优化的正则清洗逻辑,直接做成了一个免编译的底层补丁包

你不需要去翻找虚拟环境的隐藏路径,也不需要懂正在疯狂变化的正则贪婪匹配逻辑。

👉 来 GitCode 极速下载已修复正则过滤逻辑的 [display_cleaner.py 覆盖包] (https://gitcode.com/GitHub_Trending/he/hermes-agent)

替换操作像呼吸一样简单:

  1. 访问 GitCode 仓库,拉取打包好的 display_cleaner.py(https://gitcode.com/GitHub_Trending/he/hermes-agent)
  2. 将文件直接扔进你的项目对应目录,覆盖原文件。
  3. 重新拉起 Hermes-Agent。

完成覆盖后,立刻去终端随便丢给 DeepSeek-R1 一个极其烧脑的逻辑题。你会发现,那些原本能刷满三屏的 think 标签 和推理过程被完美地静默吞噬在了内存层,屏幕上只剩下纯净、优雅、极速流式输出的最终答案。

拿去用,少踩坑,把精力留给真正有价值的业务逻辑编排上。

登录后查看全文
热门项目推荐
相关项目推荐