DEV Community

Cover image for Meta-Prompting: Prompts That Write Prompts
Gantz AI for Gantz

Posted on

Meta-Prompting: Prompts That Write Prompts

I got tired of writing prompts.

So I wrote a prompt to write prompts for me.

It worked better than my hand-crafted ones.

Here's the technique.

What is meta-prompting?

Meta-prompting uses an LLM to generate, improve, or customize prompts.

Traditional:
Human writes prompt → LLM uses prompt → Output

Meta-prompting:
Human describes goal → LLM writes prompt → LLM uses prompt → Output
Enter fullscreen mode Exit fullscreen mode

You tell the AI what you want. It figures out how to ask itself.

Why bother?

Reason 1: LLMs know what works for LLMs

# My hand-written prompt
"Analyze this code for bugs"

# LLM-generated prompt
"Analyze the following code systematically:
1. Check for null/undefined access
2. Look for off-by-one errors
3. Identify potential race conditions
4. Find security vulnerabilities (injection, XSS)
5. Note any resource leaks

For each issue found, specify:
- Line number
- Issue type
- Severity (high/medium/low)
- Suggested fix"
Enter fullscreen mode Exit fullscreen mode

The LLM knows what structure helps it produce better output.

Reason 2: Personalization at scale

User 1 is a senior developer → Technical, terse prompts
User 2 is a beginner → Explanatory, step-by-step prompts
User 3 prefers Python → Python-focused examples
User 4 prefers TypeScript → TypeScript-focused examples
Enter fullscreen mode Exit fullscreen mode

One meta-prompt generates thousands of personalized prompts.

Reason 3: Dynamic adaptation

# Static prompt - same every time
"Review this code"

# Meta-generated - adapts to input
input_type = detect_type(code)  # Python, JavaScript, SQL...
complexity = estimate_complexity(code)  # Simple, moderate, complex
context = get_project_context()  # Web app, CLI, library...

prompt = generate_prompt(input_type, complexity, context)
# "Review this complex Python web application code, focusing on
#  Django-specific patterns, middleware security, and ORM efficiency..."
Enter fullscreen mode Exit fullscreen mode

The basic pattern

def meta_prompt(goal: str, context: str = "") -> str:
    """Generate a prompt for a specific goal"""

    meta = f"""Create an effective prompt for an AI assistant.

Goal: {goal}
{f'Context: {context}' if context else ''}

The prompt should:
- Be specific and actionable
- Include output format if helpful
- Guide the AI to be thorough
- Avoid ambiguity

Return ONLY the prompt, nothing else."""

    return llm.create(meta)

# Usage
goal = "Review Python code for security issues"
prompt = meta_prompt(goal)

# Generated:
# "Analyze the following Python code for security vulnerabilities:
#  1. SQL injection risks
#  2. Command injection
#  3. Path traversal
#  4. Insecure deserialization
#  5. Hardcoded secrets
#  6. Improper input validation
#
#  For each vulnerability:
#  - Cite the specific line(s)
#  - Explain the attack vector
#  - Provide a secure alternative
#
#  Code:
#  {code}"
Enter fullscreen mode Exit fullscreen mode

Use cases

1. Task-specific prompt generation

def generate_task_prompt(task_type: str, details: dict) -> str:
    return llm.create(f"""
Generate a prompt for: {task_type}

Details:
{json.dumps(details, indent=2)}

Create a prompt that will produce high-quality output for this specific task.
Return only the prompt.
""")

# Usage
prompt = generate_task_prompt("code_review", {
    "language": "TypeScript",
    "framework": "React",
    "focus_areas": ["performance", "accessibility"],
    "expertise_level": "senior"
})

# Result: A TypeScript/React specific review prompt focusing on
# performance and a11y, written for senior developer audience
Enter fullscreen mode Exit fullscreen mode

2. Prompt optimization

def optimize_prompt(original_prompt: str, failure_examples: list) -> str:
    """Improve a prompt based on where it failed"""

    failures = "\n".join([
        f"Input: {f['input']}\nExpected: {f['expected']}\nGot: {f['actual']}"
        for f in failure_examples
    ])

    return llm.create(f"""
This prompt is producing incorrect outputs:

CURRENT PROMPT:
{original_prompt}

FAILURE CASES:
{failures}

Analyze why the prompt fails in these cases and create an improved version.
Return only the improved prompt.
""")

# Usage
original = "Summarize this text"
failures = [
    {"input": "Technical paper", "expected": "Key findings", "got": "Introduction only"},
    {"input": "Long article", "expected": "3-5 sentences", "got": "10 paragraphs"}
]

better_prompt = optimize_prompt(original, failures)
# "Summarize the following text in 3-5 sentences. Focus on:
#  - Main thesis or key findings
#  - Supporting evidence
#  - Conclusions
#  Do not include background or introductory material..."
Enter fullscreen mode Exit fullscreen mode

3. Few-shot example generation

def generate_examples(task: str, num_examples: int = 3) -> list:
    """Generate few-shot examples for a task"""

    response = llm.create(f"""
Create {num_examples} diverse examples for this task: {task}

Each example should have:
- Input: realistic input
- Output: ideal output

Format as JSON array: [{{"input": "...", "output": "..."}}, ...]
""")

    return json.loads(response)

def build_few_shot_prompt(task: str, actual_input: str) -> str:
    examples = generate_examples(task)

    prompt = f"Task: {task}\n\n"
    for i, ex in enumerate(examples, 1):
        prompt += f"Example {i}:\nInput: {ex['input']}\nOutput: {ex['output']}\n\n"

    prompt += f"Now complete this:\nInput: {actual_input}\nOutput:"

    return prompt
Enter fullscreen mode Exit fullscreen mode

4. User preference learning

class AdaptivePromptGenerator:
    def __init__(self):
        self.user_preferences = {}

    def learn_from_feedback(self, user_id: str, prompt: str, output: str, feedback: str):
        """Learn what the user likes"""
        self.user_preferences[user_id] = self.user_preferences.get(user_id, [])
        self.user_preferences[user_id].append({
            "prompt": prompt,
            "output": output,
            "feedback": feedback
        })

    def generate_prompt(self, user_id: str, task: str) -> str:
        """Generate prompt tailored to user preferences"""
        prefs = self.user_preferences.get(user_id, [])

        if not prefs:
            return self.default_prompt(task)

        # Use past feedback to shape prompt
        pref_summary = llm.create(f"""
Analyze this user's feedback history and identify their preferences:
{json.dumps(prefs[-10:], indent=2)}

What style, format, and approach does this user prefer?
""")

        return llm.create(f"""
Create a prompt for: {task}

User preferences: {pref_summary}

Generate a prompt that matches their preferred style.
""")
Enter fullscreen mode Exit fullscreen mode

5. Tool description generation

def generate_tool_description(tool_name: str, code: str) -> str:
    """Generate optimal tool description for LLM consumption"""

    return llm.create(f"""
Analyze this tool and create a description optimized for AI assistants.

Tool name: {tool_name}
Implementation:
{code}

Create a description that:
- Clearly states what the tool does
- Specifies when to use it vs alternatives
- Mentions important parameters
- Notes any limitations

Keep it under 100 words. Be precise.
""")

# Usage
code = '''
def search_files(query: str, path: str = ".", file_type: str = None):
    """Search for text in files"""
    cmd = f"rg '{query}' {path}"
    if file_type:
        cmd += f" --type {file_type}"
    return subprocess.run(cmd, capture_output=True, text=True)
'''

description = generate_tool_description("search_files", code)
# "Search for text patterns in files using ripgrep. Use for finding code,
#  text, or patterns across a directory. Parameters: query (required),
#  path (default: current dir), file_type (e.g., 'py', 'js'). Fast for
#  large codebases. Use instead of read_file when you don't know which
#  file contains what you're looking for."
Enter fullscreen mode Exit fullscreen mode

Advanced: Self-improving prompts

class SelfImprovingPrompt:
    def __init__(self, initial_prompt: str, eval_fn):
        self.prompt = initial_prompt
        self.eval_fn = eval_fn  # Returns score 0-1
        self.history = []

    def run(self, input_data: str) -> str:
        output = llm.create(f"{self.prompt}\n\nInput: {input_data}")
        return output

    def improve(self, test_cases: list) -> bool:
        """Run test cases and improve if needed"""

        # Evaluate current prompt
        results = []
        for case in test_cases:
            output = self.run(case["input"])
            score = self.eval_fn(output, case["expected"])
            results.append({
                "input": case["input"],
                "expected": case["expected"],
                "output": output,
                "score": score
            })

        avg_score = sum(r["score"] for r in results) / len(results)

        if avg_score >= 0.9:
            return False  # Good enough

        # Find failures
        failures = [r for r in results if r["score"] < 0.8]

        # Generate improved prompt
        new_prompt = llm.create(f"""
Current prompt (avg score: {avg_score:.2f}):
{self.prompt}

Failed cases:
{json.dumps(failures, indent=2)}

Analyze the failures and create an improved prompt.
Return only the new prompt.
""")

        self.history.append({
            "prompt": self.prompt,
            "score": avg_score
        })
        self.prompt = new_prompt

        return True  # Improved

    def iterate(self, test_cases: list, max_iterations: int = 5):
        """Keep improving until good enough or max iterations"""
        for i in range(max_iterations):
            improved = self.improve(test_cases)
            if not improved:
                print(f"Converged after {i+1} iterations")
                break
        return self.prompt
Enter fullscreen mode Exit fullscreen mode

Meta-prompts for agents

Generate system prompts

def generate_agent_prompt(agent_type: str, tools: list, constraints: list) -> str:
    tool_list = "\n".join([f"- {t['name']}: {t['description']}" for t in tools])
    constraint_list = "\n".join([f"- {c}" for c in constraints])

    return llm.create(f"""
Create a system prompt for an AI agent.

Agent type: {agent_type}

Available tools:
{tool_list}

Constraints:
{constraint_list}

Generate a system prompt that:
- Clearly defines the agent's role
- Explains when to use each tool
- Enforces the constraints
- Guides effective tool usage patterns

Return only the system prompt.
""")

# Usage
tools = [
    {"name": "read", "description": "Read file contents"},
    {"name": "write", "description": "Write to file"},
    {"name": "run", "description": "Run shell command"},
]
constraints = [
    "Always read before modifying",
    "Confirm before deleting",
    "Stay within /workspace directory"
]

system_prompt = generate_agent_prompt("coding assistant", tools, constraints)
Enter fullscreen mode Exit fullscreen mode

Generate tool selection rules

def generate_tool_rules(tools: list, past_mistakes: list) -> str:
    """Generate rules for when to use which tool"""

    return llm.create(f"""
Given these tools and past mistakes, create clear rules for tool selection.

Tools:
{json.dumps(tools, indent=2)}

Past mistakes (wrong tool chosen):
{json.dumps(past_mistakes, indent=2)}

Create a decision guide:
- When to use each tool
- When NOT to use each tool
- Order of preference for common tasks

Be specific and actionable.
""")
Enter fullscreen mode Exit fullscreen mode

When meta-prompting helps

Scenario Benefit
Many similar tasks Generate specialized prompts automatically
User personalization Adapt to preferences at scale
Prompt optimization Improve based on failures
Dynamic contexts Adapt prompts to input characteristics
Few-shot generation Create examples automatically

When to avoid

Scenario Why
Simple, static tasks Overhead not worth it
Latency-critical Extra LLM call adds delay
Highly regulated Need deterministic, auditable prompts
Tight token budget Meta-prompting uses extra tokens

With Gantz

Gantz Run focuses on tools, but your client can use meta-prompting to generate tool descriptions:

# Generate descriptions for gantz tools
def generate_gantz_config(tools_code: dict) -> str:
    tools_yaml = []

    for name, code in tools_code.items():
        description = llm.create(f"""
Create a concise tool description for AI assistants.

Tool: {name}
Code: {code}

Return only the description (1-2 sentences).
""")
        tools_yaml.append(f"""  - name: {name}
    description: {description}
    script:
      shell: {code}""")

    return "tools:\n" + "\n".join(tools_yaml)

# Usage
tools = {
    "search": 'rg "{{query}}" . --max-count=20',
    "read": 'cat "{{path}}"',
    "run": '"{{command}}"'
}

config = generate_gantz_config(tools)
# Generates gantz.yaml with AI-optimized descriptions
Enter fullscreen mode Exit fullscreen mode

The meta-meta level

Yes, you can go deeper:

def generate_meta_prompt(goal: str) -> str:
    """Generate a prompt that generates prompts"""

    return llm.create(f"""
Create a meta-prompt (a prompt that generates other prompts).

The meta-prompt should help generate prompts for: {goal}

The generated meta-prompt should:
- Ask the right questions about the specific task
- Include guidelines for good prompt structure
- Produce consistent, high-quality prompts

Return only the meta-prompt.
""")
Enter fullscreen mode Exit fullscreen mode

But usually one level of meta is enough. Don't overthink it.

Summary

Meta-prompting patterns:

Pattern Use case
Task-specific generation Create prompts for different task types
Prompt optimization Improve based on failures
Few-shot generation Create examples automatically
User personalization Adapt to preferences
Self-improving Iterate until quality target met

When to use:

  • Many variations needed
  • Personalization at scale
  • Optimizing existing prompts
  • Dynamic adaptation required

When to skip:

  • Simple, static tasks
  • Latency matters
  • Need auditability

Let the AI write its own instructions. It often knows better.


Have you used meta-prompting? What worked best?

Top comments (0)