返回首页

如何在 LangChain 中为 CoT 模型保存推理内容

本文解释了为什么 LangChain 忽略 CoT 模型中的推理内容,并提供了一个有效的补丁来将此块保存到 additional_kwargs 中。适用于使用 polza.ai 或 StepFun 等提供商通过 OpenAI 兼容 API 的开发者。

在 LangChain 中丢失推理内容?这是修复方法
Advertisement 728x90

在 LangChain 中保存推理内容:CoT 模型的补丁

在使用 LangChain 处理 CoT 模型时,开发者常常遇到推理内容丢失的问题——这是模型在给出最终答案前生成的推理关键块。这不是 bug,而是架构决策的结果:像 ChatOpenAI 这样的类严格遵守官方 OpenAI API,忽略了如 reasoning、reasoning_content 或 reasoning_details 等非标准字段。这导致响应时间变慢,用户体验变差,因为用户看不到 AI 的中间思考步骤。下面介绍如何在代码层面修复这个问题,而无需等待官方更新。

LangChain 为什么忽略推理内容

LangChain 被定位为兼容多种大语言模型提供商的通用框架。为了避免碎片化,其核心类(如 ChatOpenAI)仅实现官方 OpenAI Chat Completion API 规范。像 reasoning_content、reasoning 等字段是由 DeepSeek、xAI、vLLM 等引入的扩展。每家提供商使用自己的命名和格式,造成混乱:

  • DeepSeek → reasoning_content
  • vLLM → 从 reasoning_content 切换到 reasoning
  • xAI / OpenRouter → 可能使用 reasoning_details

在 ChatOpenAI 中支持所有变体将需要:

Google AdInline article slot
  • 持续监控第三方提供商的 API 变更。
  • 维护不断增长的字段映射列表。
  • 将 ChatOpenAI 变成通用适配器——这违背了其目的。

这就是为什么 LangChain 开发者推荐使用像 ChatDeepSeek 这样的专用类。但大多数提供商(包括 polza.ai、StepFun 等)仍提供 OpenAI 格式的 API——问题就持续存在。

在 base.py 中应用补丁

解决方案是修改 LangChain 源代码中的两个函数:_convert_dict_to_message_convert_delta_to_message_chunk。这些函数负责将原始大语言模型响应转换为框架内使用的 Message 对象。您需要添加推理字段的提取,并将其保存到 additional_kwargs 中。

以下是最小补丁:

Google AdInline article slot
def _convert_dict_to_message(_dict):
    ...
    content = _dict.get("content", "") or ""
    reasoning = _dict.get("reasoning", "") or ""  # added
    additional_kwargs: dict = {}
    if reasoning:  # added
        additional_kwargs['reasoning_content'] = reasoning  # added
    if function_call := _dict.get("function_call"):
        additional_kwargs["function_call"] = dict(function_call)
    tool_calls = []
    ...

流式模式类似:

def _convert_delta_to_message_chunk(_dict):
    ...
    id_ = _dict.get("id")
    role = cast(str, _dict.get("role"))
    content = cast(str, _dict.get("content") or "")
    reasoning = cast(str, _dict.get("reasoning", "") or "")  # added
    additional_kwargs: dict = {}
    if reasoning:  # added
        additional_kwargs['reasoning_content'] = reasoning  # added
    ...

这个补丁:

  • 不破坏现有架构。
  • 保持与官方 API 的兼容性。
  • 为任何返回 reasoning 字段的提供商添加对 reasoning_content 的支持。

带流式输出的示例用法

应用补丁后,推理内容可在每个 chunk 的 additional_kwargs 中获取。以下是设置输出的方式:

Google AdInline article slot
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="stepfun/step-3.5-flash",
    openai_api_base="https://api.polza.ai/v1",
    openai_api_key="your_api_key",
)

for chunk in model.stream('Hello!'):
    if "reasoning_content" in chunk.additional_kwargs:
        print(chunk.additional_kwargs['reasoning_content'], end="", flush=True)
    else:
        print(chunk.content, end="", flush=True)

现在,用户可以实时看到模型的推理过程——这在 CoT 应用中尤其宝贵,其中数据分析、假设比较和逻辑检查等中间步骤至关重要。

替代方法和限制

虽然补丁有效,但有局限性:

  • 本地修改:变更仅影响您的 LangChain 副本。包更新时会丢失。
  • 字段名不兼容:如果提供商使用 reasoning_details 而非 reasoning,需调整代码。
  • 缺乏标准化:没有提供商间的统一协议,每个情况都需要手动调整。

干净的替代方案:

  • 自定义 MessageConverter——创建继承自 BaseMessageConverter 的类,并覆盖解析逻辑。
  • 中间件层——在响应到达 LangChain 前拦截提供商的 HTTP 响应,并规范化字段。
  • LangChain 表达式语言 (LCEL)——用 RunnableLambda 包装模型调用,在传递给链前提取推理。

然而,这些方法更复杂,需要深入理解框架内部。对于最小可行产品 (MVP) 或快速修复,修补 base.py 是最佳解决方案。

关键要点

  • 推理内容丢失不是因为 bug,而是因为 LangChain 严格遵循 OpenAI API。
  • 补丁在 additional_kwargs 中添加对 reasoning 字段的支持,而不破坏向后兼容性。
  • 适用于常规调用和流式调用。
  • 每次 LangChain 更新后需要手动重新应用。
  • 通过让用户看到模型的思考过程,提升 CoT 应用的用户体验。

— Editorial Team

Advertisement 728x90

继续阅读