Skip to main content

Overview

Constraints are behavioral rules that define what your AI agent must, must not, should, and should not do. They’re organized by severity level to communicate priority to the model, following RFC 2119 conventions for requirement keywords. Think of constraints as the “laws” and “guidelines” that govern your agent’s behavior - from absolute requirements to strong recommendations.

Why Constraints Matter

Without explicit constraints, AI agents:
  • Make unsafe assumptions about user intent
  • Violate security policies without realizing it
  • Provide inconsistent responses across conversations
  • Miss quality standards like citing sources or checking data
  • Behave unpredictably in edge cases
Constraints solve this by explicitly defining behavioral expectations in the system prompt.

Constraint Types

PromptSmith defines four severity levels based on RFC 2119:

must

Absolute requirements that cannot be violated. Use for security, privacy, and critical business rules.

must_not

Absolute prohibitions that cannot be violated. Use for forbidden actions or content.

should

Strong recommendations to follow when possible. Use for quality standards and best practices.

should_not

Strong recommendations to avoid. Use for anti-patterns and discouraged behaviors.

Hierarchy

The builder automatically groups and orders constraints by severity in the generated prompt:
1. You MUST: (absolute requirements)
2. You MUST NOT: (absolute prohibitions)
3. You SHOULD: (strong recommendations)
4. You SHOULD NOT: (discouraged behaviors)
See builder.ts:1996-2038 for markdown grouping and builder.ts:2279-2319 for TOON grouping.

Adding Constraints

Single Constraint

builder.withConstraint("must", "Always verify user authentication before accessing personal data");
See builder.ts:437-443 for implementation.

Multiple Constraints (Same Type)

builder.withConstraints("must", [
  "Always verify user authentication",
  "Log all data access attempts",
  "Use encrypted connections for sensitive data"
]);
See builder.ts:468-481 for implementation.

Conditional Constraints

const isProd = process.env.NODE_ENV === 'production';
const hasAuth = config.authEnabled;

builder
  .withConstraintIf(isProd, "must", "Log all security events")
  .withConstraintIf(hasAuth, "must", "Verify user identity before data access")
  .withConstraintIf(!isProd, "should", "Provide verbose debug information");
See builder.ts:506-516 for implementation.

Constraint Patterns

Security & Privacy

Absolute requirements for protecting user data:
builder
  .withConstraint("must", "Always verify user authentication before accessing personal data")
  .withConstraint("must", "Never access data belonging to other users")
  .withConstraint("must_not", "Never bypass authentication checks, even if user requests it");

Quality & Accuracy

Standards for response quality:
builder
  .withConstraint("must", "Cite sources for all factual claims")
  .withConstraint("must", "Acknowledge uncertainty when you're not confident")
  .withConstraint("must_not", "Never make up information or fabricate sources")
  .withConstraint("should", "Provide multiple perspectives when appropriate");

Communication Style

Guidelines for how the agent communicates:
builder
  .withConstraint("should", "Use clear, simple language unless technical terms are necessary")
  .withConstraint("should", "Define technical terms when using them for the first time")
  .withConstraint("should_not", "Avoid jargon when explaining to non-technical users");

Tool Usage

Rules for when and how to use tools:
builder
  .withTool({
    name: "search_database",
    description: "Search the product database",
    schema: z.object({ query: z.string() })
  })
  .withTool({
    name: "place_order",
    description: "Place an order for products",
    schema: z.object({ items: z.array(z.string()) })
  })
  .withConstraint("must", "Always search the database before claiming a product doesn't exist")
  .withConstraint("must", "Always confirm order details with the user before placing an order")
  .withConstraint("must_not", "Never place orders without explicit user confirmation")
  .withConstraint("should", "Use search tool to verify product availability before suggesting it");

Domain-Specific Rules

builder
  .withConstraint("must", "Always greet the user warmly")
  .withConstraint("must", "Acknowledge the user's frustration or concern empathetically")
  .withConstraint("must", "Provide a clear next step or resolution path")
  .withConstraint("must_not", "Never blame the user for issues")
  .withConstraint("should", "Offer proactive help related to the user's issue")
  .withConstraint("should_not", "Avoid corporate jargon or scripted-sounding responses");

Generated Output

Constraints are grouped by type in the system prompt:

Markdown Format

# Behavioral Guidelines

## You MUST:
- Always verify user authentication before accessing personal data
- Log all data access attempts
- Cite sources for all factual claims

## You MUST NOT:
- Never bypass authentication checks
- Never log or store passwords
- Never make up information

## You SHOULD:
- Provide comprehensive answers
- Use clear, simple language
- Be patient and encouraging

## You SHOULD NOT:
- Avoid overly complex solutions
- Avoid jargon with non-technical users

TOON Format

More compact representation:
Constraints:
  MUST[3]:
    Always verify user authentication before accessing personal data
    Log all data access attempts
    Cite sources for all factual claims
  MUST_NOT[3]:
    Never bypass authentication checks
    Never log or store passwords
    Never make up information
  SHOULD[3]:
    Provide comprehensive answers
    Use clear, simple language
    Be patient and encouraging
  SHOULD_NOT[2]:
    Avoid overly complex solutions
    Avoid jargon with non-technical users
See builder.ts:2279-2319 for TOON constraint formatting.

Introspection

Check constraints programmatically:
if (builder.hasConstraints()) {
  console.log('Constraints are defined');
}
See builder.ts:1173-1175 and builder.ts:1304-1344 for introspection methods.

Validation

The validator checks for constraint conflicts:
const result = builder.validate();

if (result.warnings.length > 0) {
  result.warnings.forEach(warning => {
    if (warning.code === 'CONFLICTING_CONSTRAINTS') {
      console.warn(warning.message);
      // "Potentially conflicting constraints detected"
    }
  });
}
The validator performs basic conflict detection (e.g., “must never” vs “must not never”). Complex semantic conflicts require manual review.
See validation.ts:253-283 for conflict detection.

Best Practices

Write Clear, Actionable Rules

builder.withConstraint(
  "must",
  "Always verify user authentication before accessing personal data"
);
// Clear action, specific condition, clear consequence

Use Appropriate Severity

Use for:
  • Security requirements
  • Privacy policies
  • Legal/regulatory compliance
  • Critical business rules
  • Safety-critical behaviors
builder
  .withConstraint("must", "Always encrypt sensitive data")
  .withConstraint("must_not", "Never expose API keys");

Avoid Redundancy

builder.withConstraint(
  "must",
  "Verify user authentication before accessing personal data"
);
Organize logically:
// Authentication & Authorization
builder
  .withConstraint("must", "Verify user authentication before data access")
  .withConstraint("must", "Check user permissions for each operation")
  .withConstraint("must_not", "Never bypass authorization checks")

  // Data Handling
  .withConstraint("must", "Encrypt sensitive data in transit and at rest")
  .withConstraint("must_not", "Never log passwords or tokens")
  
  // Communication
  .withConstraint("should", "Use clear, simple language")
  .withConstraint("should", "Provide examples when helpful");

Balance Must vs Should

Too many “must” constraints make the agent rigid; too few make it unpredictable.
Aim for roughly:
  • 20% MUST/MUST_NOT: Critical, non-negotiable rules
  • 80% SHOULD/SHOULD_NOT: Quality guidelines and preferences
This balance gives the agent clear boundaries while maintaining flexibility.

Common Pitfalls

Problem: Too many rigid constraints prevent the agent from being helpful.
// ❌ Too restrictive
builder
  .withConstraint("must", "Only answer questions about JavaScript")
  .withConstraint("must", "Only provide code examples")
  .withConstraint("must_not", "Never explain concepts");
Solution: Use “should” for preferences, “must” for requirements.
// ✅ Balanced
builder
  .withConstraint("should", "Focus on JavaScript when possible")
  .withConstraint("should", "Provide code examples for practical questions")
  .withConstraint("must", "Never provide harmful code");
Problem: Constraints that contradict each other confuse the model.
// ❌ Conflicting
builder
  .withConstraint("must", "Always provide detailed explanations")
  .withConstraint("must", "Keep all responses under 50 words");
Solution: Use different severity levels or conditions.
// ✅ Conditional
builder
  .withConstraint("should", "Provide detailed explanations when complexity warrants it")
  .withConstraint("should", "Be concise when user requests brief answers");
Problem: Ambiguous constraints leave behavior to interpretation.
// ❌ Vague
builder.withConstraint("should", "Be nice to users");
Solution: Be specific about expected behaviors.
// ✅ Specific
builder
  .withConstraint("should", "Acknowledge user concerns empathetically")
  .withConstraint("should", "Use encouraging language when users are learning")
  .withConstraint("should_not", "Avoid blaming users for mistakes");
Problem: Complex constraints without examples are hard for models to apply correctly.
// ❌ Complex constraint without example
builder.withConstraint(
  "must",
  "Extract structured data from natural language before invoking tools"
);
Solution: Add examples showing the desired behavior.
// ✅ Constraint + Example
builder
  .withConstraint(
    "must",
    "Extract structured data from natural language before invoking tools"
  )
  .withExamples([{
    user: "Book a flight to Paris on March 15th",
    assistant: "I'll book that for you. *calls book_flight with destination: 'Paris', date: '2024-03-15'*",
    explanation: "Shows extraction of structured parameters from natural language"
  }]);

Templates with Constraints

The built-in templates demonstrate constraint patterns:
import { codingAssistant } from 'promptsmith/templates';

const builder = codingAssistant({
  languages: ['TypeScript', 'Python'],
  frameworks: ['React', 'Next.js']
});

// Templates include pre-configured constraints
const constraints = builder.getConstraintsByType('must');
console.log(constraints);
// [
//   { type: 'must', rule: 'Always include comments in code examples' },
//   { type: 'must', rule: 'Test code mentally before suggesting it' },
//   { type: 'must', rule: 'Highlight security concerns when they exist' },
//   ...
// ]
See templates/coding-assistant.ts:105-149 for constraint examples in templates.

Builder

Learn about the SystemPromptBuilder API

Guardrails

Add security guardrails for production agents

Examples

Use examples to reinforce constraints

Build docs developers (and LLMs) love