git:20260503.72c1838 to git:20260507.a8ed8a2
3 added, 3 removed. Audit A to A.
# LLM Prompts Module
## Overview
The `infrastructure/llm/prompts/` directory contains the prompt engineering system for the Large Language Model infrastructure. This module provides structured, reusable prompt templates, composition logic, and fragment management to ensure consistent, high-quality LLM interactions across the research template system.
## Directory Structure
```mermaid
flowchart TB
PR[/infrastructure/llm/prompts//]
PR --> META[AGENTS.md · __init__.py]
PR --> COMP[composer.py<br/>Prompt composition & assembly]
PR --> LOAD[loader.py<br/>Prompt loading & validation]
- PR --> CMP_DIR[/compositions/<br/>Pre-built prompt compositions/]
- PR --> FRAG[/fragments/<br/>Reusable prompt components/]
- PR --> TPL[/templates/<br/>Prompt templates/]
+ PR --> CMP_DIR[/compositions<br/>Pre-built prompt compositions/]
+ PR --> FRAG[/fragments<br/>Reusable prompt components/]
+ PR --> TPL[/templates<br/>Prompt templates/]
CMP_DIR --> CMP_F[retry_prompts.json<br/>error recovery]
FRAG --> FRAG_F[content_requirements.json · format_requirements.json ·<br/>section_structures.json · system_prompts.json ·<br/>token_budget_awareness.json · validation_hints.json]
TPL --> TPL_F[manuscript_reviews.json · paper_summarization.json]
classDef d fill:#0f172a,stroke:#0f172a,color:#fff
classDef pkg fill:#1e3a8a,stroke:#0f172a,color:#fff
classDef code fill:#0f766e,stroke:#0f172a,color:#fff
class PR d
class CMP_DIR,FRAG,TPL pkg
class META,COMP,LOAD,CMP_F,FRAG_F,TPL_F code
```
## Key Components
### Prompt Composition (`composer.py`)
**Dynamic prompt assembly from modular components:**
#### Prompt Composer Engine
**Modular Prompt Assembly:**
```python
class PromptComposer:
"""Assembles prompts from modular components."""
def __init__(self, fragments_dir: Path = None):
self.fragments_dir = fragments_dir or Path(__file__).parent / "fragments"
self.templates_dir = fragments_dir.parent / "templates"
self._fragments_cache = {}
def compose_prompt(self, template_name: str, variables: Dict[str, Any],
options: CompositionOptions = None) -> str:
"""Compose a prompt from template and variables.
Process:
1. Load template structure
2. Substitute variables
3. Apply fragment insertions
4. Validate final prompt
5. Optimize for token usage
Args:
template_name: Name of template to use
variables: Variables to substitute in template
options: Composition options
Returns:
assembled prompt
"""
```
**Template Loading and Processing:**
```python
def _load_template(self, template_name: str) -> Dict[str, Any]:
"""Load template configuration from templates directory."""
template_path = self.templates_dir / f"{template_name}.json"
if not template_path.exists():
raise PromptError(f"Template not found: {template_name}")
with open(template_path, 'r', encoding='utf-8') as f:
template = json.load(f)
# Validate template structure
self._validate_template_structure(template)
return template
```
#### Fragment Integration
**Dynamic Fragment Insertion:**
```python
def _apply_fragments(self, prompt_parts: List[str], context: Dict[str, Any]) -> List[str]:
"""Apply fragment insertions based on context."""
enhanced_parts = []
for part in prompt_parts:
# Check for fragment placeholders
if "{{" in part and "}}" in part:
# Extract fragment references
fragments = self._extract_fragment_references(part)
# Load and insert fragments
for fragment_name in fragments:
fragment_content = self._load_fragment(fragment_name)
part = part.replace(f"{{{{{fragment_name}}}}}", fragment_content)
enhanced_parts.append(part)
return enhanced_parts
```
#### Variable Substitution
**Advanced Variable Processing:**
```python
def _substitute_variables(self, template_parts: List[str],
variables: Dict[str, Any]) -> List[str]:
"""Substitute variables in template parts with advanced processing."""
processed_parts = []
for part in template_parts:
# Standard string substitution
processed = part.format(**variables)
# Advanced processing for special variable types
processed = self._process_special_variables(processed, variables)
# Content validation
processed = self._validate_content(processed, variables)
processed_parts.append(processed)
return processed_parts
```
### Prompt Loading (`loader.py`)
**Template and fragment loading with validation:**
#### Template Management
**Template Discovery and Loading:**
```python
class PromptLoader:
"""Loads and validates prompt templates and fragments."""
def __init__(self, prompts_dir: Path = None):
self.prompts_dir = prompts_dir or Path(__file__).parent
self._template_cache = {}
self._fragment_cache = {}
def load_template(self, name: str, validate: bool = True) -> Dict[str, Any]:
"""Load template with optional validation."""
if name in self._template_cache:
return self._template_cache[name]
template_path = self.prompts_dir / "templates" / f"{name}.json"
if not template_path.exists():
raise FileNotFoundError(f"Template not found: {name}")
with open(template_path, 'r', encoding='utf-8') as f:
template = json.load(f)
if validate:
self.validate_template(template)
self._template_cache[name] = template
return template
def load_fragment(self, name: str) -> str:
"""Load reusable prompt fragment."""
if name in self._fragment_cache:
return self._fragment_cache[name]
fragment_path = self.prompts_dir / "fragments" / f"{name}.json"
if not fragment_path.exists():
raise FileNotFoundError(f"Fragment not found: {name}")
with open(fragment_path, 'r', encoding='utf-8') as f:
fragment_data = json.load(f)
# Extract content (fragments are simple content dictionaries)
content = fragment_data.get('content', '')
if not content:
raise ValueError(f"Invalid fragment format: {name}")
self._fragment_cache[name] = content
return content
```
#### Template Validation
**Template Validation:**
```python
def validate_template(self, template: Dict[str, Any]) -> None:
"""Validate template structure and content."""
required_fields = ['name', 'description', 'parts', 'variables']
for field in required_fields:
if field not in template:
raise ValidationError(f"Missing required field: {field}")
# Validate parts structure
if not isinstance(template['parts'], list):
raise ValidationError("Template parts must be a list")
for i, part in enumerate(template['parts']):
if not isinstance(part, dict):
raise ValidationError(f"Part {i} must be a dictionary")
if 'type' not in part:
raise ValidationError(f"Part {i} missing 'type' field")
if part['type'] not in ['instruction', 'context', 'example', 'fragment']:
raise ValidationError(f"Invalid part type: {part['type']}")
# Validate variables
self._validate_variables(template.get('variables', {}))
```
### Fragment Components
#### Content Requirements (`fragments/content_requirements.json`)
**Quality and completeness requirements:**
```json
{
"content": "Ensure your response demonstrates:\n- Deep understanding of the subject matter\n- Clear and logical reasoning\n- Evidence-based conclusions\n- Appropriate level of technical detail\n- coverage of key aspects\n- Balanced perspective on limitations and assumptions"
}
```
#### Format Requirements (`fragments/format_requirements.json`)
**Output structure and formatting standards:**
```json
{
"content": "Format your response using:\n- Clear section headers and subheaders\n- Bullet points for lists and key items\n- Numbered steps for procedures\n- Code blocks for technical content\n- Tables for comparative data\n- Consistent terminology and notation"
}
```
#### System Prompts (`fragments/system_prompts.json`)
**Role definition and behavioral guidelines:**
```json
{
"content": "You are an expert research assistant specializing in academic writing and scientific analysis. Your responses should be:\n- Accurate and evidence-based\n- Clear and accessible to educated readers\n- Methodologically sound\n- Ethically responsible\n- Critically thoughtful"
}
```
## Integration with LLM System
### Template-Based Review Generation
**Review Template Usage:**
```python
from infrastructure.llm.prompts import PromptComposer
# Initialize composer
composer = PromptComposer()
# Compose manuscript review prompt
variables = {
'manuscript_content': manuscript_text,
'word_count': len(manuscript_text.split()),
'review_type': 'comprehensive'
}
review_prompt = composer.compose_prompt('manuscript_reviews', variables)
# Use with LLM client
from infrastructure.llm.core import LLMClient
client = LLMClient()
response = client.query_structured(review_prompt, schema=review_schema)
```
### Summarization Template Usage
**Paper Summarization:**
```python
# Compose paper summarization prompt
summarization_vars = {
'paper_content': paper_text,
'max_summary_length': 500,
'focus_areas': ['methodology', 'findings', 'implications']
}
summary_prompt = composer.compose_prompt('paper_summarization', summarization_vars)
# Generate summary
summary = client.query(summary_prompt, options=GenerationOptions(max_tokens=600))
```
## Fragment System Architecture
### Fragment Types
**System-Level Fragments:**
- **system_prompts**: Define AI role and behavioral guidelines
- **content_requirements**: Quality and completeness standards
- **format_requirements**: Output structure specifications
**Task-Specific Fragments:**
- **section_structures**: Document organization templates
- **validation_hints**: Input validation guidance
- **token_budget_awareness**: Token usage optimization
### Fragment Composition Rules
**Fragment Assembly Logic:**
```python
def _compose_fragments(self, fragment_names: List[str]) -> str:
"""Compose multiple fragments with proper separation."""
composed = []
for name in fragment_names:
fragment = self._load_fragment(name)
# Add separator if not first fragment
if composed:
composed.append("\n\n")
composed.append(fragment)
return "".join(composed)
```
## Template Structure
### Template Schema
**Standard Template Format:**
```json
{
"name": "manuscript_reviews",
"description": "manuscript review template",
"version": "1.0",
"variables": {
"manuscript_content": {
"type": "string",
"required": true,
"description": "Full manuscript text to review"
},
"review_type": {
"type": "string",
"required": false,
"default": "comprehensive",
"enum": ["comprehensive", "technical", "editorial"]
}
},
"parts": [
{
"type": "instruction",
"content": "You are conducting a {{review_type}} review of an academic manuscript."
},
{
"type": "fragment",
"content": "{{system_prompts}}"
},
{
"type": "context",
"content": "Manuscript content:\n{{manuscript_content}}"
},
{
"type": "instruction",
"content": "Provide detailed feedback following these requirements:\n{{content_requirements}}"
},
{
"type": "fragment",
"content": "{{format_requirements}}"
}
]
}
```
## Testing
### Template Validation Testing
**Template Structure Tests:**
```python
def test_template_validation():
"""Test template loading and validation."""
loader = PromptLoader()
# Test valid template
template = loader.load_template('manuscript_reviews')
assert template['name'] == 'manuscript_reviews'
assert 'variables' in template
assert 'parts' in template
# Test invalid template
with pytest.raises(ValidationError):
loader.load_template('invalid_template')
def test_fragment_loading():
"""Test fragment loading functionality."""
loader = PromptLoader()
# Test existing fragment
content = loader.load_fragment('system_prompts')
assert isinstance(content, str)
assert len(content) > 0
# Test non-existent fragment
with pytest.raises(FileNotFoundError):
loader.load_fragment('nonexistent_fragment')
```
### Composition Testing
**Prompt Assembly Tests:**
```python
def test_prompt_composition():
"""Test prompt composition workflow."""
composer = PromptComposer()
variables = {
'manuscript_content': 'Sample manuscript text...',
'review_type': 'technical'
}
# Compose prompt
prompt = composer.compose_prompt('manuscript_reviews', variables)
# Verify composition
assert isinstance(prompt, str)
assert len(prompt) > 0
assert 'technical review' in prompt.lower()
assert 'Sample manuscript text' in prompt
# Verify fragment insertion
assert 'expert research assistant' in prompt.lower()
```
**Variable Substitution Tests:**
```python
def test_variable_substitution():
"""Test variable substitution in templates."""
composer = PromptComposer()
# Test simple substitution
template_parts = ["Review type: {review_type}", "Content: {content}"]
variables = {'review_type': 'comprehensive', 'content': 'test content'}
result = composer._substitute_variables(template_parts, variables)
assert result[0] == "Review type:"
assert result[1] == "Content: test content"
```
## Performance Considerations
### Template Caching
**Efficient Template Loading:**
```python
def _get_cached_template(self, name: str) -> Dict[str, Any]:
"""Get template from cache or load from disk."""
if name not in self._template_cache:
self._template_cache[name] = self._load_template_from_disk(name)
return self._template_cache[name]
```
**Fragment Caching:**
```python
def _get_cached_fragment(self, name: str) -> str:
"""Get fragment from cache or load from disk."""
if name not in self._fragment_cache:
self._fragment_cache[name] = self._load_fragment_from_disk(name)
return self._fragment_cache[name]
```
### Token Optimization
**Prompt Size Management:**
```python
def _optimize_prompt_size(self, prompt: str, max_tokens: int = None) -> str:
"""Optimize prompt size for token limits."""
if not max_tokens:
return prompt
# Estimate token count
estimated_tokens = self._estimate_token_count(prompt)
if estimated_tokens <= max_tokens:
return prompt
# Truncate with intelligent chunking
return self._truncate_with_context(prompt, max_tokens)
```
## Error Handling
### Template Validation Errors
**Validation:**
```python
def _validate_template_structure(self, template: Dict[str, Any]) -> None:
"""Validate template structure."""
# Required fields
required = ['name', 'description', 'parts', 'variables']
missing = [field for field in required if field not in template]
if missing:
raise TemplateValidationError(f"Missing required fields: {missing}")
# Parts validation
if not isinstance(template['parts'], list):
raise TemplateValidationError("Parts must be a list")
for i, part in enumerate(template['parts']):
self._validate_part(part, i)
# Variables validation
if not isinstance(template.get('variables', {}), dict):
raise TemplateValidationError("Variables must be a dictionary")
```
### Fragment Loading Errors
**Robust Fragment Handling:**
```python
def _load_fragment_safely(self, name: str) -> str:
"""Load fragment with error handling."""
try:
return self._load_fragment_from_disk(name)
except FileNotFoundError:
logger.warning(f"Fragment not found: {name}")
return f"[Fragment '{name}' not found]"
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON in fragment {name}: {e}")
return f"[Invalid fragment '{name}']"
except Exception as e:
logger.error(f"Error loading fragment {name}: {e}")
return f"[Error loading fragment '{name}']"
```
## Usage Examples
### Basic Template Usage
**Simple Prompt Composition:**
```python
from infrastructure.llm.prompts import PromptComposer
composer = PromptComposer()
# Basic review prompt
variables = {
'manuscript_content': manuscript,
'review_focus': 'methodology'
}
prompt = composer.compose_prompt('manuscript_reviews', variables)
```
### Advanced Composition
**Custom Fragment Integration:**
```python
# Load specific fragments
composer = PromptComposer()
# Custom composition with specific fragments
variables = {
'task_description': 'Analyze this research paper',
'content': paper_text,
'fragments': ['system_prompts', 'content_requirements']
}
prompt = composer.compose_prompt('custom_analysis', variables)
```
### Template Development
**Creating New Templates:**
```python
# Template creation workflow
template_data = {
'name': 'custom_review',
'description': 'Custom manuscript review template',
'variables': {
'content': {'type': 'string', 'required': True},
'style': {'type': 'string', 'default': 'formal'}
},
'parts': [
{'type': 'fragment', 'content': '{{system_prompts}}'},
{'type': 'instruction', 'content': 'Review this content in {{style}} style'},
{'type': 'context', 'content': '{{content}}'}
]
}
# Save template
import json
with open('templates/custom_review.json', 'w') as f:
json.dump(template_data, f, indent=2)
```
## Configuration
### Directory Configuration
**Custom Prompt Directories:**
```python
# Use custom prompt directory
composer = PromptComposer(prompts_dir=Path('/custom/prompts'))
# Or set via environment
import os
os.environ['LLM_PROMPTS_DIR'] = '/custom/prompts'
```
### Template Search Paths
**Multiple Template Sources:**
```python
def _find_template_path(self, name: str) -> Path:
"""Find template in configured search paths."""
search_paths = [
self.templates_dir,
Path.home() / '.llm_templates',
Path('/usr/local/share/llm_templates')
]
for path in search_paths:
template_path = path / f"{name}.json"
if template_path.exists():
return template_path
raise FileNotFoundError(f"Template not found: {name}")
```
## Future Enhancements
### Advanced Features
**Planned Improvements:**
- **Template Inheritance**: Base templates with overrides
- **Dynamic Fragment Selection**: Context-aware fragment insertion
- **Template Versioning**: Version management and migration
- **Performance Profiling**: Template composition timing analysis
**Integration Features:**
- **Hot Reloading**: Runtime template updates without restart
- **Template Marketplace**: Shared template repository
- **Collaborative Editing**: Multi-user template development
- **A/B Testing**: Template performance comparison
## Troubleshooting
### Common Template Issues
**Template Not Found:**
```python
# Check template directory
prompts_dir = Path(__file__).parent
templates_dir = prompts_dir / "templates"
print(f"Templates directory: {templates_dir}")
print(f"Contents: {list(templates_dir.glob('*.json'))}")
# Verify template exists
template_path = templates_dir / "manuscript_reviews.json"
print(f"Template exists: {template_path.exists()}")
```
**Fragment Loading Errors:**
```python
# Check fragment structure
import json
fragment_path = prompts_dir / "fragments" / "system_prompts.json"
with open(fragment_path) as f:
fragment = json.load(f)
print(f"Fragment structure: {fragment.keys()}")
assert 'content' in fragment, "Fragment missing 'content' field"
```
**Variable Substitution Issues:**
```python
# Debug variable substitution
template_parts = ["Review: {content}", "Type: {review_type}"]
variables = {'content': 'test', 'review_type': 'full'}
try:
result = composer._substitute_variables(template_parts, variables)
print(f"Substitution successful: {result}")
except KeyError as e:
print(f"Missing variable: {e}")
except Exception as e:
print(f"Substitution error: {e}")
```
### Performance Debugging
**Composition Timing:**
```python
import time
start_time = time.time()
prompt = composer.compose_prompt('manuscript_reviews', variables)
end_time = time.time()
print(f"Composition time: {end_time - start_time:.3f} seconds")
print(f"Prompt length: {len(prompt)} characters")
```
## See Also
**Related Documentation:**
- [`../core/AGENTS.md`](../core/AGENTS.md) - LLM core functionality
- [`../templates/AGENTS.md`](../templates/AGENTS.md) - Template system
- [`../review/AGENTS.md`](../review/AGENTS.md) - Review generation
**System Documentation:**
- [`../../../AGENTS.md`](../../../AGENTS.md) - system overview
- [`../../../docs/development/contributing.md`](../../../docs/development/contributing.md) - Template development guide