Build a Prompt Template for Blog Outlines

La Lakshmi Priya Updated 07 Nov 2026
8 min read ·Lesson 132 of 140

Project Goal Build a reusable prompt template that turns a topic and a few editorial settings into a structured blog post outline, then validates the outline and renders it as Markdown, ready for a writer or a drafting step. The template must:

Accept variables: topic, audience, reader's level, target length, keywords, number of sections, and sections to avoid (Lesson 38) Ask for a progressive structure, from basic to advanced (the same principle this tutorial follows) Return JSON so the outline can be checked in code (Lesson 99) Validate the outline against the settings: section count, duplicate headings, excluded sections, key points per section, and keyword coverage Render the validated outline as Markdown Outlines are a good place to apply prompt engineering in a content workflow: decisions about structure are made and reviewed before any full text is written, when changes are cheap.

Requirements Requirement Details Python 3.10 or later Packages pip install openai API key OPENAI_API_KEY environment variable Reuse llm.py from Project 128 Project Structure blog_outliner/ ├── llm.py # call_model(instructions, prompt) ├── outline.py # template, validation, Markdown rendering └── main.py # generate, validate, retry, save outline.md Step 1: The Template outline.py (first part)

import json from string import Template

OUTLINE_TEMPLATE = Template("""Create a blog post outline.

Topic: $topic Audience: $audience Reader's level: $level Target length of the final post: about $word_count words Keywords to cover naturally: $keywords Number of main sections: $min_sections to $max_sections Do not include sections named: $excluded_sections

For each section, give a clear heading and 2 to 4 key points the section must explain. Order the sections from basic to advanced, so each section builds on the previous one.

Return only JSON: {"title": "...", "sections": [{"heading": "...", "key_points": ["...", "..."]}]}""")

def build_outline_prompt(options) -> str: options = { options, "keywords": ", ".join(options["keywords"]), "excluded_sections": ", ".join(options["excluded_sections"]) or "none", } return OUTLINE_TEMPLATE.substitute(options) Design Decisions string.Template Uses $name placeholders, which do not conflict with the JSON braces in the template. With str.format(), every { and } in the JSON example would need to be doubled (Lesson 38). substitute() raises an error if any variable is missing.

Variables that shape quality

Variable Why It Matters audience and level Decide which concepts need explaining and which can be assumed (Lesson 89) word_count Helps the model choose a realistic number of sections and depth keywords Topics the post must cover, written naturally rather than stuffed min_sections, max_sections Keep outlines within a predictable size excluded_sections Encodes editorial rules, such as avoiding generic sections a publication does not use Key points per section Asking for 2 to 4 key points turns each heading into a concrete plan of what to explain, rather than a vague title.

Basic to advanced Progressive ordering makes the post easier to learn from, and matches how readers build understanding.

Step 2: Validating the Outline def validate_outline(outline: dict, options: dict) -> list[str]: problems = [] sections = outline.get("sections", []) if not outline.get("title"): problems.append("missing title") if not options["min_sections"] <= len(sections) <= options["max_sections"]: problems.append(f"{len(sections)} sections; expected {options['min_sections']}-{options['max_sections']}") headings = [section.get("heading", "").strip() for section in sections] if len({heading.lower() for heading in headings}) != len(headings): problems.append("duplicate headings") excluded = {name.lower() for name in options["excluded_sections"]} for heading in headings: if any(name in heading.lower() for name in excluded): problems.append(f"excluded section present: {heading!r}") for section in sections: if not 2 <= len(section.get("key_points", [])) <= 4: problems.append(f"section {section.get('heading')!r} needs 2-4 key points") text = json.dumps(outline).lower() missing = [keyword for keyword in options["keywords"] if keyword.lower() not in text] if missing: problems.append(f"keywords not covered: {missing}") return problems Code Explanation Section count must fall within the configured range. Duplicate headings are detected case-insensitively. Excluded sections are detected if a heading contains an excluded name, so "Introduction to Relationships" counts as an introduction section. Key points must number between 2 and 4 per section. Keyword coverage checks the whole outline, converted to lowercase text, for each keyword. These checks turn editorial requirements into rules that are applied the same way every time, regardless of who reviews the outline.

Step 3: Rendering as Markdown def to_markdown(outline: dict) -> str: lines = [f"# {outline['title']}", ""] for section in outline["sections"]: lines.append(f"## {section['heading']}") lines.extend(f"- {point}" for point in section["key_points"]) lines.append("") return "\n".join(lines).rstrip() + "\n" The title becomes a level-1 heading, each section a level-2 heading, and each key point a bullet. The result can be pasted into a document, a content management system, or the next prompt in a drafting chain (Lesson 61).

Step 4: Testing Without the API options = { "topic": "Django model relationships", "audience": "beginner Python developers", "level": "knows Python and basic Django views", "word_count": 1800, "keywords": ["ForeignKey", "ManyToManyField", "related_name"], "min_sections": 4, "max_sections": 6, "excluded_sections": ["Introduction", "Conclusion"], }

prompt = build_outline_prompt(**options) print(prompt.splitlines()[5], "|", prompt.splitlines()[7])

outline = { "title": "Django Model Relationships Explained", "sections": [ {"heading": "Introduction to Relationships", "key_points": ["Why models relate"]}, {"heading": "One-to-Many with ForeignKey", "key_points": ["Defining a ForeignKey", "on_delete options"]}, {"heading": "Many-to-Many Relationships", "key_points": ["ManyToManyField basics", "Through models"]}, {"heading": "Querying Related Objects", "key_points": ["Reverse lookups", "select_related and prefetch_related"]}, ], } print(validate_outline(outline, options))

fixed = {**outline, "sections": [ {"heading": "Why Models Need Relationships", "key_points": ["Real data is connected", "Avoiding duplicate data"]}, *outline["sections"][1:3], {"heading": "Querying Related Objects", "key_points": ["Reverse lookups with related_name", "select_related and prefetch_related"]}, ]} print(validate_outline(fixed, options)) print(to_markdown(fixed)) Output:

Target length of the final post: about 1800 words | Number of main sections: 4 to 6 ["excluded section present: 'Introduction to Relationships'", "section 'Introduction to Relationships' needs 2-4 key points", "keywords not covered: ['related_name']"] []

Django Model Relationships Explained

Why Models Need Relationships

  • Real data is connected
  • Avoiding duplicate data

One-to-Many with ForeignKey

  • Defining a ForeignKey
  • on_delete options

Many-to-Many Relationships

  • ManyToManyField basics
  • Through models
  • Reverse lookups with related_name
  • select_related and prefetch_related

What the Test Confirms Template filling works: the word count and section range appear in the prompt. The first outline fails three checks: an excluded "Introduction" section, a section with only one key point, and the missing keyword related_name. The corrected outline passes and renders as clean Markdown, with the first section now explaining why relationships are needed, which is real content rather than a generic introduction. Step 5: Generate, Validate, Retry main.py

import json

from llm import call_model from outline import build_outline_prompt, to_markdown, validate_outline

OPTIONS = { "topic": "Django model relationships", "audience": "beginner Python developers", "level": "knows Python and basic Django views", "word_count": 1800, "keywords": ["ForeignKey", "ManyToManyField", "related_name"], "min_sections": 4, "max_sections": 6, "excluded_sections": ["Introduction", "Conclusion"], }

prompt = build_outline_prompt(**OPTIONS) for attempt in range(1, 3): raw = call_model("You plan clear, practical technical tutorials.", prompt) try: outline = json.loads(raw) except json.JSONDecodeError: problems = ["output is not valid JSON"] else: problems = validate_outline(outline, OPTIONS) if not problems: with open("outline.md", "w", encoding="utf-8") as file: file.write(to_markdown(outline)) print("Saved outline.md") break prompt += "\n\nFix these problems in a new outline:\n" + "\n".join(f"- {p}" for p in problems) else: print("Outline needs manual editing:", problems) Code Explanation The outline is generated, parsed, and validated. If problems are found, they are appended to the prompt as feedback, and the model tries once more (Lesson 73). The for ... else construction runs the else block only if the loop ends without break, meaning no attempt passed validation. For reliable JSON, you can also use Structured Outputs with a schema (Lesson 99) and keep validate_outline() for the editorial rules that a schema cannot express.

Step 6: Using Outlines in a Content Workflow Stage Who or What Why Generate outline This template Fast, consistent structure Review outline Editor or author Cheap to change structure before writing Draft sections One prompt per section, using the section's key points (Lesson 61) Focused, controllable writing Verify facts and code Author, tests, documentation (Lesson 89) Technical accuracy Final edit Author Voice, originality, and quality Generating one section at a time from approved key points keeps each drafting prompt focused, makes long posts manageable, and keeps the author in control of structure and content.

Common Mistakes Mistake 1: Generating Full Posts Without an Outline What it is: Asking for a complete article in one prompt.

Why it happens: It is the fastest path to a draft.

What it causes: Structural problems are discovered only after everything is written.

How to fix it: Generate and approve an outline first.

Mistake 2: Editorial Rules Only in the Prompt What it is: Writing "avoid generic sections" in the prompt without checking.

Why it happens: The instruction seems clear.

What it causes: Outlines that break the rules occasionally reach writers.

How to fix it: Encode rules as settings and validate them in code.

Mistake 3: Keyword Stuffing What it is: Demanding that keywords appear many times.

Why it happens: Keywords are associated with search visibility.

What it causes: Unnatural content that serves readers poorly.

How to fix it: Ask for natural coverage, and check only that each keyword is covered.

Best Practices Make every editorial decision a variable. Audience, level, length, keywords, structure, and exclusions.

Use string.Template for prompts containing JSON. Avoid brace conflicts.

Ask for key points, not just headings. They make outlines actionable.

Validate outlines against the settings. Then render consistently.

Review structure before writing. Draft section by section from approved outlines.

Try It Yourself Exercise 1: Outlines for Your Topics Generate outlines for three tutorial topics you plan to write, with different audiences and lengths. Run validate_outline() on each, and record which rules the model broke most often.

Exercise 2: Add a Rule Add a setting required_sections, a list of headings that must appear, such as "Common Mistakes" for tutorials. Update the template and validate_outline(), and test it with an outline that omits one required section.

Exercise 3: Draft One Section Write a second template that takes one section from a validated outline, with its heading, key points, audience, and level, and drafts that section only. Generate one section for an outline from Exercise 1 and check that it covers every key point.

0 Comments

Reviewed before they appear

No comments yet.

Prompt Engineering
Prompt Engineering Introduction How Large Language Models Respond to Prompts Prompting Basics Elements of an Effective Prompt Writing Clear Instructions for AI Models Adding Context to a Prompt Providing Input Data in Prompts Using Output Indicators and Response Formats Prompt Formatting: Questions, Instructions, and Templates General Tips for Designing Better Prompts Common Prompt Engineering Mistakes Iterative Prompt Design and Refinement Understanding Instruction, Context, Input, and Output How Specificity and Conciseness Affect Prompt Quality Breaking Large Tasks into Smaller Prompting Tasks Zero-Shot Prompting Zero-Shot Prompting Examples for Everyday Tasks Zero-Shot Text Classification Zero-Shot Question Answering Zero-Shot Summarization Zero-Shot Translation Zero-Shot Prompting for Information Extraction When to Use Zero-Shot Prompting Limitations of Zero-Shot Prompting Improving Weak Zero-Shot Prompts Few-Shot Prompting One-Shot Prompting vs Few-Shot Prompting Choosing Good Examples for Few-Shot Prompts Formatting Examples in Few-Shot Prompts Few-Shot Classification with Examples Few-Shot Prompting for Structured Output Few-Shot Prompting Zero-Shot vs Few-Shot Prompting Role Prompting and Persona Instructions Instruction-Based Prompting Task-Oriented Prompt Templates Using Delimiters to Separate Prompt Sections Prompt Templates with Variables Chain-of-Thought Prompting Zero-Shot Chain-of-Thought Prompting Few-Shot Chain-of-Thought Prompting Automatic Chain-of-Thought (Auto-CoT) When Chain-of-Thought Prompting Helps Limitations of Chain-of-Thought Prompting Self-Consistency Prompting Comparing Chain-of-Thought and Self-Consistency Tree of Thoughts (ToT) Prompting Exploring Multiple Solutions with Tree of Thoughts Least-to-Most Prompting Complexity-Based Prompting Maieutic Prompting Self-Consistency: Sampling Multiple Reasoning Paths Self-Consistency for Arithmetic and Logic Tasks Tree of Thoughts: Search, Evaluation, and Backtracking Meta Prompting Generate Knowledge Prompting Directional Stimulus Prompting Program-Aided Language Models (PAL) React: Reasoning and Acting with Language Models Reflexion: Improving Agents Through Feedback Prompt Chaining Designing Multi-Step Prompt Chains Decomposing Complex Tasks into Prompt Chains Automatic Prompt Engineer (APE) Active-Prompt: Selecting Useful Reasoning Examples Optimizing Prompts Systematically Prompt Iteration and Versioning Building Reusable Prompt Libraries Testing Prompt Variants Prompt Chaining Fundamentals Passing Outputs Between Prompt Steps Prompt Chains for Summarization and Extraction Validating Intermediate Results in a Prompt Chain Debugging a Multi-Step Prompt Workflow Graph Prompting Multimodal Chain-of-Thought Prompting Automatic Reasoning and Tool-Use (ART) Prompting for Tool Selection and Use Combining Prompting Techniques for Complex Tasks Prompts for Text Summarization Prompts for Information Extraction Prompts for Text Classification Prompts for Question Answering Prompts for Sentiment Analysis Prompts for Rewriting and Editing Prompts for Translation and Localization Prompts for Brainstorming and Creative Writing Prompts for Generating Structured Data Prompts for Explaining Technical Concepts Prompts for Generating and Reviewing Code Prompting for Code Generation Prompting for Code Explanation Prompting for Debugging and Error Analysis Prompting for Unit Test Generation Prompting for SQL Query Generation Prompting for API Documentation Prompting for Technical Documentation Using Prompting to Extract Requirements Creating JSON-Style Structured Responses with Prompts Designing Prompts for Repeatable Developer Workflows Retrieval-Augmented Generation (RAG) How Prompt Engineering Supports RAG Writing Prompts That Use Retrieved Context Grounding Answers in Provided Documents Handling Missing Information in RAG Prompts Reducing Unsupported Claims in Context-Based Answers Prompt Engineering for AI Agents ReAct Prompting for Tool-Using Agents Writing Tool Instructions for AI Agents Prompting Agents to Plan Multi-Step Tasks Prompting Agents to Observe Tool Results Using Feedback and Reflection in Agent Workflows Prompting for Agent Error Recovery How to Evaluate Prompt Quality Creating Test Cases for Prompts Comparing Prompt Outputs with a Rubric Evaluating Accuracy, Relevance, and Completeness Regression Testing for Prompt Changes Building a Prompt Evaluation Dataset Common Causes of Inconsistent Prompt Outputs Prompt Injection: Introduction and Examples Recognizing Prompt Injection Attempts Prompt Leaking and Instruction Disclosure Risks Jailbreaking: Understanding Adversarial Prompts Writing Safer Prompts for Untrusted Inputs Reducing Hallucinations with Clear Instructions and Context Prompt Safety: Limitations and Best Practices Build a Zero-Shot Sentiment Classifier Project Goal Build a Few-Shot Text Classification Prompt Build a Prompt-Based Text Summarizer Project Goal Build a Structured Information Extraction Prompt Build a Prompt Template for Blog Outlines Build a Prompt-Based SQL Assistant Build a Code Explanation Prompt Workflow Build a Multi-Step Research and Summarization Workflow Build a Prompt Comparison and Evaluation Pipeline Build a Prompt-Chaining Workflow for Document Analysis Build a RAG Prompt for Question Answering over Documents Build a Tool-Using ReAct Assistant Create a Reusable Prompt Library for a Web Application
Ask about this post
AI Ask about this post

Ask questions about Build a Prompt Template for Blog Outlines and get answers drawn from it.

Signed-in readers only.