Unified Prompt Architecture¶
Overview¶
The Unified Prompt Architecture provides a single, coherent entry point for prompt construction across all execution paths in Victor. It bridges the legacy YAML workflow and StateGraph agent systems, enabling evolved content injection and constraint activation throughout the framework.
Motivation¶
Problem 1: Dual Prompt Builders (Disconnected)
- Legacy builder: victor/agent/prompt_builder.py (700+ lines, uses PromptDocument)
- Framework builder: victor/framework/prompt_builder.py (uses PromptBuilder)
- No integration between them
- Different section names (e.g., "tool_effectiveness_guidance" vs "ASI_TOOL_EFFECTIVENESS_GUIDANCE")
- Missing evolvable sections in framework builder
Problem 2: Constraint Activation Gap
- Legacy YAML workflow: ✅ Uses IsolationMapper.from_constraints() → activates WritePathPolicy
- StateGraph agent nodes: ❌ No constraint activation
- SubAgent.spawn(): ❌ No constraint activation
- Missing: Shared constraint activation utility
Problem 3: Evolved Content Not Consumed
- 5 EVOLVABLE_SECTIONS defined (ASI_TOOL_EFFECTIVENESS_GUIDANCE, GROUNDING_RULES, COMPLETION_GUIDANCE, FEW_SHOT_EXAMPLES, INIT_SYNTHESIS_RULES)
- RL system evolves these sections
- Framework builder doesn't check OptimizationInjector for evolved content
- No pathway for evolved candidates to flow into framework prompts
Solution Architecture¶
┌─────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Legacy YAML │ │ StateGraph │ │ SubAgent │ │
│ │ Workflows │ │ Agent Nodes │ │ spawn() │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼──────────────────┼──────────────────┼───────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Facade Layer (New) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ PromptOrchestrator (Unified Entry Point) │ │
│ │ - Coordinates builder selection │ │
│ │ - Manages evolved content injection │ │
│ │ - Handles constraint activation │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Service Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Constraint │ │ Evolved │ │ Prompt │ │
│ │ Activator │ │ Content │ │ Section │ │
│ │ Service │ │ Resolver │ │ Registry │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Foundation Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Legacy │ │ Framework │ │ Optimiz. │ │
│ │ Prompt │ │ Prompt │ │ Injector │ │
│ │ Builder │ │ Builder │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Isolation │ │ Content │ │
│ │ Mapper │ │ Registry │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Core Components¶
1. PromptOrchestrator (Facade)¶
Location: victor/agent/prompt_orchestrator.py
Purpose: Unified entry point for prompt construction across legacy and StateGraph paths.
Key Features:
- Auto-detects builder type (legacy vs framework) based on kwargs
- Injects evolved content from OptimizationInjector
- Activates constraints before prompt construction
- Singleton pattern for consistent state management
Usage Example:
from victor.agent.prompt_orchestrator import get_prompt_orchestrator
from victor.workflows.definition import FullAccessConstraints
# Get singleton instance
orchestrator = get_prompt_orchestrator()
# Build system prompt (auto-detects builder type)
prompt = orchestrator.build_system_prompt(
builder_type="auto", # "legacy", "framework", or "auto"
provider="anthropic",
model="claude-sonnet-4-6",
task_type="edit",
base_prompt="You are an assistant.", # For framework builder
prompt_contributors=[], # For legacy builder
)
# Activate constraints
constraints = FullAccessConstraints()
success = orchestrator.activate_constraints(constraints, "coding")
# ... use prompt ...
# Cleanup
orchestrator.deactivate_constraints()
Configuration:
from victor.agent.prompt_orchestrator import OrchestratorConfig
config = OrchestratorConfig(
use_evolved_content=True, # Enable evolved content injection
enable_constraint_activation=True, # Enable constraint activation
fallback_to_static=True, # Fall back to static content if evolved unavailable
cache_evolved_content=True, # Cache evolved content for performance
)
orchestrator = PromptOrchestrator(config=config)
2. EvolvedContentResolver¶
Location: victor/agent/evolved_content_resolver.py
Purpose: Bridges OptimizationInjector and prompt builders, providing unified interface for fetching evolved content with fallback to static defaults.
Key Features:
- Queries OptimizationInjector for evolved content
- Falls back to static content when evolved version unavailable
- Caches results for performance
- Supports batch resolution for multiple sections
Usage Example:
from victor.agent.evolved_content_resolver import EvolvedContentResolver
# Create resolver (typically done internally by PromptOrchestrator)
resolver = EvolvedContentResolver(optimization_injector=None)
# Resolve single section
result = resolver.resolve_section(
section_name="ASI_TOOL_EFFECTIVENESS_GUIDANCE",
provider="anthropic",
model="claude-sonnet-4-6",
task_type="edit",
fallback_text="Use tools judiciously.",
)
print(result.text) # Evolved or fallback content
print(result.source) # "evolved" or "static"
# Resolve multiple sections
results = resolver.resolve_multiple(
section_names=[
"ASI_TOOL_EFFECTIVENESS_GUIDANCE",
"GROUNDING_RULES",
"COMPLETION_GUIDANCE",
],
fallback_map={
"ASI_TOOL_EFFECTIVENESS_GUIDANCE": "Tool guidance",
"GROUNDING_RULES": "Grounding text",
"COMPLETION_GUIDANCE": "Completion text",
},
)
# Clear cache
resolver.clear_cache()
ResolvedContent Structure:
@dataclass(frozen=True)
class ResolvedContent:
section_name: str # Canonical section name
text: str # Resolved content text
source: str # "evolved", "static", or "custom"
metadata: Dict[str, Any] # Additional metadata
def is_evolved(self) -> bool:
return self.source == "evolved"
def is_static(self) -> bool:
return self.source == "static"
3. ConstraintActivationService¶
Location: victor/agent/constraint_activation_service.py
Purpose: Single service for activating constraints across all execution paths (legacy YAML, StateGraph, SubAgent).
Key Features:
- Singleton pattern for consistent constraint state
- Integrates with IsolationMapper.from_constraints()
- Activates WritePathPolicy based on constraints
- Automatic cleanup on deactivation
Usage Example:
from victor.agent.constraint_activation_service import get_constraint_activator
from victor.workflows.definition import FullAccessConstraints, ComputeOnlyConstraints
# Get singleton instance
activator = get_constraint_activator()
# Activate constraints
constraints = FullAccessConstraints()
result = activator.activate_constraints(constraints, "coding")
if result.success:
print(f"Policy: {result.write_path_policy}")
print(f"Isolation: {result.isolation_config}")
# Check active state
active_policy = activator.get_active_policy()
active_constraints = activator.get_active_constraints()
# Deactivate (cleanup)
activator.deactivate_constraints()
ActivationResult Structure:
@dataclass(frozen=True)
class ActivationResult:
success: bool # Whether activation succeeded
write_path_policy: Optional[WritePathPolicy] # Activated policy
isolation_config: Optional[dict] # Isolation configuration
error: Optional[str] # Error message if failed
4. UnifiedSectionRegistry¶
Location: victor/agent/prompt_section_registry.py
Purpose: Single source of truth for all prompt sections with consistent naming and alias support.
Key Features:
- Canonical uppercase names (e.g., "ASI_TOOL_EFFECTIVENESS_GUIDANCE")
- Alias support for backward compatibility (e.g., "tool_effectiveness_guidance")
- Section categories (grounding, tool_guidance, completion, etc.)
- Evolvable section tracking
Usage Example:
from victor.agent.prompt_section_registry import get_section_registry, SectionDefinition, SectionCategory
# Get singleton registry
registry = get_section_registry()
# Register new section
section = SectionDefinition(
name="CUSTOM_SECTION",
aliases={"custom_section", "custom"},
category=SectionCategory.TASK_HINTS,
default_text="Default text",
evolvable=True,
required=False,
priority=50,
)
registry.register(section)
# Get section by name or alias
section = registry.get("ASI_TOOL_EFFECTIVENESS_GUIDANCE")
section = registry.get("tool_effectiveness_guidance") # Alias works too
# Get all evolvable sections
evolvable = registry.get_evolvable_sections()
Integration Points¶
Legacy YAML Workflows¶
Location: victor/agent/prompt_builder.py (unchanged)
Integration: Legacy builder continues working as before. PromptOrchestrator detects legacy usage via prompt_contributors parameter.
# Legacy workflow (unchanged)
from victor.agent.prompt_builder import SystemPromptBuilder
builder = SystemPromptBuilder(
provider="anthropic",
model="claude-sonnet-4-6",
vertical="coding",
)
prompt = builder.build(prompt_contributors=[])
# NEW: Via PromptOrchestrator
from victor.agent.prompt_orchestrator import get_prompt_orchestrator
orchestrator = get_prompt_orchestrator()
prompt = orchestrator.build_system_prompt(
builder_type="legacy",
provider="anthropic",
model="claude-sonnet-4-6",
vertical="coding",
prompt_contributors=[],
)
StateGraph Agent Nodes¶
Location: victor/workflows/executors/agent.py (modified)
Integration: AgentNodeExecutor now activates constraints before spawning sub-agent.
# Before (no constraint activation)
async def execute(self, node: "AgentNode", state: "WorkflowState") -> "WorkflowState":
# ... execute agent ...
return state
# After (with constraint activation)
async def execute(self, node: "AgentNode", state: "WorkflowState") -> "WorkflowState":
from victor.agent.constraint_activation_service import get_constraint_activator
activator = get_constraint_activator()
result = activator.activate_constraints(
constraints=node.constraints,
vertical=getattr(node, "vertical", "coding"),
)
if not result.success:
logger.error(f"Constraint activation failed: {result.error}")
try:
# ... existing execution logic ...
return state
finally:
activator.deactivate_constraints()
SubAgent Spawning¶
Location: victor/agent/subagents/orchestrator.py (modified)
Integration: SubAgent.spawn() now accepts constraints parameter.
# Before (no constraint support)
result = await subagent.spawn(
role=SubAgentRole.RESEARCHER,
task="Investigate X",
)
# After (with constraint support)
from victor.workflows.definition import ComputeOnlyConstraints
constraints = ComputeOnlyConstraints()
result = await subagent.spawn(
role=SubAgentRole.RESEARCHER,
task="Investigate X",
constraints=constraints, # NEW parameter
)
Framework Prompt Builder¶
Location: victor/framework/prompt_builder.py (modified)
Integration: Added add_evolved_section() method for evolved content support.
from victor.framework.prompt_builder import PromptBuilder
builder = PromptBuilder()
builder.add_system_prompt("You are an assistant.")
builder.add_evolved_section(
section_name="ASI_TOOL_EFFECTIVENESS_GUIDANCE",
provider="anthropic",
model="claude-sonnet-4-6",
task_type="edit",
)
prompt = builder.build()
Migration Guide¶
For Legacy Code¶
Step 1: Identify Prompt Builder Usage
Find all places where SystemPromptBuilder or PromptBuilder is used directly.
# Find legacy builder usage
grep -r "SystemPromptBuilder" --include="*.py" .
# Find framework builder usage
grep -r "from victor.framework.prompt_builder import" --include="*.py" .
Step 2: Migrate to PromptOrchestrator
Replace direct builder usage with PromptOrchestrator:
# Before
from victor.agent.prompt_builder import SystemPromptBuilder
builder = SystemPromptBuilder(provider="anthropic", model="claude-sonnet-4-6")
prompt = builder.build(prompt_contributors=[])
# After
from victor.agent.prompt_orchestrator import get_prompt_orchestrator
orchestrator = get_prompt_orchestrator()
prompt = orchestrator.build_system_prompt(
builder_type="auto",
provider="anthropic",
model="claude-sonnet-4-6",
prompt_contributors=[],
)
Step 3: Add Constraint Activation
Add constraint activation where needed:
# Before
result = await agent.run(task)
# After
from victor.workflows.definition import FullAccessConstraints
orchestrator = get_prompt_orchestrator()
constraints = FullAccessConstraints()
orchestrator.activate_constraints(constraints, "coding")
try:
result = await agent.run(task)
finally:
orchestrator.deactivate_constraints()
For New Code¶
Recommended Pattern:
from victor.agent.prompt_orchestrator import get_prompt_orchestrator
from victor.workflows.definition import FullAccessConstraints
async def execute_with_constraints(task: str):
orchestrator = get_prompt_orchestrator()
# Build prompt with evolved content
prompt = orchestrator.build_system_prompt(
builder_type="auto",
provider="anthropic",
model="claude-sonnet-4-6",
task_type="edit",
)
# Activate constraints
constraints = FullAccessConstraints()
orchestrator.activate_constraints(constraints, "coding")
try:
# Execute task
result = await execute_task(task, prompt)
return result
finally:
# Always cleanup
orchestrator.deactivate_constraints()
Design Principles¶
1. Single Responsibility¶
- Each class has one clear purpose
- Resolver resolves, Activator activates, Orchestrator coordinates
2. Open/Closed¶
- Extensible via new strategies without modifying existing code
- New sections can be registered without changing core logic
3. Dependency Inversion¶
- Depend on abstractions (protocols), not concrete implementations
- Easy to mock for testing
4. Framework-First¶
- Reuse existing utilities (OptimizationInjector, IsolationMapper)
- Don't reinvent functionality that already exists
5. Backward Compatibility¶
- Legacy builder continues working
- Gradual migration path
- No breaking changes to existing code
6. Facade Pattern¶
- PromptOrchestrator provides unified API
- Doesn't reimplement builder logic
- Delegates to existing builders
Performance Considerations¶
Caching¶
Evolved content is cached by default to reduce redundant OptimizationInjector queries:
Cache can be cleared manually:
from victor.agent.evolved_content_resolver import EvolvedContentResolver
resolver = EvolvedContentResolver()
# ... use resolver ...
resolver.clear_cache() # Clear cache
Builder Detection¶
Auto-detection adds minimal overhead (<1ms):
# Auto-detection (recommended)
prompt = orchestrator.build_system_prompt(
builder_type="auto", # Detects based on kwargs
...
)
# Explicit (slightly faster if builder type is known)
prompt = orchestrator.build_system_prompt(
builder_type="legacy", # Skip detection
...
)
Constraint Activation¶
Constraint activation is lightweight (~5-10ms):
Testing¶
Unit Tests¶
tests/unit/agent/test_prompt_orchestrator.py(39 tests)tests/unit/agent/test_evolved_content_resolver.py(39 tests)tests/unit/agent/test_constraint_activation_service.py(39 tests)
Integration Tests¶
tests/integration/agent/test_unified_prompt_construction.py(10 tests)
Running Tests¶
# Run all unified prompt tests
pytest tests/unit/agent/test_prompt_orchestrator.py \
tests/unit/agent/test_evolved_content_resolver.py \
tests/unit/agent/test_constraint_activation_service.py \
tests/integration/agent/test_unified_prompt_construction.py \
-v
# Run backward compatibility tests
pytest tests/unit/agent/test_orchestrator_core.py \
tests/unit/agent/test_tool_graph_builder.py \
-v
Troubleshooting¶
Issue: Evolved Content Not Loading¶
Symptoms: ResolvedContent.source == "static" when expecting "evolved"
Solutions:
1. Check OptimizationInjector is initialized: get_rl_coordinator()
2. Verify section is in EVOLVABLE_SECTIONS
3. Check prompt optimization is enabled: settings.prompt_optimization.enabled
4. Verify provider/model/task_type match evolved content
Issue: Constraint Activation Fails¶
Symptoms: ActivationResult.success == False
Solutions:
1. Check constraints object is valid (FullAccessConstraints, ComputeOnlyConstraints)
2. Verify vertical is correct ("coding", "dataanalysis", etc.)
3. Check IsolationMapper logs for errors
4. Verify WritePathPolicy is not already active
Issue: Builder Detection Wrong¶
Symptoms: Wrong builder type selected with builder_type="auto"
Solutions:
1. Use explicit builder_type="legacy" or builder_type="framework"
2. Check kwargs match expected pattern:
- Legacy: prompt_contributors parameter
- Framework: base_prompt parameter
Future Enhancements¶
Phase 4+ (Deferred)¶
- Performance monitoring and metrics
- Advanced caching strategies (LRU, TTL)
- Multi-provider evolved content resolution
- Constraint composition (combining multiple constraints)
- Prompt versioning and rollback
- A/B testing for evolved content
References¶
- Plan Document: See approved plan for detailed implementation timeline
- FEP-XXXX: Framework Enhancement Proposal (if applicable)
- Related Docs:
docs/architecture/prompt-optimization.mddocs/architecture/constraint-system.mddocs/architecture/workflow-execution.md
Changelog¶
2026-04-30¶
- ✅ Phase 1: UnifiedSectionRegistry, EvolvedContentResolver
- ✅ Phase 2: ConstraintActivationService
- ✅ Phase 3: PromptOrchestrator
- ✅ Phase 4: Integration points (AgentNodeExecutor, SubAgent.spawn())
- ✅ Phase 5: Testing and verification (49 new tests + 428 total passing)