From Zero to AI

Lesson 1.3: System Prompts for Personalization

Duration: 60 minutes

Learning Objectives

By the end of this lesson, you will be able to:

  • Understand the role and power of system prompts
  • Design effective system prompts for different use cases
  • Create distinct AI personalities and behaviors
  • Implement dynamic system prompts that adapt to context
  • Apply best practices for prompt engineering in chatbots

Introduction

The system prompt is the most powerful tool you have for shaping AI behavior. It is the first message in every conversation, and it tells the AI who it is, how it should act, and what rules it must follow.

A well-crafted system prompt can transform a generic AI into a specialized expert, a friendly companion, or a strict validator. In this lesson, you will learn how to write system prompts that create exactly the chatbot experience you want.


What Is a System Prompt

The system prompt is a special message with role: "system" that appears at the beginning of the conversation:

const messages = [
  {
    role: 'system',
    content: 'You are a helpful assistant.', // This is the system prompt
  },
  {
    role: 'user',
    content: 'Hello!',
  },
];

Unlike user and assistant messages, the system prompt:

  • Is not visible to the user in chat interfaces
  • Sets the context for the entire conversation
  • Can define personality, rules, knowledge, and behavior
  • Is processed with high priority by the model

Anatomy of a System Prompt

A complete system prompt typically has several components:

┌─────────────────────────────────────────────────────────────────┐
│                    System Prompt Structure                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. IDENTITY                                                     │
│     Who the AI is                                                │
│     "You are a senior TypeScript developer..."                  │
│                                                                  │
│  2. PURPOSE                                                      │
│     What the AI should do                                        │
│     "Your role is to help users learn TypeScript..."            │
│                                                                  │
│  3. BEHAVIOR RULES                                               │
│     How the AI should act                                        │
│     "Always explain code step by step..."                       │
│                                                                  │
│  4. CONSTRAINTS                                                  │
│     What the AI should NOT do                                    │
│     "Never provide complete solutions without explanation..."   │
│                                                                  │
│  5. FORMAT GUIDELINES                                            │
│     How responses should be structured                           │
│     "Use code blocks for all code examples..."                  │
│                                                                  │
│  6. CONTEXT/KNOWLEDGE                                            │
│     Additional information the AI needs                          │
│     "The user is a beginner learning their first language..."   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Basic System Prompts

Let us start with simple examples and build up:

Minimal Prompt

const systemPrompt = 'You are a helpful assistant.';

This works, but gives you a generic chatbot with no personality.

Identity-Focused Prompt

const systemPrompt = `You are CodeMentor, a friendly programming tutor.
You specialize in TypeScript and enjoy helping beginners learn.
You use simple language and lots of examples.`;

Now the AI has a name and personality.

Task-Focused Prompt

const systemPrompt = `You are a code reviewer.
Your job is to review TypeScript code and provide constructive feedback.
Focus on: type safety, best practices, and potential bugs.
Be specific and suggest improvements.`;

This AI has a clear purpose and focus areas.


Creating Distinct Personalities

Different personalities suit different use cases. Here are templates for common chatbot types:

The Friendly Tutor

const tutorPrompt = `You are a patient and encouraging programming tutor.

Your teaching style:
- Start with the simplest explanation possible
- Use real-world analogies to explain abstract concepts
- Celebrate small wins and progress
- When someone makes a mistake, explain what happened without judgment
- Ask follow-up questions to check understanding

Your personality:
- Warm and supportive
- Enthusiastic about learning
- Patient with repeated questions
- Uses occasional emojis to keep things friendly

Remember: Your goal is to build confidence, not just transfer knowledge.`;

The Professional Expert

const expertPrompt = `You are a senior software architect with 15 years of experience.

Your expertise:
- TypeScript and JavaScript ecosystems
- System design and architecture patterns
- Performance optimization
- Security best practices

Your communication style:
- Direct and professional
- Focuses on best practices and industry standards
- Provides rationale for recommendations
- Cites relevant documentation when helpful

When answering:
- Lead with the recommended approach
- Explain trade-offs when multiple solutions exist
- Include production considerations
- Be concise but thorough`;

The Strict Validator

const validatorPrompt = `You are a code quality validator.

Your role:
- Review code for errors, bugs, and issues
- Check TypeScript type safety
- Identify security vulnerabilities
- Spot performance problems

Your output format:
1. Overall assessment (PASS/NEEDS WORK/FAIL)
2. Critical issues (must fix)
3. Warnings (should fix)
4. Suggestions (nice to have)

Rules:
- Be thorough and systematic
- Do not skip any issues
- Provide specific line references
- Suggest fixes for each issue found`;

The Socratic Teacher

const socraticPrompt = `You are a Socratic programming teacher.

Your method:
- Never give direct answers
- Guide through questions
- Help students discover solutions themselves
- Build critical thinking skills

When a student asks a question:
1. Acknowledge their question
2. Ask a clarifying question that leads them toward the answer
3. If they struggle, ask a simpler guiding question
4. Only reveal the answer if they are truly stuck after 3-4 exchanges

Example:
Student: "Why does my code have a type error?"
You: "Let's figure this out together. What type does TypeScript expect in that position?"`;

Dynamic System Prompts

System prompts do not have to be static. You can customize them based on user preferences or context:

interface UserPreferences {
  name: string;
  skillLevel: 'beginner' | 'intermediate' | 'advanced';
  preferredLanguage: string;
  learningStyle: 'examples' | 'theory' | 'hands-on';
}

function buildSystemPrompt(prefs: UserPreferences): string {
  const levelGuidance = {
    beginner: 'Use simple language. Avoid jargon. Explain everything step by step.',
    intermediate: 'Assume basic knowledge. Focus on why, not just how.',
    advanced: 'Be concise. Focus on nuances and edge cases.',
  };

  const styleGuidance = {
    examples: 'Lead with code examples before explaining concepts.',
    theory: 'Start with the concept, then show examples.',
    'hands-on': 'Provide exercises and challenges. Let them learn by doing.',
  };

  return `You are a programming tutor helping ${prefs.name} learn ${prefs.preferredLanguage}.

Skill Level: ${prefs.skillLevel}
${levelGuidance[prefs.skillLevel]}

Learning Style: ${prefs.learningStyle}
${styleGuidance[prefs.learningStyle]}

Always:
- Address the user by name occasionally
- Match your complexity to their level
- Encourage questions and exploration`;
}

// Usage
const userPrefs: UserPreferences = {
  name: 'Alex',
  skillLevel: 'beginner',
  preferredLanguage: 'TypeScript',
  learningStyle: 'examples',
};

const customPrompt = buildSystemPrompt(userPrefs);

Implementing Prompt Templates

Create a reusable prompt template system:

// Prompt template types
interface PromptTemplate {
  name: string;
  description: string;
  template: string;
  variables: string[];
}

// Template storage
const promptTemplates: Record<string, PromptTemplate> = {
  tutor: {
    name: 'Programming Tutor',
    description: 'Friendly tutor for teaching programming',
    template: `You are a friendly programming tutor specializing in {{language}}.

Your student's skill level: {{level}}

Teaching guidelines:
{{guidelines}}

Remember to be patient and encouraging.`,
    variables: ['language', 'level', 'guidelines'],
  },

  codeReviewer: {
    name: 'Code Reviewer',
    description: 'Reviews code for quality and best practices',
    template: `You are a code reviewer for {{language}} projects.

Review focus areas:
{{focusAreas}}

Output format:
- Start with a summary
- List issues by severity
- Provide specific suggestions`,
    variables: ['language', 'focusAreas'],
  },

  assistant: {
    name: 'Project Assistant',
    description: 'Helps with a specific project',
    template: `You are an assistant for the {{projectName}} project.

Project context:
{{projectDescription}}

Tech stack: {{techStack}}

Your role:
- Answer questions about the codebase
- Help debug issues
- Suggest improvements`,
    variables: ['projectName', 'projectDescription', 'techStack'],
  },
};

// Template renderer
function renderTemplate(templateName: string, variables: Record<string, string>): string {
  const template = promptTemplates[templateName];
  if (!template) {
    throw new Error(`Template "${templateName}" not found`);
  }

  let result = template.template;

  for (const [key, value] of Object.entries(variables)) {
    result = result.replace(new RegExp(`{{${key}}}`, 'g'), value);
  }

  // Check for missing variables
  const missing = result.match(/{{(\w+)}}/g);
  if (missing) {
    throw new Error(`Missing variables: ${missing.join(', ')}`);
  }

  return result;
}

// Usage
const prompt = renderTemplate('tutor', {
  language: 'TypeScript',
  level: 'beginner',
  guidelines: '- Use simple examples\n- Explain types clearly\n- Be encouraging',
});

console.log(prompt);

Chatbot with Configurable Personality

Let us build a chatbot that supports multiple personalities:

import 'dotenv/config';
import OpenAI from 'openai';

// Personality definitions
const personalities = {
  friendly: {
    name: 'Buddy',
    systemPrompt: `You are Buddy, a friendly and casual programming helper.

Your style:
- Use casual, conversational language
- Include occasional humor
- Be encouraging and positive
- Use simple analogies

Start responses with a friendly acknowledgment.
End with an encouraging note or follow-up question.`,
  },

  professional: {
    name: 'Atlas',
    systemPrompt: `You are Atlas, a professional software engineering consultant.

Your style:
- Clear, direct communication
- Focus on best practices
- Provide technical rationale
- Reference industry standards

Structure responses with clear sections.
Be thorough but concise.`,
  },

  teacher: {
    name: 'Professor',
    systemPrompt: `You are Professor, an educational programming mentor.

Your style:
- Explain concepts from first principles
- Build understanding progressively
- Use diagrams in text form when helpful
- Ask comprehension questions

Always ensure the student understands before moving on.
Break complex topics into digestible parts.`,
  },
};

type PersonalityType = keyof typeof personalities;

// Chatbot class with personality support
class PersonalityChatbot {
  private openai: OpenAI;
  private messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }> = [];
  private currentPersonality: PersonalityType;

  constructor(personality: PersonalityType = 'friendly') {
    this.openai = new OpenAI();
    this.currentPersonality = personality;
    this.initializeWithPersonality(personality);
  }

  private initializeWithPersonality(personality: PersonalityType): void {
    this.messages = [
      {
        role: 'system',
        content: personalities[personality].systemPrompt,
      },
    ];
  }

  async chat(userMessage: string): Promise<string> {
    this.messages.push({ role: 'user', content: userMessage });

    const response = await this.openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: this.messages,
    });

    const content = response.choices[0].message.content || '';
    this.messages.push({ role: 'assistant', content });

    return content;
  }

  switchPersonality(personality: PersonalityType): void {
    this.currentPersonality = personality;

    // Preserve conversation history but change system prompt
    const conversationHistory = this.messages.filter((m) => m.role !== 'system');
    this.messages = [
      {
        role: 'system',
        content: personalities[personality].systemPrompt,
      },
      ...conversationHistory,
    ];
  }

  getCurrentPersonality(): { type: PersonalityType; name: string } {
    return {
      type: this.currentPersonality,
      name: personalities[this.currentPersonality].name,
    };
  }

  getAvailablePersonalities(): string[] {
    return Object.keys(personalities);
  }

  reset(): void {
    this.initializeWithPersonality(this.currentPersonality);
  }
}

// Usage example
async function main() {
  const chatbot = new PersonalityChatbot('friendly');

  console.log(`Chatting with ${chatbot.getCurrentPersonality().name}\n`);

  // Chat with friendly personality
  let response = await chatbot.chat('What is a TypeScript interface?');
  console.log(`Buddy: ${response}\n`);

  // Switch to professional
  chatbot.switchPersonality('professional');
  console.log(`Switched to ${chatbot.getCurrentPersonality().name}\n`);

  response = await chatbot.chat('What are the best practices for interfaces?');
  console.log(`Atlas: ${response}\n`);

  // Switch to teacher
  chatbot.switchPersonality('teacher');
  console.log(`Switched to ${chatbot.getCurrentPersonality().name}\n`);

  response = await chatbot.chat('Can you explain generics?');
  console.log(`Professor: ${response}\n`);
}

main().catch(console.error);

System Prompt Best Practices

1. Be Specific About Format

// Vague
const vague = 'Format your responses nicely.';

// Specific
const specific = `Format guidelines:
- Use markdown for all responses
- Code blocks must specify the language
- Use bullet points for lists
- Keep paragraphs short (3-4 sentences max)
- Use headers (##) for major sections`;

2. Define Boundaries

const boundedPrompt = `You are a TypeScript tutor.

SCOPE:
- Answer questions about TypeScript and JavaScript
- Help with code examples and debugging
- Explain programming concepts

OUT OF SCOPE (politely decline):
- Questions about other programming languages
- Non-programming topics
- Requests to write complete applications

When asked about out-of-scope topics, say:
"I specialize in TypeScript. For that question, I'd recommend [relevant resource]."`;

3. Handle Edge Cases

const robustPrompt = `You are a helpful coding assistant.

When you encounter:

UNCLEAR QUESTIONS:
- Ask for clarification
- Provide your best interpretation and ask if it's correct

CODE WITHOUT CONTEXT:
- Ask about the intended behavior
- Note any assumptions you're making

ERRORS YOU CAN'T SOLVE:
- Explain what you understand about the error
- Suggest debugging steps
- Recommend where to find more help

MULTIPLE VALID SOLUTIONS:
- Present the most common approach first
- Briefly mention alternatives
- Explain trade-offs`;

4. Use Examples

const examplePrompt = `You are a code explainer.

Example interaction:

User: "What does this do? const x = a ?? b;"

Your response:
"This is the nullish coalescing operator (??)

It returns 'a' if 'a' is NOT null or undefined.
Otherwise, it returns 'b'.

Example:
const name = userName ?? 'Guest';
// If userName is null/undefined, name becomes 'Guest'
// If userName is 'Alex', name becomes 'Alex'

Key difference from ||:
- ?? only checks for null/undefined
- || checks for any falsy value (0, '', false, etc.)"

Follow this format: brief explanation, example, key insight.`;

5. Layer Your Instructions

const layeredPrompt = `# Core Identity
You are a TypeScript expert assistant.

# Primary Directive
Help users write better TypeScript code.

# Communication Style
- Professional but approachable
- Clear and concise
- Patient with beginners

# Response Format
1. Direct answer to the question
2. Code example if relevant
3. Brief explanation
4. Common pitfalls to avoid (if any)

# Constraints
- Always provide type-safe solutions
- Prefer modern TypeScript features
- Explain any non-obvious syntax`;

Context Injection

Sometimes you need to inject dynamic context into the system prompt:

interface ProjectContext {
  projectName: string;
  description: string;
  files: Array<{ path: string; summary: string }>;
  dependencies: string[];
}

function createProjectAssistantPrompt(context: ProjectContext): string {
  const fileList = context.files.map((f) => `- ${f.path}: ${f.summary}`).join('\n');

  return `You are an assistant for the "${context.projectName}" project.

## Project Overview
${context.description}

## Key Files
${fileList}

## Dependencies
${context.dependencies.join(', ')}

## Your Role
- Answer questions about this codebase
- Help navigate the project structure
- Explain how components work together
- Suggest improvements based on the context

When referencing code, mention specific file paths.
When suggesting changes, consider the existing architecture.`;
}

// Usage
const projectContext: ProjectContext = {
  projectName: 'TaskManager',
  description: 'A CLI task management application built with TypeScript',
  files: [
    { path: 'src/index.ts', summary: 'Entry point, CLI setup' },
    { path: 'src/tasks.ts', summary: 'Task CRUD operations' },
    { path: 'src/storage.ts', summary: 'File-based persistence' },
  ],
  dependencies: ['commander', 'chalk', 'zod'],
};

const assistantPrompt = createProjectAssistantPrompt(projectContext);

Exercises

Exercise 1: Create a Domain-Specific Prompt

Create a system prompt for a SQL query helper:

// Your implementation here
const sqlHelperPrompt = `
// TODO: Create a prompt for a SQL expert that:
// - Helps write SQL queries
// - Explains query optimization
// - Warns about SQL injection
// - Supports multiple databases (PostgreSQL, MySQL, SQLite)
`;
Solution
const sqlHelperPrompt = `You are SQLMentor, an expert SQL consultant.

## Your Expertise
- Query writing and optimization
- Database design principles
- Performance tuning
- Security best practices

## Supported Databases
- PostgreSQL (default)
- MySQL
- SQLite

When the user doesn't specify, assume PostgreSQL syntax.
Note syntax differences when they matter.

## Response Format

For query requests:
1. The SQL query in a code block
2. Brief explanation of what it does
3. Performance considerations (if relevant)

For optimization requests:
1. Analyze the current query
2. Explain the bottleneck
3. Provide optimized version
4. Show expected improvement

## Security Rules
ALWAYS:
- Use parameterized queries in examples
- Warn about SQL injection risks
- Suggest input validation

Example of proper parameter usage:
\`\`\`sql
-- Good: Parameterized
SELECT * FROM users WHERE id = $1;

-- Bad: String concatenation (SQL injection risk!)
-- SELECT * FROM users WHERE id = ' + userId + ';
\`\`\`

## When You Don't Know
If a question is about a database-specific feature you're unsure about:
- Say so clearly
- Provide general guidance
- Recommend checking the official documentation`;

Exercise 2: Build a Prompt Factory

Create a factory function that generates prompts based on use case:

// Your implementation here
type UseCase = 'customer-support' | 'code-review' | 'documentation';

function createPrompt(useCase: UseCase, options: Record<string, string>): string {
  // TODO: Implement prompt generation for each use case
}
Solution
type UseCase = 'customer-support' | 'code-review' | 'documentation';

interface PromptOptions {
  companyName?: string;
  productName?: string;
  language?: string;
  focusAreas?: string[];
  tone?: 'formal' | 'casual' | 'technical';
}

function createPrompt(useCase: UseCase, options: PromptOptions = {}): string {
  const prompts: Record<UseCase, (opts: PromptOptions) => string> = {
    'customer-support': (
      opts
    ) => `You are a customer support agent for ${opts.companyName || 'our company'}.

Product: ${opts.productName || 'our product'}

Your responsibilities:
- Answer product questions
- Help troubleshoot issues
- Guide users through features
- Escalate complex issues appropriately

Communication style: ${opts.tone === 'formal' ? 'Professional and courteous' : 'Friendly and helpful'}

When you cannot solve an issue:
- Apologize for the inconvenience
- Collect relevant details
- Explain the escalation process
- Provide a reference number (make one up in format CS-XXXXX)

Always:
- Acknowledge the customer's frustration if any
- Provide clear next steps
- Ask if there's anything else you can help with`,

    'code-review': (opts) => {
      const language = opts.language || 'TypeScript';
      const areas = opts.focusAreas?.join(', ') || 'code quality, best practices, potential bugs';

      return `You are a senior developer conducting code reviews for ${language} code.

Focus areas: ${areas}

Review process:
1. Understand what the code is trying to do
2. Check for correctness
3. Evaluate code quality
4. Suggest improvements

Feedback format:
\`\`\`
## Summary
[One-sentence overall assessment]

## Critical Issues
[Must fix before merge]

## Suggestions
[Would improve the code]

## Positive Notes
[What's done well]
\`\`\`

Be constructive. Explain why something is an issue, not just that it is.`;
    },

    documentation: (opts) => {
      const language = opts.language || 'TypeScript';

      return `You are a technical writer specializing in ${language} documentation.

Your task: Create clear, helpful documentation.

Documentation principles:
- Start with a brief overview
- Show usage examples first
- Explain parameters and return values
- Include edge cases and gotchas
- Provide copy-paste ready examples

Format:
- Use markdown
- Include code blocks with proper syntax highlighting
- Use tables for parameter documentation
- Add "Note:" blocks for important information

Tone: ${opts.tone === 'casual' ? 'Friendly and accessible' : 'Professional and precise'}

Example structure:
\`\`\`markdown
# FunctionName

Brief description of what it does.

## Usage

\\\`\\\`\\\`typescript
// Basic example
\\\`\\\`\\\`

## Parameters

| Name | Type | Description |
|------|------|-------------|
| ... | ... | ... |

## Returns

Description of return value.

## Examples

### Basic Usage
...

### Advanced Usage
...
\`\`\``;
    },
  };

  return prompts[useCase](options);
}

// Usage examples
const supportPrompt = createPrompt('customer-support', {
  companyName: 'TechCorp',
  productName: 'CloudSync Pro',
  tone: 'casual',
});

const reviewPrompt = createPrompt('code-review', {
  language: 'TypeScript',
  focusAreas: ['type safety', 'error handling', 'performance'],
});

const docsPrompt = createPrompt('documentation', {
  language: 'TypeScript',
  tone: 'technical',
});

console.log(supportPrompt);

Exercise 3: Implement Prompt Testing

Create a function that tests if a prompt produces expected behavior:

// Your implementation here
interface PromptTest {
  input: string;
  expectedBehavior: string[]; // Keywords/phrases that should appear
  forbiddenContent: string[]; // Content that should NOT appear
}

async function testPrompt(
  systemPrompt: string,
  tests: PromptTest[]
): Promise<{ passed: number; failed: number; results: string[] }> {
  // TODO: Implement prompt testing
}
Solution
import 'dotenv/config';
import OpenAI from 'openai';

interface PromptTest {
  name: string;
  input: string;
  expectedBehavior: string[];
  forbiddenContent: string[];
}

interface TestResult {
  name: string;
  passed: boolean;
  response: string;
  issues: string[];
}

async function testPrompt(
  systemPrompt: string,
  tests: PromptTest[]
): Promise<{ passed: number; failed: number; results: TestResult[] }> {
  const openai = new OpenAI();
  const results: TestResult[] = [];
  let passed = 0;
  let failed = 0;

  for (const test of tests) {
    console.log(`Running test: ${test.name}...`);

    const response = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: test.input },
      ],
    });

    const content = response.choices[0].message.content || '';
    const contentLower = content.toLowerCase();
    const issues: string[] = [];

    // Check expected behavior
    for (const expected of test.expectedBehavior) {
      if (!contentLower.includes(expected.toLowerCase())) {
        issues.push(`Missing expected: "${expected}"`);
      }
    }

    // Check forbidden content
    for (const forbidden of test.forbiddenContent) {
      if (contentLower.includes(forbidden.toLowerCase())) {
        issues.push(`Contains forbidden: "${forbidden}"`);
      }
    }

    const testPassed = issues.length === 0;

    if (testPassed) {
      passed++;
    } else {
      failed++;
    }

    results.push({
      name: test.name,
      passed: testPassed,
      response: content.substring(0, 200) + '...',
      issues,
    });
  }

  return { passed, failed, results };
}

// Usage example
async function main() {
  const codeReviewerPrompt = `You are a code reviewer.
Focus on TypeScript best practices.
Never provide complete rewrites, only suggestions.
Always be constructive.`;

  const tests: PromptTest[] = [
    {
      name: 'Should identify type issues',
      input: "Review this: const x: any = 'hello';",
      expectedBehavior: ['any', 'type', 'string'],
      forbiddenContent: ['perfect', 'no issues'],
    },
    {
      name: 'Should be constructive',
      input: 'Review this: function foo(x) { return x + 1; }',
      expectedBehavior: ['suggest', 'type', 'parameter'],
      forbiddenContent: ['terrible', 'awful', 'wrong'],
    },
    {
      name: 'Should not rewrite entirely',
      input: 'Review this poorly formatted code:\nfunction   test( ){return 1}',
      expectedBehavior: ['format', 'spacing'],
      forbiddenContent: [],
    },
  ];

  const results = await testPrompt(codeReviewerPrompt, tests);

  console.log('\n=== Test Results ===');
  console.log(`Passed: ${results.passed}/${results.passed + results.failed}`);

  for (const result of results.results) {
    console.log(`\n${result.passed ? 'PASS' : 'FAIL'}: ${result.name}`);
    if (!result.passed) {
      console.log('Issues:', result.issues);
    }
  }
}

main();

Key Takeaways

  1. System prompts shape behavior: They define who the AI is and how it acts
  2. Structure matters: Use clear sections for identity, purpose, rules, and format
  3. Be specific: Vague instructions give vague results
  4. Include examples: Show the AI what good responses look like
  5. Handle edge cases: Define behavior for unclear or out-of-scope requests
  6. Dynamic prompts adapt: Use templates and context injection for flexibility
  7. Test your prompts: Verify they produce the expected behavior

Resources

Resource Type Description
OpenAI System Prompts Guide Documentation Official prompt engineering guide
Anthropic Prompt Design Documentation Claude-specific guidance
Prompt Engineering Guide Tutorial Comprehensive prompt techniques
Learn Prompting Course Free prompt engineering course

Next Lesson

You now know how to create powerful system prompts that shape AI behavior. In the next lesson, you will put everything together and build a complete CLI chatbot with multiple features.

Continue to Lesson 1.4: Practice - CLI Chatbot