Writing PAL Prompts

This guide covers the fundamentals of writing effective PAL prompts.

Basic Structure

Every PAL prompt file (.pal) follows this structure:

pal_version: "1.0"
id: "unique-identifier"
version: "1.0.0"  # Semantic versioning
description: "What this prompt does"

imports:  # Optional
  alias: "./path/to/library.pal.lib"

variables:  # Optional
  - name: variable_name
    type: string
    description: "Variable description"
    required: true
    default: "optional default value"

metadata:  # Optional
  author: "Your Name"
  tags: ["category", "use-case"]

composition:
  - "Line 1 of your prompt"
  - "Line 2 with {{ variable_name }}"
  - "{{ alias.component_name }}"

Variables

Variable Types

PAL supports these variable types:

  • string - Text values

  • integer - Whole numbers

  • float - Decimal numbers

  • boolean - true/false

  • list - Arrays of values

  • dict - Key-value pairs

  • any - Any type (use sparingly)

Variable Definition

variables:
  - name: api_name
    type: string
    description: "Name of the API to design"
    required: true
    
  - name: max_endpoints
    type: integer
    description: "Maximum number of endpoints"
    required: false
    default: 10
    
  - name: features
    type: list
    description: "List of features to include"
    required: true
    
  - name: config
    type: dict
    description: "Configuration options"
    required: false

Templating with Jinja2

PAL uses Jinja2 for templating. All standard Jinja2 features are supported:

Basic Variable Substitution

composition:
  - "Design an API for {{ api_name }}"
  - "The API should support {{ features | length }} features"

Conditionals

composition:
  - "{% if advanced_mode %}"
  - "Include advanced features like caching and rate limiting"
  - "{% else %}"
  - "Focus on basic CRUD operations"
  - "{% endif %}"

Loops

composition:
  - "Required features:"
  - "{% for feature in features %}"
  - "- {{ feature }}"
  - "{% endfor %}"

Filters

composition:
  - "API Name: {{ api_name | upper }}"
  - "Endpoints: {{ endpoints | join(', ') }}"
  - "Description: {{ description | title }}"

Using Component Libraries

Importing Libraries

imports:
  personas: "./libraries/personas.pal.lib"
  tasks: "./libraries/tasks.pal.lib"
  formats: "https://example.com/formats.pal.lib"

Using Components

composition:
  - "{{ personas.expert_developer }}"
  - ""
  - "{{ tasks.code_review }}"
  - ""
  - "Code to review:"
  - "{{ code }}"
  - ""
  - "{{ formats.json_output }}"

Best Practices

1. Use Descriptive IDs

# Good
id: "api-design-restful-v2"

# Bad  
id: "prompt1"

2. Version Your Prompts

Use semantic versioning to track changes:

version: "1.2.3"
# Major.Minor.Patch
# Major: Breaking changes
# Minor: New features
# Patch: Bug fixes

3. Provide Clear Descriptions

description: "Generates RESTful API specifications from requirements"

variables:
  - name: requirements
    type: string
    description: "Business requirements in plain English"  # Clear!

4. Organize with Composition

Break complex prompts into logical sections:

composition:
  # System message
  - "{{ personas.api_designer }}"
  - ""
  
  # Context
  - "## Context"
  - "{{ context }}"
  - ""
  
  # Task
  - "## Task"
  - "Design a RESTful API based on these requirements:"
  - "{{ requirements }}"
  - ""
  
  # Output format
  - "## Output Format"
  - "{{ formats.openapi_spec }}"

5. Use Metadata

metadata:
  author: "Jane Doe"
  created: "2024-01-01"
  tags: ["api", "design", "openapi"]
  tested_with: ["gpt-4", "claude-3"]

Advanced Patterns

Multi-line Content

For complex content, use YAML’s multi-line syntax:

composition:
  - |
    This is a multi-line string
    that preserves line breaks
    and formatting.
  - >
    This is a folded string that
    will be rendered as a single
    line with spaces.

Dynamic Component Selection

variables:
  - name: expertise_level
    type: string
    description: "beginner, intermediate, or expert"

composition:
  - "{% if expertise_level == 'beginner' %}"
  - "{{ personas.patient_teacher }}"
  - "{% elif expertise_level == 'expert' %}"
  - "{{ personas.technical_expert }}"
  - "{% else %}"
  - "{{ personas.helpful_assistant }}"
  - "{% endif %}"

Nested Templates

composition:
  - "{% for section in sections %}"
  - "## {{ section.title }}"
  - "{{ section.content }}"
  - "{% if section.examples %}"
  - "Examples:"
  - "{% for example in section.examples %}"
  - "- {{ example }}"
  - "{% endfor %}"
  - "{% endif %}"
  - "{% endfor %}"

Testing Your Prompts

Always test your prompts with various inputs:

from pal import PromptCompiler

compiler = PromptCompiler()

# Test with different variable values
test_cases = [
    {"api_name": "UserService", "features": ["auth", "profile"]},
    {"api_name": "PaymentAPI", "features": ["stripe", "paypal", "crypto"]},
]

for variables in test_cases:
    prompt = compiler.compile_from_file_sync("api_design.pal", variables)
    print(f"Test case: {variables['api_name']}")
    print(prompt)
    print("-" * 40)