Best Practices for AI Prompt Engineering with APTL

Guidelines and recommendations for building effective, maintainable AI prompts using APTL.

Template Organization

Organize your prompts into well-defined sections using logical grouping. This makes prompts easier to understand, modify, and debug:

@section identity
  // Agent's identity and role
@end

@section objective
  // What the agent should accomplish
@end

@section guidelines
  // How the agent should behave
@end

@section constraints
  // Limitations and boundaries
@end

@section context
  // Current situation and relevant information
@end

Use descriptive, consistent section names for clarity:

Good:

@section user_context
@section error_handling_guidelines
@section few_shot_examples

Avoid:

@section stuff
@section temp
@section section1

Keep your templates focused - one template should serve one clear purpose:

Good:

  • code-review-agent.aptl
  • customer-support-bot.aptl
  • content-generator.aptl

Avoid:

  • all-agents.aptl (too broad)
  • misc-templates.aptl (unfocused)

Prompt Structure

Follow a standard structure across similar templates for consistency:

// 1. Identity - Who is the AI?
@section identity
  You are @{agentName}...
@end

// 2. Capabilities - What can it do?
@section capabilities
  You can help with...
@end

// 3. Guidelines - How should it behave?
@section guidelines
  Follow these principles...
@end

// 4. Examples - Show, don't just tell
@section examples
  @examples
    @case input="..." output="..."
  @end
@end

// 5. Context - Current situation
@section context
  Current context...
@end

// 6. Task - What to do now
@section task
  Your task is...
@end

Always begin with a clear identity statement:

@section identity
  You are @{agentName}, a @{agentRole} specialized in @{domain}.

  @if expertise
    Your areas of expertise:
    @each area in expertise
      • @{area}
    @end
  @end
@end

Be specific about what you want the AI to accomplish. Clearly state your objectives:

Good:

@section objective
  Your goal is to review the code and provide:
  1. A summary of the main issues
  2. Specific suggestions for improvement
  3. An overall quality score (1-10)
@end

Avoid:

@section objective
  Review the code.
@end

Variable Management

Use meaningful variable names that clearly indicate their purpose:

Good:

@{user.firstName}
@{product.priceInUSD}
@{setting.maxRetryAttempts}

Avoid:

@{x}
@{temp}
@{data1}

Always provide default values for optional variables to ensure graceful fallback behavior:

Welcome, @{user.name|"Guest"}!
Timeout: @{config.timeout|30} seconds
Theme: @{preferences.theme|"light"}

Check for required data before using it to prevent errors:

@if user.name and user.email
  @section identity
    User: @{user.name} (@{user.email})
  @end
@else
  @section error
    Missing required user information
  @end
@end

Keep data structures flat when possible, but use logical grouping when it helps clarity:

Good:

{
  userName: 'Alice',
  userEmail: 'alice@example.com',
  userRole: 'admin'
}

Also Good (when logical grouping helps):

{
  user: {
    name: 'Alice',
    email: 'alice@example.com',
    role: 'admin'
  }
}

Avoid Deep Nesting:

{
  data: {
    user: {
      profile: {
        personal: {
          name: 'Alice'
        }
      }
    }
  }
}

Conditionals and Logic

Keep your template logic simple by moving complex calculations to your data preparation code:

Good:

// Prepare data
const data = {
  userLevel: calculateUserLevel(user),
  showPremiumFeatures: user.tier === 'premium'
};

// Simple template
const template = `
  @if showPremiumFeatures
    Premium features available
  @end
`;

Avoid:

@if (user.purchaseCount > 10 and user.accountAge > 365 and user.totalSpent > 1000) or user.tier == "vip"
  // Complex condition in template
@end

Use @elif for multiple conditions to create clear branching logic:

@if userLevel == "expert"
  Advanced technical content
@elif userLevel == "intermediate"
  Balanced technical content
@elif userLevel == "beginner"
  Simple explanations
@else
  General content
@end

Avoid deeply nested conditionals as they become hard to read and maintain:

Good:

@if showSection
  @section content
    @if hasItems
      @each item in items
        - @{item.name}
      @end
    @end
  @end
@end

Avoid:

@if condition1
  @if condition2
    @if condition3
      @if condition4
        // Too deep!
      @end
    @end
  @end
@end

Template Inheritance

Create reusable base templates that can be extended by specialized templates:

// base-agent.aptl
@section identity(overridable=true)
  You are an AI assistant.
@end

@section guidelines(overridable=true)
  • Be helpful
  • Be accurate
  • Be concise
@end

@section footer
  Maintain professional standards.
@end

Use override attributes strategically to control how child templates modify parent sections:

  • override=true - Replace completely
  • prepend=true - Add before parent content
  • append=true - Add after parent content
@extends "base-agent.aptl"

// Override identity completely
@section identity(override=true)
  You are a specialized medical assistant.
@end

// Add to existing guidelines
@section guidelines(prepend=true)
  Medical-specific guidelines:
  • Use evidence-based information
  • Recommend professional consultation

@end

Mark sections as overridable to make it clear which sections children can customize:

@section identity(overridable=true)
  Default identity
@end

@section fixed_policy
  This cannot be overridden
@end

Performance

Compile templates once and render them multiple times for better performance:

// ✅ Good: Compile once, render many times
const compiled = await engine.compile(template);

for (const data of dataArray) {
  const output = await compiled.render(data);
  // Use output
}
// ❌ Avoid: Compiling repeatedly
for (const data of dataArray) {
  const output = await engine.render(template, data);
}

Use the template registry to load templates once and reuse them:

// ✅ Good: Load templates once
const registry = new TemplateRegistry(engine);
await registry.loadDirectory('./templates');

// Use many times
const template = registry.get('agent-prompt');
const output = await template.render(data);

Cache frequently used static data to avoid repeated processing:

// Prepare static data once
const staticData = {
  agentName: 'Assistant',
  guidelines: loadGuidelines(),
  examples: loadExamples()
};

// Merge with dynamic data for each request
const output = await template.render({
  ...staticData,
  userInput: request.input,
  context: request.context
});

Testing and Validation

Test your templates with edge cases to ensure robust behavior:

// Test with empty data
await template.render({});

// Test with missing optional fields
await template.render({ name: 'Test' });

// Test with maximum data
await template.render(fullDataSet);

// Test with special characters
await template.render({ name: 'User@123<>"' });

Validate templates to catch missing variables and other issues:

import { VariableResolver } from '@finqu/aptl';

const resolver = new VariableResolver();
const variables = resolver.extractVariables(template);

// Check that all required variables are documented
console.log('Required variables:', variables);

// Test against sample data
const missing = resolver.validateTemplate(template, sampleData);
if (missing.length > 0) {
  console.warn('Missing variables:', missing);
}

Use TypeScript interfaces for type safety when rendering templates:

interface AgentData {
  agentName: string;
  agentRole: string;
  domain: string;
  credentials?: string[];
  primaryGoal: string;
}

function renderAgent(data: AgentData): Promise<string> {
  return engine.render(agentTemplate, data);
}

Security

Never directly inject untrusted user input - always sanitize data first:

// ✅ Good: Sanitize input
import { escapeHtml } from 'some-library';

const safeData = {
  userInput: escapeHtml(rawUserInput),
  userName: sanitize(rawUserName)
};

await engine.render(template, safeData);

Validate data types to ensure data matches expected schemas:

function validateData(data: any): boolean {
  // Ensure data matches expected schema
  return (
    typeof data.name === 'string' &&
    typeof data.age === 'number' &&
    Array.isArray(data.items)
  );
}

if (validateData(userData)) {
  await engine.render(template, userData);
}

Avoid exposing sensitive information in your prompts:

// ❌ Avoid exposing sensitive data in prompts
@section context
  User password: @{user.password}
  API key: @{config.apiKey}
@end

// ✅ Good: Only include necessary information
@section context
  User role: @{user.role}
  Access level: @{user.accessLevel}
@end

Maintainability

Add comments to your templates to explain their purpose and logic:

// Agent identity and capabilities
@section identity
  You are @{agentName}.
@end

// Context-aware guidelines based on user level
// Beginner: Simple language
// Intermediate: Balanced approach
// Expert: Technical terminology
@section guidelines
  @if userLevel == "beginner"
    // Use simple explanations
    ...
  @end
@end

Version your templates to track changes over time:

// Version: 2.1.0
// Last updated: 2025-01-15
// Changes: Added support for multi-language

@section identity
  ...
@end

Document required data at the top of your templates:

// Required data:
// - agentName: string
// - agentRole: string
// - domain: string
//
// Optional data:
// - credentials: string[]
// - secondaryGoals: string[]

@section identity
  You are @{agentName}...
@end

Use consistent formatting conventions:

  • Indent with 2 spaces
  • Use blank lines to separate sections
  • Keep line length reasonable (< 100 chars)
  • Use consistent naming conventions

AI Prompt Engineering Specifics

Use few-shot examples effectively to teach desired behavior patterns:

@examples
@case input="Long function" output="Break into smaller functions" explanation="Improves readability and testability"
@case input="No error handling" output="Add try-catch blocks" explanation="Prevents crashes and improves UX"
@case input="Magic numbers" output="Use named constants" explanation="Makes code self-documenting"
@end

Be explicit about tone and style to ensure appropriate communication:

@section tone
  @if audienceType == "technical"
    • Use technical terminology
    • Focus on accuracy and precision
    • Include code examples
  @elif audienceType == "business"
    • Use business terminology
    • Focus on ROI and outcomes
    • Include metrics and KPIs
  @else
    • Use plain language
    • Focus on benefits
    • Include relatable examples
  @end
@end

Specify the exact output format you expect from the AI:

@section output_format
  Provide your response in this format:

  **Summary**: [One sentence summary]

  **Analysis**:
  - Point 1
  - Point 2
  - Point 3

  **Recommendation**: [Actionable recommendation]

  **Confidence**: [Low/Medium/High]
@end

Include clear constraints and limitations to define boundaries:

@section constraints
  You must:
  • Keep responses under 500 words
  • Cite sources when making factual claims
  • Acknowledge when uncertain

  You must not:
  • Make medical diagnoses
  • Provide financial advice
  • Share personal opinions on controversial topics
@end
← API Reference Back to Home