Prompt Customization

Copy Markdown View Source

This guide covers customizing SubAgent system prompts for different LLMs, execution modes, and use cases.

Execution Modes

SubAgent prompts are built from a 2-axis architecture:

Behavior axis — how the agent returns results:

BehaviorDescriptionWhen to use
:single_shotLast expression IS the answermax_turns: 1, simple queries
:explicit_returnMust call (return ...) or (fail ...)Multi-turn exploration with tools

Reference — language documentation (tool syntax, Java interop, restrictions):

The language reference is included by default. Use reference: :none to omit it for capable models that don't need syntax guidance.

Additive capabilities:

CapabilityDescriptionWhen to use
:journalAdds task, step-done, task-reset docsPlan-driven agents with idempotent steps

Default Selection

SubAgent automatically selects a prompt based on configuration:

ConditionDefault Language Spec
max_turns <= 1:single_shot
journaling: true:explicit_journal
Otherwise:explicit_return

Note: plan: provides display-only progress labels and does not affect language spec selection. To enable journal capabilities (task, step-done), set journaling: true explicitly.

You rarely need to set this manually — the defaults match the runtime behavior.

Canonical Language Specs

Pre-composed specs available via system_prompt: %{language_spec: atom}:

SpecComponentsDescription
:single_shotreference + single-shotLast expr = answer, one turn
:explicit_returnreference + multi-turn + explicit returnMust call (return ...)/(fail ...)
:explicit_journalreference + multi-turn + explicit return + journalWith task caching

The language reference is included by default. Use reference: :none in a structured profile to omit it.

# Single-turn query (default for max_turns: 1)
SubAgent.new(
  prompt: "Count items over $100",
  max_turns: 1
)

# Multi-turn exploration (default for max_turns > 1)
SubAgent.new(
  prompt: "Analyze sales trends",
  max_turns: 5
)

# Omit language reference for capable models
SubAgent.new(
  prompt: "Count items",
  max_turns: 1,
  system_prompt: %{language_spec: {:profile, :single_shot, reference: :none}}
)

Structured Profiles

For programmatic composition, use the {:profile, behavior, opts} tuple:

# Omit language reference for a capable model
SubAgent.new(
  prompt: "Execute the plan",
  system_prompt: %{
    language_spec: {:profile, :explicit_return, reference: :none}
  }
)

# Add journal capability
SubAgent.new(
  prompt: "Execute the plan",
  system_prompt: %{
    language_spec: {:profile, :explicit_return, journal: true}
  }
)

# Both reference (default) and journal
system_prompt: %{language_spec: {:profile, :explicit_return, journal: true}}

# Short form (defaults: reference: :full, journal: false)
system_prompt: %{language_spec: {:profile, :explicit_return}}

Options:

OptionValuesDefault
:reference:full or :none:full
:journaltrue or falsefalse

Validation: Raises ArgumentError for invalid combinations (e.g., single_shot + journal).

System Prompt Customization

The system_prompt field accepts three forms:

# Map with options
system_prompt: %{
  prefix: "You are an expert data analyst.",
  suffix: "Always validate results before returning.",
  language_spec: :explicit_return,
  output_format: "..."
}

# Function transformer
system_prompt: fn prompt -> "CUSTOM PREFIX\n\n" <> prompt end

# Complete override (use with caution)
system_prompt: "Your entire custom prompt here..."

Map Options

OptionDescription
:prefixPrepended before generated content
:suffixAppended after generated content
:language_specReplaces PTC-Lisp reference section (atom, string, tuple, or callback)
:output_formatReplaces output format instructions

Dynamic Language Spec

Use a callback to change prompts based on runtime context:

SubAgent.new(
  prompt: "Process the data",
  system_prompt: %{
    language_spec: fn ctx ->
      if ctx.turn == 1 do
        PtcRunner.Lisp.LanguageSpec.get(:single_shot)
      else
        PtcRunner.Lisp.LanguageSpec.get(:explicit_return)
      end
    end
  }
)

The callback receives:

KeyTypeDescription
:turnintegerCurrent turn number (1-indexed)
:modelatom or functionThe LLM
:memorymapCurrent memory state
:messageslistConversation history

LLM-Specific Prompts

Different models may need different prompt styles. Capable models may work fine without the language reference, saving tokens:

defmodule MyApp.Prompts do
  alias PtcRunner.Lisp.LanguageSpec

  def for_model(ctx) do
    case ctx.model do
      model when model in [:sonnet, :gpt4o] ->
        # Capable models: omit language reference to save tokens
        LanguageSpec.resolve_profile({:profile, :explicit_return, reference: :none})

      _ ->
        # Default: include language reference
        LanguageSpec.get(:explicit_return)
    end
  end
end

SubAgent.new(
  prompt: "Analyze orders",
  system_prompt: %{language_spec: &MyApp.Prompts.for_model/1}
)

Custom Prompt Addons

Build on top of library prompts:

defmodule MyApp.Prompts do
  alias PtcRunner.Lisp.LanguageSpec

  def with_domain_context do
    """
    #{LanguageSpec.get(:single_shot)}

    ## Domain Context

    - Orders have statuses: pending, shipped, delivered, cancelled
    - Products belong to categories: electronics, clothing, food
    - Use `data/current_user` for permission checks
    """
  end
end

SubAgent.new(
  prompt: "Find high-value orders",
  system_prompt: %{language_spec: MyApp.Prompts.with_domain_context()}
)

Prompt Preview

Inspect the generated prompt without execution:

agent = SubAgent.new(
  prompt: "Find emails for {{user}}",
  system_prompt: %{
    prefix: "You are a helpful assistant.",
    language_spec: :explicit_return
  }
)

preview = SubAgent.preview_prompt(agent, context: %{user: "alice"})

IO.puts(preview.system)  # Full system prompt
IO.puts(preview.user)    # Expanded user prompt

Text Mode Templating

Text mode uses full Mustache templating with sections for iterating lists. This differs from PTC-Lisp mode where data appears in the Data Inventory section.

See Text Mode Guide for Mustache syntax including {{#section}}, {{^inverted}}, and {{.}} notation.

See Also