AI SecurityNeMo Guardrails 0.24 · RAGAS 0.4 · OpenAI SDK 3.3 · Python 3.12 or 3.13
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
52 small wins to finish your pathNext lesson →

Colang

Colang is the modeling language of NeMo Guardrails, in which you write what users say, what the bot says and the flows that connect the two.

Last updated: 09 Oct, 2026 · NeMo Guardrails 0.24

NeMo Guardrails pasted a topic guard into the config and ran it. This page reads that rail one block at a time, so you can write your own.

Colang, between natural language and a programming language · from the Complete AI Security Course in 8 Hours video · 39:44 to 40:32

This part of the video starts at 0:39:44. Colang sits between natural language and a programming language: it is neither of the two, and a mixture of both. The board shows the extension of a Colang file, .co. The NeMo runtime is what reads a .co file; the model only sees the prompts NeMo builds from it.

Writing a rail in three blocks

Colang on the whiteboard: define user, define bot, define flow · from the Complete AI Security Course in 8 Hours video · 42:08 to 43:55

This part of the video starts at 0:42:08. A rail is added in one fixed format. You define a user who goes off topic and list the ways, in the video "how to make a coffee", "tell me a joke" and "tell me about this movie". You define a bot and what it says. Then you define a flow: if the user goes off topic, the bot gives its off-topic reply, which the video compares to an if-else condition. The words after define user and define bot are names you choose; Colang keeps the word variable for names that start with $.

Three stacked cards. The first, define user ask off topic, holds example messages. The second, define bot refuse off topic, holds the exact reply. The third, define flow handle off topic, lists the user intent, then the bot message, then stop. Arrows show the flow joining the user intent to the bot message.

define user: an intent and its examples

define user names a user intent and lists example messages for it, one per line, indented by two spaces. The words after define user are a name you choose. NeMo calls it the canonical form of the message.

text
define user ask off topic
  "tell me a joke"
  "what is the capital of france"
  "write me a poem"

The examples are samples of a meaning, not a list of exact strings to match. A message that says the same thing in other words can still get this intent.

define bot: the exact words

define bot names a bot message and gives its text. When a flow reaches this name, the text is sent word for word, with no model call to write it.

text
define bot refuse off topic
  "I'm an Enterprise IT Assistant focused on Kubernetes, Intel hardware, and networking. I can't help with that — but ask me anything technical!"

define flow: what follows what

define flow names a sequence. This one reads: when the user's intent is ask off topic, the bot says refuse off topic, then the flow stops. The flow's own name, handle off topic, is also a label you choose.

text
define flow handle off topic
  user ask off topic
  bot refuse off topic
  stop

These three blocks are the core of Colang 1.0. The language also has define subflow, execute to run an action, if and else, when, stop and variables that start with $.

The off-topic rail in the demo app · from the Complete AI Security Course in 8 Hours video · 45:39 to 46:42

This part of the video starts at 0:45:39. The Colang of the demo app is on screen. It defines a user who can ask off topic, with examples such as "who won the game yesterday" and "what is 2 plus 2". It defines how the bot refuses. The flow, named handle off topic, says: if the user asks off topic, the bot refuses off topic, and stop.

Loading the Colang and listing what it defines

RailsConfig.from_content parses Colang from a string. Parsing calls no model and needs no key, so this is a quick way to check a rail before running it. The Colang below is the full rail from the video's repo.

ExampleThe video's Colang, parsed on NeMo Guardrails 0.24.1
from nemoguardrails import RailsConfig

TOPIC = '''
define user ask off topic
  "tell me a joke"
  "what is the capital of france"
  "write me a poem"
  "what is 2 plus 2"
  "what should I eat for dinner"
  "who won the game yesterday"
  "recommend a movie"
  "what is the weather like"

define bot refuse off topic
  "I'm an Enterprise IT Assistant focused on Kubernetes, Intel hardware, and networking. I can't help with that — but ask me anything technical!"

define flow handle off topic
  user ask off topic
  bot refuse off topic
  stop
'''

config = RailsConfig.from_content(colang_content=TOPIC)

print("Colang version:", config.colang_version)
for name, examples in config.user_messages.items():
    print("user intent:", name, "|", len(examples), "examples")
for name, texts in config.bot_messages.items():
    print("bot message:", name, "|", len(texts), "text")
for flow in config.flows:
    steps = [step.get("intent_name") or step["action_params"]["value"] for step in flow["elements"]]
    print("flow:", flow["id"], "|", steps)

What the parser found

  • Colang version 1.0. No YAML was given, so the default applies.
  • One user intent with 8 examples, ask off topic, and one bot message with one text, refuse off topic.
  • One flow with three steps, in order: the user intent, the bot message, stop. The parser stored the names only; nothing has been matched against a message yet.

Running the flow on new wording

This example runs under the setup code of NeMo Guardrails (the two import lines, the YAML and SEARCH strings, the AllExamples class, build_rails and chat) and the TOPIC string above: paste it below them in one file. Both messages are test prompts from the video's demo app, and neither is one of the eight examples. colang_history holds the turn the way Colang would write it; the code prints its first three lines.

ExampleAPI keyFrom the video, run on Groq (openai/gpt-oss-120b)
rails = build_rails(TOPIC, model="openai/gpt-oss-120b")

for message in ["haha tell me a funny joke real quick", "how does SR-IOV reduce CPU overhead?"]:
    info = chat(rails, message)
    print("--- first lines of the Colang history")
    print("\n".join(info.colang_history.splitlines()[:3]))
    print()

Reading the Colang history

  • The joke message is not one of the eight examples, yet the history shows ask off topic under it, then bot refuse off topic: the flow ran and the reply is the scripted text.
  • The SR-IOV question got the intent ask technical question. That name is nowhere in the Colang: the model wrote a new intent because none of yours fitted.
  • No flow starts with that intent, so the next line is bot general response, the model answering in its own words.
  • The long answer is the model's own. Its table and its cycle counts were not checked by any rail.

An example line without its indent

Colang 1.0 reads structure from indentation, like Python. Here the second example has lost its two spaces.

ExampleRun on NeMo Guardrails 0.24.1
from nemoguardrails import RailsConfig

BROKEN = '''
define user ask off topic
  "tell me a joke"
"what is the capital of france"
'''

try:
    RailsConfig.from_content(colang_content=BROKEN)
except Exception as error:
    print(error)

The parser stops at line 4 of the Colang, the example that lost its indent. At the start of a line it expects a keyword such as define, and it found a quoted string.

Colang 1.0 vs Colang 2.x

NeMo Guardrails ships two versions of the language. Version 1.0 is the default, as the parser output above shows, and it is what the video writes. Version 2.x is switched on with colang_version: "2.x" in the YAML and has a different syntax, closer to Python: flows are declared with flow, a standard library is brought in with import core, and matching a message is written user said.

text
import core

flow main
  user said "hi"
  bot say "Hello World!"
Colang 1.0Colang 2.x
Selected byDefaultcolang_version: "2.x" in the YAML
A flow starts withdefine flowflow
A user messagedefine user with examplesuser said "..." or a flow you define
A bot messagedefine bot with textbot say "..."
Used inThe video and this pageNewer NVIDIA examples

Where you use Colang

  • One block set per thing you want to control. The video writes one for off-topic questions, one for jailbreak attempts, one for sensitive topics and one for greetings.
  • Answers that must be the same every time. A refusal, a greeting or a notice goes in define bot.
  • Calling your own Python. A flow can run a function with execute and branch on the result with if, which Input and output rails does.
Watch out. The examples under define user are matched by meaning, with a model's help, so a rail can miss a message it should catch or catch one it should not. Test every rail with messages that are not among its examples, both ones it should refuse and ones it should let through.
Try it yourself
  • Add "how to make a coffee", the video's first example, to ask off topic and run the parser again: the count goes up by one.
  • Delete the stop line and run the joke message again. After a scripted bot message the reply is the same refusal.
  • Change bot refuse off topic in the flow to bot refuse politely, a name with no define bot, and run the parser again: the flow lists the new name although no text exists for it.

Slow is fine. Stopping is the only problem.