Middleware-ul controlează ce se întâmplă în bucla unui agent creat cu create_agent. Se dă ca listă în parametrul middleware și rulează în graful LangGraph întors de create_agent, deci rămâne activ și când agentul e folosit ca nod într-un graf mai mare. În v1 a înlocuit parametrii pre_model_hook, post_model_hook și state_modifier ai lui create_react_agent.
Există două feluri de hook-uri. Cele de tip nod rulează în puncte fixe: before_agent și after_agent (o dată pe invocare), before_model și after_model (la fiecare apel de model). Cele de tip wrap înconjoară un apel și decid dacă handler-ul rulează de zero ori, o dată sau de mai multe ori: wrap_model_call și wrap_tool_call. Se scriu ca decoratori (@before_model, @after_model, @wrap_model_call, @wrap_tool_call, @dynamic_prompt) sau ca subclase ale AgentMiddleware.
Ordinea din listă contează: hook-urile before_* rulează de la primul middleware la ultimul, cele after_* în ordine inversă, iar wrap_* se îmbină ca apelurile de funcții (primul le înconjoară pe celelalte). Un hook poate încheia bucla întorcând {"jump_to": "end"}, dacă a declarat can_jump_to. Pe lângă middleware-ul propriu, langchain.agents.middleware are variante gata făcute pentru cazurile frecvente.
Concepte cheie și caracteristici
| Concept / Parametru | Descriere practică |
|---|---|
SummarizationMiddleware |
Rezumă mesajele vechi când istoricul se apropie de limita de tokeni (trigger) și păstrează ultimele mesaje (keep). |
HumanInTheLoopMiddleware |
Oprește execuția înaintea uneltelor din interrupt_on și așteaptă o decizie: approve, edit, reject sau respond. Cere checkpointer. |
ModelRetryMiddleware / ToolRetryMiddleware |
Reîncearcă apelurile eșuate de model sau de unelte, cu backoff exponențial. |
ModelFallbackMiddleware |
Trece la alte modele, în ordine, dacă modelul principal eșuează. |
ModelCallLimitMiddleware |
Limitează apelurile de model pe fir (thread_limit, cere checkpointer) sau pe rulare (run_limit); exit_behavior este "end" (implicit) sau "error". |
ToolCallLimitMiddleware |
Limitează numărul de apeluri de unelte, pentru toate uneltele sau pentru una anume. |
PIIMiddleware |
Detectează și tratează datele personale, de exemplu PIIMiddleware("email"). |
TodoListMiddleware |
Dă agentului o listă de sarcini pentru planificare. |
before_model / after_model |
Hook-uri de tip nod, la fiecare apel de model; primesc starea și runtime-ul și pot întoarce actualizări de stare. |
wrap_model_call / wrap_tool_call |
Hook-uri care înconjoară apelul; folosite pentru reîncercări, cache, schimbarea modelului sau tratarea erorilor uneltelor. |
Exemple practice și scenarii reale
Exemplul 1: Hook propriu cu decorator: limită de mesaje
După exemplul din documentație. can_jump_to permite hook-ului să încheie bucla.
from typing import Any
from langchain.agents import create_agent
from langchain.agents.middleware import before_model, AgentState
from langchain.messages import AIMessage
from langgraph.runtime import Runtime
@before_model(can_jump_to=["end"])
def limita_mesaje(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if len(state["messages"]) >= 50:
return {"messages": [AIMessage("Am atins limita conversației.")], "jump_to": "end"}
return None
agent = create_agent(model="gpt-6-astra", tools=[], middleware=[limita_mesaje])
Exemplul 2: Erorile uneltelor transformate în mesaje pentru model
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage
@wrap_tool_call
def erori_unelte(request, handler):
try:
return handler(request)
except ValueError as e: # doar erorile cauzate de intrări greșite
return ToolMessage(
content=f"Eroare la unealtă: verifică argumentele ({e})",
tool_call_id=request.tool_call["id"],
)
Exemplul 3: Aprobare umană și reluare
Cu version="v2", invoke întoarce un obiect cu atributul interrupts; reluarea folosește același thread_id.
from langgraph.types import Command
config = {"configurable": {"thread_id": "email-1"}}
rez = agent.invoke(
{"messages": [{"role": "user", "content": "Trimite raportul lui Mihai"}]},
config=config, version="v2",
)
print(rez.interrupts) # acțiunile care așteaptă o decizie
agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}),
config=config, version="v2")
Greșeli frecvente și capcane
- Să folosești
HumanInTheLoopMiddlewarefără checkpointer. Pauza se bazează pe persistența LangGraph; în producție, un checkpointer persistent. - Să răspunzi cu
respondca să refuzi o unealtă cu efecte. Documentația spune că mesajul ei e tratat ca rezultat reușit al uneltei; pentru refuz se foloseștereject. - Să prinzi în
wrap_tool_callorice excepție. Documentația recomandă să tratezi doar erorile cauzate de intrări greșite; erorile de rețea țin de middleware-ul de retry, iar bug-urile din unealtă trebuie să rămână vizibile.