Prompts as Views
Keep growing Agent instructions readable by moving them into a prompt view: an ERB file that LittleGhost finds and renders for the Agent. The Agent prepares any application values the prompt needs, while the view holds the instructions sent to the model.
Start with the inline prompt
The Agent from Getting Started keeps its first instruction close to the model:
class CustomerSupportAgent < LittleGhost::Agent model "openrouter:openai/gpt-5.6-luna" system_prompt "Answer customer questions clearly and concisely." end
Inline prompts are a good fit while the whole instruction is one thought.
Move a growing prompt into a view
Remove system_prompt from the class:
class CustomerSupportAgent < LittleGhost::Agent model "openrouter:openai/gpt-5.6-luna" tools HelpCenterLookupTool, OrderStatusTool end
Then create app/prompts/customer_support/system_prompt.erb:
You help customers understand their orders and account. Answer clearly and concisely. Never invent company guidance. Check the help center when policy matters. Use the order status tool before making a claim about a private order.
That is enough. LittleGhost turns CustomerSupportAgent into the logical path customer_support and looks for app/prompts/customer_support/system_prompt.erb.
The prompt is still a system instruction sent to the selected model provider. Keeping it in a view improves organization; it does not keep the content inside your process.
Prepare application values with Ruby
Define system_prompt as an instance method when the view needs values from your application. Assign those values to instance variables:
class CustomerSupportAgent < LittleGhost::Agent def system_prompt @company_name = "Northstar" @policy_version = SupportPolicy.current_version end end
LittleGhost calls the method before it renders the view. The instance variables are available there by the same names:
You are a customer support agent for <%= @company_name %>. Follow support policy <%= @policy_version %>. Answer clearly and concisely.
If you know Rails controllers, the pairing is familiar: the method prepares values, and the ERB file presents them. The method’s return value is ignored. If it raises an exception, the Run fails in the usual way.
An inline system_prompt declaration still takes precedence over a view. Remove the inline declaration when you want LittleGhost to call the instance method and render system_prompt.erb.
Subclass actions can call super before adding their own values:
class BillingSupportAgent < CustomerSupportAgent def system_prompt super @billing_region = "US" end end
Prompt views also receive three built-in local variables: invocation for the current request, run for its lifecycle, and agent for the run-scoped Agent. Use them when an instruction genuinely depends on the current request. Keep the customer’s wording in the caller message unless it belongs in the system instruction.
Every rendered value may be sent to the model provider. Put only data that belongs in the prompt into the view.
Share a small partial
Partials keep repeated instructions in one place. Create app/prompts/shared/_voice.erb:
Use a warm, direct voice for <%= @company_name %>. Prefer one clear next step over a long list of possibilities.
Render it from the Agent’s system view:
You are a customer support agent for <%= @company_name %>. <%= partial "shared/voice" %>
The underscore marks a partial. Partials share the Agent’s instance variables, so @company_name remains available without extra wiring.
Use an explicit local for a value that belongs only to one partial. For example, create app/prompts/shared/_closing.erb:
Offer one clear next step for <%= audience %>.
Then pass audience when you render it:
<%= partial "shared/closing", locals: {audience: "account owners"} %>
Locals do not flow into nested partials unless you pass them again.
Keep repeated renders predictable
LittleGhost calls system_prompt once for each file-backed prompt render. Each call starts with the instance variables captured after LittleGhost’s after_initialize callbacks finish. Values left behind by a later callback, Tool, or earlier render do not become prompt assigns by accident.
After preparing the view, LittleGhost restores the Agent’s live instance variables. The objects assigned to the view are still ordinary Ruby objects, though, so the view sees the same mutable objects. Treat them as read-only, or duplicate a value before assigning it when the template needs an isolated copy.
For state that should change across invocations, read from run, a session, or an application object in system_prompt instead of relying on an earlier prompt render.
Choose a different template path
Most named Agents can rely on their conventional path. Use system_template when a class should read a differently named view:
class BillingSupportAgent < LittleGhost::Agent system_template "customer_support/billing" end
LittleGhost chooses one prompt source in this order:
-
An inline
system_prompt -
An explicit
system_template -
The Agent’s conventional
system_prompt.erbview
Applications can add prompt lookup roots through Configuration#prompt_paths. Earlier roots win, so an application can override a view from a shared prompt package.
Customize LittleGhost’s framework prompts
You can change LittleGhost’s own model-facing language without replacing its Ruby behavior. Tool descriptions, structured-result repair requests, context summaries, code-mode instructions, and other framework messages all come from bundled prompt views under the little_ghost/ namespace.
Decide when to customize
Models can respond differently to the same instruction, even when the Tool, schema, and task stay the same. Wording that works well with one model may lead another model to miss a Tool constraint, mishandle a repair request, or recover less reliably from a failed call. Adjusting the relevant framework prompt gives you a direct way to improve that interaction without forking LittleGhost.
An override can also align framework language with your application’s terminology and voice. Start with the narrowest template that covers the behavior you want to change, and evaluate it with the models your application uses. Keep protocol checks, authorization, and execution limits in Ruby; prompt wording guides the model but does not enforce those boundaries.
Find the prompt you want to change
Browse the catalog from your application:
$ little_ghost prompts list $ little_ghost prompts list structured_output $ little_ghost prompts list structured_output/repair $ little_ghost prompts show structured_output/repair/request
The bare list command shows top-level features with template counts. Pass a feature or component path to browse its immediate children, or use list --all to print every template key. Keys follow feature/component/purpose. For example, built-in Tool descriptions live under tools/built_in, while JSON Schema feedback lives under tools/validation/schema. Directories named feedback contain conditions the model can correct. Directories named errors contain operational failures.
show prints the bundled ERB source. Every bundled template starts with a structured ERB comment that explains where LittleGhost renders it, its locals, and any formatting constraints. These headers use <%# ... -%>, so neither the comment nor its trailing newline enters the rendered prompt. Each interior line also starts with # so Ruby-aware editors recognize the whole header as a comment. A # line outside <%# ... -%> and an HTML comment are output text and would be sent to the model.
Bundled prompts use plain text and Markdown. When a prompt needs headings or structured sections, the default template uses Markdown headings and lists rather than XML-like wrapper tags.
Copy an override into your application
Copy one template into the default application prompt root before editing it:
$ little_ghost prompts copy structured_output/repair/request
That creates app/prompts/little_ghost/structured_output/repair/request.erb. Use --root when the application configures a different prompt root:
$ little_ghost prompts copy structured_output/repair/request --root config/prompts
Use --agent when one Agent needs different wording from the rest of the application. Pass the Agent’s logical path, which is its underscored class name without the Agent suffix:
$ little_ghost prompts copy structured_output/repair/request --agent customer_support
That creates app/prompts/customer_support/little_ghost/structured_output/repair/request.erb. Namespaced classes use a namespaced logical path, such as Admin::CustomerSupportAgent becoming admin/customer_support.
Copy the complete catalog when an application deliberately wants to own every piece of framework wording:
$ little_ghost prompts copy --all
Copy commands refuse to overwrite any existing destination. With --all, the command checks every destination before it starts writing. Copied templates retain their invisible documentation headers so the override remains self-explanatory in the application.
Choose the override scope
For every framework prompt that applies to an Agent invocation, LittleGhost uses the first matching template in this order:
-
An invocation-specific
TrustedPathroot -
The Agent-scoped directory inside a configured prompt root
-
The runtime-wide directory inside a configured prompt root
-
The template bundled with
LittleGhost
Each root keeps the same little_ghost/<key>.erb namespace. Agent scope inserts the logical path before that namespace.
Runtime-wide overrides normally live in the default app/prompts root. Add or replace configured roots when the application uses another location:
LittleGhost.configure do |config| config.prompt_paths = ["config/prompts", "app/prompts"] end
The uncommon invocation-specific layer is explicit because it can change model instructions for only one call. From config/little_ghost.rb:
campaign_prompts = LittleGhost::TrustedPath.new( path: File.expand_path("campaign_prompts", __dir__) ) CustomerSupportAgent.ask( "Where is my order?", template_paths: [campaign_prompts] )
The invocation root contains little_ghost/structured_output/repair/request.erb, as a runtime root does. Direct LittleGhost.generate calls accept the same template_paths: option.
Know what an override can change
Framework templates control model-facing language, not protocol roles, Tool names, schemas, validation behavior, security checks, or execution limits. A required template cannot render as blank.
LittleGhost is pre-1.0. Template keys and their documented locals are maintained on a best-effort basis until 1.0, so review release notes and copied overrides when upgrading.
Treat views as application code
Prompt views run as ERB inside the Ruby process and can call Ruby. Keep prompt directories with the rest of your application code rather than letting a request choose one.
Use TrustedPath only when application code needs to select a request-specific root. Do not derive its path from a request parameter, model output, or Tool argument. The wrapper confirms that the directory exists and resolves symbolic links. It does not inspect who can edit the directory or restrict what its ERB can execute.
Build request-specific input in a Workflow
A prompt view defines reusable instructions for one Agent. A Workflow may still build request-specific input for that Agent:
invoke CustomerSupportAgent, input: <<~MESSAGE #{input.text} Verified research: #{research} MESSAGE
The Workflow is composing this request. CustomerSupportAgent still receives its own system prompt view when it runs.
Continue with Tools to give those Agents application capabilities while keeping model input, trusted context, and delegated sandbox operations distinct.