NeMo Guardrailsnemoguardrails 0.24.1 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson →

The prompt behind the intent

The intent prompt is the prompt NeMo renders for the generate_user_intent task: your instructions, your examples and the conversation, ending with a request for the intent of the last user message.

Last updated: 30 Sep, 2026 · NeMo Guardrails 0.24.1

The Embedding model lesson ended with the LLM naming the intent. It does that from a prompt NeMo builds, and prompts.yml changes that prompt.

Syntax:

yaml
prompts:
  - task: generate_user_intent     # which prompt to replace
    content: |-                    # a Jinja template
      {{ general_instructions }}
      {{ examples | verbose_v1 }}
      {{ history | colang | verbose_v1 }}
    output_parser: verbose_v1      # how the reply is read

How NeMo picks a prompt

NeMo ships prompt sets for model families, chosen by the model's name. openai/gpt-3.5-turbo and openai/gpt-4 get chat prompts; a name with no set of its own, such as openai/gpt-oss-120b, gets the general set, written for completion models that carry on a document.

Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports these files. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep them in the same folder.
View the code here
rails.co
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.py
from nemoguardrails.embeddings.index import EmbeddingsIndex


class EveryExample(EmbeddingsIndex):
    """Hands the model every example instead of the closest few."""

    def __init__(self, **kwargs):
        self.items = []

    async def add_items(self, items):
        self.items.extend(items)

    async def build(self):
        pass

    async def search(self, text, max_results=5, threshold=None):
        return self.items


def init(app):
    app.register_embedding_search_provider("every_example", EveryExample)
config.yml
models:
  - type: main
    engine: openai
    model: openai/gpt-oss-120b
    api_key_env_var: GROQ_API_KEY
    parameters:
      base_url: https://api.groq.com/openai/v1
      temperature: 0

instructions:
  - type: general
    content: |
      You are an Enterprise IT Assistant specialising in Kubernetes,
      Intel hardware, and enterprise networking.
      Only answer questions about these topics.
      Answer in one or two short sentences.

core:
  embedding_search_provider:
    name: every_example
prompts.yml
prompts:
  - task: generate_user_intent
    content: |-
      """
      {{ general_instructions }}
      """

      # This is how a conversation between a user and the bot can go:
      {{ sample_conversation | verbose_v1 }}

      # This is how the user talks:
      {{ examples | verbose_v1 }}

      # This is the current conversation between the user and the bot:
      {{ sample_conversation | first_turns(2) | verbose_v1 }}
      {{ history | colang | verbose_v1 }}

      Do not answer the user. Reply with one line: the user intent of the last message.
      Use an intent from the examples when one fits, otherwise write a new short intent.
    output_parser: verbose_v1

The bug: the model answers instead of classifying

The topic guard with prompts.yml deleted from the folder:

ExampleAPI key
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))


def chat(message):
    reply = rails.generate(messages=[{"role": "user", "content": message}])
    print("User:", message)
    print("Bot :", reply["content"])

chat("Tell me a funny joke!")
print(rails.explain().colang_history)

Read the line under the user's message. Where the history should hold an intent name, it holds a sentence addressed to the user. The general prompt ends with the user's message and waits for the next line; a chat model like gpt-oss-120b treats it as a conversation and answers. No flow can match that sentence, so the turn takes the long path.

The fix

prompts.yml copies the general prompt and adds two lines at the end: do not answer the user, reply with the intent, and use an intent from the examples when one fits. It also drops the general prompt's Choose intent from this list line, which pushed every message into the only intent on the list, even on-topic questions.

yaml
      Do not answer the user. Reply with one line: the user intent of the last message.
      Use an intent from the examples when one fits, otherwise write a new short intent.

The same message with prompts.yml

ExampleAPI key
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))


def chat(message):
    reply = rails.generate(messages=[{"role": "user", "content": message}])
    print("User:", message)
    print("Bot :", reply["content"])

chat("Tell me a funny joke!")
print(rails.explain().colang_history)
print(rails.explain().llm_calls[0].prompt[-230:])

What the prompt changed

  • The history now holds ask off topic, so the flow ran and the refusal came from define bot.
  • The last 230 characters of the prompt end with the user's message and the two added lines, so the last thing the model reads is the request for an intent.

Why the video did not need this

The video's YAML names gpt-3.5-turbo as a placeholder while the real model comes from llm=. NeMo picks prompts by the name in the YAML, so the video's model got the chat prompt set, written for that name. Naming the real model in config.yml, as this course does, means choosing the prompt too.

The general prompt setWith prompts.yml
Last line the model seesThe user's messageA request for the intent
gpt-oss-120b replies withAn answer to the userAn intent name
FlowsNever matchMatch

Where you override prompts

  • A model with no prompt set of its own, as here.
  • The self-check prompts in the input and output rail lessons, which you always write yourself.
Watch out. A wrong intent is silent. The reply may even look right, as it did here, because the model answered politely. Check colang_history after changing a model.
Try it yourself
  • Print rails.explain().llm_calls[0].prompt in full and find where your examples were pasted.
  • Put back the Choose intent from this list line and ask What is a Kubernetes ConfigMap?.

You understood something today that you didn't yesterday.