Regex Guardrail¶
The Regex Guardrail node matches text from an input slot against a list
of regex patterns (one per string in a
string storage) and branches the
graph on the result. Typical use: short-circuit
disallowed, off-topic, or jailbreak-style inputs to a
canned-response node before they reach a
Generate node. Changing string_storage to a name that cannot
be loaded, or to empty, returns 3007 InvalidParamValue and leaves the
previous patterns in force.
By default the node reads the reserved user_message slot. Set input
to any upstream slot (for example a Transform node's output_name) to
guardrail that text instead. Retrieve has no output slot: it attaches
knowledge, which a later Generate template reads as knowledge_<source>.
Patterns are compiled once at agent creation and matched with
std::regex_search — i.e. a pattern hits if it matches anywhere
in the text, case-insensitively.
NodeType: RegexGuardrail.
String storage kinds
RegexGuardrailNode accepts any string storage kind (List,
Map, Multimap) and uses the list view — each value is treated
as a regex pattern regardless of any key. Topic-tagged deflection
(writing the matched pattern's category key onto the interaction for
use by a downstream CannedResponseNode) is planned as future work.
Parameters¶
Input¶
| Param | Type | Default | Range | Structural | Localizable | Description |
|---|---|---|---|---|---|---|
input |
str | empty | — | ✓ | — | Text checked against the patterns: the player's message or an earlier node's output. Empty = the player's message. |
Storage¶
| Param | Type | Default | Range | Structural | Localizable | Description |
|---|---|---|---|---|---|---|
string_storage |
str | empty | — | — | — | File of regex patterns, one per line. Used instead of Inline Strings. Both empty = the server's default patterns. |
inline_strings |
list[str] | empty list | — | ✓ | — | Regex patterns typed here; used when String Storage is empty. |
Exits¶
Each exit is a structural string field on the node's params; its value names the target node (empty = END).
| Exit | Param field | Description |
|---|---|---|
triggered |
triggered_exit |
Node to run when the Input matches a pattern. Empty = end the turn. |
not_triggered |
not_triggered_exit |
Node to run when no pattern matches. Empty = end the turn. |
Source resolution
Priority order:
1. string_storage as a relative file path under the process storage root (loaded on demand at CreateAgent time).
2. string_storage as the name of an explicitly created session storage (via CreateStringStorageRequest).
3. inline_strings authored inline.
4. Server default file (default_guardrail_patterns_path in server-config.json).
Patterns are matched case-insensitively. At agent creation, a pattern that does
not compile is skipped with a server warning and the agent is created with the
remaining patterns — check parameters.pattern_count in diagnostics. When a
string_storage rebind via ChangeAgentParam contains a pattern that does not
compile, the change is rejected with
3007 InvalidParamValue and the previous patterns
stay in force.
Exit routes¶
| Route | Fires when |
|---|---|
triggered |
At least one pattern matched the input slot text. |
not_triggered |
No patterns matched (or the input slot is missing). |
An empty exit field routes to END and is valid. A non-empty exit that does not
name a node in the graph fails agent creation with
3003 GraphCompilationFailed.
Side effects¶
None on the dialog. The node does not modify the current
interaction, does not emit AnswerText
frames, and does not touch the KV cache. It is pure routing.
Diagnostics¶
When enable_diagnostics = true, this node contributes to
debug_info.nodes[].diagnostics. See
Turn Diagnostics JSON for the envelope.
| Key | Meaning | Absent when |
|---|---|---|
parameters.string_storage |
Name of the pattern storage that was matched against. | — |
parameters.pattern_count |
How many patterns compiled and ran. A count of 0 means the guardrail could never fire. |
— |
output.triggered |
Whether any pattern matched. | — |
The node deliberately records which storage and how many patterns, but never the
pattern that matched or the text it matched against. Use the node entry's exit_route
(triggered / not_triggered) together with the input slot in the turn's interaction
snapshot when you need to reconstruct a decision.
Minimum working example¶
from tryll_client.graph import (
GraphDescription, RegexGuardrailParams,
CannedResponseParams, GenerateParams,
)
client.create_string_storage(
name="jailbreak_patterns",
strings=[
r"ignore\s+previous\s+instructions?",
r"you\s+are\s+DAN",
],
)
graph = (
GraphDescription()
.add_node("guard", RegexGuardrailParams(
string_storage="jailbreak_patterns",
triggered_exit="refuse",
not_triggered_exit="answer",
))
.add_node("refuse", CannedResponseParams(
default_exit="", # uses server default; empty = END
))
.add_node("answer", GenerateParams(
default_exit="", # empty = END
))
.set_start_node("guard")
.set_default_model_name("My Local Model")
)
agent = client.create_agent(graph)
using namespace Tryll::Client;
using namespace Tryll::NodeParams;
client.CreateStringStorage("jailbreak_patterns", {
R"(ignore\s+previous\s+instructions?)",
R"(you\s+are\s+DAN)",
});
RegexGuardrailParamsT guardP;
guardP.string_storage = "jailbreak_patterns";
guardP.triggered_exit = "refuse";
guardP.not_triggered_exit = "answer";
// refuseP and answerP use defaults (server default canned responses / END)
CannedResponseParamsT refuseP;
GenerateParamsT answerP;
GraphDescription graph;
graph.AddRegexGuardrail("guard", std::move(guardP))
.AddCannedResponse("refuse", std::move(refuseP))
.AddGenerate("answer", std::move(answerP))
.SetStartNode("guard")
.SetDefaultModelName("My Local Model");
auto agent = client.CreateAgent(graph);
using Tryll.Client;
var graph = new TryllGraphBuilder()
.AddRegexGuardrail("guard", new TryllRegexGuardrailParams
{
StringStorage = "jailbreak_patterns",
TriggeredExit = "refuse",
NotTriggeredExit = "answer",
})
.AddCannedResponse("refuse", new TryllCannedResponseParams())
.AddGenerate("answer", new TryllGenerateParams())
.SetStartNode("guard")
.SetDefaultModelName("My Local Model")
.Build();
#include "Generated/TryllGraphBuilder.Nodes.h"
#include "Generated/TryllNodeParamsFactory.h"
auto* Subsystem = GetGameInstance()->GetSubsystem<UTryllSubsystem>();
Subsystem->RequestCreateStringStorage(
TEXT("jailbreak_patterns"),
{ TEXT(R"(ignore\s+previous\s+instructions?)"),
TEXT(R"(you\s+are\s+DAN)") });
UTryllRegexGuardrailParams* GuardP = UTryllNodeParamsFactory::MakeRegexGuardrailParams(this);
GuardP->bOverrideStringStorage = true;
GuardP->StringStorage = TEXT("jailbreak_patterns");
GuardP->TriggeredExit = TEXT("refuse");
GuardP->NotTriggeredExit = TEXT("answer");
FTryllGraphDescription Graph = FTryllGraphBuilder()
.AddNode(TEXT("guard"), GuardP)
.AddNode(TEXT("refuse"), UTryllNodeParamsFactory::MakeCannedResponseParams(this))
.AddNode(TEXT("answer"), UTryllNodeParamsFactory::MakeGenerateParams(this))
.SetStartNode(TEXT("guard"))
.SetDefaultModelName(TEXT("My Local Model"))
.Build();
See the full flow in How to use canned responses and guardrails.
Client bindings¶
- C++:
GraphDescription::AddRegexGuardrail(name, RegexGuardrailParamsT)—GraphDescription.h - Python:
GraphDescription.add_node(name, RegexGuardrailParams(...))—tryll_client.graph - Unity:
TryllGraphBuilder.AddRegexGuardrail(name, new TryllRegexGuardrailParams{...})—Runtime/Generated/TryllGraphBuilder.Nodes.cs - Unreal:
AddRegexGuardrailNode(builder, name, UTryllRegexGuardrailParams*)—Generated/TryllGraphBuilder.Nodes.h
Related¶
- String Storage
- Canned Response — the common downstream
node on the
triggeredroute. - How to use canned responses and guardrails
- Server Configuration — default file
- Glossary