Lesson 8 of 8

Advanced Prompting Techniques

Self-consistency, ReAct, Tree of Thoughts, and other cutting-edge methods

Advanced Prompting Techniques

Beyond basic prompting, researchers have developed sophisticated techniques that significantly improve LLM performance on complex tasks. These methods push the boundaries of what's possible with prompt engineering.

Self-Consistency

Self-consistency generates multiple reasoning paths and selects the most common answer. It's like asking multiple experts and going with the consensus.

// Self-Consistency Implementation
async function selfConsistencyPrompt(question, samples = 5) {
  const responses = await Promise.all(
    Array(samples).fill(null).map(() =>
      prompt(`
        ${question}
        
        Let's think step by step, then give your final answer
        in the format: ANSWER: [your answer]
      `, { temperature: 0.7 }) // Higher temp for diversity
    )
  );
  
  // Extract answers and count votes
  const answers = responses.map(r => 
    r.match(/ANSWER:\s*(.+)/i)?.[1]?.trim()
  );
  
  const votes = {};
  answers.forEach(a => votes[a] = (votes[a] || 0) + 1);
  
  // Return most common answer
  return Object.entries(votes)
    .sort((a, b) => b[1] - a[1])[0][0];
}

// Example usage
const answer = await selfConsistencyPrompt(
  "What is the time complexity of quicksort in the average case?"
);
// Samples might give: O(n log n), O(n log n), O(n²), O(n log n), O(n log n)
// Consensus: O(n log n)

🧠 ReAct: Reasoning + Acting

ReAct interleaves reasoning with actions (like tool use). The model thinks about what to do, takes an action, observes the result, and continues reasoning.

ReAct Pattern

// ReAct Prompt Structure
"You have access to these tools:
- search(query): Search the web for information
- calculate(expression): Evaluate math expressions
- lookup(term): Look up a term in a knowledge base

For each step, use this format:
Thought: [your reasoning about what to do next]
Action: [tool_name(input)]
Observation: [result from the tool]
... (repeat until done)
Final Answer: [your answer]

Question: What is the population of France divided by 10?"

// Model Response:
Thought: I need to find the population of France first.
Action: search("population of France 2024")
Observation: France has a population of approximately 68 million.

Thought: Now I need to divide 68 million by 10.
Action: calculate(68000000 / 10)
Observation: 6800000

Thought: I have the answer.
Final Answer: The population of France divided by 10 is 6.8 million.

Tree of Thoughts (ToT)

Tree of Thoughts explores multiple reasoning paths in a tree structure, evaluating and pruning branches to find the best solution.

// Tree of Thoughts Implementation
async function treeOfThoughts(problem, maxDepth = 3) {
  // Generate initial thoughts
  const thoughts = await prompt(`
    Problem: ${problem}
    
    Generate 3 different initial approaches to solve this.
    Format each as: APPROACH 1: [description]
  `);
  
  const approaches = parseApproaches(thoughts);
  
  // Evaluate each approach
  const evaluated = await Promise.all(
    approaches.map(async approach => {
      const evaluation = await prompt(`
        Problem: ${problem}
        Approach: ${approach}
        
        Rate this approach 1-10 for:
        - Likely correctness
        - Efficiency
        - Completeness
        
        Return: SCORE: [number] REASON: [brief reason]
      `);
      
      return { approach, ...parseScore(evaluation) };
    })
  );
  
  // Continue with best approach
  const best = evaluated.sort((a, b) => b.score - a.score)[0];
  
  // Develop the winning approach further
  const solution = await prompt(`
    Problem: ${problem}
    Best Approach: ${best.approach}
    
    Develop this approach into a complete solution.
    Show your step-by-step reasoning.
  `);
  
  return solution;
}

// Example: Solving an algorithm problem
const solution = await treeOfThoughts(
  "Find the longest palindromic substring in a string"
);
// Explores: brute force, dynamic programming, expand around center
// Evaluates each, develops the best one

Least-to-Most Prompting

Break a complex problem into subproblems, solve them from simplest to hardest, building on previous solutions.

// Least-to-Most Pattern
async function leastToMost(complexProblem) {
  // Step 1: Decompose into subproblems
  const subproblems = await prompt(`
    Break this problem into smaller subproblems,
    ordered from simplest to most complex:
    
    Problem: ${complexProblem}
    
    List subproblems, simplest first:
  `);
  
  // Step 2: Solve each subproblem, building context
  let context = "";
  const solutions = [];
  
  for (const subproblem of parseSubproblems(subproblems)) {
    const solution = await prompt(`
      Using what we've solved so far:
      ${context}
      
      Now solve: ${subproblem}
    `);
    
    solutions.push(solution);
    context += `\nSolved: ${subproblem}\nSolution: ${solution}\n`;
  }
  
  // Step 3: Combine into final solution
  const finalSolution = await prompt(`
    Given these solutions to subproblems:
    ${context}
    
    Provide the complete solution to: ${complexProblem}
  `);
  
  return finalSolution;
}

// Example
const solution = await leastToMost(
  "Build a real-time collaborative text editor"
);
// Subproblems: 1) Basic text editing, 2) WebSocket connection,
// 3) Conflict resolution, 4) Operational transform, etc.

Automatic Prompt Optimization

// APO: Let the model improve prompts
async function optimizePrompt(initialPrompt, testCases, iterations = 3) {
  let currentPrompt = initialPrompt;
  
  for (let i = 0; i < iterations; i++) {
    // Test current prompt
    const results = await Promise.all(
      testCases.map(async tc => {
        const output = await prompt(currentPrompt + tc.input);
        return {
          input: tc.input,
          expected: tc.expected,
          actual: output,
          correct: output.includes(tc.expected)
        };
      })
    );
    
    const score = results.filter(r => r.correct).length / results.length;
    
    if (score === 1) break; // Perfect score
    
    // Ask model to improve the prompt
    const failures = results.filter(r => !r.correct);
    
    currentPrompt = await prompt(`
      This prompt scored ${score * 100}% on test cases.
      
      Current prompt: ${currentPrompt}
      
      Failed cases:
      ${failures.map(f => 
        `Input: ${f.input}, Expected: ${f.expected}, Got: ${f.actual}`
      ).join('\n')}
      
      Improve the prompt to handle these cases.
      Return only the improved prompt.
    `);
  }
  
  return currentPrompt;
}

// Example: Optimize a classification prompt
const betterPrompt = await optimizePrompt(
  "Classify this email as spam or not spam: ",
  [
    { input: "Win a free iPhone!", expected: "spam" },
    { input: "Meeting at 3pm tomorrow", expected: "not spam" },
    // more test cases...
  ]
);

Meta-Prompting

// Use AI to generate prompts
async function metaPrompt(task, context) {
  // Generate an optimal prompt for the task
  const generatedPrompt = await prompt(`
    You are a prompt engineering expert.
    
    Create an optimal prompt for this task:
    Task: ${task}
    Context: ${context}
    
    Consider:
    - What role should the AI play?
    - What context is needed?
    - What format should the output be?
    - What examples would help?
    - What constraints are important?
    
    Return the complete prompt ready to use.
  `);
  
  return generatedPrompt;
}

// Example: Generate a prompt for code review
const reviewPrompt = await metaPrompt(
  "Review React code for performance issues",
  "Senior frontend developer, production codebase, strict review"
);

// Generated prompt might include:
// - React performance expert role
// - Specific patterns to look for (memo, useMemo, useCallback)
// - Format for feedback
// - Severity levels
// - Example of good feedback

Technique Selection Guide

Technique Best For Trade-off
Chain of Thought Reasoning, math, logic More tokens
Self-Consistency High-stakes decisions Higher cost (multiple calls)
ReAct Tasks requiring external data Complex implementation
Tree of Thoughts Complex problem solving Many API calls
Least-to-Most Multi-step problems Sequential (slower)

✅ When to Use Advanced Techniques

  • • Complex reasoning where basic prompting fails
  • • High-stakes decisions requiring verification
  • • Tasks that benefit from multiple perspectives
  • • Problems that can be decomposed into subproblems
  • • When you need to integrate external tools/data