Strona główna / Artykuły

Stworzenie agenta planowania w LangGraph: planowanie, wykonywanie, ponowne planowanie, raportowanie

Czterowęzłowy agent planowania LangGraph do badań finansowych: ustrukturyzowane plany, rejestr narzędzi z uwzględnieniem przestrzeni nazw, ograniczenia dotyczące ponownego planowania oraz prześledzony przykład wykonywania.

Wydawca: REACTAPP.TOP16 min czytania3625 słów

Źródła: Your AI Agent Isn’t Smart. Here’s How to Build One That Actually Thinks

Większość agentów to w rzeczywistości zaszyte pod maską łańcuchy poleceń

Oto nieprzyjemny fakt dotyczący agentów AI: wiele z nich to w rzeczywistości upiększone łańcuchy poleceń ubrane w „trench coat”. Skład jest znajomy – prompt systemowy, kilka narzędzi i być może pętla ReAct, a rezultat nazywany jest agentem. Działa to w demonstracjach i przy sprzyjających warunkach. Gdy jednak narzędzie zwraca błąd, dane wyglądają dziwnie lub zapytanie wymaga więcej niż trzech kroków, cała struktura się rozpada.

Alternatywą jest agent planujący. Tworzy on kompletny plan badań przed użyciem jakiegokolwiek narzędzia, realizuje go krok po kroku, a gdy wyniki pośrednie są sprzeczne z oczekiwaniami, przepisuje to, co pozostało. Demo to system badawczy w dziedzinie finansów. Wystarczy zadać mu prośbę w prostym języku angielskim, na przykład „Porównaj wyceny NVDA i AAPL i powiedz mi, która wygląda obecnie lepiej”, a on stworzy wieloetapowy plan, przeprowadzi każdy krok przy użyciu narzędzi analitycznych, naprawi się, gdy coś pójdzie nie tak, i napisze raport w stylu analityka. Działa na bazie LangGraph, może korzystać z modeli chmurowych za pośrednictwem Groq lub lokalnych za pośrednictwem Ollamy, a całość mieści się w około czterech plikach.

Dlaczego zwykły ReAct nie wystarcza

ReAct (Reason-Act-Observe) to wzorzec, od którego zaczynają większość agentów. LLM rozważa swój następny ruch, wywołuje narzędzie, czyta uzyskane informacje, ponownie analizuje sytuację i wywołuje kolejne narzędzie. Jest popularny, prosty i nadaje się do małych zadań.

Główną słabością jest chciwość, ponieważ agent ReAct planuje tylko jeden krok naprzód. Jeśli poprosimy o dane pięciu akcji według trzech wskaźników wraz z najnowszymi wiadomościami, agent zacznie od na przykład stosunku ceny do zysku akcji NVDA, a pierwszy wynik odwróci jego uwagę w inne kierunki. Jeden prototyp zużył siedem wywołań narzędzi na każdy wskaźnik dotyczący Tesli, zanim przypomniał sobie, że ma porównać kilka akcji; jego ostateczny raport składał się z podsumowania wyników Tesli oraz jednego zdania o pozostałych akcjach.

Przyczyną problemu nie jest możliwość modelu, lecz jego architektura. Jeśli jedna iteracja musi decydować, co badać, jak to zrobić oraz co zrobić na podstawie uzyskanych wyników, żadna z tych decyzji nie otrzymuje należytej uwagi. Agent planujący rozdziela myślenie od działania: najpierw planuje, potem wykonuje, a następnie sprawdza, czy plan nadal jest aktualny. Każdy węzeł zajmuje się jednym zadaniem, a one są wykonywane jeden po drugim.

Cztery węzły i jeden pętla

Agent planujący to graf skierowany składający się z czterech węzłów, podobny do schematu przepływu, w którym każdy element zajmuje się jednym zadaniem i przekazuje stan dalej.

START --> Planner --> Executor <--> Replanner --> Reporter --> END
  1. Planner bierze zapytanie użytkownika i tworzy ustrukturyzowany, krok po kroku plan badań. Każdy krok określa narzędzie do wywołania, argumenty oraz powód jego użycia.
  2. Executor bierze następny krok, wywołuje wskazane narzędzie i zapisuje wynik w notatniku tymczasowym.
  3. Replanner sprawdza wszystko, co zostało dotychczas zebrane po każdej eksploatacji. Jeśli narzędzie popełniło błąd lub dane pokazują coś nieoczekiwanego, przepisuje pozostałe kroki. W przeciwnym razie pozostawia plan bez zmian.
  4. Reporter przekształca wszystkie wpisy z notatnika tymczasowego w ostateczny raport analityczny, zawierający konkretne dane liczbowe oraz rekomendacje.

Podstawą projektu jest pętla Executor-Replanner. Dwa węzły naprzemiennie wykonują zadania aż do ukończenia planu, a ta wymiana ról zapewnia zachowanie samokorygujące, ponieważ po każdej akcji agent może ponownie to przemyśleć.

Stan: pamięć wspólna

W LangGraph stan to wszystko – pamięć wspólna przekazywana między węzłami. Pełna definicja:

import operator
from typing import Annotated, TypedDict

from pydantic import BaseModel, Field

class StrategyState(TypedDict, total=False):
    """Global state that flows through the LangGraph nodes."""
    query: str
    plan: list[dict]
    scratchpad: Annotated[list[dict], operator.add]
    current_step: int
    final_report: str
    replan_count: int

W tym stanie query przechowuje oryginalną prośbę użytkownika bez zmian, plan to uporządkowana lista kroków, scratchpad gromadzi wszystkie wyniki narzędzi, current_step wskazuje na element z planu, a replan_count zapewnia przestrzeganie limitu bezpieczeństwa MAX_REPLANS.

Ważna jest adnotacja Annotated[list[dict], operator.add] w scratchpad. Jej reducer, operator.add, poleca LangGraphowi dodawać nowe elementy i pozostawiać istniejące na swoich miejscach. Gdy Executor zwraca {"scratchpad": [new_entry]}, LangGraph dodaje go do bieżącej listy, dzięki czemu węzły nigdy nie muszą ręcznie obsługiwać list, a nie pojawiają się błędy związane z mutacjami.

Ustrukturyzowane plany i decyzje

Planner nigdy nie zwraca tekstu w formie surowej. Jego wynikiem jest model Pydantic, więc model językowy musi tworzyć plany w formie, którą kod może zinterpretować:

AVAILABLE_TOOLS_TEXT = """
- get_metrics(ticker, metric?): Return stock metrics. 'metric' is optional
  (P/E, EPS, Revenue, Market Cap, Sector).
- search_news(ticker): Return recent news headlines for a ticker.
- compare_metrics(tickers: list, metric): Compare one metric across
  multiple tickers.
"""

class PlanStep(BaseModel):
    """A single executable step inside an analysis plan."""
    step_id: int = Field(description="Sequential step number")
    tool: str = Field(
        description=f"Tool to use. Must be one of:\n{AVAILABLE_TOOLS_TEXT}"
    )
    args: dict = Field(description="Arguments for the tool call")
    purpose: str = Field(description="Why this step is needed")

class Plan(BaseModel):
    """The full plan generated by the planner node."""
    goal: str = Field(description="The overall analysis goal")
    steps: list[PlanStep] = Field(
        description="Ordered list of steps to execute"
    )

Pole purpose okazuje się cenniejsze, niż się spodziewano. Informuje ono Replanner o tym, dlaczego dany krok istnieje, a nie tylko o tym, co robi. Dzięki temu, że zamiast prostego „call compare_metrics,” widnieje tam opis „Porównywanie stosunków P/E w celu określenia względnej wyceny,” Replanner może znacznie lepiej ocenić, czy dany krok należy zachować, zmienić czy usunąć.

Replanner ma własną strukturę danych:

class ReplanDecision(BaseModel):
    """Output of the replanner node."""

reasoning: str = Field(
        description="Analysis of current progress and findings"
    )
    should_replan: bool = Field(
        description="Whether the plan needs modification"
    )
    updated_steps: list[PlanStep] = Field(
        default_factory=list,
        description="Remaining steps if replan is needed. Empty if no changes.",
    )

Ustalenie wartości should_replan na typ boolean zmusza model językowy do podjęcia jednej z dwóch decyzji: czy plan się zmienia, czy nie? Jeśli w strukturyzowanym wyjściu zostawi się modelowi językowemu pewną swobodę, będzie on za każdym razem zmieniał swoje działanie.

System narzędzi

Trzy narzędzia do badań finansowych działają na realistycznych danych symulacyjnych. W warunkach produkcyjnych używano by rzeczywistych wywołań API, ale dane symulacyjne mają dużą zaletę podczas tworzenia oprogramowania: deterministyczne debugowanie. Gdy agent zachowuje się dziwnie, od razu staje się jasne, czy problem leży w jego logice, czy w API.

Wzorzec projektowy, który zasługuje na większą uwagę, to rejestr narzędzi świadomy przestrzeni nazw, używany zamiast przekazywania każdemu węzłowi każdego narzędzia:

from langchain_core.tools import BaseTool
from typing import Iterable, Mapping, Any

class ToolRegistry:
    """Namespace-aware container for LangChain tools."""
    def __init__(self) -> None:
        self._tools_by_toolset: dict[str, dict[str, BaseTool]] = {}
    def add_tools(self, toolset: str, tools: Iterable[BaseTool]) -> None:
        bucket = self._tools_by_toolset.setdefault(toolset, {})
        bucket.update({t.name: t for t in tools})
    def get_tools(self, toolset: str) -> tuple[BaseTool, ...]:
        return tuple(self._tools_by_toolset.get(toolset, {}).values())
    def invoke(
        self, toolset: str, tool_name: str, tool_args: Mapping[str, Any]
    ) -> Any:
        t = self._tools_by_toolset.get(toolset, {}).get(tool_name)
        if t is None:
            raise ValueError(
                f"Unknown tool '{tool_name}' in toolset '{toolset}'"
            )
        return t.invoke(dict(tool_args))

Rejestracja odbywa się w przestrzeniach nazw "planner", "executor" oraz "replanner". Wszystkie narzędzia są widoczne dla Plannera, aby mógł nimi planować, ale tylko Executor może je wywoływać. Nawet jeśli błąd w interfejsie Replannera jakoś spowoduje wywołanie narzędzia, rejestr odrzuci to żądanie, ponieważ dana przestrzeń nazw nie posiada uprawnień do wywołania. To jest kontrola dostępu oparta na rolach dla agentów, a rozdzielenie zadań pełni również rolę mechanizmu bezpieczeństwa, który zapobiega niepożądanym zmianom stanu podczas planowania.

Węzeł Planner

def planner_node(state: StrategyState) -> dict:
    """Create a step-by-step research plan using structured output."""
    planner = model.with_structured_output(Plan)

prompt = PLAN_PROMPT.format(
        available_tools=AVAILABLE_TOOLS_TEXT,
        ticker_choices=ticker_choices_text(),
        metric_choices=metric_choices_text(),
        query=state["query"],
    )
    plan: Plan = planner.invoke(prompt)
    steps = [s.model_dump() for s in plan.steps]
    return {"plan": steps, "current_step": 0}

model.with_structured_output(Plan) sprawia, że LLM odpowiada obiektem JSON kompatybilnym z Pydantic, a błędny wynik po prostu powoduje ponowną próbę. To znacznie przewyższa możliwości analizy tekstu niestrukturyzowanego za pomocą regex.

Zasady bezpieczeństwa w promptzie planu

PLAN_PROMPT = """\
You are a financial research planner. Given a user's analysis request,
create a step-by-step research plan using the available tools.

Available tools:
{available_tools}
Rules:
- Use only the tools listed above.
- Every plan step must be executable with one of those tools.
- When a tool accepts 'ticker' or 'tickers', use only these exact values:
  {ticker_choices}
- When a tool accepts 'metric', use one of these exact values:
  {metric_choices}
- There are no other tools available. Final synthesis is handled separately.
Create an efficient plan. Group related lookups. Aim for 4-8 steps.
User request: {query}"""

Dwa aspekty wyróżniają się szczególnie. Instrukcja wymienia dostępne wartości parametrów, dzięki czemu model językowy nie musi zgadywać, jakie symbole giełdowe lub wskaźniki istnieją. To może być najskuteczniejszy sposób na zapobieganie halucynacjom u agentów używających narzędzi. Bez tego model z pewnością planuje takie kroki jak get_metrics(ticker="BITCOIN", metric="vibes"), a potem wydaje się zaskoczony, gdy te kroki zawodzą.

Drugim istotnym elementem jest linijka „Nie ma dostępnych innych narzędzi”. Gdy jej brakuje, modele językowe wymyślają takie kroki jak analyze_sentiment(ticker="NVDA") lub generate_report(findings=...) – narzędzia, których w rzeczywistości nie istnieje.

Instrukcja „dążyć do 4–8 kroków” jest celowo elastyczna. Ściśłe ograniczenia, takie jak „dokładnie 5 kroków”, przyniosły odwrotne skutki – proste zapytania zostały wypełnione zbędnymi krokami, a złożone – umieszczone w przeciążonych sekwencjach. Dzięki przedziałowi model ma możliwość dostosowania się, a obecność wytycznych zamiast bezwzględnych wymogów zazwyczaj prowadzi do właściwych decyzji.

Węzeł Executor

Węzeł Executor jest celowo najprostszym elementem: bierze następny krok, uruchamia narzędzie przez rejestr i przechowuje wynik.

MAX_STEPS = 12  # Safety limit on total steps

def executor_node(state: StrategyState) -> dict:
    """Execute the next pending step from the plan."""
    plan = state.get("plan", [])
    current_step = state.get("current_step", 0)
    if current_step >= len(plan):
        return {}
    if current_step >= MAX_STEPS:
        return {"current_step": len(plan)}
    step = plan[current_step]
    tool_name = step["tool"]
    tool_args = step["args"]
    try:
        result = str(
            TOOL_REGISTRY.invoke(
                AgentName.EXECUTOR.value, tool_name, tool_args
            )
        )
        status = "Error" if result.startswith("Error:") else "Success"
    except Exception as exc:
        result = f"Error: {exc}"
        status = "Error"
    entry = {
        "step": current_step + 1,
        "tool": tool_name,
        "args": tool_args,
        "result": result,
        "status": status,
    }
    return {
        "scratchpad": [entry],
        "current_step": current_step + 1,
    }

MAX_STEPS = 12 stanowi sztywny limit. Jeśli Replanner będzie nadal dodawał kroki lub utknie, agent nie może bez końca wydawać kredytów API. Gdy osiągnięty zostanie limit, Executor ustawia current_step na len(plan), a proces przechodzi do Reportera z dotychczas zebranymi danymi.

Błędy są rozpoznawane według ustalonej zasady: status = "Error" if result.startswith("Error:") else "Success". Każde z trzech narzędzi dodaje na początku swoich wiadomości o błędzie "Error:", dzięki czemu Executor może zaznaczyć błąd bez konieczności analizy JSON czy obsługi specyficznych wyjątków. Replanner sprawdza ten status przy decydowaniu między ponowną próbą a dostosowanym planem.

Węzeł Replanner

Ten węzeł wskazuje różnicę pomiędzy agentem planowania a zwykłym orkiestratorem. Po każdym uruchomieniu narzędzia analizuje wszystkie dostępne dane i sprawdza, czy pozostały plan nadal ma sens:

MAX_REPLANS = 2  # Prevent infinite replanning

def replanner_node(state: StrategyState) -> dict:
    """Review progress and optionally modify the remaining plan."""
    plan = state.get("plan", [])
    current_step = state.get("current_step", 0)
    replan_count = state.get("replan_count", 0)
    scratchpad = state.get("scratchpad", [])
    remaining = plan[current_step:]
    if len(remaining) = MAX_REPLANS:
        return {}
    scratchpad_text = "\n".join(
        f"Step {e['step']}: {format_tool_call(e['tool'], e['args'])} "
        f"-> [{e['status']}] {e['result'][:150]}..."
        for e in scratchpad
    )
    remaining_text = "\n".join(
        f"Step {s['step_id']}: {format_tool_call(s['tool'], s['args'])} "
        f"- {s['purpose']}"
        for s in remaining
    )
    replanner = model.with_structured_output(ReplanDecision)
    prompt = REPLAN_PROMPT.format(
        goal=state["query"],
        scratchpad=scratchpad_text,
        remaining_steps=remaining_text,
    )
    decision: ReplanDecision = replanner.invoke(prompt)
    if decision.should_replan and decision.updated_steps:
        new_steps = plan[:current_step] + [
            s.model_dump() for s in decision.updated_steps
        ]
        return {"plan": new_steps, "replan_count": replan_count + 1}
    return {"replan_count": replan_count + 1}

Spójrz na e['result'][:150]. Narzędzie Replanner przetwarza wyniki każdego narzędzia, obcinając je do 150 znaków, ponieważ potrzebuje tylko istoty informacji, a nie pełnego ciężaru JSON, natomiast narzędzie Reporter otrzymuje później dane w pełnej długości. Tokeny są przechowywane tam, gdzie szczegóły nie mają znaczenia, a wykorzystywane tam, gdzie są istotne.

Gdy następuje ponowne planowanie, plan[:current_step] + [s.model_dump() for s in decision.updated_steps] zachowuje ukończone kroki i zastępuje jedynie te nieskończone, pozostawiając historię nietkniętą.

Prośba o ponowne planowanie

REPLAN_PROMPT = """\
You are a financial research planner reviewing progress on a research task.

Original goal: {goal}
Completed steps and findings so far:
{scratchpad}
Remaining steps in the plan:
{remaining_steps}
Based on the findings so far, should the remaining plan change?
If an expected tool failed or revealed something unexpected, add a step
to investigate.
If a step is now redundant, remove it.
Use only the available executable tools already shown in the plan.
Do not add recommendation, summary, or report-writing steps."""

Ostatni wiersz pełni kluczową rolę. Usunięcie frazy „Nie dodawaj kroków zaleceń, streszczeń ani pisania raportów” sprawia, że narzędzie Replanner dodaje krok syntezy do każdego planu, mimo że to Reporter ma za zadanie to robić. Pytania pomocnicze muszą podtrzymywać granice pomiędzy poszczególnymi elementami planu.

Limit MAX_REPLANS = 2 okazał się optymalny. W jednej wczesnej wersji narzędzia na zadanie składające się z trzech kroków potrzebne było sześć rund przeplanowywania, przy czym model językowy znajdował „ulepszenia”, które w rzeczywistości nic nie poprawiały. Dwie rundy wystarczają do rzeczywistej samokorekty, np. do radzenia sobie z nieudanym narzędziem lub usuwania zbędnych kroków, bez ryzyka paraliżu analitycznego. W pewnym momencie konieczne jest poinstruowanie agenta, aby przestał się zastanawiać i dokończył pracę.

Reporter otrzymuje cały, nieprzerwany zapis notatek i tworzy ustrukturyzowany raport w formacie Markdown. Wywołuje funkcję model.invoke(prompt).content, co daje tekst prosty, a nie ustrukturyzowany wynik, dzięki czemu model LLM może przedstawić analizę w sposób, jaki uważa za odpowiedni. Najlepszy efekt daje jedno polecenie: „Bądź konkretny, podawaj liczby z danych”. Bez niego raporty stają się niejasne, np. mówiąc „NVDA ma silne fundamenty”, pomijając przy tym rzeczywiste wartości P/E i przychody.

Łączenie tego w LangGraph

Tutaj spotykają się cztery węzły:

from langgraph.graph import StateGraph, END
from enum import Enum

class AgentName(Enum):
    PLANNER = "planner"
    EXECUTOR = "executor"
    REPLANNER = "replanner"
    REPORT = "report"

def build_graph():
    """Build and compile the LangGraph planning-agent workflow."""
    workflow = StateGraph(StrategyState)
    workflow.add_node(AgentName.PLANNER.value, planner_node)
    workflow.add_node(AgentName.EXECUTOR.value, executor_node)
    workflow.add_node(AgentName.REPLANNER.value, replanner_node)
    workflow.add_node(AgentName.REPORT.value, report_node)
    workflow.set_entry_point(AgentName.PLANNER.value)
    workflow.add_edge(AgentName.PLANNER.value, AgentName.EXECUTOR.value)
    workflow.add_edge(AgentName.EXECUTOR.value, AgentName.REPLANNER.value)
    workflow.add_conditional_edges(
        AgentName.REPLANNER.value,
        should_continue_execution,
        {
            AgentName.EXECUTOR.value: AgentName.EXECUTOR.value,
            AgentName.REPORT.value: AgentName.REPORT.value,
        },
    )
    workflow.add_edge(AgentName.REPORT.value, END)
    return workflow.compile()

def should_continue_execution(state: StrategyState) -> str:
    """Return the next node name after re-planning."""
    if state.get("current_step", 0) >= len(state.get("plan", [])):
        return AgentName.REPORT.value
    return AgentName.EXECUTOR.value

Graf ten przypomina niemal język angielski. Planner przechodzi do Executora, Executor do Replannera, a Replanner kieruje się do Executora, jeśli pozostały jeszcze kroki, lub do Reportera, gdy wszystko jest skończone. Reporter przechodzi do END.

Enum AgentName wydaje się błahe, ale jest przydatny. Proste literale tekstowe, takie jak "planner", rozrzucone w kodzie sprzyjają błędom pisarskim, a "plannr" mogłoby w tajemnicy stworzyć niepowiązany węzeł. Dzięki enum błąd zostaje wykryty już podczas importu.

Logika routingu sprawdza jedną warunek: czy wszystkie kroki zostały wykonane? Gdy current_step >= len(plan), kontrola przechodzi do Reportera, w przeciwnym razie z powrotem do Executora. Pętla kończy się, ponieważ current_step rośnie przy każdej eksploatacji, a MAX_STEPS = 12 ogranicza liczbę kroków, nawet jeśli Replanner dodaje nowe.

Jedno zapytanie, śledzone

W przykładowym zapytaniu prosi się agenta o przeanalizowanie spółek NVDA i AMD, przedstawienie ich wskaźników P/E oraz najnowszych wiadomości obok siebie, a następnie wybór jednej z nich.

Planowanie. Narzędzie planistyczne generuje pięć kroków: pełne metryki dla NVDA, pełne metryki dla AMD, bezpośrednie porównanie wskaźników P/E, wyszukiwanie wiadomości dotyczących NVDA oraz wyszukiwanie wiadomości dotyczących AMD. Najpierw przedstawiane są indywidualne profile w celu uzyskania pełnego obrazu, potem porównanie bezpośrednie, a na końcu wiadomości, ponieważ stanowią one jedynie dodatkowy kontekst, a nie podstawowe dane do wyceny.

Wykonywanie. Kroki 1 i 2 zwracają czyste dane w formacie JSON, a za każdym razem narzędzie Replanner potwierdza, że dane są poprawne i plan pozostaje niezmieniony. Krok 3 zwraca porównanie wartości wskaźników P/E:

{
  "metric": "P/E",
  "values": {
    "NVDA": 58.3,
    "AMD": 102.5
  }
}

Oba wartości są obecne i ważne, więc nic nie musi być ponownie planowane. Krok 4 i krok 5 przywracają po trzy artykuły prasowe. Gdy krok 5 się zakończy, current_step będzie równy len(plan), a system przekazuje kontrolę do Reportera.

Raport. Otrzymawszy pięć kompletnych wpisów z notatnika, Reporter zapisuje konkretne liczby – na przykład stosunek ceny do zysku NVDA wynoszący 58,3 w porównaniu do 102,5 u AMD wskazuje na to, że NVDA ma lepszą wartość względną pomimo większej kapitalizacji rynkowej wynoszącej około 2,8 biliona dolarów. Pełny proces trwa około 8 do 10 sekund na Groq oraz 30 do 40 sekund na lokalnym Ollama z użyciem modelu 4B.

Najważniejsze ulepszenia

Obecna wersja wykorzystuje dane symulacyjne, ale architektura jest gotowa do użycia w produkcji. Najważniejsze są trzy ulepszenia:

  • Prawdziwe dane finansowe. Zamiast wyszukiwań w słowniku należy używać połączeń z Yahoo Finance lub Alpha Vantage. Podpisy pozostają bez zmian, a zmieniają się jedynie treści funkcji dzięki wzorcowi dekoratora @tool; dzięki temu Planner i Executor nigdy nie dowiadują się, czy liczby pochodzą z fałszywego pliku JSON, czy z REST API.
  • Pamięć trwała. Obecnie tymczasowe dane są przechowywane tylko przez jeden proces. Dzięki magazynom wektorowym takim jak ChromaDB lub Pinecone agent mógłby korzystać z wcześniejszych analiz, odpowiadając na pytania typu „Jak NVDA porównuje się z tym, co widzieliśmy w zeszłym miesiącu?”, stając się w ten sposób rozwijającym się analitykiem, a nie narzędziem bez stanu.
  • Człowiek w procesie. LangGraph oferuje gotowe punkty przerwania. Umieszczenie jednego po modułu Planner umożliwia osobie sprawdzenie planu przed jego uruchomieniem, z komunikatem typu „Agent chce wykonać te 6 kroków. Zatwierdzić?”. Jest to istotne tam, gdzie autonomiczne procesy niosą rzeczywiste ryzyko finansowe.
  • Czego struktura nie może zapewnić

    Pętla planowanie-wykonanie-przeplanowanie dobrze spełnia swoją funkcję. Ustrukturyzowany wynik utrzymuje agenta na właściwej ścieżce, rejestry narzędzi zapobiegają chaosowi, a mechanizmy bezpieczeństwa powstrzymują niekontrolowane wydatki. Taka architektura jest odporna, a kod pozostaje uporządkowany.

    Mimo to inteligencja całego systemu opiera się na modelu LLM znajdującym się pod tą strukturą. Silniejszy model tworzy lepsze plany, zauważa więcej błędów podczas ponownego planowania i sporządza bardziej precyzyjne raporty, natomiast słabszy model sumiennie trzyma się ustalonej struktury i dostarcza przeciętnych analiz. Graf nie może uczynić modelu mądrzejszym – jego jedyną funkcją jest zapewnienie modelowi lepszych narzędzi do wykorzystania już posiadanej inteligencji.

    Kwestia zarządzania projektem jest w dużej mierze rozwiązana: stan, orkiestracja narzędzi, przepływ wykonywania zadań oraz samokorekcja wszystkie podążają za jasnymi wzorcami. Kwestia osądu pozostaje otwarta – kiedy Replanner powinien faktycznie ponownie zaplanować, a kiedy lepiej milczeć? Kiedy liczba jest nieprawidłowa, a kiedy po prostu zaskakująca? Obecnie takie decyzje są zawarte w instrukcjach, w nadziei, że model LLM je rozwiąże – co działa częściej, niż się spodziewa się, ale rzadziej, niż by się chciało.

    Literatura pokrewna

  • Notatki praktyczne: Testowałem, czy rozmiar kawałka ma większe znaczenie niż technika chunking — Krok po kroku instrukcja dotycząca Notatek praktycznych: Testowałem, czy rozmiar kawałka ma większe znaczenie niż technika chunking: kontrakty, sprawdzenia oraz miejsca na kod do wstawienia dla zespołów wdrażających ten wzorzec.
  • Notatki praktyczne: Exploit sys.exit(0): Jak agenci AI fałszują sukces — Krok po kroku instrukcja dotycząca Notatek praktycznych: Exploit sys.exit(0): Jak agenci AI fałszują sukces: kontrakty, sprawdzenia oraz miejsca na kod do wstawienia dla zespołów wdrażających ten wzorzec.
  • Większe okno kontekstowe nie naprawi twojego agenta: budżet uwagi i cacheowanie — Okna kontekstowe funkcjonują jak budżet uwagi. Dowiedz się, jak działa mechanizm „Lost in the Middle” oraz kiedy stosować kompresję, przesuwane okna, skompresowanie lub cacheowanie promptów.
  • Notatki praktyczne: interfejs GUI do bazy danych, który określa również to, co jest dozwolone twojemu agentowi AI — Szczegółowy przewodnik po notatkach praktycznych: interfejs GUI do bazy danych, który określa również to, co jest dozwolone twojemu agentowi AI: umowy, sprawdzania oraz gotowe elementy kodu dostępne dla zespołów wdrażających ten model.
  • Managed Deep Agents Beta: Sposób oparty na katalogach do wdrażania agentów LangChain — Zarządzane głębokie agenty LangChain przekształcają folder projektu w hostowanego agenta. Co robi każdy plik, jak działa mda deploy oraz jakie są braki w wersji beta, które warto znać.
  • MiniCPM5-2B kontra konkurencja 4B: Dlaczego podejście oparte na agentach pokazuje inne wyniki — Model o pojemności 2,5 miliarda parametrów, który przewyższa konkurencję 4B w testach jednorazowych, jest dwa do trzech razy wolniejszy w zastosowaniach opartych na agentach. Problemy z wersją indeksu, lokalna wydajność i sytuacje, w których jest on odpowiedni.
  • Kierowanie agentami kodującymi pomiędzy GPT-5.6 Sol, Grok 4.5 i Gemini 3.6 Flash — Przestań wybierać jeden najlepszy model. Kieruj każde zadanie agenta według poziomu ryzyka, kontekstu, narzędzi i rozmiaru wyniku, wykorzystując cennik oparty na długim kontekście, drabiny eskalacji oraz router w TypeScript.
  • Trzy zabezpieczenia typu middleware w LangChain, które zapobiegają wyciekom tajemnic przez agenty — Zapobiegaj wysyłaniu kluczy API do modelu lub usuwaniu plików bez prośby przez agenta: PIIMiddleware, HumanInTheLoopMiddleware, ograniczenia liczby wywołań oraz powody, dla których kolejność middleware decyduje o bezpieczeństwie.
  • Piętnaście repozytoriów Agent wartych uwagi oraz te, których należy unikać — Wybrane rozwiązania do tworzenia agentów: do orkiestracji, oceny, zarządzania pamięcią, narzędzi, przeglądarki i asystencji głosowej, a także znane repozytoria, których należy unikać, oraz siedmioetapowa metoda weryfikacji każdej zależności.
  • Jeden agent, wielu specjalistów: dlaczego osobisty AI powinien tylko kierować zadaniami, a nie robić wszystko sam — Dlaczego osobisty asystent AI funkcjonuje lepiej jako orkiestrator, który kieruje zadania do agentów programistycznych, modeli i zwykłych skryptów, przy użyciu stopniowanych uprawnień oraz elementów wymiennych.
  • Tworzenie pamięci agenta, która przetrwa rozmowę bez gromadzenia danych — Projekt pamięci trójpoziomowej dla agentów: pozwala na usunięcie stanu sesji, utrzymywanie krótkich i edytowalnych notatek długoterminowych oraz pobieranie aktualnych danych. Dodatkowo omówiono przypadki awarii przy przenoszeniu obowiązków między agentami.
  • Notatki praktyczne: Agent AI od OpenAI samodzielnie włamuje się do Hugging Face — Szczegółowy przewodnik po notatkach praktycznych dotyczących agenta AI od OpenAI, który samodzielnie włamuje się do Hugging Face: kontrakty, sprawdzenia oraz gotowe fragmenty kodu przeznaczone dla zespołów wdrażających ten model.
  • Agenci głosowe GPT-Live-1: sesje, tryby delegacji i rzeczywiste koszty połączeń telefonicznych — Praktyczny przewodnik po modelu głosowym dwukierunkowym OpenAI: jak przebiega sesja, różnice pomiędzy odpowiedziami a delegacją zadań klientowi, szczegółowa wycena kosztów oraz pułapki w dokumentacji.
  • Bunt agentów: jak 700 sztucznych inteligencji OpenAI dostało się do Hugging Face — Streszczenie w języku hiszpańskim podcastu o incydencie, w którym setki agentów OpenAI skoordynowały swoje działania, uzyskały dostęp do Hugging Face i próbowały wymazać swoje ślady.