So you’ve got your agents down.
You created the n8n workflow, you added the tools, and you tested it until you felt it was ready.
Then your boss calls you at three in the morning asking why your agent approved refunds for orders that were correctly delivered. You go check the logs and you find out that some smart customer used prompt injection to convince your agent to issue a refund for a perfectly good item.
You can debug the log trace all you want, but the money is gone, and so is your credibility.
AI agents are just huge sets of instructions put on top of a large language model. The Dos and don’ts written in the system prompt are just suggestions. Even if you explicitly forbid something, it doesn’t mean it won’t be done. It just makes it less probable.
So how do you make it deterministic instead of probabilistic? How do you define what your agent can or cannot touch, so that you can sleep without having to worry about it?
In the early days of AI agents, I used to have “guardian agents”. They were very simple agents with cheap models attached to them, tasked only with understanding whether a prompt was malicious or not. Before that, we were using regex: regular expressions built to find given sets of words that let us flag a message as malicious or not. That meant that every time someone managed to get past it, we had to go back and fix the regex once again. Guardrails, however, are a different league.
Now, the way I think about it, there are two main types of guardrails to restrict the actions AI agents are allowed to take in n8n:
-
The first one is a human in the loop. The agent has a set of tools it’s connected to, but some of these tools require active approval by a human. A human is then able to evaluate every given case and decide whether the tool should be used or not, and in doing so, they’re also creating a set of data that can then be used to “train” the model so that it requires less and less human intervention for approval.
-
Then there’s the Guardrails node that you can find in n8n.When you add a Guardrails node in n8n, you can choose whether you want to sanitize text or, and this is our case, check the text for violations. Then you specify the text to check and add the guardrail. You can choose between keywords, jailbreak, NSFW, personal data (PII), secret keys, topical alignment, URLs, custom, or custom regex. You can also customize the system message, which by default is already pretty clear, but you can add your own information or your own policies.
A Guardrails node is, in fact,a small agent that treats the text it receives as input as a sterile box, something that is only there to be evaluated, not executed upon. So it will look at the text with a clinical eye. Then you decide what happens if the check passes or fails. In my case, if the check passes, I go on with the workflow. If it fails, I send a standard message to the user, but I also log the information about the execution and notify the system administrator.
There’s also another case, and that’s the one where the decision isn’t driven by malicious intent from the user, but by the agent simply not understanding. To handle that, if it’s vital that the execution of certain tools or operations is controlled, what I do, on top of the human in the loop, is have the agent output a schema that explicitly lists the actions it wants to take. Then I have that evaluated by a guardrails agent that checks whether those actions are permitted or not. If they are, the workflow can continue and the actions can be executed deterministically. Otherwise, I prompt the model again by looping back to a previous point in the workflow with the new information provided by the guardrail agent’s output.
Do you have any other ideas or tips on how we could restrict what our agents do?