# Zachowywanie reasoning content w LangChain: poprawka dla modeli CoT
Przy pracy z modelami CoT za pośrednictwem LangChain deweloperzy napotykają utratę reasoning content — kluczowego bloku rozumowań generowanego przez model przed ostateczną odpowiedzią. To nie błąd, lecz konsekwencja decyzji architektonicznej: klasy takie jak ChatOpenAI ściśle przestrzegają oficjalnego OpenAI API, ignorując niestandardowe pola takie jak reasoning, reasoning_content czy reasoning_details. Rezultat — zwiększony czas odpowiedzi i pogorszony UX, ponieważ użytkownik nie widzi pośrednich kroków myślenia AI. Poniżej opisano, jak to naprawić na poziomie kodu bez czekania na oficjalną aktualizację.
Dlaczego LangChain ignoruje reasoning content
LangChain pozycjonuje się jako uniwersalny framework kompatybilny z wieloma dostawcami LLM. Aby uniknąć fragmentacji, jego główne klasy (np. ChatOpenAI) implementują tylko oficjalną specyfikację OpenAI Chat Completion API. Pola reasoning_content, reasoning i podobne — to rozszerzenia wprowadzone przez DeepSeek, xAI, vLLM i inne. Każdy dostawca używa własnej nazwy i formatu, co powoduje chaos:
- DeepSeek →
reasoning_content - vLLM → przeszedł z
reasoning_contentnareasoning - xAI / OpenRouter → mogą używać
reasoning_details
Obsługa wszystkich wariantów w ChatOpenAI wymagałaby:
- Ciągłego monitorowania zmian w API zewnętrznych dostawców.
- Obsługi rosnącej listy mapowań pól.
- Przekształcenia ChatOpenAI w uniwersalny adapter — co przeczy jego przeznaczeniu.
Dlatego deweloperzy LangChain zalecają używanie specjalistycznych klas, takich jak ChatDeepSeek. Ale większość dostawców (w tym polza.ai, StepFun i inne) nadal oferuje API w formacie OpenAI — i tam problem pozostaje aktualny.
Jak wdrożyć poprawkę w base.py
Rozwiązanie to modyfikacja dwóch funkcji w kodzie źródłowym LangChain: _convert_dict_to_message i _convert_delta_to_message_chunk. Odpowiadają one za konwersję surowych odpowiedzi od LLM na obiekty Message używane wewnątrz frameworka. Należy dodać wyodrębnianie pola reasoning i zapisanie go w additional_kwargs.
Oto minimalna poprawka:
def _convert_dict_to_message(_dict):
...
content = _dict.get("content", "") or ""
reasoning = _dict.get("reasoning", "") or "" # dobavleno
additional_kwargs: dict = {}
if reasoning: # dobavleno
additional_kwargs['reasoning_content'] = reasoning # dobavleno
if function_call := _dict.get("function_call"):
additional_kwargs["function_call"] = dict(function_call)
tool_calls = []
...
Podobnie dla trybu strumieniowego:
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 "") # dobavleno
additional_kwargs: dict = {}
if reasoning: # dobavleno
additional_kwargs['reasoning_content'] = reasoning # dobavleno
...
Ta poprawka:
- Nie narusza istniejącej architektury.
- Zachowuje kompatybilność z oficjalnym API.
- Dodaje obsługę reasoning_content dla dowolnego dostawcy zwracającego pole
reasoning.
Przykład użycia ze strumieniowym wyjściem
Po zastosowaniu poprawki reasoning content staje się dostępne w additional_kwargs każdego chunku. Oto jak zorganizować wyjście:
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)
Teraz użytkownik widzi proces rozumowania modelu w czasie rzeczywistym — to szczególnie cenne w aplikacjach CoT, gdzie ważne są pośrednie kroki: analiza danych, porównanie hipotez, weryfikacja logiki.
Alternatywne podejścia i ograniczenia
Chociaż poprawka jest skuteczna, ma ograniczenia:
- Lokalna modyfikacja: zmiany dotyczą tylko twojej kopii LangChain. Przy aktualizacji pakietu poprawka zniknie.
- Niezgodność nazw pól: jeśli dostawca używa
reasoning_detailszamiastreasoning, trzeba dostosować kod. - Brak standaryzacji: dopóki nie ma jednolitego porozumienia między dostawcami, każdy przypadek wymaga ręcznej konfiguracji.
Czyste alternatywy:
- Niestandardowy MessageConverter — stworzyć własną klasę dziedziczącą po BaseMessageConverter i nadpisać logikę parsowania.
- Warstwa middleware — przejmować odpowiedzi HTTP od dostawcy przed dotarciem do LangChain i normalizować pola.
- Użycie LangChain Expression Language (LCEL) — opakować wywołanie modelu w RunnableLambda, który wyodrębni reasoning przed przekazaniem do chain.
Te metody są jednak bardziej skomplikowane i wymagają głębokiego zrozumienia wnętrzności frameworka. Dla MVP i szybkiej naprawy poprawka w base.py to optymalne rozwiązanie.
Co ważne
- Reasoning content gubi się nie z powodu błędu, lecz z powodu ścisłego przestrzegania OpenAI API w LangChain.
- Poprawka dodaje obsługę pola
reasoningw additional_kwargs bez naruszania kompatybilności wstecznej. - Działa zarówno dla zwykłych, jak i strumieniowych wywołań.
- Wymaga ręcznej aktualizacji przy każdej aktualizacji LangChain.
- Poprawia UX w aplikacjach CoT, czyniąc proces myślenia modelu przejrzystym dla użytkownika.
— Editorial Team
Brak komentarzy.