Implementing Agentic VLAN Management Patterns in Langflow¶
Implementation Patterns Tested¶
Four Langflow implementation patterns were tested for the VLAN management workflow:
| Pattern | Description | Status |
|---|---|---|
| Plain orchestrator | A single agent uses MCP tools and prompt rules for the full workflow | Working |
| Guarded orchestrator | A single agent uses Python tools for deterministic lookups and guards | Working and recommended |
| Multi-agent pipeline | Separate Planner, Lookup, and Patch agents are chained together | Abandoned |
| Tool-guarded agent | Python tools handle nearly all logic, including approval and state | Abandoned |
The guarded orchestrator was selected as the most practical Langflow implementation pattern.
The Working Pattern: Guarded Orchestrator¶
The guarded orchestrator is a single-agent Langflow flow backed by Python tools. It keeps the conversational interface and tool-calling flexibility of an agent, but moves all safety-critical work into deterministic code.
ChatInput
└─ Guarded Orchestrator Agent
├─ lookup_and_guard
├─ netbox_patch_interface
└─ netbox_rollback_interface
└─ ChatOutput
The flow has five main components:
- ChatInput
- Guarded Orchestrator Agent
lookup_and_guardnetbox_patch_interfacenetbox_rollback_interface- ChatOutput
The LLM handles the natural-language interaction, while Python tools handle the operational logic.
How the Guarded Orchestrator Works¶
The workflow uses a two-turn approval pattern.
Turn 1: Request and Proposal¶
The user asks for a VLAN change:
Add VLAN 20 to ge-0/0/0 on rtr-lab-01
The agent extracts:
{
"action": "add",
"device": "rtr-lab-01",
"interface": "ge-0/0/0",
"vlan_vid": 20
}
It then calls lookup_and_guard.
The Python tool performs all NetBox lookups, guard checks, and patch computation. If validation succeeds, it returns structured data that the agent uses to produce a proposal table and an APPROVAL_TOKEN.
If validation fails, the tool returns an error and the agent reports it to the user.
Turn 2: Approval or Rejection¶
If the user replies approve, the agent reads the APPROVAL_TOKEN from the conversation history and calls netbox_patch_interface.
If the user replies reject, the operation is cancelled and no write operation is performed.
After the PATCH call, the result is verified. If verification fails, netbox_rollback_interface restores the previous state.
The lookup_and_guard Tool¶
The lookup_and_guard tool is the main guard layer. It receives four values from the agent:
| Field | Meaning |
|---|---|
action |
add or remove |
device |
Device name |
interface |
Interface name |
vlan_vid |
VLAN number visible to the network engineer |
The tool performs the following steps:
| Step | NetBox call | Guard |
|---|---|---|
| 1 | Find device by name | Not found or ambiguous device produces an error |
| 2 | Find interface by device ID and interface name | Not found or ambiguous interface produces an error |
| 3 | Read interface mode | Interface must be in tagged mode |
| 4 | Find VLAN by VID | Missing VLAN produces an error |
| 5 | Check current tagged VLANs | Add must not duplicate; remove must not remove an absent VLAN |
| 6 | Resolve current tagged VLAN IDs to VIDs | Used for display |
| 7 | Compute new tagged VLAN ID list | Used for PATCH |
| 8 | Compute rollback ID list | Used if verification fails |
On success, the tool returns:
{
"ok": true,
"action": "add",
"device": "rtr-lab-01",
"interface": "ge-0/0/0",
"vlan_vid": 20,
"vlan_status": "active",
"interface_id": 2,
"vlan_id": 2,
"rollback_ids": [3],
"new_tagged_ids": [2, 3],
"current_vids": [30],
"new_vids": [20, 30]
}
The LLM does not compute the PATCH payload and does not extract VLAN IDs from raw NetBox objects.
Avoiding VID and DB-ID Confusion¶
NetBox uses two different identifiers that are easy to confuse:
| Identifier | Example | Meaning |
|---|---|---|
| VLAN VID | 20 |
The 802.1Q VLAN number visible to engineers |
| VLAN database ID | 2 |
The internal NetBox ID required for API PATCH calls |
The guarded orchestrator avoids this problem by separating the values before they reach the LLM:
| Field | Used for |
|---|---|
current_vids |
Proposal display |
new_vids |
Proposal display |
new_tagged_ids |
PATCH payload |
rollback_ids |
Rollback payload |
This makes the workflow structurally safer than a pattern where the LLM reads raw nested NetBox responses and decides which integer to use.
The Approval Token Pattern¶
The APPROVAL_TOKEN is embedded at the end of the proposal response:
APPROVAL_TOKEN: {"interface_id":2,"vlan_id":2,"new_tagged_ids":[2,3],"rollback_ids":[3],"vlan_vid":20,"action":"add","device":"rtr-lab-01","interface":"ge-0/0/0"}
The token contains all information needed for the approval turn:
| Field | Purpose |
|---|---|
interface_id |
Interface to patch |
vlan_id |
Target VLAN database ID |
new_tagged_ids |
Complete tagged VLAN list after change |
rollback_ids |
Previous tagged VLAN list |
vlan_vid |
Display value |
action |
Add or remove |
device |
Display value |
interface |
Display value |
This avoids external state storage and works reliably in the single-agent Langflow flow.
What the LLM Does and Does Not Do¶
| Task | LLM | Python tool |
|---|---|---|
| Parse natural-language request | Yes | No |
| Look up NetBox objects | No | Yes |
| Extract database IDs | No | Yes |
| Check interface mode | No | Yes |
| Check idempotency | No | Yes |
| Compute PATCH payload | No | Yes |
| Format proposal | Yes | No |
| Ask for approval | Yes | No |
| Apply PATCH after approval | Calls tool | Executes PATCH |
| Roll back if needed | Calls tool | Executes rollback |
The result is not a fully deterministic workflow, because the LLM still controls tool calling inside the agent. However, the parts that must be exact are handled in Python.
Why the Plain Orchestrator Was Not Preferred¶
The plain orchestrator worked, but it delegated too much responsibility to the LLM. The agent performed several MCP lookups, interpreted raw NetBox responses, extracted IDs, applied guard logic from the prompt, and computed the patch payload.
This works in simple cases, but it leaves important reliability issues:
- the LLM sees raw nested NetBox objects;
- VID and database ID values can be confused;
- guard checks depend on prompt compliance;
- patch computation depends on LLM reasoning.
The guarded orchestrator keeps the same simple single-agent structure but moves these operations into Python.
Why the Multi-Agent Pipeline Was Abandoned¶
The multi-agent pipeline attempted to split the workflow into separate agents:
ChatInput
└─ Planner Agent
└─ Lookup Agent
└─ Patch Agent
└─ ChatOutput
The intended design was:
| Agent | Responsibility |
|---|---|
| Planner | Parse the natural-language request |
| Lookup | Query NetBox and run guard checks |
| Patch | Format the proposal or apply the approved change |
This pattern was abandoned because Langflow has no pause-and-wait node. Every user message runs the full canvas. As a result, all agents run on both the initial request and the approval response.
On the approval turn, the Planner and Lookup agents have no meaningful work to do. They must detect that the message is only an approval response and pass it through unchanged. This passthrough pattern proved unreliable, especially when previous tokens and previous requests remained in the conversation history.
The problem is structural: in Langflow, the agents themselves must decide whether to act. In a graph-routed platform, the graph decides which node runs next.
Why the Tool-Guarded Agent Was Abandoned¶
The tool-guarded agent attempted to move almost all logic into Python tools, including turn detection, approval parsing, patching, verification, and result formatting. Pending state was stored in /tmp/vlan_pending.json instead of in the conversation history.
This seemed safer, but it exposed a different limitation. After a Python tool returned a successful result, the LangChain agent loop still asked the LLM what to do next. In testing, the LLM sometimes continued calling tools autonomously, including calls for the opposite VLAN action. This could overwrite the pending file and corrupt state for the next request.
This showed that Python can guard what happens inside each tool call, but it cannot fully control which tool the Langflow agent decides to call next.
Selected Pattern¶
The selected pattern is therefore the guarded orchestrator:
- one agent;
- deterministic Python guard tool;
- explicit approval token;
- Python PATCH and rollback tools;
- no multi-agent passthrough;
- no external pending file.
This pattern fits Langflow's strengths while avoiding the parts of the platform that are difficult to control deterministically.