# 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_contentnareasoning - xAI / OpenRouter → mohou používat
reasoning_details
Podpora všech variant v ChatOpenAI by vyžadovala:
- 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:
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:
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_detailsmístoreasoning, 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
reasoningdo 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
Zatím žádné komentáře.