rails.explain(): what the runtime did
Lesson 5 built a model that answers. This lesson is about the other half of the conversation: what the runtime asked it, and in what order. rails.explain() is the answer, and it is the tool to reach for every time something in this course surprises you.
Learn it now, while the program is small enough that you already know what should have happened. A debugging tool taught in the last lesson is a tool nobody used.
Two views of one turn
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig
PRETEND_YML = """
models:
- type: main
engine: pretend
model: pretend-1
"""
rails = LLMRails(RailsConfig.from_content(yaml_content=PRETEND_YML))
rails.generate(messages=[{"role": "user", "content": "How long does a refund take?"}])
print(rails.explain().colang_history)
for call in rails.explain().llm_calls:
print(call.task, "->", call.completion.strip())colang_history is the turn written out in Colang, the language a configuration is written in. It reads as a transcript, and lesson 7 starts writing in it.
llm_calls is every time the runtime went to the model. Each entry carries a task, which is the runtime's own name for what it was asking, and it is the same string task_name() returned inside the stand-in.
There was exactly one call, and its task was general. That is what happens when a configuration has no conversation rules: the runtime hands the question straight to the model and hands the answer straight back. Lesson 11 counts the calls again once there are rules.
A summary instead of a dump
rails = LLMRails(RailsConfig.from_content(yaml_content=PRETEND_YML))
rails.generate(messages=[{"role": "user", "content": "Is delivery free?"}])
rails.explain().print_llm_calls_summary()One line per call with the task name and what it cost. When a later lesson claims one message costs three calls and another costs one, this is the line the number came from.
And the prompt itself
rails = LLMRails(RailsConfig.from_content(yaml_content=PRETEND_YML))
rails.generate(messages=[{"role": "user", "content": "Is delivery free?"}])
print(rails.llm.prompts[0])That list is not a NeMo feature; it is a list the stand-in appends to, which a real provider would not give you. It is worth having, because most of the surprising behaviour in the rest of this course turns out to be perfectly reasonable once you have read the prompt that caused it.
The instruction at the top of that prompt is the default general_instructions, and it is the reason the stand-in was asked about a shop at all.
explain().colang_historyshows the turn as Colang.explain().llm_callslists every model call and the task it served.- A configuration with no conversation rules makes one call, named
general.
- Call
generatetwice on onerailsobject, then printllm_callsand see whether it covers both turns. - Add
instructionsto the YAML with your own text and readprompts[0]again.
Little by little, you're building something great.