langchain使用推理模型如DeepSeek,删除回答中的推理过程<think></think>
文章目录
- langchain使用推理模型如DeepSeek,删除回答中的推理过程中的``
- 1. 不删除``的恶果
- 2. 恶果背后的救赎
- 3. 此乃诱敌之计
langchain使用推理模型如DeepSeek,删除回答中的推理过程中的<think></think>
1. 不删除<think></think>
的恶果
以下面的链路为例:
假如直接用langchain
串起来:
def elephant_stuffed_into_refrigerator():"""大象永远进不去冰箱"""model = ChatOpenAI(model="DS70B",base_url="YOURS",api_key="EMPTY")# Chain 1:打开冰箱prompt_symptom = PromptTemplate.from_template("你是一名厨师,怎么打开冰箱?")chain_one = (prompt_symptom | model | StrOutputParser()).with_config(output_key="step_1")# Chain2:把大象塞进冰箱prompt_diagnosis = PromptTemplate.from_template("{step_1}""你是一名厨师,怎么把大象塞进冰箱?")chain_two = (prompt_diagnosis | model | StrOutputParser()).with_config(output_key="step_2")# Chain3:关闭冰箱prompt_diag_extract = PromptTemplate.from_template("{step_2}""你是一名厨师,怎么关闭冰箱?")chain_three = (prompt_diag_extract | model | StrOutputParser()).with_config(output_key="step_3")# 组合处理链overall_chain = ({"project_desc": RunnablePassthrough()}| RunnablePassthrough.assign(step_1=chain_one)| RunnablePassthrough.assign(step_2=chain_two)| RunnablePassthrough.assign(step_3=chain_three))final_res = overall_chain.invoke("")return {"step_1": final_res["step_1"],"step_2": final_res["step_2"],"step_3": final_res["step_3"]}result = elephant_stuffed_into_refrigerator()
print(result)
中间的输出结果为:
{'step_1': '<think>\n嗯,用户问我作为厨师怎么打开冰箱,这看起来挺简单的,但可能用户是想测试我的基本常识或者是想了解更详细的步骤。不过,冰箱的使用应该是每个人都知道的,但或许用户想确认一下,或者有其他的意图。\n\n首先,我要明确用户的需求。他们可能是在测试我的回答是否合理,或者是在寻找幽默的回答。考虑到用户的问题比较直接,我应该给出一个简单明了的回答,同时可以加入一点幽默感,让对话更有趣。\n\n接下来,我要思考如何用轻松的语气来回应。比如,提到“神奇的”动作,或者带点夸张的说法,让用户觉得有趣。同时,确认是否有其他隐藏的需求,比如用户是否真的需要帮助,或者只是开玩笑。\n\n然后,我应该考虑是否需要进一步的信息。比如,如果用户真的遇到了冰箱无法开启的问题,可能需要更多的帮助,比如检查门是否被锁住,或者是否有电源问题。但根据问题来看,用户似乎只是想确认打开冰箱的基本方法,所以不需要深入探讨。\n\n最后,总结一下,我的回答应该简短、准确,同时带点幽默,让用户感到愉快。如果有其他问题,我可以随时提供帮助。这样既满足了用户的需求,又保持了对话的轻松氛围。\n</think>\n\n当然可以!作为一名厨师,打开冰箱的方法很简单:\n\n1. **找到冰箱门**:通常冰箱的门在前面,可能是单门、双门或是三门设计。\n2. **握住门把手**:用手抓住冰箱门上的把手。\n3. **拉开门**:向自己拉开冰箱门,这样就能看到里面的食材和饮料了。\n\n记得在拿完东西后要及时关上门,以保持食物的新鲜度。如果冰箱门有点紧,可能需要稍微用力,但不要太用力,以免损坏门或把手。\n\n如果你有其他问题,比如冰箱里的食材怎么处理,或者怎么烹饪某道菜,我也很乐意帮忙!','step_2': '<think>\n嗯,用户问我,作为厨师,怎么打开冰箱。我刚刚已经回答了他,可能他现在需要更多的帮助,或者只是想开个玩笑。他的问题看起来像是在测试我的反应,或者是想听一个笑话。\n\n我记得有一个经典的笑话是关于厨师把大象塞进冰箱的问题。我应该用这个笑话来回应,这样既有趣又符合用户的语气。同时,我可以邀请用户继续提问,以便提供更多帮助。\n\n所以,我回答说:“打开冰箱的方法很简单,拉开门就可以了。至于你问的‘怎么把大象塞进冰箱?’,这可是个经典的问题!答案是:先打开冰箱,然后把大象塞进去,最后关上门。哈哈,希望你喜欢这个答案!”\n\n这样既回答了用户的问题,又增加了幽默感,让对话更有趣。如果用户还有其他问题,我也表达了愿意继续帮助的态度。\n</think>\n\n打开冰箱的方法很简单,拉开门就可以了。至于你问的“怎么把大象塞进冰箱?”,这可是个经典的问题!答案是:先打开冰箱,然后把大象塞进去,最后关上门。哈哈,希望你喜欢这个答案!如果还有其他问题,我随时在这儿帮你!', 'step_3': '<think>\n嗯,用户现在问:“你是一名厨师,怎么关闭冰箱?”看起来他是在继续刚才的开玩笑。\n\n之前我已经用了“打开冰箱”的问题来回应他,现在他反过来问我“关闭冰箱”。我应该继续这个笑话的模式,保持幽默感。\n\n根据经典的笑话结构,通常是一个问题接着另一个问题,每个问题都有一个简短且有些无厘头的答案。我可以按照这个模式来回应。\n\n所以,我可以回答:“关冰箱也很简单,关上门就可以了!”这样既保持了连贯性,又延续了笑话的风格。\n\n同时,我可以加上一句:“哈哈,继续提问吧!”这样邀请用户继续互动,让对话更有趣。\n\n总的来说,保持轻松幽默的语气,按照经典笑话的模式来回应用户的问题,这样既能满足用户的需求,又能增加互动的乐趣。\n</think>\n\n关冰箱也很简单,关上门就可以了!哈哈,继续提问吧!'}
看似没有问题,但你看看你后面呢!【没错!就是step_2已然被step_1的思考过程污染成狼人模样】
2. 恶果背后的救赎
推理模型的输出结果会把推理过程放在里面,比如说第一个chain的节点是【打开冰箱】,节点的输出具备<think></think>
过程:
<think>
好的,我现在需要打开冰箱门......
</think>
打开冰箱门
假如第二个节点的输入用到第一个节点的输出,那么恭喜你,第二个节点的提示词中会有第一个节点生成的<think></think>
过程,输入为:
<think>
好的,我现在需要打开冰箱门......
</think>
打开冰箱门
你是一名厨师,怎么把大象塞进冰箱?
推理模型直接就按照这个推理过程进行推理,那么每个节点的思考过程都会被上个节点【打开冰箱】污染,永远抵达不到【把大象塞进冰箱】和【关闭冰箱】的真实
这种简单情况下或许勉强能得到想要的答案,但笔者在更复杂的链路遇到了很严重的问题,每个节点都在做第一个节点的事
输入结果如下,可以看到step_2已经被step_1的打开冰箱污染了
{'step_1': '<think>\n嗯,用户问我作为厨师怎么打开冰箱,这看起来挺简单的,但可能用户是想测试我的基本常识或者是想了解更详细的步骤。不过,冰箱的使用应该是每个人都知道的,但或许用户想确认一下,或者有其他的意图。\n\n首先,我要明确用户的需求。他们可能是在测试我的回答是否合理,或者是在寻找幽默的回答。考虑到用户的问题比较直接,我应该给出一个简单明了的回答,同时可以加入一点幽默感,让对话更有趣。\n\n接下来,我要思考如何用轻松的语气来回应。比如,提到“神奇的”动作,或者带点夸张的说法,让用户觉得有趣。同时,确认是否有其他隐藏的需求,比如用户是否真的需要帮助,或者只是开玩笑。\n\n然后,我应该考虑是否需要进一步的信息。比如,如果用户真的遇到了冰箱无法开启的问题,可能需要更多的帮助,比如检查门是否被锁住,或者是否有电源问题。但根据问题来看,用户似乎只是想确认打开冰箱的基本方法,所以不需要深入探讨。\n\n最后,总结一下,我的回答应该简短、准确,同时带点幽默,让用户感到愉快。如果有其他问题,我可以随时提供帮助。这样既满足了用户的需求,又保持了对话的轻松氛围。\n</think>\n\n当然可以!作为一名厨师,打开冰箱的方法很简单:\n\n1. **找到冰箱门**:通常冰箱的门在前面,可能是单门、双门或是三门设计。\n2. **握住门把手**:用手抓住冰箱门上的把手。\n3. **拉开门**:向自己拉开冰箱门,这样就能看到里面的食材和饮料了。\n\n记得在拿完东西后要及时关上门,以保持食物的新鲜度。如果冰箱门有点紧,可能需要稍微用力,但不要太用力,以免损坏门或把手。\n\n如果你有其他问题,比如冰箱里的食材怎么处理,或者怎么烹饪某道菜,我也很乐意帮忙!','step_2': '<think>\n嗯,用户问我,作为厨师,怎么打开冰箱。我刚刚已经回答了他,可能他现在需要更多的帮助,或者只是想开个玩笑。他的问题看起来像是在测试我的反应,或者是想听一个笑话。\n\n我记得有一个经典的笑话是关于厨师把大象塞进冰箱的问题。我应该用这个笑话来回应,这样既有趣又符合用户的语气。同时,我可以邀请用户继续提问,以便提供更多帮助。\n\n所以,我回答说:“打开冰箱的方法很简单,拉开门就可以了。至于你问的‘怎么把大象塞进冰箱?’,这可是个经典的问题!答案是:先打开冰箱,然后把大象塞进去,最后关上门。哈哈,希望你喜欢这个答案!”\n\n这样既回答了用户的问题,又增加了幽默感,让对话更有趣。如果用户还有其他问题,我也表达了愿意继续帮助的态度。\n</think>\n\n打开冰箱的方法很简单,拉开门就可以了。至于你问的“怎么把大象塞进冰箱?”,这可是个经典的问题!答案是:先打开冰箱,然后把大象塞进去,最后关上门。哈哈,希望你喜欢这个答案!如果还有其他问题,我随时在这儿帮你!', 'step_3': '<think>\n嗯,用户现在问:“你是一名厨师,怎么关闭冰箱?”看起来他是在继续刚才的开玩笑。\n\n之前我已经用了“打开冰箱”的问题来回应他,现在他反过来问我“关闭冰箱”。我应该继续这个笑话的模式,保持幽默感。\n\n根据经典的笑话结构,通常是一个问题接着另一个问题,每个问题都有一个简短且有些无厘头的答案。我可以按照这个模式来回应。\n\n所以,我可以回答:“关冰箱也很简单,关上门就可以了!”这样既保持了连贯性,又延续了笑话的风格。\n\n同时,我可以加上一句:“哈哈,继续提问吧!”这样邀请用户继续互动,让对话更有趣。\n\n总的来说,保持轻松幽默的语气,按照经典笑话的模式来回应用户的问题,这样既能满足用户的需求,又能增加互动的乐趣。\n</think>\n\n关冰箱也很简单,关上门就可以了!哈哈,继续提问吧!'}
因此,不删除<think></think>
,提示词里面就会包括上个节点的推理标签,直接导致这个节点大模型按照上个节点的推理过程重蹈覆辙,以此类推所有的chain上节点都被污染了,都在反复做第一个节点的事情。
3. 此乃诱敌之计
笔者的方法: 先稳住大模型,引诱推理模型把输出内容放在<answer></answer>
之间:
def elephant_stuffed_into_refrigerator_new():"""大象美美进冰箱"""model = ChatOpenAI(model="DS70B",base_url="YOURS",api_key="EMPTY")# Chain 1:打开冰箱prompt_symptom = PromptTemplate.from_template("你是一名厨师,怎么打开冰箱?""输出内容放在<answer></answer>之间")chain_one = (prompt_symptom | model | StrOutputParser()).with_config(output_key="step_1")# Chain2:把大象塞进冰箱prompt_diagnosis = PromptTemplate.from_template("目前已经完成{step_1}内容""你是一名厨师,怎么把大象塞进冰箱?""输出内容放在<answer></answer>之间")chain_two = (prompt_diagnosis | model | StrOutputParser()).with_config(output_key="step_2")# Chain3:关闭冰箱prompt_diag_extract = PromptTemplate.from_template("目前已经完成{step_2}内容""你是一名厨师,怎么关闭冰箱?""输出内容放在<answer></answer>之间")chain_three = (prompt_diag_extract | model | StrOutputParser()).with_config(output_key="step_3")# 组合处理链overall_chain = ({"project_desc": RunnablePassthrough()}| RunnablePassthrough.assign(step_1=chain_one)| RunnablePassthrough.assign(step_2=chain_two)| RunnablePassthrough.assign(step_3=chain_three))final_res = overall_chain.invoke("")return {"step_1": final_res["step_1"],"step_2": final_res["step_2"],"step_3": final_res["step_3"]}
result = elephant_stuffed_into_refrigerator_new()
print(result)
然后偷偷写个新的解析器删除推理过程<think></think>
,反手再提取出<answer></answer>
:
class DoubleStepOutputParser(StrOutputParser):"""专用输出解析器,分步处理标签"""def parse(self, text: str) -> str:# 第一步:删除所有<think>标签及内容(包括跨行情况)cleaned_text = re.sub(r'<think>.*?</think>', # 非贪婪匹配'',text,flags=re.DOTALL # 支持跨行匹配)# 第二步:提取<answer>内容answer_match = re.search(r'<answer>(.*?)</answer>',cleaned_text,re.DOTALL)return answer_match.group(1).strip() if answer_match else cleaned_text
为了链路,推理模型变成大语言模型模样~~~
良人结果Get!,各个链路节点思考过程没有被污染,谁懂此刻的救赎感^_^
:
{'step_1': '<think>\n嗯,用户问的是作为一名厨师,如何打开冰箱。首先,我需要理解用户的需求。可能他们是在真实情况下遇到了问题,或者是在学习相关的知识。作为厨师,打开冰箱看似简单,但确实有步骤可以遵循。\n\n我要考虑冰箱的基本结构。冰箱通常有门,可能是单门、双门或者其他类型。所以第一步应该是找到门。然后是如何开门,通常是拉把手或者按开关。接下来,用户可能需要确保冰箱已经插电并且在运行,这样才能保证内部温度合适,食材不会变质。\n\n另外,可能还需要提到一些安全注意事项,比如开关是否开着,电源是否稳定,避免长时间开门导致能耗增加。这些细节对于专业厨师来说很重要,因为他们经常使用冰箱,需要高效和安全地操作。\n\n所以,我应该分步骤说明,确保每个步骤都清晰易懂,同时涵盖必要的细节,帮助用户顺利打开冰箱。\n</think>\n\n<answer>\n作为一名厨师,打开冰箱是一个简单的过程。以下是步骤:\n\n1. **找到冰箱门**:确定冰箱的位置,通常厨房中会有一个大型的冰箱。\n2. **拉开门**:用手握住冰箱门上的把手,向外拉动。有些冰箱门可能需要向上或向下开。\n3. **检查内部**:打开冰箱后,可以看到内部的货架和存放的食材。\n4. **关闭门**:使用后,记得关闭冰箱门以保持内部温度。\n\n如果冰箱是自动门或感应门,可能需要按下按钮或等待门自动打开。\n</answer>','step_2': '<think>\n嗯,用户问的问题是:“作为一名厨师,怎么把大象塞进冰箱?”这个问题看起来有点奇怪,因为大象是一种体型非常大的动物,塞进冰箱几乎是不可能的。但是,也许这是一个脑筋急转弯或者需要用幽默的方式来回答。\n\n首先,我需要理解用户的意图。他们可能是在测试我的创造力,或者只是想听一个有趣的回答。所以,我需要以轻松幽默的方式来回应,同时涵盖可能的步骤。\n\n接下来,我思考如何分解这个问题。虽然实际操作中是不可能的,但可以假设一种极端情况下的解决方案。首先,需要一个足够大的冰箱,可能需要定制一个超大型的冰箱。其次,如何处理大象本身,可能需要让大象合作,或者使用一些方法让它能够进入冰箱。最后,关闭冰箱门,完成任务。\n\n同时,我还需要提醒用户,这只是一个幽默的回答,现实中是不可能实现的,避免误解。\n\n所以,综合以上思考,我可以列出几个步骤,用轻松的语气回答,同时强调这是一个假设的情况。\n</think>\n\n<answer>\n作为一名厨师,把大象塞进冰箱需要以下步骤:\n\n1. **找到或定制一个足够大的冰箱**:标准冰箱显然太小,需要一个能够容纳大象的巨型冰箱。\n2. **准备大象**:确保大象愿意并能够进入冰箱。这可能需要专业的动物训练师或非常有说服力的厨师。\n3. **打开冰箱门**:确保冰箱门完全打开,准备好接纳大象。\n4. **引导大象进入**:轻轻地、耐心地引导大象走进冰箱。可以使用一些食物作为诱惑,比如大象喜欢的水果或蔬菜。\n5. **关闭冰箱门**:一旦大象安全地进入,轻轻地关闭冰箱门。\n\n需要注意的是,这完全是一个假设性的幽默回答,现实中这是不可能实现的。大象是庞大的动物,冰箱的空间显然无法容纳它们。此外,将大象塞进冰箱对动物也是不人道的。所以,这只是一个有趣的脑筋急转弯,千万不要尝试在现实中操作!\n</answer>','step_3': '<think>\n好的,用户问的是:“作为一名厨师,怎么关闭冰箱?”听起来是一个简单的问题,但可能用户是在测试我的基本知识或者想听一个幽默的回答。既然用户之前问过一个有趣的问题,现在的问题看起来也许更简单,但同样可能需要一个轻松的回答。\n\n首先,关闭冰箱的门通常是直接的,但作为厨师,可能有一些特别的步骤或者需要注意的地方。比如,确保冰箱里的食材保存得当,或者在关闭门时的动作是否需要特别注意。\n\n其次,考虑到厨师在厨房里工作,冰箱门的关闭可能涉及到节能或者食品保存的考虑。所以,可能需要提到关闭门时的力度适中,不要太用力,避免损坏门或封条,同时确保门完全关闭以防止冷空气泄漏。\n\n另外,可能还要提到如果冰箱门有自动关闭功能,厨师只需要轻轻推门,然后让门自己关上。或者,如果冰箱门比较重,可能需要用手或其他工具轻轻推门。\n\n总的来说,回答这个问题需要简明扼要,同时可以加入一些厨师日常工作中的小细节,让回答更生动有趣。同时,保持语气轻松,让用户觉得这是一个简单而有趣的步骤。\n</think>\n\n<answer>\n作为一名厨师,关闭冰箱门的步骤如下:\n\n1. **轻轻推门**:用手轻轻推冰箱的门,确保门没有卡住或阻碍。\n2. **关紧门**:继续推门直到听到“咔嗒”一声,表示门已经完全关闭。\n3. **检查密封**:确认门是否完全关紧,避免冷空气泄漏。有时可能需要稍微用力按下门,确保密封条紧密贴合。\n4. **完成**:冰箱门成功关闭,食品和食材得以妥善保存。\n\n当然,这只是一个简单的日常操作,但作为厨师,确保冰箱门关闭得当,可以帮助保持厨房的整洁和食品的新鲜。\n</answer>'}