Skip to content

AgentNode

nae.AgentNode

AgentNode(name: Optional[str] = None, llm: Optional[Any] = None, node_prompt: str = 'you are a helpful assistant', tools: Optional[list] = None, max_tool_iterations: int = 25, input_field: str = 'messages', output_field: str = 'messages', reads: Optional[List[str]] = None, writes: Optional[Union[List[str], Dict[str, type]]] = None, reasoning_effort: Optional[str] = None, cache_ttl: Optional[int] = None, retry: bool = False)

Bases: LLMNode

An LLM agent: prompt the model over the history and return a delta.

Prepends node_prompt as a SystemMessage to the history (read from input_field, default messages), calls the LLM, and returns only the keys it updates. Two optional modes change what it does with the response:

  • Tool-call loop (tools=[...]): every requested tool is executed, ToolMessages are appended, and the model is re-invoked until it stops calling tools (capped by max_tool_iterations) — all of it folded into one {"messages": [...]} delta. Cannot be combined with multi-field structured writes.
  • Structured output (writes=[...] with two+ keys, or a writes={key: type} dict): the model is wrapped with with_structured_output, so it returns one field per key. The dict form types each field, so values come back native ({"score": int} -> 9, an int) rather than stringified.

A non-default single output_field/writes key writes the final response's content to that key instead of appending to messages (a transform node).

Examples:

from nae import AgentNode, AgenticGraph

def add(a: int, b: int) -> int:      return a + b
def multiply(a: int, b: int) -> int: return a * b

agent = AgentNode(llm=llm, tools=[add, multiply])   # name inferred -> "agent"
graph = AgenticGraph(start_node=agent, end_nodes={agent})

graph.invoke(message="What is 21 + 21, then times 3?")   # -> 126

Initialize an AgentNode.

Parameters:

Name Type Description Default
name Optional[str]

Unique identifier for the node

None
llm Optional[Any]

Language model instance to be used by this node

None
node_prompt str

System prompt/instructions for the language model

'you are a helpful assistant'
tools Optional[list]

Tools this agent can call. A StructuredTool/BaseTool is used as-is; a plain callable is wrapped via StructuredTool.from_function. When set, the node runs an internal tool-call loop (capped by max_tool_iterations). Cannot be combined with multi-field structured writes.

None
max_tool_iterations int

Safety cap on the internal tool-call loop; a model that keeps requesting tools beyond this raises RuntimeError.

25
input_field str

State key to read the message history from.

'messages'
output_field str

State key to write to. When "messages" (default) the produced messages are appended to the conversation; any other key receives the final response's string content instead (useful for transform nodes — that key must exist in your state schema).

'messages'
reads Optional[List[str]]

State keys this node reads (multi-key form; back-compat generalization of input_field). Each read is dispatched by VALUE type: a non-empty list of messages becomes conversation history (concatenated in order), anything else is a scalar interpolated into node_prompt via .format_map. Use reads OR input_field.

None
writes Optional[Union[List[str], Dict[str, type]]]

State keys this node writes (multi-key form; back-compat generalization of output_field). Accepts either a list[str] or a dict[str, type]. ["messages"] appends to the conversation; a single scalar key (list form) writes the final response content; two or more scalar keys switch to structured output (one field per key, no tool loop). A dict[str, type] ALWAYS uses structured output (even for one key) and types each field as given, so the node returns NATIVE-typed values (e.g. {"score": int} -> 9, an int) instead of all-str. A list[str] types every field as str. Use writes OR output_field.

None
reasoning_effort Optional[str]

Reasoning effort for reasoning-capable models (e.g. "low"/"medium"/"high" on gpt-5.x). Passed as a per-call kwarg to the LLM. (DecisionNode does not expose this — reasoning effort conflicts with structured output on current OpenAI models.)

None
cache_ttl Optional[int]

see Node — node-level result caching.

None
retry bool

see Node — node-level retry on exception.

False

__call__

__call__(state: dict) -> dict

Process the current state and generate a response.

Runs the agent and, when tools are set, its internal tool-call loop, accumulating every message produced this turn (the AIMessage, any ToolMessages, and follow-up AIMessages) into a single delta. Returns only the delta — the state reducers append it to canonical state.

Parameters:

Name Type Description Default
state dict

Current conversation state containing message history

required

Returns:

Type Description
dict

A state delta: {"messages": [...new...], "log": [...new...]}

Raises:

Type Description
ValueError

If LLM is not set or if state is invalid