Zpět na domů

Jak zachovat reasoning content v LangChain pro modely CoT

Článek vysvětluje, proč LangChain ignoruje reasoning content v modelech CoT, a nabízí funkční patch pro uchování tohoto bloku v additional_kwargs. Vhodné pro vývojáře používající poskytovatele jako polza.ai nebo StepFun přes OpenAI-kompatibilní API.

Ztráta reasoning content v LangChain? Tady je, jak to opravit
Advertisement 728x90

# Zachování obsahu reasoning v LangChain: záplata pro modely CoT

Při práci s modely CoT prostřednictvím LangChain se vývojáři setkávají se ztrátou obsahu reasoning – klíčového bloku uvažování, který model generuje před finální odpovědí. To není bug, ale důsledek architektonického rozhodnutí: třídy jako ChatOpenAI striktně dodržují oficiální OpenAI API a ignorují nestandardní pole jako reasoning, reasoning_content nebo reasoning_details. Výsledek – prodloužená doba odezvy a zhoršený UX, protože uživatel nevidí mezikroky myšlení AI. Níže – jak to opravit na úrovni kódu bez čekání na oficiální aktualizaci.

Proč LangChain ignoruje obsah reasoning

LangChain se prezentuje jako univerzální framework kompatibilní s řadou poskytovatelů LLM. Aby se zabránilo fragmentaci, jeho hlavní třídy (např. ChatOpenAI) implementují pouze oficiální specifikaci OpenAI Chat Completion API. Pole reasoning_content, reasoning a podobná jsou rozšíření zaváděná DeepSeek, xAI, vLLM a dalšími. Každý poskytovatel používá svůj vlastní název a formát, což vytváří chaos:

  • DeepSeek → reasoning_content
  • vLLM → přešel z reasoning_content na reasoning
  • xAI / OpenRouter → mohou používat reasoning_details

Podpora všech variant v ChatOpenAI by vyžadovala:

Google AdInline article slot
  • Neustálé sledování změn v API třetích stran.
  • Údržbu rostoucího seznamu mapování polí.
  • Proměnu ChatOpenAI v univerzální adaptér – což odporuje jeho účelu.

Proto vývojáři LangChain doporučují používat specializované třídy jako ChatDeepSeek. Většina poskytovatelů (včetně polza.ai, StepFun a dalších) však stále poskytuje API ve formátu OpenAI – a právě tam problém přetrvává.

Jak implementovat záplatu v base.py

Řešení spočívá v úpravě dvou funkcí v zdrojovém kódu LangChain: _convert_dict_to_message a _convert_delta_to_message_chunk. Tyto funkce zajišťují převod syrových odpovědí od LLM na objekty Message používané uvnitř frameworku. Je třeba přidat extrakci pole reasoning a jeho uložení do additional_kwargs.

Zde je minimální záplata:

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

Podobně pro streamovací režim:

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 "")  # přidáno
    additional_kwargs: dict = {}
    if reasoning:  # přidáno
        additional_kwargs['reasoning_content'] = reasoning  # přidáno
    ...

Tato záplata:

  • Neruší stávající architekturu.
  • Zachovává kompatibilitu s oficiálním API.
  • Přidává podporu reasoning_content pro jakýkoli poskytovatele, který vrací pole reasoning.

Příklad použití se streamovacím výstupem

Po aplikaci záplaty se obsah reasoning stává dostupným v additional_kwargs každého chunku. Zde je, jak zorganizovat výstup:

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('Ahoj!'):
    if "reasoning_content" in chunk.additional_kwargs:
        print(chunk.additional_kwargs['reasoning_content'], end="", flush=True)
    else:
        print(chunk.content, end="", flush=True)

Nyní uživatel vidí proces uvažování modelu v reálném čase – to je obzvláště cenné v aplikacích CoT, kde jsou důležité mezikroky: analýza dat, srovnání hypotéz, kontrola logiky.

Alternativní přístupy a omezení

Ačkoli je záplata efektivní, má omezení:

  • Lokální úprava: změny se týkají pouze vaší kopie LangChain. Při aktualizaci balíčku záplata zmizí.
  • Nekompatibilní názvy polí: pokud poskytovatel používá reasoning_details místo reasoning, je třeba kód upravit.
  • Absence standardizace: dokud neexistuje jednotné dohodnutí mezi poskytovateli, každý případ vyžaduje ruční nastavení.

Čisté alternativy:

  • Vlastní MessageConverter – vytvořit vlastní třídu dědící z BaseMessageConverter a přepsat logiku parsování.
  • Middleware vrstva – zachytávat HTTP odpovědi od poskytovatele před vstupem do LangChain a normalizovat pole.
  • Použití LangChain Expression Language (LCEL) – zabalit volání modelu do RunnableLambda, které extrahuje reasoning před předáním do chainu.

Tyto metody jsou však složitější a vyžadují hluboké porozumění vnitřkům frameworku. Pro MVP a rychlou opravu je záplata v base.py optimálním řešením.

Co je důležité

  • Obsah reasoning se ztrácí ne kvůli chybě, ale kvůli striktnímu dodržování OpenAI API v LangChain.
  • Záplata přidává podporu pole reasoning do additional_kwargs bez narušení zpětné kompatibility.
  • Funguje jak pro běžné, tak pro streamovací volání.
  • Vyžaduje ruční aktualizaci při každé aktualizaci LangChain.
  • Zlepšuje UX v aplikacích CoT tím, že činí proces myšlení modelu průhledným pro uživatele.

— Editorial Team

Advertisement 728x90

Číst dál