A LangGraph web-search node needs typed state and a route that ends the search. Keep attempts, evidence status and sources in the state so repeated retrieval cannot continue just because another tool call is possible. Test the stop condition without relying on a model to choose it.
A deterministic graph fixture
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class ResearchState(TypedDict):
attempts: int
max_attempts: int
sufficient: bool
sources: list[str]
def research_graph():
def retrieve(state):
return {'attempts': state['attempts'] + 1,
'sources': list(dict.fromkeys(state['sources'] + ['https://example.org/docs']))}
def route(state):
return END if state['sufficient'] or state['attempts'] >= state['max_attempts'] else 'retrieve'
graph = StateGraph(ResearchState)
graph.add_node('retrieve', retrieve)
graph.add_edge(START, 'retrieve')
graph.add_conditional_edges('retrieve', route, {END: END, 'retrieve': 'retrieve'})
return graph.compile()
result = research_graph().invoke({"attempts": 0, "max_attempts": 2,
"sufficient": False, "sources": []})
assert result["attempts"] == 2
assert len(result["sources"]) == 1
The fixture was run with installed LangGraph 1.2.14. It uses a synthetic source and stops when evidence is sufficient or the attempt budget is exhausted. It verifies graph construction and routing, not live search or the quality of a model answer.
Replace the fixture node without weakening its boundary
| State or rule | Production purpose |
|---|---|
| attempts and max_attempts | Bound repeated retrieval |
| sources with stable identities | Preserve and deduplicate evidence |
| sufficient | Store an evaluated evidence decision |
| conditional edge | Choose another attempt or END |
| caller deadline | Stop work even when the attempt budget remains |
Keep failure visible to the caller
A transport error should not become a successful empty evidence record. Preserve the error and decide whether a bounded retry is eligible. When evidence remains insufficient, end with that status instead of creating another unbounded query.
Use current StateGraph and conditional-routing documentation. This page implements a node boundary; the parent guide covers the complete research-agent architecture. No token-saving or accuracy-improvement percentage is inferred from the graph running.
Reference documentation: docs.langchain.com — /oss/python/langgraph/graph-api; www.searchcans.com — /google-search-api.