Budowanie agenta ReAct w LangChain: Executorzy, tablice robocze i ich ograniczenia
Stwórz agenta typu ReAct wykorzystującego narzędzia, zobacz, jak agent i AgentExecutor dzielą pracę, i dowiedz się, dlaczego LangGraph lepiej radzi sobie z przepływem sterowania, cyklami i stanem.
Czym jest agent
Agent sztucznej inteligencji to system, który bierze od osoby cel o wysokim poziomie abstrakcji, a następnie sam planuje, podejmuje decyzje i działa. Wykonywał sekwencję kroków przy użyciu zewnętrznych narzędzi, API lub źródeł wiedzy, zachowuje kontekst na przestrzeni całego procesu, rozumuje na podstawie kilku kroków, dostosowuje się po pojawieniu się nowych informacji i dąży do osiągnięcia zamierzonego rezultatu.
W uproszczeniu agent to LLM w połączeniu z kilkoma narzędziami. LLM zajmuje się rozumowaniem i podejmowaniem decyzji, natomiast narzędzia odpowiadają za działanie.
Cechy agenta
- Kierowany celem. Podaje się, czego chce się osiągnąć, a nie jak to zrobić.
- Planuje samodzielnie. Agent dzieli problem na części i układa zadania bez pomocy.
- Wykorzystuje narzędzia. Korzysta z API, kalkulatorów, narzędzi wyszukiwania i podobnych. LLM wie, jakie narzędzia istnieją i kiedy należy je użyć.
- Zdolny do rozumienia kontekstu. Przechowuje informacje z poprzednich kroków, dzięki czemu wcześniejsze wyniki wpływają na późniejsze działania.
- Adaptacyjny. Gdy coś się zmienia, na przykład gdy API przestaje działać lub brakuje danych, dostosowuje swój plan.
Agent działający w kodzie
Poniższy skrypt tworzy małego agenta wyposażonego w dwa narzędzia: wyszukiwarkę internetową przez DuckDuckGo oraz funkcję sprawdzania pogody, i zadaje mu pytanie składające się z dwóch części. Aby sprawdzić pogodę, potrzebna jest własna klucz API od dostawcy pogody, tutaj przedstawiona jako zamiennik.
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
import requests
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub # It is a place from where we can get many type of prompts, so if we want any predefined prompt, we can get it from here
# Step 1: Creating tool definitions
search_tool = DuckDuckGoSearchRun()
@tool
def get_weather_data(city: str) -> str:
"""
This function fetches the current weather data for a given city
"""
url = f'https://api.weatherstack.com/current?access_key=4d1d8ae207a8c845a52df8a67bf3623e&query={city}'
response = requests.get(url)
return response.json()
llm = ChatOpenAI()
# Step 2: Pull the ReAct prompt from LangChain Hub
prompt = hub.pull("hwchase17/react") # pulls the standard ReAct agent prompt
# The agent which we are gonna make is a ReAct agent (reasoning + action = ReAct)
# ReAct is a design pattern
# Step 3: Create the ReAct agent manually with the pulled prompt
agent = create_react_agent(
llm=llm,
tools=[search_tool, get_weather_data],
prompt=prompt
)
# Step 4: Wrap it with AgentExecutor
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool, get_weather_data],
verbose=True # whatever the agent thinks, it will be visible to us
)
# Step 5: Invoke
response = agent_executor.invoke({"input": "Find the capital of Madhya Pradesh, then find it's current weather condition"})
print(response)
'''
Entering new AgentExecutor chain...
I should first find out the capital of Madhya Pradesh and then check the current weather condition for that city.
Action: duckduckgo_search
Action Input: "capital of Madhya Pradesh" Madhya Pradesh, state of India that is situated in the heart of the country. It has no coastline and no international frontier. Its physiography is characterized by low hills, extensive plateaus, and river valleys. The capital is Bhopal, in the west-central part of the state. Bhopal, city, capital of Madhya Pradesh state, central India. Situated in the fertile plain of the Malwa Plateau, the city lies just north of the Vindhya Range, along the slopes of a sandstone ridge. It is a major rail junction and has an airport. Pop. (2001) 1,437,354; (2011) 1,798,218. Bhopal was Indore (/ ɪ n ˈ d ɔːr / ⓘ; ISO: Indaura, Hindi: [ɪn̪d̪ɔːr]) is the largest and most populous city in the Indian state of Madhya Pradesh. [15] It is the commercial hub of Madhya Pradesh. It is consistently ranked as the cleanest city in India. [16] It serves as the headquarters of both the Indore District and the Indore Division.It is also considered the state education hub and ... In 1956, Bhopal became part of the state of Madhya Pradesh. Bhopal district was carved out on October 2, 1972, and is one of the 45 districts in the state. 4. Why is Bhopal famous? Bhopal is the capital city of the Indian state of Madhya Pradesh. It is known as the City of Lakes due to the presence of various natural and artificial lakes. In 1948, Madhya Bharat was created, with Indore designated as its summer capital and Gwalior as its winter capital. This arrangement highlights Indore's significance even then. However, Madhya Bharat was a temporary entity, and the map of central India was soon to be redrawn. The Creation of Madhya PradeshNow that I know the capital of Madhya Pradesh is Bhopal, I can use the get_weather_data function to check its current weather condition.
Action: get_weather_data
Action Input: Bhopal {'request': {'type': 'City', 'query': 'Bhopal, India', 'language': 'en', 'unit': 'm'}, 'location': {'name': 'Bhopal', 'country': 'India', 'region': 'Madhya Pradesh', 'lat': '23.267', 'lon': '77.400', 'timezone_id': 'Asia/Kolkata', 'localtime': '2025-05-01 17:52', 'localtime_epoch': 1746121920, 'utc_offset': '5.50'}, 'current': {'observation_time': '12:22 PM', 'temperature': 40, 'weather_code': 116, 'weather_icons': ['https://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png'], 'weather_descriptions': ['Partly Cloudy '], 'astro': {'sunrise': '05:47 AM', 'sunset': '06:48 PM', 'moonrise': '08:38 AM', 'moonset': '11:01 PM', 'moon_phase': 'Waxing Crescent', 'moon_illumination': 15}, 'air_quality': {'co': '510.6', 'no2': '1.665', 'o3': '180', 'so2': '10.175', 'pm2_5': '29.045', 'pm10': '68.635', 'us-epa-index': '2', 'gb-defra-index': '2'}, 'wind_speed': 12, 'wind_degree': 302, 'wind_dir': 'WNW', 'pressure': 1005, 'precip': 0, 'humidity': 7, 'cloudcover': 25, 'feelslike': 40, 'uv_index': 1, 'visibility': 6, 'is_day': 'yes'}}The current weather condition in Bhopal, Madhya Pradesh is partly cloudy with a temperature of 40°C.
Final Answer: The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.
> Finished chain.
{'input': "Find the capital of Madhya Pradesh, then find it's current weather condition", 'output': 'The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.'}
'''
response['output']
# The capital of Madhya Pradesh is Bhopal, and the current weather condition in Bhopal is partly cloudy with a temperature of 40°C.
Pięć numerowanych kroków w tym skrypcie obejmuje definicje narzędzi, pobieranie standardowego promptu ReAct z huba LangChain, tworzenie agenta, otoczenie go wykonawcą oraz jego uruchomienie. Wydrukowany na końcu zapis pokazuje, że agent najpierw postanawia poszukać stolicy, a następnie sprawdzi pogodę.
ReAct
LangChain wspiera kilka podejść projektowych do agentów. To, które jest tu omówione, to ReAct, skrót od Reasoning plus Acting. Umożliwia on modelowi przechodzenie między wewnętrznym rozumowaniem, czyli procesem myślenia, a działaniami zewnętrznymi, takimi jak wywoływanie narzędzi, w uporządkowanym cyklu obejmującym kilka kroków.
Model nie generuje odpowiedzi za jednym razem. Myśli krok po kroku, decyduje, co robić dalej, i może korzystać z narzędzi takich jak API, kalkulatory lub wyszukiwarki internetowe w celu uzyskania pomocy.
To podejście pasuje do:
- problemów wymagających kilku kroków;
- Zadań, które potrzebują narzędzi;
- przypadki, gdy chcesz, aby rozumowanie było widoczne i poddawalne weryfikacji.
Jako przykład poproś model o informację o populacji stolicy Francji. Najpierw dochodzi do wniosku, że musi zidentyfikować stolicę, sprawdza to, widzi, że odpowiedzią jest Paryż, następnie rozważa, że potrzebuje informacji o populacji Paryża, szuka ponownie i w końcu udziela odpowiedzi.
ReAct składa się z pięciu elementów:
- Myśl. Prywatne rozumowanie agenta, w którym ocenia obecną sytuację i planuje kolejny krok. Wyjaśnia, dlaczego wybrano określone narzędzie lub co nadal jest nieznane.
- Działanie. Decyzja o użyciu jednego z dostępnych narzędzi, określona funkcją lub interfejsem API do wywołania.
- Wartości wejściowe dla działania. Argumenty przekazywane temu narzędziu. Jeśli działaniem jest
search, wartością wejściową jest dokładny tekst zapytania, na przykład „pogoda w Delhi”. - Obserwacja. Surowe dane, które narzędzie zwraca po uruchomieniu przez wykonawcę. Agent je odczytuje, aby ocenić, czy zadanie zostało zakończone, czy potrzebne są dodatkowe kroki.
- Odpowiedź końcowa. Ostateczna odpowiedź, tworzona w momencie, gdy agent stwierdza, że ma już wystarczająco informacji; łączy ona wszystkie obserwacje w prostym języku.
Ścieżka szablonu promptu, która napędza ten cykl, wygląda następująco:
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action …
(this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
Thought:{agent_scratchpad} —
W tym szablonie obserwacja to po prostu informacja, która pojawia się po zakończeniu działania narzędzia.
Notatnik agenta
Notatnik pełni rolę krótkoterminowej pamięci lub dziennika roboczego agenta. Zapisuje on historię bieżącego zadania: wcześniejsze rozważania, podjęte działania, takie jak wywołania narzędzi, oraz informacje o obserwacjach, które zostały uzyskane.
Czytając ten notatnik, agent przypomina sobie, że już ustalił stolicę Francji i teraz musi znaleźć liczbę mieszkańców Paryża. Jest to tymczasowe przechowywanie danych, które aktualizuje się za każdym razem, gdy wykonawca dostarcza nowe informacje. Gdy ostateczna odpowiedź trafia do użytkownika, notatnik zazwyczaj jest usuwany lub archiwizowany, ponieważ jego zadanie w ramach tego zadania zostało zakończone.
Nazwa staje się zrozumiała, gdy przypomnimy sobie, że LLM nie ma stanu. Nie pamięta automatycznie tego, co wydarzyło się kilka sekund temu w pętli kodu. Dlatego LangChain bierze całą historię bieżącej pętli i ponownie wstawia ją do promptu za każdym razem, gdy pyta agenta, co ma robić dalej.
Agent versus AgentExecutor
Te dwa komponenty pełnią odrębne zadania.
Agent to myśliciel, nieco podobny do menedżera projektu. Czyta prośbę, dzieli ją na mniejsze kroki i decyduje, jakie narzędzia są potrzebne. Jego dane wejściowe zawierają zarówno pytanie użytkownika, jak i wszystko, co zostało dotychczas zrobione.
AgentExecutor to wykonawca, bardziej przypominający asystenta. Słucha instrukcji agenta, faktycznie uruchamia kod dla każdego narzędzia i przekazuje wyniki z powrotem.
Inny sposób na to spojrzenie: agent to architekt, który rysuje plan, a wykonawca to ekipa budowlana, która realizuje budowę. Bez agenta nie ma planu, a bez wykonawcy plan nigdy nie opuszcza papieru.
Wykonawca koordynuje cały proces:
- Wysyła dane wejściowe oraz wcześniejsze wiadomości do agenta, aby ten znał pytanie i to, co się wydarzyło.
- Otrzymuje od agenta kolejną akcję do wykonania.
- Uruchamia narzędzie z podanymi danymi wejściowymi.
- Dodaje obserwację uzyskaną przez narzędzie do historii.
- Wysyła zaktualizowaną historię wraz z pytaniem użytkownika z powrotem do agenta, a proces ten powtarza się, dopóki agent nie poda Odpowiedzi końcowej.
Weźmy prośbę o temperaturę w Delhi pomnożoną przez 10. Agent decyduje, że potrzebny jest narzędzie do przewidywania pogody, wykonawca je wywołuje, otrzymuje wynik obserwacji, agent decyduje, że konieczne jest pomnożenie, wykonawca je przeprowadza, a agent w końcu podaje odpowiedź. Wykonawca realizuje to, co uzna agent za konieczne.
Ograniczenia tego podejścia
Z góry przygotowany wykonawca sprawdza się w prostych przypadkach, ale ma poważne wady, gdy agenci muszą radzić sobie z bardziej złożonymi zadaniami. Poniższe porównanie dotyczy LangGraph, biblioteki opartej na grafach od LangChain. Należy również zauważyć, że nowsze wersje LangChain zachęcają do używania LangGraph dla agentów, dlatego przed korzystaniem ze starszych klas wykonawców sprawdź aktualną dokumentację.
1. Czarna skrzynka
Standardowy AgentExecutor jest już przygotowany. Wystarczy mu dostarczyć narzędzia i instrukcje, a niewidzialna pętla while będzie działać aż do zakończenia zadania. Interwencja jest trudna. Jeśli chcesz, aby agent zawsze pytał człowieka przed wydaniem pieniędzy lub aby postępował według określonej kolejności, na przykład najpierw krok A, potem krok B, executor się temu sprzeciwia, ponieważ został zaprojektowany jako całkowicie autonomiczny.
Rozważmy agenta posiadającego narzędzia search_flights i book_flight. Wewnątrz executora LangChain model LLM decyduje o dalszych krokach w ukrytej pętli. Gdy zostanie poproszony o rezerwację lotu, może natychmiast go wyszukać i zarezerwować bez pytania, być może za cenę dziesięciu tysięcy dolarów, podczas gdy użytkownik chciał najpierw zatwierdzić zakup. Pośrednie kroki, takie jak obowiązkowe zatwierdzenie, nie mogą być ściśle egzekwowane, ponieważ przepływ sterowania jest ukryty i kontrolowany przez model.
W LangGraph proces jest definiowany jako wyraźne kroki lub węzły, takie jak wyszukiwanie lotów, proszenie o zatwierdzenie i dokonywanie rezerwacji. Są one połączone w taki sposób, że system musi najpierw uzyskać zatwierdzenie, zanim będzie mógł dokonać rezerwacji. Dzięki temu uzyskuje się pełną kontrolę i ułatwia się egzekwowanie zasad, dodawanie ludzkich punktów kontrolnych, prób ponownych lub warunkowych gałęzi.
2. Cykle i pętle
Prawdziwe procesy rzadko są liniowe. Obejmują powrót do poprzednich etapów, korygowanie błędów oraz przetwarzanie serii dokumentów w pętlach.
- Łańcuchy w LangChain są przede wszystkim projektowane jako grafy skierowane acykliczne, więc dane przepływają w jednym kierunku.
- LangGraph został stworzony do obsługi cykli. Modeluje przepływ jako mapę, w której węzłami są funkcje, a krawędziami ścieżki między nimi.
W zadaniach typu pytanie-odpowiedź dotyczących dokumentów, pipeline LangChain może pobrać dokumenty, napisać odpowiedź i ją zwrócić. Jeśli odpowiedź jest błędna lub niekompletna, nie wraca automatycznie, aby ponownie pobrać dane lub udoskonalić zapytanie bez dodatkowej ręcznej logiki. Gdy już przejdzie do dalszych kroków, nie może łatwo wrócić, aby się poprawić.
LangGraph umożliwia wyraźne tworzenie pętli: pobieranie danych, odpowiadanie, sprawdzanie jakości, a jeśli wynik jest kiepski, udoskonalanie zapytania i ponowne pobieranie danych, powtarzając ten proces aż do uzyskania dobrego rezultatu. System może krążyć między węzłami, dopóki nie zostanie spełniona określona warunek, co nadaje się do zadań wymagających iteracji, korekty i informacji zwrotnej.
3. Stan i trwałość
To ma największe znaczenie dla skalowalności.
Pamięć w LangChain dotyczy głównie kontekstu rozmowy, np. przechowywania wcześniejszych wiadomości, aby model mógł lepiej odpowiadać. Nie jest ściśle powiązana z procesem pracy i nie śledzi postępów w wykonywanej zadaniu. Jeśli nie zostanie wyraźnie zapisana, zazwyczaj ginie po ponownym uruchomieniu systemu, więc zadania trwające długi czas nie są niezawodne.
LangGraph traktuje pamięć jako pełny stan procesu pracy, a nie tylko historię rozmów. Na każdym kroku zapisuje to, co zostało zrobione, jakie wyniki powstały oraz co następuje dalej. Jeśli proces zostanie przerwany w połowie, można go wznowić dokładnie tam, gdzie się zatrzymał. Pozwala również na pauzowanie, sprawdzanie i edycję stanu przy udziale człowieka, a nawet powrót do wcześniejszego kroku, co sprawia, że złożone zadania wieloetapowe są znacznie bardziej odpornie na awarie.
Załóżmy długie zadanie, takie jak przetwarzanie dziesięciu dokumentów po kolei, które zawodzi na 7. kroku. Bez wbudowanej funkcji przechowywania wszystko jest tracione i musimy zaczynać od nowa, bez żadnych informacji o postępach. Dzięki zapisywaniu stanu po każdym kroku możemy wznowić pracę od 7. kroku. Można również wstrzymać proces, pozwolić komuś przejrzeć lub edytować stan, a następnie kontynuować lub wrócić do wcześniejszego kroku – co przypomina podróż w czasie. Długie procesy pracy stają się bardziej niezawodne, można je wznowić, a także łatwiej je debugować lub audytować.
Wniosek jest taki, że podstawowy mechanizm wykonywania zadań utrudnia tworzenie skalowalnych, gotowych do użycia agentów, dlatego istnieją narzędzia oparte na grafach do tego celu.
Literatura pokrewna
- Zdjęcia ekranu pamięci agenta, umiejętności, degradacja i zakres pamięci w gałęziach — Cztery ulepszenia warstwy pamięci typu vector-DB przeznaczonej dla agentów programistycznych: przenośne zdjęcia ekranu z możliwością łączenia, aktywacja umiejętności, degradacja z kontrolą konfliktów oraz pamięć ograniczona do konkretnych gałęzi.
- Twój pierwszy agent LangGraph: asystent do piłki nożnej z narzędziami i routowaniem — Krok po kroku przewodnik po LangGraph: stan wspólny, narzędzia, węzeł planowania, krawędzie warunkowe oraz podsumowanie w formacie markdown, stworzone wokół symulowanego asystenta do statystyk piłkarskich.