Prompt Rendering

Render reusable ERB prompt templates from app/prompts with RubyLLM.render_prompt

Table of contents

  1. Rendering a Prompt
  2. Static Prompts
  3. Locals
  4. Nested Paths and Extensions
  5. Missing Prompts
  6. Using Rendered Prompts in Chat
  7. Using Prompts with Agents
  8. Safety Notes

After reading this guide, you will know:

  • How to store prompt templates in app/prompts.
  • How to render templates with RubyLLM.render_prompt.
  • How locals, nested paths, and .txt.erb filenames work.
  • How rendered prompts fit into chats and agents.
  • Which errors to expect when a prompt file is missing.

Rendering a Prompt

RubyLLM.render_prompt renders a local ERB template and returns the rendered string. It does not call a model or add anything to a chat by itself.

Create a prompt file:

<!-- app/prompts/support/instructions.txt.erb -->
You are a support assistant for <%= product_name %>.

The current customer is <%= customer_name %>.
Answer with concise, practical steps.

Render it with keyword locals:

instructions = RubyLLM.render_prompt(
  "support/instructions",
  product_name: "BillingHub",
  customer_name: current_user.name
)

chat = RubyLLM.chat
chat.with_instructions(instructions)
chat.ask("How do I update my invoice email?")

RubyLLM.render_prompt("support/instructions") resolves to:

app/prompts/support/instructions.txt.erb

In Rails apps, the path is relative to Rails.root. Outside Rails, it is relative to the current working directory.

Static Prompts

Prompts do not need locals:

<!-- app/prompts/reviewer.txt.erb -->
You are a careful code reviewer. Focus on correctness, security, and missing tests.
instructions = RubyLLM.render_prompt("reviewer")
chat.with_instructions(instructions)

Locals

Every keyword argument passed to render_prompt is available in the ERB template:

<!-- app/prompts/messages/welcome.txt.erb -->
Welcome <%= name %>.

Your plan is <%= plan_name %>.
RubyLLM.render_prompt(
  "messages/welcome",
  name: "Ada",
  plan_name: "Pro"
)
# => "Welcome Ada.\n\nYour plan is Pro.\n"

If the template references a local you did not pass, ERB raises an error while rendering.

Nested Paths and Extensions

Prompt names can include nested directories:

RubyLLM.render_prompt("work_assistant/instructions", display_name: "Ada")

This renders:

app/prompts/work_assistant/instructions.txt.erb

You can also pass the full filename:

RubyLLM.render_prompt("work_assistant/instructions.txt.erb", display_name: "Ada")

RubyLLM prompt templates use .txt.erb.

Missing Prompts

If RubyLLM cannot find the prompt file, it raises RubyLLM::PromptNotFoundError:

begin
  RubyLLM.render_prompt("missing")
rescue RubyLLM::PromptNotFoundError => error
  Rails.logger.warn(error.message)
end

Using Rendered Prompts in Chat

Use rendered prompts anywhere you would use a string:

system_prompt = RubyLLM.render_prompt(
  "analysis/instructions",
  timezone: Time.zone.name,
  account_type: current_account.plan_name
)

chat = RubyLLM.chat(model: "gpt-5-nano")
chat.with_instructions(system_prompt)

response = chat.ask(
  RubyLLM.render_prompt("analysis/question", topic: params[:topic])
)

Prompt rendering is local string templating. For provider-side reuse of large stable prompt prefixes, see Prompt Caching.

Using Prompts with Agents

Agents build on the same prompt renderer. Named agents automatically render their conventional prompt when it exists:

class WorkAssistant < RubyLLM::Agent
  chat_model Chat
end

For WorkAssistant, RubyLLM looks for:

app/prompts/work_assistant/instructions.txt.erb

If the file exists, it is used as the agent’s system instructions. If it does not exist and the agent has no instructions macro, the agent starts without system instructions.

Call instructions with no arguments when the file is required and should raise RubyLLM::PromptNotFoundError if missing:

class WorkAssistant < RubyLLM::Agent
  chat_model Chat
  instructions
end

You can pass locals to the conventional prompt:

class WorkAssistant < RubyLLM::Agent
  chat_model Chat

  instructions display_name: -> { chat.user.display_name_or_email }
end

For the full class-based conventions, see Agents.

Safety Notes

ERB templates execute Ruby code, so prompt files should be trusted application code. Do not pass untrusted user input as the prompt name; keep prompt names as application-controlled constants or map user choices to known prompt names.

Locals are inserted exactly as rendered by ERB. If your prompt format needs escaping or quoting, do that before passing the local or inside the template.