Agent Architecture
How AI agents work — the planning, execution, and tool-calling lifecycle that injectAgent() connects your Angular app to. Let's walk the Python patterns behind modern agents and see exactly how each one surfaces in Angular through @threadplane/langgraph.
Every section below shows the Python backend code first, then the Angular frontend code that consumes it. You need both halves to build a production agent application — LangGraph handles the intelligence, injectAgent() handles the reactivity.
The Agent Loop
Every agent follows a five-phase cycle. It's worth understanding, because each phase maps to a specific injectAgent() signal in your Angular app.
The user sends a message. On the Angular side, submit() posts input to LangGraph Platform. On the Python side, the message lands in the graph's messages state key.
The LLM examines the full message history plus any accumulated state. It decides what to do next — respond directly, call one or more tools, or delegate to a subagent.
If the LLM decided to call tools, LangGraph routes to the tool node. Tools run — database queries, API calls, code execution — and their results feed back into state as ToolMessage entries.
After tools finish (or if no tools were needed), the agent streams its final response token by token. injectAgent() updates the messages() signal in real time so your Angular template re-renders incrementally.
LangGraph checkpoints the full state — messages, tool results, plan, everything. The agent may loop back to Plan (if tools returned data that needs further reasoning) or finish. The checkpoint is what enables time-travel debugging via history().
ReAct Pattern
ReAct (Reason + Act) is the most common agent pattern. The agent reasons about the user's question, decides to call a tool, observes the result, and loops until it has enough information to answer.
Here's the key insight: should_continue is the decision point. If the LLM's response contains tool_calls, the graph routes to the tools node. If not, it ends. After tools execute, the graph loops back to model so the LLM can reason about the tool results. This loop continues until the LLM responds without requesting any tools.
Tool Calling Deep Dive
Tools are how agents interact with the outside world. You'll want both halves here — the Python definition and the Angular consumption.
Defining Tools in Python
Every tool is a Python function decorated with @tool. LangGraph converts the function signature and docstring into the JSON schema that the LLM uses to decide when and how to call it:
The LLM reads the docstring to decide when to call a tool. A vague docstring like "does stuff" means the LLM will not know when to use it. Be specific: what the tool does, what it returns, when to use it.
How Tools Surface in Angular
When the agent calls a tool, injectAgent() exposes the execution lifecycle through toolCalls():
Tool Execution Flow
The full lifecycle from Python tool definition to Angular UI update:
The model returns an AIMessage with a tool_calls array. Each entry specifies the tool name and arguments.
The should_continue conditional edge detects tool_calls and routes to the tools node.
ToolNode calls the Python function. The result is wrapped in a ToolMessage and appended to state.
LangGraph Platform streams the tool call and result as SSE events to the Angular client.
toolCalls() updates as the tool moves through pending, running, complete, and error states. Each update triggers OnPush change detection.
Multi-Agent Architecture
When a single agent with tools is not enough, you can compose multiple agents into a supervisor-worker architecture. A supervisor agent receives the user's request, decides which specialist to delegate to, and synthesizes the final answer.
The subagentToolNames option tells injectAgent() which tool calls spawn subagents. The default Deep Agents tool name is task; set this option when your graph uses custom delegation tool names. Ordinary LangGraph subgraph nodes stream through the parent signals, but they do not appear in subagents() unless they are represented by matching delegation tool calls.
Error Handling and Recovery
Agents fail. Tools throw exceptions, APIs time out, LLMs hallucinate invalid tool arguments. A robust architecture handles all of these gracefully.
Python-Side Error Handling
When handle_tool_error=True is set, LangGraph catches ToolException and feeds the error message back to the LLM as a ToolMessage. The LLM sees the error and can retry with corrected arguments or explain the failure to the user.
How Errors Surface in Angular
Error Recovery Strategies
| Error type | Python behavior | Angular signal |
|---|---|---|
Tool throws ToolException | Error fed back to LLM, agent retries | toolCalls() shows error in result |
| Tool throws unexpected error | LangGraph catches it, marks tool as failed | error() fires with details |
| LLM returns invalid tool args | ToolNode validation fails, error fed to LLM | toolCalls() shows failed status |
| Transport error (network) | N/A | error() fires, status() becomes 'error' |
| Agent exceeds recursion limit | Graph raises GraphRecursionError | error() fires with recursion message |
LangGraph defaults to 25 recursion steps. If your agent loops between model and tools more than 25 times, it stops with a GraphRecursionError. Increase the limit in production by passing it in the run config — graph.invoke(input, config={"recursion_limit": 50}) — or redesign the agent to converge faster.
Checkpointing and Debugging
Every time a node completes, LangGraph saves a checkpoint — a full snapshot of the agent's state at that moment. injectAgent() exposes this checkpoint timeline to Angular, giving you time-travel debugging for free.
How Checkpoints Work
Exposing Checkpoints in Angular
Building a Debug Timeline
Each entry in agent.history() is an AgentCheckpoint — a runtime-neutral snapshot of one point in the run:
Bind to those fields when you render the timeline (for the raw per-node metadata LangGraph attaches, read agent.langGraphHistory() instead):
When you submit from a previous checkpoint, LangGraph creates a new branch from that point. The original timeline is preserved. The branch() signal tells you which branch is currently active. See the Time Travel guide for the full walkthrough.
Choosing an Architecture
Not every application needs a multi-agent swarm. Here's how I'd pick the right level of complexity — and why simpler usually wins until it can't.
Single Agent with Tools
Use when: Most applications. The user has a conversation, the agent calls tools as needed, and responds.
Angular signals used: messages(), toolCalls(), status()
Single Agent with Human-in-the-Loop
Use when: The agent takes high-stakes actions (sending emails, modifying data, making purchases) that need human approval.
Angular signals used: messages(), interrupt(), status() plus submit({ resume }) to approve
Multi-Agent Supervisor
Use when: The task naturally decomposes into specialist roles (researcher, analyst, writer), and each specialist needs its own tools, prompts, and reasoning chain.
Angular signals used: messages(), toolCalls(), status(); subagents() only when delegation happens through tracked tool calls
Decision Matrix
| Factor | Single agent | Single + approval | Multi-agent |
|---|---|---|---|
| Tool count | 1-10 | 1-10 | 10+ across specialists |
| Task complexity | Single domain | Single domain, high stakes | Cross-domain |
| Latency budget | Low | Medium (human wait) | Higher (multiple LLM calls) |
| State isolation | Shared | Shared + interrupt | Only if you design it |
| Angular complexity | Low | Medium | Higher |
State isolation is the one row that is not automatic. Adding a compiled graph as a node — as in the supervisor example above — runs the child against the parent's state schema, so parent and child read and write the same channels. To actually isolate a specialist, give its StateGraph its own state schema and share only the keys you want crossing the boundary: LangGraph passes a subgraph node through the keys the two schemas have in common.
Begin with a single agent and tools. Add human-in-the-loop when you need approval flows. Graduate to multi-agent only when a single agent's context window cannot hold all the tools and instructions it needs.
What's Next
Learn the graph, node, and edge primitives that agents are built on.
Stream token-by-token responses with multiple stream modes.
Build human-in-the-loop approval flows that pause and resume agents.
Compose multi-agent systems with orchestrators and specialist workers.
Debug agents by stepping through checkpoint history and branching.
How Signals power the reactive model behind injectAgent().