DeepSeek 输入 <think> 就乱说话?不是隐藏咒语,是前缀续写效应

0 阅读12分钟

前言

网上掀起了 DeepSeek 安全隐患的热潮?具体是有人发现:在新对话里什么问题都不问,只输入一个 <think>,模型可能直接进入“已思考”状态,开始自说自话、角色扮演,甚至输出看似无关的推理片段。有人把它称为隐藏咒语,也有人怀疑这是训练数据泄漏。那它到底是安全漏洞,还是机器学习里的前缀续写效应?本文就从这个现象出发,拆解 <think> 背后的自回归生成、CoT 分布和工程边界。

think


一、先说结论:这不是咒语,而是前缀改变了生成分布

💡 核心结论: <think> 未闭合更像是给模型提供了一个“思考区块正在开始、但还没有结束”的强前缀。模型会沿着这个前缀继续生成,于是输出更容易进入 reasoning / CoT 风格的续写分布。

这件事可以拆成一条因果链:

输入 <think>
-> 模型看到一个类似“思考开始”的前缀
-> 后续 token 概率分布向 reasoning / CoT 内容倾斜
-> 如果没有明确问题作为锚点,生成方向就更容易漂移
-> 输出可能出现故事、计算、角色扮演、无关解释等训练分布中的模式片段

这里最关键的词是:前缀

大语言模型不是在“理解完整问题后再查数据库回答”,而是根据当前上下文不断预测下一个 token。一个不同的开头,会改变后面所有内容的概率分布。


二、现象对照:为什么新对话里的 <think> 更明显

在观察实验中,一个重要条件是:只有开新对话,<think> 的效果才明显

这很合理。因为旧对话里已经存在上下文,模型有历史问题、历史回答、任务方向作为锚点;而新对话几乎没有上下文,裸 <think> 就会成为最强的上下文信号。

可以用下面几组输入做对照:

输入内容观察重点可能说明
<think>是否进入长段思考、自述、跑偏输出未闭合思考前缀可能触发 reasoning 续写
<think></think>是否比未闭合版本更稳定闭合标签削弱“继续写思考”的压力
<abc>是否只是解释普通字符串测试未知 XML/HTML 样式标签本身是否足以触发漂移
1+1 等于几是否稳定回答 2明确任务锚点会约束生成方向
&lt;think&gt;是否被当作普通文本转义形式可用于判断前端/模型是否按标签处理

think thinkthink abc

从这些对照可以看到:普通问题会把模型拉回明确任务,而裸 <think> 更容易让模型进入“我应该开始思考点什么”的状态。

更精确地说,<abc> 测的是“未知标签形式”的影响,<think> 测的是“已知 reasoning / CoT 结构标记”的影响。两者对比的意义在于:如果 <abc> 没有明显触发同类输出,而 <think> 触发了,那么关键就不是尖括号或标签形式本身,而是 think 这个字符串与“思考开始”模式之间的强关联。


三、从机器学习角度看:模型为什么会“继续写”

先补一个核心定义:什么是 CoT / reasoning 分布?

CoT 是 Chain-of-Thought,也就是“思维链”。这类训练文本通常长得像:

问题
思考过程:我们可以先……然后……注意这里可能有陷阱……
最终答案

它的特征是包含大量中间推理、自我修正、步骤拆解和草稿式表达。模型学过大量这类文本后,就会学到一种模式:遇到“开始思考”的信号时,后面更可能接一段推理过程,而不是直接输出最终答案。

所以本文说的 reasoning / CoT 续写分布,不是一个神秘开关,而是模型在训练中学到的一类文本模式:先打草稿,再给答案

大语言模型的基础生成方式是自回归生成。简单说,就是:

P(下一个 token | 前面所有 token)

模型每一步都根据前面已经出现的内容,预测下一个最可能出现的 token。然后把新 token 接到上下文后面,再继续预测下一个。

所以模型的工作方式不是:

看到问题 -> 找标准答案 -> 输出答案

而更接近:

看到前缀 -> 判断后面最像什么 -> 继续写下去

这就是为什么 <think> 这种前缀有影响。它不是普通的三个单词,而是一个很像 reasoning 模型内部格式的结构提示。

如果模型在训练或对齐过程中大量见过类似结构:

<think>
这里是一段推理、分析、尝试、修正……
</think>
最终答案……

那么当它在新对话开头看到一个未闭合的 <think> 时,后续分布自然会更靠近“继续写思考内容”。

可以把这个过程想象成一张巨大的高维概率地形图。用户输入就像把一颗小球放到某个地形点上:

输入类型小球落点后续趋势
普通问题标准问答区域沿着“解释问题、给出答案”的路径前进
<abc>未知标签区域可能询问含义,也可能解释输入本身
<think>思考链深谷入口更容易沿着“先推理、再输出”的谷底继续滚动
<think> + 明确问题思考链入口 + 任务锚点既可能展示推理痕迹,也更容易回到具体问题

因此,“乱说”不是模型突然失控,而是它被放到了“思考链”这条深谷的起点;在没有具体问题导航时,它只能顺着概率地形继续滑下去。


四、为什么“未闭合”比“闭合”更容易触发续写

未闭合结构会给语言模型一种很强的补全压力。

比如人类看到:

<div>
  这里是一段内容

也会自然期待后面出现:

</div>

语言模型同样会学习这种结构延续关系。<think> 的特殊之处在于,它不只是一个普通 XML/HTML 样式标签,还容易和 reasoning / CoT 的格式联系起来。

对比一下:

写法结构状态模型可能倾向
<think>思考区块刚开始,未闭合继续写思考内容
<think></think>空思考区块已闭合询问用户具体问题或解释输入
<abc>未知普通标签解释字符串或询问意图
&lt;think&gt;转义文本更可能被当成普通文本

💡 核心结论: 真正重要的不是 <think> 这几个字符“神秘”,而是它在模型分布里像一个“思考内容开始”的结构前缀。


五、为什么它会输出看似“训练数据碎片”的内容

当模型只有 <think>,但没有真实问题时,它缺少生成锚点。

正常情况下,用户会问:

请解释 Transformer 的注意力机制

这时模型的输出会被“Transformer”“注意力机制”“解释”这些关键词约束。

但如果新对话里只有:

<think>

模型知道“好像要开始思考”,却不知道“思考什么”。于是它只能从训练过程中学到的大量模式里继续采样。可能是数学题,可能是对话,可能是故事,也可能是某种角色扮演。

这会让输出看起来像“从训练数据里冒出来的碎片”。

为什么会出现故事、角色扮演或看起来很具体的场景?一个合理解释是:许多 reasoning 样本本身就不是纯公式,而是带有场景的任务文本。例如数学题会写“我手里有 3 个苹果”,代码题会描述一个业务场景,角色类指令会要求模型先分析身份、目标和约束。对模型来说,这些都是“思考链”训练分布的一部分。

<think> 把模型推向这类分布,但又没有真实问题限制方向时,模型就可能抽到某个场景化模式:一会儿像数学题,一会儿像小说开头,一会儿像角色扮演。这更像是训练分布中的模式回声,而不是可以直接判定为逐字泄漏。

但这里必须谨慎区分两个概念:

说法是否可以直接下结论说明
训练分布中的模式片段可以谨慎使用表示模型生成了训练中常见的风格、结构、任务形式
疑似训练数据碎片可以作为待验证说法需要继续检索输出文本是否能匹配公开语料
训练数据泄漏不建议直接写死需要大段逐字一致证据和可重复实验

⚠️ 误区:输出像训练数据,就等于训练数据泄漏

正确理解: 大模型确实可能记忆并复现训练数据片段,但“风格像”“结构像”“主题像”都不能直接证明逐字泄漏。要证明泄漏,至少需要把输出文本拿去检索,确认是否存在大段逐字一致来源。


六、DeepSeek API 文档给了哪些工程证据

DeepSeek 官方 reasoning model API 文档里有一个非常重要的工程事实:deepseek-reasoner 的输出中,思考内容和最终回答是分开的。

字段含义工程处理建议
reasoning_content模型生成的 CoT / 思考内容可以展示、折叠、记录,但不要直接带回下一轮请求
content最终回答多轮对话中作为 assistant 内容加入历史消息

官方文档还说明:如果输入消息序列中包含 reasoning_content 字段,API 会返回 400 错误。因此,下一轮对话要移除旧的 reasoning_content,只保留最终回答 content

✅ 正确处理 reasoning 输出的最小示例

from openai import OpenAI

client = OpenAI(
    api_key="<DeepSeek API Key>",
    base_url="https://api.deepseek.com"
)

messages = [
    {"role": "user", "content": "9.11 和 9.8 哪个更大?"}
]

response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=messages
)

reasoning_content = response.choices[0].message.reasoning_content
content = response.choices[0].message.content

# 下一轮只保留最终回答 content,不把 reasoning_content 放回 messages
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": "strawberry 里有几个 r?"})

这说明 reasoning 内容本来就应该作为一个独立通道处理,而不是粗暴混进正文、标题、TTS 或下一轮上下文。


七、这和 Prompt Injection 有什么关系

从安全角度看,<think> 现象只是一个入口更低的例子。更大的问题是:模型如何区分“用户普通文本”和“系统结构信号”?

如果一些原本只应该由系统后端生成的结构标记,被用户、网页、RAG 文档或工具返回值伪造,就可能出现更复杂的问题。

场景可能风险
普通聊天输入 <think>输出漂移、进入思考续写
RAG 文档夹带伪造角色标签模型误把文档内容当成指令
Agent 工具返回伪造系统提示模型可能误判权限和操作意图
标题生成/TTS 混入 reasoning 内容用户体验混乱,甚至泄露不该展示的中间内容

所以工程上通常需要:

  • 对用户输入做必要的转义和边界隔离;
  • 不让用户文本直接伪装成系统控制 token;
  • 将 reasoning 内容与最终回答分离处理;
  • 对 RAG 文档和工具返回值标记来源;
  • 对自动标题、摘要、TTS 等任务流过滤 <think> 或 reasoning 内容。

八、这篇文章里哪些结论需要保留边界

为了避免把现象写成过度结论,下面这些说法不建议直接使用:

⚠️ 误区:DeepSeek 输入 <think> 就说明训练数据泄漏

正确理解: 目前更稳妥的说法是:<think> 未闭合可能让模型进入 reasoning 续写分布,并生成训练分布中常见的模式内容。是否构成训练数据泄漏,需要逐字检索和可重复实验。

⚠️ 误区:截图能证明 tokenizer 一定把 <think> 当作特殊 token id

正确理解: 截图只能证明产品层输出行为异常或有差异,不能直接证明内部 tokenization。要证明这一点,需要 tokenizer 配置、API 复现、模型模板或官方说明。

⚠️ 误区:一次截图就能证明稳定触发

正确理解: 单次截图适合做现象展示,不适合证明概率结论。要证明“稳定触发”,需要每组输入重复测试,并记录时间、模式、入口和完整输出。


总结

关键问题稳妥结论
<think> 为什么会影响输出?它像一个 reasoning / CoT 的开始前缀,会改变后续生成分布
为什么新对话更明显?新对话没有历史锚点,<think> 成为最强上下文信号
为什么会乱写?模型必须根据前缀继续预测,下游分布缺少具体任务约束
是否等于训练数据泄漏?不能直接下结论,需要逐字检索和可重复实验
工程上该注意什么?分离 reasoning_contentcontent,不要把旧思考链带回下一轮

💡 核心结论: <think> 未闭合不是所谓隐藏咒语,而是一个前缀触发分布变化的机器学习现象。它让模型更像是在继续写一段未完成的思考过程;当没有明确问题约束时,输出就可能漂移到训练分布中的各种模式。

如果要继续验证,最有价值的实验不是多截几张“跑偏图”,而是做系统对照:<think><think></think><abc>、普通问题、完整转义 &lt;think&gt;,每组重复多次,再把疑似训练片段拿去逐字检索。这样才能把“有趣现象”推进到“可信结论”。