471 lines
11 KiB
Markdown
471 lines
11 KiB
Markdown
# Prompt Template Systems
|
|
|
|
## Template Architecture
|
|
|
|
### Basic Template Structure
|
|
```python
|
|
class PromptTemplate:
|
|
def __init__(self, template_string, variables=None):
|
|
self.template = template_string
|
|
self.variables = variables or []
|
|
|
|
def render(self, **kwargs):
|
|
missing = set(self.variables) - set(kwargs.keys())
|
|
if missing:
|
|
raise ValueError(f"Missing required variables: {missing}")
|
|
|
|
return self.template.format(**kwargs)
|
|
|
|
# Usage
|
|
template = PromptTemplate(
|
|
template_string="Translate {text} from {source_lang} to {target_lang}",
|
|
variables=['text', 'source_lang', 'target_lang']
|
|
)
|
|
|
|
prompt = template.render(
|
|
text="Hello world",
|
|
source_lang="English",
|
|
target_lang="Spanish"
|
|
)
|
|
```
|
|
|
|
### Conditional Templates
|
|
```python
|
|
class ConditionalTemplate(PromptTemplate):
|
|
def render(self, **kwargs):
|
|
# Process conditional blocks
|
|
result = self.template
|
|
|
|
# Handle if-blocks: {{#if variable}}content{{/if}}
|
|
import re
|
|
if_pattern = r'\{\{#if (\w+)\}\}(.*?)\{\{/if\}\}'
|
|
|
|
def replace_if(match):
|
|
var_name = match.group(1)
|
|
content = match.group(2)
|
|
return content if kwargs.get(var_name) else ''
|
|
|
|
result = re.sub(if_pattern, replace_if, result, flags=re.DOTALL)
|
|
|
|
# Handle for-loops: {{#each items}}{{this}}{{/each}}
|
|
each_pattern = r'\{\{#each (\w+)\}\}(.*?)\{\{/each\}\}'
|
|
|
|
def replace_each(match):
|
|
var_name = match.group(1)
|
|
content = match.group(2)
|
|
items = kwargs.get(var_name, [])
|
|
return '\\n'.join(content.replace('{{this}}', str(item)) for item in items)
|
|
|
|
result = re.sub(each_pattern, replace_each, result, flags=re.DOTALL)
|
|
|
|
# Finally, render remaining variables
|
|
return result.format(**kwargs)
|
|
|
|
# Usage
|
|
template = ConditionalTemplate("""
|
|
Analyze the following text:
|
|
{text}
|
|
|
|
{{#if include_sentiment}}
|
|
Provide sentiment analysis.
|
|
{{/if}}
|
|
|
|
{{#if include_entities}}
|
|
Extract named entities.
|
|
{{/if}}
|
|
|
|
{{#if examples}}
|
|
Reference examples:
|
|
{{#each examples}}
|
|
- {{this}}
|
|
{{/each}}
|
|
{{/if}}
|
|
""")
|
|
```
|
|
|
|
### Modular Template Composition
|
|
```python
|
|
class ModularTemplate:
|
|
def __init__(self):
|
|
self.components = {}
|
|
|
|
def register_component(self, name, template):
|
|
self.components[name] = template
|
|
|
|
def render(self, structure, **kwargs):
|
|
parts = []
|
|
for component_name in structure:
|
|
if component_name in self.components:
|
|
component = self.components[component_name]
|
|
parts.append(component.format(**kwargs))
|
|
|
|
return '\\n\\n'.join(parts)
|
|
|
|
# Usage
|
|
builder = ModularTemplate()
|
|
|
|
builder.register_component('system', "You are a {role}.")
|
|
builder.register_component('context', "Context: {context}")
|
|
builder.register_component('instruction', "Task: {task}")
|
|
builder.register_component('examples', "Examples:\\n{examples}")
|
|
builder.register_component('input', "Input: {input}")
|
|
builder.register_component('format', "Output format: {format}")
|
|
|
|
# Compose different templates for different scenarios
|
|
basic_prompt = builder.render(
|
|
['system', 'instruction', 'input'],
|
|
role='helpful assistant',
|
|
instruction='Summarize the text',
|
|
input='...'
|
|
)
|
|
|
|
advanced_prompt = builder.render(
|
|
['system', 'context', 'examples', 'instruction', 'input', 'format'],
|
|
role='expert analyst',
|
|
context='Financial analysis',
|
|
examples='...',
|
|
instruction='Analyze sentiment',
|
|
input='...',
|
|
format='JSON'
|
|
)
|
|
```
|
|
|
|
## Common Template Patterns
|
|
|
|
### Classification Template
|
|
```python
|
|
CLASSIFICATION_TEMPLATE = """
|
|
Classify the following {content_type} into one of these categories: {categories}
|
|
|
|
{{#if description}}
|
|
Category descriptions:
|
|
{description}
|
|
{{/if}}
|
|
|
|
{{#if examples}}
|
|
Examples:
|
|
{examples}
|
|
{{/if}}
|
|
|
|
{content_type}: {input}
|
|
|
|
Category:"""
|
|
```
|
|
|
|
### Extraction Template
|
|
```python
|
|
EXTRACTION_TEMPLATE = """
|
|
Extract structured information from the {content_type}.
|
|
|
|
Required fields:
|
|
{field_definitions}
|
|
|
|
{{#if examples}}
|
|
Example extraction:
|
|
{examples}
|
|
{{/if}}
|
|
|
|
{content_type}: {input}
|
|
|
|
Extracted information (JSON):"""
|
|
```
|
|
|
|
### Generation Template
|
|
```python
|
|
GENERATION_TEMPLATE = """
|
|
Generate {output_type} based on the following {input_type}.
|
|
|
|
Requirements:
|
|
{requirements}
|
|
|
|
{{#if style}}
|
|
Style: {style}
|
|
{{/if}}
|
|
|
|
{{#if constraints}}
|
|
Constraints:
|
|
{constraints}
|
|
{{/if}}
|
|
|
|
{{#if examples}}
|
|
Examples:
|
|
{examples}
|
|
{{/if}}
|
|
|
|
{input_type}: {input}
|
|
|
|
{output_type}:"""
|
|
```
|
|
|
|
### Transformation Template
|
|
```python
|
|
TRANSFORMATION_TEMPLATE = """
|
|
Transform the input {source_format} to {target_format}.
|
|
|
|
Transformation rules:
|
|
{rules}
|
|
|
|
{{#if examples}}
|
|
Example transformations:
|
|
{examples}
|
|
{{/if}}
|
|
|
|
Input {source_format}:
|
|
{input}
|
|
|
|
Output {target_format}:"""
|
|
```
|
|
|
|
## Advanced Features
|
|
|
|
### Template Inheritance
|
|
```python
|
|
class TemplateRegistry:
|
|
def __init__(self):
|
|
self.templates = {}
|
|
|
|
def register(self, name, template, parent=None):
|
|
if parent and parent in self.templates:
|
|
# Inherit from parent
|
|
base = self.templates[parent]
|
|
template = self.merge_templates(base, template)
|
|
|
|
self.templates[name] = template
|
|
|
|
def merge_templates(self, parent, child):
|
|
# Child overwrites parent sections
|
|
return {**parent, **child}
|
|
|
|
# Usage
|
|
registry = TemplateRegistry()
|
|
|
|
registry.register('base_analysis', {
|
|
'system': 'You are an expert analyst.',
|
|
'format': 'Provide analysis in structured format.'
|
|
})
|
|
|
|
registry.register('sentiment_analysis', {
|
|
'instruction': 'Analyze sentiment',
|
|
'format': 'Provide sentiment score from -1 to 1.'
|
|
}, parent='base_analysis')
|
|
```
|
|
|
|
### Variable Validation
|
|
```python
|
|
class ValidatedTemplate:
|
|
def __init__(self, template, schema):
|
|
self.template = template
|
|
self.schema = schema
|
|
|
|
def validate_vars(self, **kwargs):
|
|
for var_name, var_schema in self.schema.items():
|
|
if var_name in kwargs:
|
|
value = kwargs[var_name]
|
|
|
|
# Type validation
|
|
if 'type' in var_schema:
|
|
expected_type = var_schema['type']
|
|
if not isinstance(value, expected_type):
|
|
raise TypeError(f"{var_name} must be {expected_type}")
|
|
|
|
# Range validation
|
|
if 'min' in var_schema and value < var_schema['min']:
|
|
raise ValueError(f"{var_name} must be >= {var_schema['min']}")
|
|
|
|
if 'max' in var_schema and value > var_schema['max']:
|
|
raise ValueError(f"{var_name} must be <= {var_schema['max']}")
|
|
|
|
# Enum validation
|
|
if 'choices' in var_schema and value not in var_schema['choices']:
|
|
raise ValueError(f"{var_name} must be one of {var_schema['choices']}")
|
|
|
|
def render(self, **kwargs):
|
|
self.validate_vars(**kwargs)
|
|
return self.template.format(**kwargs)
|
|
|
|
# Usage
|
|
template = ValidatedTemplate(
|
|
template="Summarize in {length} words with {tone} tone",
|
|
schema={
|
|
'length': {'type': int, 'min': 10, 'max': 500},
|
|
'tone': {'type': str, 'choices': ['formal', 'casual', 'technical']}
|
|
}
|
|
)
|
|
```
|
|
|
|
### Template Caching
|
|
```python
|
|
class CachedTemplate:
|
|
def __init__(self, template):
|
|
self.template = template
|
|
self.cache = {}
|
|
|
|
def render(self, use_cache=True, **kwargs):
|
|
if use_cache:
|
|
cache_key = self.get_cache_key(kwargs)
|
|
if cache_key in self.cache:
|
|
return self.cache[cache_key]
|
|
|
|
result = self.template.format(**kwargs)
|
|
|
|
if use_cache:
|
|
self.cache[cache_key] = result
|
|
|
|
return result
|
|
|
|
def get_cache_key(self, kwargs):
|
|
return hash(frozenset(kwargs.items()))
|
|
|
|
def clear_cache(self):
|
|
self.cache = {}
|
|
```
|
|
|
|
## Multi-Turn Templates
|
|
|
|
### Conversation Template
|
|
```python
|
|
class ConversationTemplate:
|
|
def __init__(self, system_prompt):
|
|
self.system_prompt = system_prompt
|
|
self.history = []
|
|
|
|
def add_user_message(self, message):
|
|
self.history.append({'role': 'user', 'content': message})
|
|
|
|
def add_assistant_message(self, message):
|
|
self.history.append({'role': 'assistant', 'content': message})
|
|
|
|
def render_for_api(self):
|
|
messages = [{'role': 'system', 'content': self.system_prompt}]
|
|
messages.extend(self.history)
|
|
return messages
|
|
|
|
def render_as_text(self):
|
|
result = f"System: {self.system_prompt}\\n\\n"
|
|
for msg in self.history:
|
|
role = msg['role'].capitalize()
|
|
result += f"{role}: {msg['content']}\\n\\n"
|
|
return result
|
|
```
|
|
|
|
### State-Based Templates
|
|
```python
|
|
class StatefulTemplate:
|
|
def __init__(self):
|
|
self.state = {}
|
|
self.templates = {}
|
|
|
|
def set_state(self, **kwargs):
|
|
self.state.update(kwargs)
|
|
|
|
def register_state_template(self, state_name, template):
|
|
self.templates[state_name] = template
|
|
|
|
def render(self):
|
|
current_state = self.state.get('current_state', 'default')
|
|
template = self.templates.get(current_state)
|
|
|
|
if not template:
|
|
raise ValueError(f"No template for state: {current_state}")
|
|
|
|
return template.format(**self.state)
|
|
|
|
# Usage for multi-step workflows
|
|
workflow = StatefulTemplate()
|
|
|
|
workflow.register_state_template('init', """
|
|
Welcome! Let's {task}.
|
|
What is your {first_input}?
|
|
""")
|
|
|
|
workflow.register_state_template('processing', """
|
|
Thanks! Processing {first_input}.
|
|
Now, what is your {second_input}?
|
|
""")
|
|
|
|
workflow.register_state_template('complete', """
|
|
Great! Based on:
|
|
- {first_input}
|
|
- {second_input}
|
|
|
|
Here's the result: {result}
|
|
""")
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Keep It DRY**: Use templates to avoid repetition
|
|
2. **Validate Early**: Check variables before rendering
|
|
3. **Version Templates**: Track changes like code
|
|
4. **Test Variations**: Ensure templates work with diverse inputs
|
|
5. **Document Variables**: Clearly specify required/optional variables
|
|
6. **Use Type Hints**: Make variable types explicit
|
|
7. **Provide Defaults**: Set sensible default values where appropriate
|
|
8. **Cache Wisely**: Cache static templates, not dynamic ones
|
|
|
|
## Template Libraries
|
|
|
|
### Question Answering
|
|
```python
|
|
QA_TEMPLATES = {
|
|
'factual': """Answer the question based on the context.
|
|
|
|
Context: {context}
|
|
Question: {question}
|
|
Answer:""",
|
|
|
|
'multi_hop': """Answer the question by reasoning across multiple facts.
|
|
|
|
Facts: {facts}
|
|
Question: {question}
|
|
|
|
Reasoning:""",
|
|
|
|
'conversational': """Continue the conversation naturally.
|
|
|
|
Previous conversation:
|
|
{history}
|
|
|
|
User: {question}
|
|
Assistant:"""
|
|
}
|
|
```
|
|
|
|
### Content Generation
|
|
```python
|
|
GENERATION_TEMPLATES = {
|
|
'blog_post': """Write a blog post about {topic}.
|
|
|
|
Requirements:
|
|
- Length: {word_count} words
|
|
- Tone: {tone}
|
|
- Include: {key_points}
|
|
|
|
Blog post:""",
|
|
|
|
'product_description': """Write a product description for {product}.
|
|
|
|
Features: {features}
|
|
Benefits: {benefits}
|
|
Target audience: {audience}
|
|
|
|
Description:""",
|
|
|
|
'email': """Write a {type} email.
|
|
|
|
To: {recipient}
|
|
Context: {context}
|
|
Key points: {key_points}
|
|
|
|
Email:"""
|
|
}
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
- Pre-compile templates for repeated use
|
|
- Cache rendered templates when variables are static
|
|
- Minimize string concatenation in loops
|
|
- Use efficient string formatting (f-strings, .format())
|
|
- Profile template rendering for bottlenecks
|