ReAct (Reasoning and Acting) to wzorzec projektowy dla agentów AI, który łączy rozumowanie z podejmowaniem akcji. Agent na przemian "myśli" (reasoning) i "działa" (acting), co prowadzi do lepszych rezultatów!
ReAct to paradygmat, w którym agent AI:
1┌─────────────────────────────────────────────────────────┐
2│ ReAct Loop │
3├─────────────────────────────────────────────────────────┤
4│ │
5│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
6│ │ Thought │───▶│ Action │───▶│ Observation │ │
7│ │ (Myśl) │ │ (Działaj)│ │ (Obserwuj) │ │
8│ └──────────┘ └──────────┘ └──────┬──────┘ │
9│ ▲ │ │
10│ └──────────────────────────────────┘ │
11│ (Repeat) │
12│ │
13│ Przykład: │
14│ Thought: Muszę sprawdzić pogodę w Serengeti │
15│ Action: get_weather("Serengeti") │
16│ Observation: Temperatura 28°C, słonecznie │
17│ Thought: Teraz mogę odpowiedzieć użytkownikowi │
18│ Action: final_answer("Pogoda w Serengeti...") │
19│ │
20└─────────────────────────────────────────────────────────┘1from openai import OpenAI
2from typing import Callable
3import json
4import re
5
6client = OpenAI()
7
8class ReActAgent:
9 """Agent implementujący wzorzec ReAct."""
10
11 def __init__(self, tools: dict[str, Callable], max_iterations: int = 10):
12 self.tools = tools
13 self.max_iterations = max_iterations
14 self.history: list[str] = []
15
16 def _create_system_prompt(self) -> str:
17 """Tworzy system prompt z opisem narzędzi."""
18 tool_descriptions = "\n".join([
19 f"- {name}: {func.__doc__}"
20 for name, func in self.tools.items()
21 ])
22
23 return f"""Jesteś pomocnym asystentem Safari, który rozwiązuje problemy krok po kroku.
24
25Dostępne narzędzia:
26{tool_descriptions}
27
28Używaj formatu:
29Thought: [twoje rozumowanie o tym, co robić dalej]
30Action: [nazwa_narzędzia(argumenty)]
31
32Gdy masz wystarczające informacje, aby odpowiedzieć:
33Thought: [rozumowanie końcowe]
34Answer: [twoja odpowiedź dla użytkownika]
35
36Zawsze myśl przed działaniem. Analizuj obserwacje zanim podejmiesz kolejny krok."""
37
38 def _parse_response(self, response: str) -> tuple[str, str, str]:
39 """Parsuje odpowiedź na thought, action i answer."""
40 thought_match = re.search(r'Thought:\s*(.+?)(?=Action:|Answer:|$)', response, re.DOTALL)
41 action_match = re.search(r'Action:\s*(.+?)(?=Thought:|Observation:|Answer:|$)', response, re.DOTALL)
42 answer_match = re.search(r'Answer:\s*(.+)', response, re.DOTALL)
43
44 thought = thought_match.group(1).strip() if thought_match else ""
45 action = action_match.group(1).strip() if action_match else ""
46 answer = answer_match.group(1).strip() if answer_match else ""
47
48 return thought, action, answer
49
50 def _execute_action(self, action_str: str) -> str:
51 """Wykonuje akcję i zwraca obserwację."""
52 # Parsuj nazwę funkcji i argumenty
53 match = re.match(r'(\w+)\((.*)\)', action_str)
54 if not match:
55 return f"Błąd: Nieprawidłowy format akcji: {action_str}"
56
57 func_name = match.group(1)
58 args_str = match.group(2)
59
60 if func_name not in self.tools:
61 return f"Błąd: Nieznane narzędzie: {func_name}"
62
63 try:
64 # Proste parsowanie argumentów
65 if args_str:
66 args = [arg.strip().strip('"').strip("'") for arg in args_str.split(',')]
67 result = self.tools[func_name](*args)
68 else:
69 result = self.tools[func_name]()
70 return str(result)
71 except Exception as e:
72 return f"Błąd wykonania: {str(e)}"
73
74 def run(self, query: str) -> str:
75 """Uruchamia agenta ReAct."""
76 self.history = [f"User: {query}"]
77
78 for i in range(self.max_iterations):
79 # Buduj kontekst
80 context = "\n".join(self.history)
81
82 # Wywołaj LLM
83 response = client.chat.completions.create(
84 model="gpt-5-mini",
85 messages=[
86 {"role": "system", "content": self._create_system_prompt()},
87 {"role": "user", "content": context}
88 ],
89 temperature=0
90 )
91
92 llm_response = response.choices[0].message.content
93 thought, action, answer = self._parse_response(llm_response)
94
95 # Zapisz thought
96 if thought:
97 self.history.append(f"Thought: {thought}")
98 print(f"🤔 Thought: {thought}")
99
100 # Jeśli jest odpowiedź końcowa
101 if answer:
102 print(f"✅ Answer: {answer}")
103 return answer
104
105 # Wykonaj akcję
106 if action:
107 self.history.append(f"Action: {action}")
108 print(f"⚡ Action: {action}")
109
110 observation = self._execute_action(action)
111 self.history.append(f"Observation: {observation}")
112 print(f"👁️ Observation: {observation}")
113
114 return "Przekroczono limit iteracji bez znalezienia odpowiedzi."1# Definicja narzędzi
2def get_weather(location: str) -> str:
3 """Pobiera aktualną pogodę dla danej lokalizacji."""
4 weather_data = {
5 "Serengeti": "28°C, słonecznie, wilgotność 45%",
6 "Kilimandżaro": "5°C, pochmurnie, śnieg na szczycie",
7 "Nairobi": "22°C, częściowe zachmurzenie"
8 }
9 return weather_data.get(location, f"Brak danych pogodowych dla {location}")
10
11def search_animals(query: str) -> str:
12 """Wyszukuje informacje o zwierzętach Safari."""
13 animals = {
14 "lew": "Panthera leo - największy kot Afryki, żyje w stadach zwanych watahami",
15 "słoń": "Loxodonta africana - największe zwierzę lądowe, żyje 60-70 lat",
16 "żyrafa": "Giraffa camelopardalis - najwyższe zwierzę świata, do 5.5m wysokości"
17 }
18 for key, value in animals.items():
19 if key in query.lower():
20 return value
21 return f"Nie znaleziono informacji o: {query}"
22
23def calculate_distance(from_loc: str, to_loc: str) -> str:
24 """Oblicza odległość między lokalizacjami Safari."""
25 distances = {
26 ("Nairobi", "Serengeti"): 350,
27 ("Serengeti", "Kilimandżaro"): 280,
28 ("Nairobi", "Kilimandżaro"): 200
29 }
30 key = (from_loc, to_loc)
31 reverse_key = (to_loc, from_loc)
32
33 if key in distances:
34 return f"{distances[key]} km"
35 elif reverse_key in distances:
36 return f"{distances[reverse_key]} km"
37 return f"Nieznana odległość między {from_loc} a {to_loc}"
38
39# Utworzenie agenta
40tools = {
41 "get_weather": get_weather,
42 "search_animals": search_animals,
43 "calculate_distance": calculate_distance
44}
45
46agent = ReActAgent(tools=tools)
47
48# Uruchomienie
49result = agent.run("Jaka jest pogoda w Serengeti i jakie zwierzęta tam żyją?")
50print(f"\nKońcowa odpowiedź: {result}")1from langchain.agents import create_react_agent, AgentExecutor
2from langchain_openai import ChatOpenAI
3from langchain.tools import tool
4from langchain import hub
5
6# Narzędzia jako tools LangChain
7@tool
8def safari_weather(location: str) -> str:
9 """Pobiera pogodę dla lokalizacji na Safari."""
10 return f"Pogoda w {location}: 26°C, słonecznie"
11
12@tool
13def animal_info(animal_name: str) -> str:
14 """Wyszukuje informacje o zwierzęciu Safari."""
15 return f"Informacje o {animal_name}: Wspaniałe zwierzę Afryki!"
16
17@tool
18def safari_distance(from_location: str, to_location: str) -> str:
19 """Oblicza odległość między punktami Safari."""
20 return f"Odległość z {from_location} do {to_location}: 150 km"
21
22# LLM i prompt
23llm = ChatOpenAI(model="gpt-5-mini", temperature=0)
24prompt = hub.pull("hwchase17/react")
25
26# Agent ReAct
27tools = [safari_weather, animal_info, safari_distance]
28agent = create_react_agent(llm, tools, prompt)
29
30# Executor
31agent_executor = AgentExecutor(
32 agent=agent,
33 tools=tools,
34 verbose=True,
35 handle_parsing_errors=True,
36 max_iterations=10
37)
38
39# Uruchomienie
40result = agent_executor.invoke({
41 "input": "Sprawdź pogodę w Serengeti i powiedz mi o lwach"
42})
43print(result["output"])1from openai import OpenAI
2import json
3
4client = OpenAI()
5
6# Definicja tools dla OpenAI
7tools = [
8 {
9 "type": "function",
10 "function": {
11 "name": "get_safari_info",
12 "description": "Pobiera informacje o Safari",
13 "parameters": {
14 "type": "object",
15 "properties": {
16 "topic": {
17 "type": "string",
18 "description": "Temat: weather, animals, locations"
19 },
20 "query": {
21 "type": "string",
22 "description": "Szczegółowe zapytanie"
23 }
24 },
25 "required": ["topic", "query"]
26 }
27 }
28 }
29]
30
31def execute_safari_tool(topic: str, query: str) -> str:
32 """Wykonuje narzędzie Safari."""
33 if topic == "weather":
34 return f"Pogoda dla {query}: 28°C, słonecznie"
35 elif topic == "animals":
36 return f"Informacje o {query}: Fascynujące zwierzę Safari!"
37 elif topic == "locations":
38 return f"Lokalizacja {query}: Popularne miejsce na Safari"
39 return "Nieznany temat"
40
41def react_with_functions(query: str) -> str:
42 """ReAct używając OpenAI function calling."""
43 messages = [
44 {
45 "role": "system",
46 "content": "Jesteś ekspertem Safari. Odpowiadaj krok po kroku, używając dostępnych narzędzi."
47 },
48 {"role": "user", "content": query}
49 ]
50
51 for _ in range(5): # Max iterations
52 response = client.chat.completions.create(
53 model="gpt-5-mini",
54 messages=messages,
55 tools=tools,
56 tool_choice="auto"
57 )
58
59 message = response.choices[0].message
60 messages.append(message)
61
62 # Sprawdź czy są tool calls
63 if message.tool_calls:
64 for tool_call in message.tool_calls:
65 args = json.loads(tool_call.function.arguments)
66 result = execute_safari_tool(**args)
67
68 messages.append({
69 "role": "tool",
70 "tool_call_id": tool_call.id,
71 "content": result
72 })
73 else:
74 # Brak tool calls = finalna odpowiedź
75 return message.content
76
77 return "Przekroczono limit iteracji"
78
79# Użycie
80answer = react_with_functions("Jaka pogoda panuje w Serengeti?")
81print(answer)1from dataclasses import dataclass, field
2from typing import Optional
3from datetime import datetime
4
5@dataclass
6class ThoughtStep:
7 """Krok rozumowania agenta."""
8 thought: str
9 action: Optional[str] = None
10 observation: Optional[str] = None
11 timestamp: datetime = field(default_factory=datetime.now)
12
13class AdvancedReActAgent:
14 """Zaawansowany agent ReAct z pamięcią i refleksją."""
15
16 def __init__(self, tools: dict, model: str = "gpt-5-mini"):
17 self.tools = tools
18 self.model = model
19 self.client = OpenAI()
20 self.thought_history: list[ThoughtStep] = []
21 self.long_term_memory: list[str] = []
22
23 def reflect(self) -> str:
24 """Refleksja nad dotychczasowymi krokami."""
25 if len(self.thought_history) < 2:
26 return ""
27
28 steps_summary = "\n".join([
29 f"- Thought: {s.thought}, Action: {s.action}, Result: {s.observation}"
30 for s in self.thought_history[-3:]
31 ])
32
33 response = self.client.chat.completions.create(
34 model=self.model,
35 messages=[
36 {
37 "role": "system",
38 "content": "Przeanalizuj dotychczasowe kroki i zasugeruj ulepszenia."
39 },
40 {
41 "role": "user",
42 "content": f"Dotychczasowe kroki:\n{steps_summary}"
43 }
44 ],
45 max_tokens=200
46 )
47
48 return response.choices[0].message.content
49
50 def should_reflect(self) -> bool:
51 """Decyduje czy agent powinien przeprowadzić refleksję."""
52 # Refleksja co 3 kroki lub gdy ostatnia akcja nie powiodła się
53 if len(self.thought_history) % 3 == 0 and len(self.thought_history) > 0:
54 return True
55 if self.thought_history and "błąd" in (self.thought_history[-1].observation or "").lower():
56 return True
57 return False
58
59 def run(self, query: str) -> str:
60 """Uruchamia agenta z refleksją."""
61 # ... implementacja podobna do podstawowej wersji
62 # ale z dodaną refleksją
63
64 for i in range(10):
65 if self.should_reflect():
66 reflection = self.reflect()
67 print(f"🔍 Refleksja: {reflection}")
68
69 # ... reszta logiki ReAct
70
71 return "Odpowiedź"1"""
2Popularne wzorce używane w agentach ReAct:
3
41. BASIC ReAct
5 Thought -> Action -> Observation -> Repeat
6
72. ReAct with Reflection
8 Thought -> Action -> Observation -> Reflect -> Repeat
9
103. ReAct with Planning
11 Plan -> Thought -> Action -> Observation -> Replan -> Repeat
12
134. ReAct with Self-Consistency
14 Multiple ReAct traces -> Vote on best answer
15
165. ReAct with Retrieval (RAG-ReAct)
17 Thought -> Retrieve -> Augment -> Action -> Observation
18"""
19
20# Przykład ReAct with Planning
21class PlanningReActAgent:
22 def __init__(self, tools: dict):
23 self.tools = tools
24 self.plan: list[str] = []
25 self.current_step = 0
26
27 def create_plan(self, query: str) -> list[str]:
28 """Tworzy plan działania przed rozpoczęciem."""
29 # LLM tworzy plan kroków
30 response = client.chat.completions.create(
31 model="gpt-5-mini",
32 messages=[
33 {
34 "role": "system",
35 "content": "Stwórz plan kroków do rozwiązania zadania. Każdy krok w nowej linii."
36 },
37 {"role": "user", "content": query}
38 ]
39 )
40 plan = response.choices[0].message.content.split("\n")
41 return [step.strip() for step in plan if step.strip()]
42
43 def replan(self, remaining_steps: list[str], new_info: str) -> list[str]:
44 """Aktualizuje plan na podstawie nowych informacji."""
45 # LLM może zmodyfikować pozostałe kroki
46 return remaining_steps # lub zmodyfikowane krokiReAct to potężny wzorzec dla agentów AI. Łączy rozumowanie z działaniem, co prowadzi do bardziej przemyślanych i dokładnych odpowiedzi. W następnym module poznasz jeszcze bardziej zaawansowane techniki - RAG i systemy Multi-Agent!