Advanced Prompting Techniques

Generative AI & LLM Fundamentals · Prompt engineering · 13 min read · Updated

Breaking a hard task into a chain

  1. One huge prompt that asks for everything often gives a messy answer.
  2. Decompose it. The first prompt only extracts the facts.
  3. Its output becomes the input of the second prompt, which analyses those facts.
  4. The third prompt writes the final answer from the analysis.
  5. A reflection step asks the model to check its own draft against your rules, and fix what is wrong.
  6. Each small step is easier to test and debug than one giant prompt.

Basic prompts work well for short, clear tasks. But some tasks are bigger: analyse a resume against a job post, write a summary, check it for errors, and then rewrite it. If you put all of that into one giant prompt, the model may skip steps, mix them up or drop details. The answer is to use a few proven techniques. In this lesson you will learn four of them: asking for step-by-step reasoning, using examples well, asking the model to check its own work, and splitting a big job into a prompt chain. Then you will build a chain runner in the lab and watch the output of one step flow into the next.

Technique 1: ask for the steps

Models often do better on problems with several steps if they write out the steps before the final answer. This is called chain-of-thought prompting. When the model writes the steps, each step becomes part of the text that the next token is based on. So the reasoning is not hidden. It is on the page, where the model can use it and you can read it.

Example: “A forklift driver works 8 hours a day, 5 days a week, and is paid 150 rupees an hour. What does the driver earn in 4 weeks? Work through the steps, then give the final answer on a new line starting with ANSWER.” The model will write: 8 × 150 = 1,200 a day; 1,200 × 5 = 6,000 a week; 6,000 × 4 = 24,000. Then: ANSWER: 24,000 rupees. Without the instruction, a model might jump straight to a number and make a mistake.

Two cautions. First, the steps cost tokens, so use this when the task needs it, not for simple questions. Second, a model’s written reasoning is useful but is not a perfect picture of what happens inside it, so still check the final answer. Some newer models have reasoning built in and think before they answer. For those, a short clear task often works better than long instructions about how to think. Read the guide for the model you use.

Technique 2: use examples well

You met examples in lesson 8. Here are three tips that matter more as tasks get harder.

  • Cover the variety. If your inputs can be short, long, messy or empty, show one example of each kind.
  • Show the hard case. Include an example where the right answer is “none found” or “not enough information”. Otherwise the model learns that it must always find something.
  • Keep the format identical. The model copies the pattern closely, including small details like capital letters and punctuation.

Technique 3: ask for a check

A model can often find mistakes in text more easily than it can avoid them while writing. So a useful pattern is to produce a draft, then ask for a review against a clear list: “Check the summary below against the resume. List any claim that is not in the resume. If there are none, write OK.” You can do this in the same prompt or, better, as a separate step. A separate step gives the reviewer a fresh view and a focused job.

A review step is not a guarantee. The reviewer can also be wrong, and it may agree with a bad draft. Use it to catch common problems, and still test with real examples and check what matters.

Technique 4: prompt chaining

A prompt chain splits one big task into several small prompts. The output of one prompt is placed into the next. For a resume helper, a chain might be:

  1. Extract. Read the resume and list the skills, tools and job titles.
  2. Match. Compare that list with the job post and list what is missing.
  3. Rewrite. Write three improved bullets that use the missing skills the person truly has.
  4. Check. Review the bullets for invented facts and for length.

Why is this better than one big prompt?

  • Each step is simple, so the model does it better and you can write a short, clear prompt for it.
  • You can test each step alone. If the final answer is wrong, you can look at the output of each step and find where it went wrong.
  • You can mix tools. One step can use a small cheap model and another a strong one. A step can also be ordinary code, such as checking a format.
  • You can stop early. If the extract step finds no skills, there is no need to run the others.
  • You can run steps in parallel when they do not depend on each other.

The price is more calls, so more cost and delay, and more places where an error can enter and be passed on. A small mistake in step 1 can ruin step 3. So check the output of important steps, and keep the data passed between them short and clean.

How to pass data between steps

In a chain, each step is a template: a prompt with placeholders such as {{input}} for the original text and {{prev}} for the previous output. Before a step runs, the program replaces each placeholder with real text. Because the placeholders can name earlier steps, for example {{skills}}, a later step can use any earlier output and not only the last one.

Keep two safety rules in mind. First, wrap pasted data in tags so the model sees it as data. Second, remember that the output of one step becomes the input of the next. If an earlier output could contain text from an untrusted source, the next step may follow instructions hidden inside it. Lesson 11 returns to this.

Other useful patterns

  • Role and audience switch. Ask for the same content for different readers: a hiring manager, a new employee, a parent.
  • Critique and revise. Draft, critique with a rubric, revise once. More loops rarely help.
  • Decompose then combine. Ask the model first to list the parts of a problem, then solve each part, then combine.
  • Ask for options. For creative tasks, ask for five versions, then choose or ask for a ranking with reasons.
  • Ask for missing information. Tell the model: “If you need more information, ask me one question before answering.” This works well in chat.

Your task: build a prompt chain runner

You will build a page where each step of a chain is an editable template. As soon as you change the input or a template, the chain runs again and shows the prompt and the output of every step. Because there is no API key, the “model” is a tiny pretend model that understands four tasks. This lets you focus on the chain, which is the same with any real model.

What you will build

  • A Step type with a name and a template, and a fill function that replaces {{placeholders}}.
  • A runChain function that runs each step in order and records the prompt and the output.
  • A pretend model that reads the first line, TASK: something, and does a small, predictable version of that task: extract skills, summarise, critique, or rewrite.
  • A page with the input text, the list of steps, buttons to add and remove steps, and the prompt and output for each.

Steps

  1. Create lib/chain.ts. Define Step and StepResult. Define Model as a function from a prompt string to a string.
  2. Write fill(template, vars): use a regular expression to find {{name}} and replace it with the matching value. If a name is unknown, leave the placeholder in place so that mistakes are visible.
  3. Write runChain(steps, input, model): keep a vars object with input and prev. For each step, fill the template, call the model, save the result, and update vars so that the output is available under the step’s name and as prev.
  4. Write the pretend model with a switch on the task. For example, extract_skills looks for known skill words, and critique says OK or reports a problem if the text is long.
  5. Build the page. Use computed signals so that the results update when anything changes.

Hints

  • Try renaming step 1 and watch the placeholder in step 3 stop working. The page should show the unfilled placeholder, which is a useful hint.
  • Add a step that rewrites {{prev}} and place it before the critique.
  • Replace the pretend model with a function that calls a real model, to see that the chain code does not change.
  • Notice how long each prompt is. Short prompts are a sign that the chain is well designed.

You know it works when changing the input updates every step, a step that uses {{summary}} receives the output of the step named summary, and removing a step does not crash the page.

Solution

The chain logic and the pretend model:

// A prompt chain: the output of one step becomes the input of the next.
// Each step is a small, focused prompt. That is easier to write, test and fix than one giant prompt.

export type Step = { name: string; template: string };
export type StepResult = { name: string; prompt: string; output: string };

// A "model" is just a function from a prompt to text. Here is a fake one that works offline,
// so you can study the chain itself. To use a real model, replace it with a function that calls your server.
export type Model = (prompt: string) => string;

const SKILLS = ['zendesk', 'excel', 'tally', 'welding', 'forklift', 'sql', 'python', 'billing', 'driving', 'customer service', 'inventory', 'safety training'];

// The fake model reads the first line ("TASK: ...") and does a tiny, predictable version of that task.
export const mockModel: Model = (prompt) => {
  const task = /^TASK:\s*(\w+)/m.exec(prompt)?.[1] ?? '';
  const body = prompt.split('\n').slice(1).join('\n').trim();
  switch (task) {
    case 'extract_skills': {
      const found = SKILLS.filter((s) => body.toLowerCase().includes(s));
      return found.length ? found.join(', ') : 'none found';
    }
    case 'summarise':
      return (body.split(/(?<=[.!?])\s+/)[0] ?? '').slice(0, 140);
    case 'critique':
      return body.split(/\s+/).length > 20 ? 'PROBLEM: too long, keep it under 20 words.' : 'OK';
    case 'rewrite':
      return body.replace(/^Responsible for/i, 'Managed').replace(/\s+/g, ' ');
    default:
      return `(mock model: unknown task "${task}")`;
  }
};

// Replace {{input}} (the original text), {{prev}} (the previous output) and {{stepName}} (any earlier output).
export function fill(template: string, vars: Record<string, string>): string {
  return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => vars[key] ?? `{{${key}}}`);
}

export function runChain(steps: Step[], input: string, model: Model): StepResult[] {
  const vars: Record<string, string> = { input, prev: input };
  const results: StepResult[] = [];
  for (const step of steps) {
    const prompt = fill(step.template, vars);
    const output = model(prompt);
    results.push({ name: step.name, prompt, output });
    vars[step.name] = output;
    vars['prev'] = output;
  }
  return results;
}

And the page:

import { Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { mockModel, runChain, type Step } from '../lib/chain';

@Component({
  selector: 'app-chain',
  imports: [FormsModule],
  template: `
    <h1>Prompt chains</h1>
    <p class="hint">Each step has its own small prompt. The output of one step flows into the next as {{ '{{prev}}' }}. The "model" here is a tiny offline fake so you can study how the chain works; swap in a real model call later.</p>

    <div class="card">
      <label for="i">Input text</label>
      <textarea id="i" [ngModel]="input()" (ngModelChange)="input.set($event)" style="min-height:80px"></textarea>
    </div>

    @for (s of steps(); track $index) {
      <div class="card">
        <label [attr.for]="'s' + $index">Step {{ $index + 1 }}: name</label>
        <input [id]="'s' + $index" [ngModel]="s.name" (ngModelChange)="update($index, 'name', $event)" />
        <label [attr.for]="'t' + $index">Prompt template</label>
        <textarea [id]="'t' + $index" [ngModel]="s.template" (ngModelChange)="update($index, 'template', $event)" style="min-height:70px"></textarea>
        <button class="danger" (click)="remove($index)">Remove step</button>
        @if (results()[$index]; as r) {
          <p class="muted">Prompt sent:</p><pre class="out">{{ r.prompt }}</pre>
          <p><strong>Output:</strong> {{ r.output }}</p>
        }
      </div>
    }
    <button class="secondary" (click)="add()">+ add step</button>
  `,
})
export class ChainPage {
  input = signal('Responsible for stock and loading at the warehouse. Used a forklift every day and trained new staff in safety training. The team handled 300 orders a day.');
  steps = signal<Step[]>([
    { name: 'skills', template: 'TASK: extract_skills\n{{input}}' },
    { name: 'summary', template: 'TASK: summarise\n{{input}}' },
    { name: 'check', template: 'TASK: critique\n{{summary}}' },
  ]);

  results = computed(() => runChain(this.steps(), this.input(), mockModel));

  update(i: number, key: keyof Step, value: string) {
    this.steps.update((list) => list.map((s, j) => (j === i ? { ...s, [key]: value } : s)));
  }

  add() {
    this.steps.update((list) => [...list, { name: `step${list.length + 1}`, template: 'TASK: rewrite\n{{prev}}' }]);
  }

  remove(i: number) {
    this.steps.update((list) => list.filter((_, j) => j !== i));
  }
}

What you learned

  • Asking for steps helps with multi-step problems, but costs tokens and is not a perfect view of the model’s thinking.
  • Good examples cover variety, include the hard case and keep the format identical.
  • A separate review step can catch common problems, but it is not a guarantee.
  • A prompt chain splits a big task into small, testable steps, at the price of more calls and more places for errors.

In the next lesson you will learn how to make a model’s answer safe for a program to use: asking for JSON, checking it against a schema, and asking the model to repair its own mistakes.

Download the solution (genai-basics-lesson-09.zip)

Frequently asked questions

What is chain-of-thought prompting?

It means asking the model to write out its reasoning steps before the final answer. It often improves results on tasks with several steps, such as maths or planning, because the steps become part of the text the model continues from.

When should I use a prompt chain instead of one prompt?

Use a chain when the task has clear stages, when one prompt becomes long or unreliable, or when you want to test and fix each stage on its own. For simple tasks one good prompt is cheaper and faster.

Do chains make answers always more accurate?

Not always. They often make results easier to control and debug, but each extra step adds cost and a chance for an error to pass on. Test the whole chain on real examples.

Can one step in a chain be normal code?

Yes, and it is a good idea. Steps such as checking a format, counting words or looking up a database record are better done in code than by a model.