Add ez-assistant and kerberos service folders
This commit is contained in:
@@ -0,0 +1,951 @@
|
||||
---
|
||||
role: antipatterns
|
||||
summary: |
|
||||
Common mistakes and patterns to avoid in OpenProse programs.
|
||||
Read this file to identify and fix problematic code patterns.
|
||||
see-also:
|
||||
- prose.md: Execution semantics, how to run programs
|
||||
- compiler.md: Full syntax grammar, validation rules
|
||||
- patterns.md: Recommended design patterns
|
||||
---
|
||||
|
||||
# OpenProse Antipatterns
|
||||
|
||||
This document catalogs patterns that lead to brittle, expensive, slow, or unmaintainable programs. Each antipattern includes recognition criteria and remediation guidance.
|
||||
|
||||
---
|
||||
|
||||
## Structural Antipatterns
|
||||
|
||||
#### god-session
|
||||
|
||||
A single session that tries to do everything. God sessions are hard to debug, impossible to parallelize, and produce inconsistent results.
|
||||
|
||||
```prose
|
||||
# Bad: One session doing too much
|
||||
session """
|
||||
Read all the code in the repository.
|
||||
Identify security vulnerabilities.
|
||||
Find performance bottlenecks.
|
||||
Check for style violations.
|
||||
Generate a comprehensive report.
|
||||
Suggest fixes for each issue.
|
||||
Prioritize by severity.
|
||||
Create a remediation plan.
|
||||
"""
|
||||
```
|
||||
|
||||
**Why it's bad**: The session has no clear completion criteria. It mixes concerns that could be parallelized. Failure anywhere fails everything.
|
||||
|
||||
**Fix**: Decompose into focused sessions:
|
||||
|
||||
```prose
|
||||
# Good: Focused sessions
|
||||
parallel:
|
||||
security = session "Identify security vulnerabilities"
|
||||
perf = session "Find performance bottlenecks"
|
||||
style = session "Check for style violations"
|
||||
|
||||
session "Synthesize findings and prioritize by severity"
|
||||
context: { security, perf, style }
|
||||
|
||||
session "Create remediation plan"
|
||||
```
|
||||
|
||||
#### sequential-when-parallel
|
||||
|
||||
Running independent operations sequentially when they could run concurrently. Wastes wall-clock time.
|
||||
|
||||
```prose
|
||||
# Bad: Sequential independent work
|
||||
let market = session "Research market"
|
||||
let tech = session "Research technology"
|
||||
let competition = session "Research competition"
|
||||
|
||||
session "Synthesize"
|
||||
context: [market, tech, competition]
|
||||
```
|
||||
|
||||
**Why it's bad**: Total time is sum of all research times. Each session waits for the previous one unnecessarily.
|
||||
|
||||
**Fix**: Parallelize independent work:
|
||||
|
||||
```prose
|
||||
# Good: Parallel independent work
|
||||
parallel:
|
||||
market = session "Research market"
|
||||
tech = session "Research technology"
|
||||
competition = session "Research competition"
|
||||
|
||||
session "Synthesize"
|
||||
context: { market, tech, competition }
|
||||
```
|
||||
|
||||
#### spaghetti-context
|
||||
|
||||
Context passed haphazardly without clear data flow. Makes programs hard to understand and modify.
|
||||
|
||||
```prose
|
||||
# Bad: Unclear what context is actually used
|
||||
let a = session "Step A"
|
||||
let b = session "Step B"
|
||||
context: a
|
||||
let c = session "Step C"
|
||||
context: [a, b]
|
||||
let d = session "Step D"
|
||||
context: [a, b, c]
|
||||
let e = session "Step E"
|
||||
context: [a, c, d] # Why not b?
|
||||
let f = session "Step F"
|
||||
context: [a, b, c, d, e] # Everything?
|
||||
```
|
||||
|
||||
**Why it's bad**: Unclear which sessions depend on which outputs. Hard to parallelize or refactor.
|
||||
|
||||
**Fix**: Minimize context to actual dependencies:
|
||||
|
||||
```prose
|
||||
# Good: Clear, minimal dependencies
|
||||
let research = session "Research"
|
||||
let analysis = session "Analyze"
|
||||
context: research
|
||||
let recommendations = session "Recommend"
|
||||
context: analysis # Only needs analysis, not research
|
||||
let report = session "Report"
|
||||
context: recommendations
|
||||
```
|
||||
|
||||
#### parallel-then-synthesize
|
||||
|
||||
Spawning parallel agents for related analytical work, then synthesizing, when a single focused agent could do the entire job more efficiently.
|
||||
|
||||
```prose
|
||||
# Antipattern: Parallel investigation + synthesis
|
||||
parallel:
|
||||
code = session "Analyze code path"
|
||||
logs = session "Analyze logs"
|
||||
context = session "Analyze execution context"
|
||||
|
||||
synthesis = session "Synthesize all findings"
|
||||
context: { code, logs, context }
|
||||
# 4 LLM calls, coordination overhead, fragmented context
|
||||
```
|
||||
|
||||
**Why it's bad**: For related analysis that feeds into one conclusion, the coordination overhead and context fragmentation often outweigh parallelism benefits. Each parallel agent sees only part of the picture.
|
||||
|
||||
**Fix**: Use a single focused agent with multi-step instructions:
|
||||
|
||||
```prose
|
||||
# Good: Single comprehensive investigator
|
||||
diagnosis = session "Investigate the error"
|
||||
prompt: """Analyze comprehensively:
|
||||
1. Check the code path that produced the error
|
||||
2. Examine logs for timing and state
|
||||
3. Review execution context
|
||||
Synthesize into a unified diagnosis."""
|
||||
# 1 LLM call, full context, no coordination
|
||||
```
|
||||
|
||||
**When parallel IS right**: When analyses are truly independent (security vs performance), when you want diverse perspectives that shouldn't influence each other, or when the work is so large it genuinely benefits from division.
|
||||
|
||||
#### copy-paste-workflows
|
||||
|
||||
Duplicating session sequences instead of using blocks. Leads to inconsistent changes and maintenance burden.
|
||||
|
||||
```prose
|
||||
# Bad: Duplicated workflow
|
||||
session "Security review of module A"
|
||||
session "Performance review of module A"
|
||||
session "Synthesize reviews of module A"
|
||||
|
||||
session "Security review of module B"
|
||||
session "Performance review of module B"
|
||||
session "Synthesize reviews of module B"
|
||||
|
||||
session "Security review of module C"
|
||||
session "Performance review of module C"
|
||||
session "Synthesize reviews of module C"
|
||||
```
|
||||
|
||||
**Why it's bad**: If the workflow needs to change, you must change it everywhere. Easy to miss one.
|
||||
|
||||
**Fix**: Extract into a block:
|
||||
|
||||
```prose
|
||||
# Good: Reusable block
|
||||
block review-module(module):
|
||||
parallel:
|
||||
sec = session "Security review of {module}"
|
||||
perf = session "Performance review of {module}"
|
||||
session "Synthesize reviews of {module}"
|
||||
context: { sec, perf }
|
||||
|
||||
do review-module("module A")
|
||||
do review-module("module B")
|
||||
do review-module("module C")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Robustness Antipatterns
|
||||
|
||||
#### unbounded-loop
|
||||
|
||||
A loop without max iterations. Can run forever if the condition is never satisfied.
|
||||
|
||||
```prose
|
||||
# Bad: No escape hatch
|
||||
loop until **the code is perfect**:
|
||||
session "Improve the code"
|
||||
```
|
||||
|
||||
**Why it's bad**: "Perfect" may never be achieved. The program could run indefinitely, consuming resources.
|
||||
|
||||
**Fix**: Always specify `max:`:
|
||||
|
||||
```prose
|
||||
# Good: Bounded iteration
|
||||
loop until **the code is perfect** (max: 10):
|
||||
session "Improve the code"
|
||||
```
|
||||
|
||||
#### optimistic-execution
|
||||
|
||||
Assuming everything will succeed. No error handling for operations that can fail.
|
||||
|
||||
```prose
|
||||
# Bad: No error handling
|
||||
session "Call external API"
|
||||
session "Process API response"
|
||||
session "Store results in database"
|
||||
session "Send notification"
|
||||
```
|
||||
|
||||
**Why it's bad**: If the API fails, subsequent sessions receive no valid input. Silent corruption.
|
||||
|
||||
**Fix**: Handle failures explicitly:
|
||||
|
||||
```prose
|
||||
# Good: Error handling
|
||||
try:
|
||||
let response = session "Call external API"
|
||||
retry: 3
|
||||
backoff: "exponential"
|
||||
session "Process API response"
|
||||
context: response
|
||||
catch as err:
|
||||
session "Handle API failure gracefully"
|
||||
context: err
|
||||
```
|
||||
|
||||
#### ignored-errors
|
||||
|
||||
Using `on-fail: "ignore"` when failures actually matter. Masks problems that should surface.
|
||||
|
||||
```prose
|
||||
# Bad: Ignoring failures that matter
|
||||
parallel (on-fail: "ignore"):
|
||||
session "Charge customer credit card"
|
||||
session "Ship the product"
|
||||
session "Send confirmation email"
|
||||
|
||||
session "Order complete!" # But was it really?
|
||||
```
|
||||
|
||||
**Why it's bad**: The order might be marked complete even if payment failed.
|
||||
|
||||
**Fix**: Use appropriate failure policy:
|
||||
|
||||
```prose
|
||||
# Good: Fail-fast for critical operations
|
||||
parallel: # Default: fail-fast
|
||||
payment = session "Charge customer credit card"
|
||||
inventory = session "Reserve inventory"
|
||||
|
||||
# Only ship if both succeeded
|
||||
session "Ship the product"
|
||||
context: { payment, inventory }
|
||||
|
||||
# Email can fail without blocking
|
||||
try:
|
||||
session "Send confirmation email"
|
||||
catch:
|
||||
session "Queue email for retry"
|
||||
```
|
||||
|
||||
#### vague-discretion
|
||||
|
||||
Discretion conditions that are ambiguous or unmeasurable.
|
||||
|
||||
```prose
|
||||
# Bad: What does "good enough" mean?
|
||||
loop until **the output is good enough**:
|
||||
session "Improve output"
|
||||
|
||||
# Bad: Highly subjective
|
||||
if **the user will be happy**:
|
||||
session "Ship it"
|
||||
```
|
||||
|
||||
**Why it's bad**: The VM has no clear criteria for evaluation. Results are unpredictable.
|
||||
|
||||
**Fix**: Provide concrete, evaluatable criteria:
|
||||
|
||||
```prose
|
||||
# Good: Specific criteria
|
||||
loop until **all tests pass and code coverage exceeds 80%** (max: 10):
|
||||
session "Improve test coverage"
|
||||
|
||||
# Good: Observable conditions
|
||||
if **the response contains valid JSON with all required fields**:
|
||||
session "Process the response"
|
||||
```
|
||||
|
||||
#### catch-and-swallow
|
||||
|
||||
Catching errors without meaningful handling. Hides problems without solving them.
|
||||
|
||||
```prose
|
||||
# Bad: Silent swallow
|
||||
try:
|
||||
session "Critical operation"
|
||||
catch:
|
||||
# Nothing here - error disappears
|
||||
```
|
||||
|
||||
**Why it's bad**: Errors vanish. No recovery, no logging, no visibility.
|
||||
|
||||
**Fix**: Handle errors meaningfully:
|
||||
|
||||
```prose
|
||||
# Good: Meaningful handling
|
||||
try:
|
||||
session "Critical operation"
|
||||
catch as err:
|
||||
session "Log error for investigation"
|
||||
context: err
|
||||
session "Execute fallback procedure"
|
||||
# Or rethrow if unrecoverable:
|
||||
throw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Antipatterns
|
||||
|
||||
#### opus-for-everything
|
||||
|
||||
Using the most powerful (expensive) model for all tasks, including trivial ones.
|
||||
|
||||
```prose
|
||||
# Bad: Opus for simple classification
|
||||
agent classifier:
|
||||
model: opus
|
||||
prompt: "Categorize items as: spam, not-spam"
|
||||
|
||||
# Expensive for a binary classification
|
||||
for email in emails:
|
||||
session: classifier
|
||||
prompt: "Classify: {email}"
|
||||
```
|
||||
|
||||
**Why it's bad**: Opus costs significantly more than haiku. Simple tasks don't benefit from advanced reasoning.
|
||||
|
||||
**Fix**: Match model to task complexity:
|
||||
|
||||
```prose
|
||||
# Good: Haiku for simple tasks
|
||||
agent classifier:
|
||||
model: haiku
|
||||
prompt: "Categorize items as: spam, not-spam"
|
||||
```
|
||||
|
||||
#### context-bloat
|
||||
|
||||
Passing excessive context that the session doesn't need.
|
||||
|
||||
```prose
|
||||
# Bad: Passing everything
|
||||
let full_codebase = session "Read entire codebase"
|
||||
let all_docs = session "Read all documentation"
|
||||
let history = session "Get full git history"
|
||||
|
||||
session "Fix the typo in the README"
|
||||
context: [full_codebase, all_docs, history] # Massive overkill
|
||||
```
|
||||
|
||||
**Why it's bad**: Large contexts slow processing, increase costs, and can confuse the model with irrelevant information.
|
||||
|
||||
**Fix**: Pass minimal relevant context:
|
||||
|
||||
```prose
|
||||
# Good: Minimal context
|
||||
let readme = session "Read the README file"
|
||||
|
||||
session "Fix the typo in the README"
|
||||
context: readme
|
||||
```
|
||||
|
||||
#### unnecessary-iteration
|
||||
|
||||
Looping when a single session would suffice.
|
||||
|
||||
```prose
|
||||
# Bad: Loop for what could be one call
|
||||
let items = ["apple", "banana", "cherry"]
|
||||
for item in items:
|
||||
session "Describe {item}"
|
||||
```
|
||||
|
||||
**Why it's bad**: Three sessions when one could handle all items. Session overhead multiplied.
|
||||
|
||||
**Fix**: Batch when possible:
|
||||
|
||||
```prose
|
||||
# Good: Batch processing
|
||||
let items = ["apple", "banana", "cherry"]
|
||||
session "Describe each of these items: {items}"
|
||||
```
|
||||
|
||||
#### redundant-computation
|
||||
|
||||
Computing the same thing multiple times.
|
||||
|
||||
```prose
|
||||
# Bad: Redundant research
|
||||
session "Research AI safety for security review"
|
||||
session "Research AI safety for ethics review"
|
||||
session "Research AI safety for compliance review"
|
||||
```
|
||||
|
||||
**Why it's bad**: Same research done three times with slightly different framing.
|
||||
|
||||
**Fix**: Compute once, use many times:
|
||||
|
||||
```prose
|
||||
# Good: Compute once
|
||||
let research = session "Comprehensive research on AI safety"
|
||||
|
||||
parallel:
|
||||
session "Security review"
|
||||
context: research
|
||||
session "Ethics review"
|
||||
context: research
|
||||
session "Compliance review"
|
||||
context: research
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Antipatterns
|
||||
|
||||
#### eager-over-computation
|
||||
|
||||
Computing everything upfront when only some results might be needed.
|
||||
|
||||
```prose
|
||||
# Bad: Compute all branches even if only one is needed
|
||||
parallel:
|
||||
simple_analysis = session "Simple analysis"
|
||||
model: haiku
|
||||
detailed_analysis = session "Detailed analysis"
|
||||
model: sonnet
|
||||
deep_analysis = session "Deep analysis"
|
||||
model: opus
|
||||
|
||||
# Then only use one based on some criterion
|
||||
choice **appropriate depth**:
|
||||
option "Simple":
|
||||
session "Use simple"
|
||||
context: simple_analysis
|
||||
option "Detailed":
|
||||
session "Use detailed"
|
||||
context: detailed_analysis
|
||||
option "Deep":
|
||||
session "Use deep"
|
||||
context: deep_analysis
|
||||
```
|
||||
|
||||
**Why it's bad**: All three analyses run even though only one is used.
|
||||
|
||||
**Fix**: Compute lazily:
|
||||
|
||||
```prose
|
||||
# Good: Only compute what's needed
|
||||
let initial = session "Initial assessment"
|
||||
model: haiku
|
||||
|
||||
choice **appropriate depth based on initial assessment**:
|
||||
option "Simple":
|
||||
session "Simple analysis"
|
||||
model: haiku
|
||||
option "Detailed":
|
||||
session "Detailed analysis"
|
||||
model: sonnet
|
||||
option "Deep":
|
||||
session "Deep analysis"
|
||||
model: opus
|
||||
```
|
||||
|
||||
#### over-parallelization
|
||||
|
||||
Parallelizing so aggressively that overhead dominates or resources are exhausted.
|
||||
|
||||
```prose
|
||||
# Bad: 100 parallel sessions
|
||||
parallel for item in large_collection: # 100 items
|
||||
session "Process {item}"
|
||||
```
|
||||
|
||||
**Why it's bad**: May overwhelm the system. Coordination overhead can exceed parallelism benefits.
|
||||
|
||||
**Fix**: Batch or limit concurrency:
|
||||
|
||||
```prose
|
||||
# Good: Process in batches
|
||||
for batch in batches(large_collection, 10):
|
||||
parallel for item in batch:
|
||||
session "Process {item}"
|
||||
```
|
||||
|
||||
#### premature-parallelization
|
||||
|
||||
Parallelizing tiny tasks where sequential would be simpler and fast enough.
|
||||
|
||||
```prose
|
||||
# Bad: Parallel overkill for simple tasks
|
||||
parallel:
|
||||
a = session "Add 2 + 2"
|
||||
b = session "Add 3 + 3"
|
||||
c = session "Add 4 + 4"
|
||||
```
|
||||
|
||||
**Why it's bad**: Coordination overhead exceeds task time. Sequential would be simpler and possibly faster.
|
||||
|
||||
**Fix**: Keep it simple:
|
||||
|
||||
```prose
|
||||
# Good: Sequential for trivial tasks
|
||||
session "Add 2+2, 3+3, and 4+4"
|
||||
```
|
||||
|
||||
#### synchronous-fire-and-forget
|
||||
|
||||
Waiting for operations whose results you don't need.
|
||||
|
||||
```prose
|
||||
# Bad: Waiting for logging
|
||||
session "Do important work"
|
||||
session "Log the result" # Don't need to wait for this
|
||||
session "Continue with next important work"
|
||||
```
|
||||
|
||||
**Why it's bad**: Main workflow blocked by non-critical operation.
|
||||
|
||||
**Fix**: Use appropriate patterns for fire-and-forget operations, or batch logging:
|
||||
|
||||
```prose
|
||||
# Better: Batch non-critical work
|
||||
session "Do important work"
|
||||
session "Continue with next important work"
|
||||
# ... more important work ...
|
||||
|
||||
# Log everything at the end or async
|
||||
session "Log all operations"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Maintainability Antipatterns
|
||||
|
||||
#### magic-strings
|
||||
|
||||
Hardcoded prompts repeated throughout the program.
|
||||
|
||||
```prose
|
||||
# Bad: Same prompt in multiple places
|
||||
session "You are a helpful assistant. Analyze this code for bugs."
|
||||
# ... later ...
|
||||
session "You are a helpful assistant. Analyze this code for bugs."
|
||||
# ... even later ...
|
||||
session "You are a helpful assistent. Analyze this code for bugs." # Typo!
|
||||
```
|
||||
|
||||
**Why it's bad**: Inconsistency when updating. Typos go unnoticed.
|
||||
|
||||
**Fix**: Use agents:
|
||||
|
||||
```prose
|
||||
# Good: Single source of truth
|
||||
agent code-analyst:
|
||||
model: sonnet
|
||||
prompt: "You are a helpful assistant. Analyze code for bugs."
|
||||
|
||||
session: code-analyst
|
||||
prompt: "Analyze the auth module"
|
||||
session: code-analyst
|
||||
prompt: "Analyze the payment module"
|
||||
```
|
||||
|
||||
#### opaque-workflow
|
||||
|
||||
No structure or comments indicating what's happening.
|
||||
|
||||
```prose
|
||||
# Bad: What is this doing?
|
||||
let x = session "A"
|
||||
let y = session "B"
|
||||
context: x
|
||||
parallel:
|
||||
z = session "C"
|
||||
context: y
|
||||
w = session "D"
|
||||
session "E"
|
||||
context: [z, w]
|
||||
```
|
||||
|
||||
**Why it's bad**: Impossible to understand, debug, or modify.
|
||||
|
||||
**Fix**: Use meaningful names and structure:
|
||||
|
||||
```prose
|
||||
# Good: Clear intent
|
||||
# Phase 1: Research
|
||||
let research = session "Gather background information"
|
||||
|
||||
# Phase 2: Analysis
|
||||
let analysis = session "Analyze research findings"
|
||||
context: research
|
||||
|
||||
# Phase 3: Parallel evaluation
|
||||
parallel:
|
||||
technical_eval = session "Technical feasibility assessment"
|
||||
context: analysis
|
||||
business_eval = session "Business viability assessment"
|
||||
context: analysis
|
||||
|
||||
# Phase 4: Synthesis
|
||||
session "Create final recommendation"
|
||||
context: { technical_eval, business_eval }
|
||||
```
|
||||
|
||||
#### implicit-dependencies
|
||||
|
||||
Relying on conversation history rather than explicit context.
|
||||
|
||||
```prose
|
||||
# Bad: Implicit state
|
||||
session "Set the project name to Acme"
|
||||
session "Set the deadline to Friday"
|
||||
session "Now create a project plan" # Hopes previous info is remembered
|
||||
```
|
||||
|
||||
**Why it's bad**: Relies on VM implementation details. Fragile across refactoring.
|
||||
|
||||
**Fix**: Explicit context:
|
||||
|
||||
```prose
|
||||
# Good: Explicit state
|
||||
let config = session "Define project: name=Acme, deadline=Friday"
|
||||
|
||||
session "Create a project plan"
|
||||
context: config
|
||||
```
|
||||
|
||||
#### mixed-concerns-agent
|
||||
|
||||
Agents with prompts that cover too many responsibilities.
|
||||
|
||||
```prose
|
||||
# Bad: Jack of all trades
|
||||
agent super-agent:
|
||||
model: opus
|
||||
prompt: """
|
||||
You are an expert in:
|
||||
- Security analysis
|
||||
- Performance optimization
|
||||
- Code review
|
||||
- Documentation
|
||||
- Testing
|
||||
- DevOps
|
||||
- Project management
|
||||
- Customer communication
|
||||
When asked, perform any of these tasks.
|
||||
"""
|
||||
```
|
||||
|
||||
**Why it's bad**: No focus means mediocre results across the board. Can't optimize model choice.
|
||||
|
||||
**Fix**: Specialized agents:
|
||||
|
||||
```prose
|
||||
# Good: Focused expertise
|
||||
agent security-expert:
|
||||
model: sonnet
|
||||
prompt: "You are a security analyst. Focus only on security concerns."
|
||||
|
||||
agent performance-expert:
|
||||
model: sonnet
|
||||
prompt: "You are a performance engineer. Focus only on optimization."
|
||||
|
||||
agent technical-writer:
|
||||
model: haiku
|
||||
prompt: "You write clear technical documentation."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logic Antipatterns
|
||||
|
||||
#### infinite-refinement
|
||||
|
||||
Loops that can never satisfy their exit condition.
|
||||
|
||||
```prose
|
||||
# Bad: Perfection is impossible
|
||||
loop until **the code has zero bugs**:
|
||||
session "Find and fix bugs"
|
||||
```
|
||||
|
||||
**Why it's bad**: Zero bugs is unachievable. Loop runs until max (if specified) or forever.
|
||||
|
||||
**Fix**: Use achievable conditions:
|
||||
|
||||
```prose
|
||||
# Good: Achievable condition
|
||||
loop until **all known bugs are fixed** (max: 20):
|
||||
session "Find and fix the next bug"
|
||||
|
||||
# Or: Diminishing returns
|
||||
loop until **no significant bugs found in last iteration** (max: 10):
|
||||
session "Search for bugs"
|
||||
```
|
||||
|
||||
#### assertion-as-action
|
||||
|
||||
Using conditions as actions—checking something without acting on the result.
|
||||
|
||||
```prose
|
||||
# Bad: Check but don't use result
|
||||
session "Check if the system is healthy"
|
||||
session "Deploy to production" # Deploys regardless!
|
||||
```
|
||||
|
||||
**Why it's bad**: The health check result isn't used. Deploy happens unconditionally.
|
||||
|
||||
**Fix**: Use conditional execution:
|
||||
|
||||
```prose
|
||||
# Good: Act on the check
|
||||
let health = session "Check if the system is healthy"
|
||||
|
||||
if **system is healthy**:
|
||||
session "Deploy to production"
|
||||
else:
|
||||
session "Alert on-call and skip deployment"
|
||||
context: health
|
||||
```
|
||||
|
||||
#### false-parallelism
|
||||
|
||||
Putting sequential-dependent operations in a parallel block.
|
||||
|
||||
```prose
|
||||
# Bad: These aren't independent!
|
||||
parallel:
|
||||
data = session "Fetch data"
|
||||
processed = session "Process the data" # Needs data!
|
||||
context: data
|
||||
stored = session "Store processed data" # Needs processed!
|
||||
context: processed
|
||||
```
|
||||
|
||||
**Why it's bad**: Despite being in parallel, these must run sequentially due to dependencies.
|
||||
|
||||
**Fix**: Be honest about dependencies:
|
||||
|
||||
```prose
|
||||
# Good: Sequential where needed
|
||||
let data = session "Fetch data"
|
||||
let processed = session "Process the data"
|
||||
context: data
|
||||
session "Store processed data"
|
||||
context: processed
|
||||
```
|
||||
|
||||
#### exception-as-flow-control
|
||||
|
||||
Using try/catch for expected conditions rather than exceptional errors.
|
||||
|
||||
```prose
|
||||
# Bad: Exceptions for normal flow
|
||||
try:
|
||||
session "Find the optional config file"
|
||||
catch:
|
||||
session "Use default configuration"
|
||||
```
|
||||
|
||||
**Why it's bad**: Missing config is expected, not exceptional. Obscures actual errors.
|
||||
|
||||
**Fix**: Use conditionals for expected cases:
|
||||
|
||||
```prose
|
||||
# Good: Conditional for expected case
|
||||
let config_exists = session "Check if config file exists"
|
||||
|
||||
if **config file exists**:
|
||||
session "Load configuration from file"
|
||||
else:
|
||||
session "Use default configuration"
|
||||
```
|
||||
|
||||
#### excessive-user-checkpoints
|
||||
|
||||
Prompting the user for decisions that have obvious or predictable answers.
|
||||
|
||||
```prose
|
||||
# Antipattern: Asking the obvious
|
||||
input "Blocking error detected. Investigate?" # Always yes
|
||||
input "Diagnosis complete. Proceed to triage?" # Always yes
|
||||
input "Tests pass. Deploy?" # Almost always yes
|
||||
```
|
||||
|
||||
**Why it's bad**: Each checkpoint is a round-trip waiting for user input. If the answer is predictable 90% of the time, you're adding latency for no value.
|
||||
|
||||
**Fix**: Auto-proceed for obvious cases, only prompt when genuinely ambiguous:
|
||||
|
||||
```prose
|
||||
# Good: Auto-proceed with escape hatches for edge cases
|
||||
if observation.blocking_error:
|
||||
# Auto-investigate (don't ask - of course we investigate errors)
|
||||
let diagnosis = do investigate(...)
|
||||
|
||||
# Only ask if genuinely ambiguous
|
||||
if diagnosis.confidence == "low":
|
||||
input "Low confidence diagnosis. Proceed anyway?"
|
||||
|
||||
# Auto-deploy if tests pass (but log for audit)
|
||||
if fix.tests_pass:
|
||||
do deploy(...)
|
||||
```
|
||||
|
||||
**When checkpoints ARE right**: Irreversible actions (production deployments to critical systems), expensive operations (long-running jobs), or genuine decision points where the user's preference isn't predictable.
|
||||
|
||||
#### fixed-observation-window
|
||||
|
||||
Waiting for a predetermined duration when the signal arrived early.
|
||||
|
||||
```prose
|
||||
# Antipattern: Fixed window regardless of findings
|
||||
loop 30 times (wait: 2s each): # Always 60 seconds
|
||||
resume: observer
|
||||
prompt: "Keep watching the stream"
|
||||
# Runs all 30 iterations even if blocking error detected on iteration 1
|
||||
```
|
||||
|
||||
**Why it's bad**: Wastes time when the answer is already known. If the observer detected a fatal error at +5 seconds, why wait another 55 seconds?
|
||||
|
||||
**Fix**: Use signal-driven exit conditions:
|
||||
|
||||
```prose
|
||||
# Good: Exit on significant signal
|
||||
loop until **blocking error OR completion** (max: 30):
|
||||
resume: observer
|
||||
prompt: "Watch the stream. Signal IMMEDIATELY on blocking errors."
|
||||
# Exits as soon as something significant happens
|
||||
```
|
||||
|
||||
Or use `early_exit` if your runtime supports it:
|
||||
|
||||
```prose
|
||||
# Good: Explicit early exit
|
||||
let observation = session: observer
|
||||
prompt: "Monitor for errors. Signal immediately if found."
|
||||
timeout: 120s
|
||||
early_exit: **blocking_error detected**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Antipatterns
|
||||
|
||||
#### unvalidated-input
|
||||
|
||||
Passing external input directly to sessions without validation.
|
||||
|
||||
```prose
|
||||
# Bad: Direct injection
|
||||
let user_input = external_source
|
||||
|
||||
session "Execute this command: {user_input}"
|
||||
```
|
||||
|
||||
**Why it's bad**: User could inject malicious prompts or commands.
|
||||
|
||||
**Fix**: Validate and sanitize:
|
||||
|
||||
```prose
|
||||
# Good: Validate first
|
||||
let user_input = external_source
|
||||
let validated = session "Validate this input is a safe search query"
|
||||
context: user_input
|
||||
|
||||
if **input is valid and safe**:
|
||||
session "Search for: {validated}"
|
||||
else:
|
||||
throw "Invalid input rejected"
|
||||
```
|
||||
|
||||
#### overprivileged-agents
|
||||
|
||||
Agents with more permissions than they need.
|
||||
|
||||
```prose
|
||||
# Bad: Full access for simple task
|
||||
agent file-reader:
|
||||
permissions:
|
||||
read: ["**/*"]
|
||||
write: ["**/*"]
|
||||
bash: allow
|
||||
network: allow
|
||||
|
||||
session: file-reader
|
||||
prompt: "Read the README.md file"
|
||||
```
|
||||
|
||||
**Why it's bad**: Task only needs to read one file but has full system access.
|
||||
|
||||
**Fix**: Least privilege:
|
||||
|
||||
```prose
|
||||
# Good: Minimal permissions
|
||||
agent file-reader:
|
||||
permissions:
|
||||
read: ["README.md"]
|
||||
write: []
|
||||
bash: deny
|
||||
network: deny
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Antipatterns emerge from:
|
||||
|
||||
1. **Laziness**: Copy-paste instead of abstraction, implicit instead of explicit
|
||||
2. **Over-engineering**: Parallelizing everything, using opus for all tasks
|
||||
3. **Under-engineering**: No error handling, unbounded loops, vague conditions
|
||||
4. **Unclear thinking**: God sessions, mixed concerns, spaghetti context
|
||||
|
||||
When reviewing OpenProse programs, ask:
|
||||
|
||||
- Can independent work be parallelized?
|
||||
- Are loops bounded?
|
||||
- Are errors handled?
|
||||
- Is context minimal and explicit?
|
||||
- Are models matched to task complexity?
|
||||
- Are agents focused and reusable?
|
||||
- Would a stranger understand this code?
|
||||
|
||||
Fix antipatterns early. They compound over time into unmaintainable systems.
|
||||
@@ -0,0 +1,700 @@
|
||||
---
|
||||
role: best-practices
|
||||
summary: |
|
||||
Design patterns for robust, efficient, and maintainable OpenProse programs.
|
||||
Read this file when authoring new programs or reviewing existing ones.
|
||||
see-also:
|
||||
- prose.md: Execution semantics, how to run programs
|
||||
- compiler.md: Full syntax grammar, validation rules
|
||||
- antipatterns.md: Patterns to avoid
|
||||
---
|
||||
|
||||
# OpenProse Design Patterns
|
||||
|
||||
This document catalogs proven patterns for orchestrating AI agents effectively. Each pattern addresses specific concerns: robustness, cost efficiency, speed, maintainability, or self-improvement capability.
|
||||
|
||||
---
|
||||
|
||||
## Structural Patterns
|
||||
|
||||
#### parallel-independent-work
|
||||
|
||||
When tasks have no data dependencies, execute them concurrently. This maximizes throughput and minimizes wall-clock time.
|
||||
|
||||
```prose
|
||||
# Good: Independent research runs in parallel
|
||||
parallel:
|
||||
market = session "Research market trends"
|
||||
tech = session "Research technology landscape"
|
||||
competition = session "Analyze competitor products"
|
||||
|
||||
session "Synthesize findings"
|
||||
context: { market, tech, competition }
|
||||
```
|
||||
|
||||
The synthesis session waits for all branches, but total time equals the longest branch rather than the sum of all branches.
|
||||
|
||||
#### fan-out-fan-in
|
||||
|
||||
For processing collections, fan out to parallel workers then collect results. Use `parallel for` instead of manual parallel branches.
|
||||
|
||||
```prose
|
||||
let topics = ["AI safety", "interpretability", "alignment", "robustness"]
|
||||
|
||||
parallel for topic in topics:
|
||||
session "Deep dive research on {topic}"
|
||||
|
||||
session "Create unified report from all research"
|
||||
```
|
||||
|
||||
This scales naturally with collection size and keeps code DRY.
|
||||
|
||||
#### pipeline-composition
|
||||
|
||||
Chain transformations using pipe operators for readable data flow. Each stage has a single responsibility.
|
||||
|
||||
```prose
|
||||
let candidates = session "Generate 10 startup ideas"
|
||||
|
||||
let result = candidates
|
||||
| filter:
|
||||
session "Is this idea technically feasible? yes/no"
|
||||
context: item
|
||||
| map:
|
||||
session "Expand this idea into a one-page pitch"
|
||||
context: item
|
||||
| reduce(best, current):
|
||||
session "Compare these two pitches, return the stronger one"
|
||||
context: [best, current]
|
||||
```
|
||||
|
||||
#### agent-specialization
|
||||
|
||||
Define agents with focused expertise. Specialized agents produce better results than generalist prompts.
|
||||
|
||||
```prose
|
||||
agent security-reviewer:
|
||||
model: sonnet
|
||||
prompt: """
|
||||
You are a security expert. Focus exclusively on:
|
||||
- Authentication and authorization flaws
|
||||
- Injection vulnerabilities
|
||||
- Data exposure risks
|
||||
Ignore style, performance, and other concerns.
|
||||
"""
|
||||
|
||||
agent performance-reviewer:
|
||||
model: sonnet
|
||||
prompt: """
|
||||
You are a performance engineer. Focus exclusively on:
|
||||
- Algorithmic complexity
|
||||
- Memory usage patterns
|
||||
- I/O bottlenecks
|
||||
Ignore security, style, and other concerns.
|
||||
"""
|
||||
```
|
||||
|
||||
#### reusable-blocks
|
||||
|
||||
Extract repeated workflows into parameterized blocks. Blocks are the functions of OpenProse.
|
||||
|
||||
```prose
|
||||
block review-and-revise(artifact, criteria):
|
||||
let feedback = session "Review {artifact} against {criteria}"
|
||||
session "Revise {artifact} based on feedback"
|
||||
context: feedback
|
||||
|
||||
# Reuse the pattern
|
||||
do review-and-revise("the architecture doc", "clarity and completeness")
|
||||
do review-and-revise("the API design", "consistency and usability")
|
||||
do review-and-revise("the test plan", "coverage and edge cases")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Robustness Patterns
|
||||
|
||||
#### bounded-iteration
|
||||
|
||||
Always constrain loops with `max:` to prevent runaway execution. Even well-crafted conditions can fail to terminate.
|
||||
|
||||
```prose
|
||||
# Good: Explicit upper bound
|
||||
loop until **all tests pass** (max: 20):
|
||||
session "Identify and fix the next failing test"
|
||||
|
||||
# The program will terminate even if tests never fully pass
|
||||
```
|
||||
|
||||
#### graceful-degradation
|
||||
|
||||
Use `on-fail: "continue"` when partial results are acceptable. Collect what you can rather than failing entirely.
|
||||
|
||||
```prose
|
||||
parallel (on-fail: "continue"):
|
||||
primary = session "Query primary data source"
|
||||
backup = session "Query backup data source"
|
||||
cache = session "Check local cache"
|
||||
|
||||
# Continue with whatever succeeded
|
||||
session "Merge available data"
|
||||
context: { primary, backup, cache }
|
||||
```
|
||||
|
||||
#### retry-with-backoff
|
||||
|
||||
External services fail transiently. Retry with exponential backoff to handle rate limits and temporary outages.
|
||||
|
||||
```prose
|
||||
session "Call external API"
|
||||
retry: 5
|
||||
backoff: "exponential"
|
||||
```
|
||||
|
||||
For critical paths, combine retry with fallback:
|
||||
|
||||
```prose
|
||||
try:
|
||||
session "Call primary API"
|
||||
retry: 3
|
||||
backoff: "exponential"
|
||||
catch:
|
||||
session "Use fallback data source"
|
||||
```
|
||||
|
||||
#### error-context-capture
|
||||
|
||||
Capture error context for intelligent recovery. The error variable provides information for diagnostic or remediation sessions.
|
||||
|
||||
```prose
|
||||
try:
|
||||
session "Deploy to production"
|
||||
catch as err:
|
||||
session "Analyze deployment failure and suggest fixes"
|
||||
context: err
|
||||
session "Attempt automatic remediation"
|
||||
context: err
|
||||
```
|
||||
|
||||
#### defensive-context
|
||||
|
||||
Validate assumptions before expensive operations. Cheap checks prevent wasted computation.
|
||||
|
||||
```prose
|
||||
let prereqs = session "Check all prerequisites: API keys, permissions, dependencies"
|
||||
|
||||
if **prerequisites are not met**:
|
||||
session "Report missing prerequisites and exit"
|
||||
context: prereqs
|
||||
throw "Prerequisites not satisfied"
|
||||
|
||||
# Expensive operations only run if prereqs pass
|
||||
session "Execute main workflow"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Efficiency Patterns
|
||||
|
||||
#### model-tiering
|
||||
|
||||
Match model capability to task complexity:
|
||||
|
||||
| Model | Best For | Examples |
|
||||
|-------|----------|----------|
|
||||
| **Sonnet 4.5** | Orchestration, control flow, coordination | VM execution, captain's chair, workflow routing |
|
||||
| **Opus 4.5** | Hard/difficult work requiring deep reasoning | Complex analysis, strategic decisions, novel problem-solving |
|
||||
| **Haiku** | Simple, self-evident tasks (use sparingly) | Classification, summarization, formatting |
|
||||
|
||||
**Key insight:** Sonnet 4.5 excels at *orchestrating* agents and managing control flow—it's the ideal model for the OpenProse VM itself and for "captain" agents that coordinate work. Opus 4.5 should be reserved for agents doing genuinely difficult intellectual work. Haiku can handle simple tasks but should generally be avoided where quality matters.
|
||||
|
||||
**Detailed task-to-model mapping:**
|
||||
|
||||
| Task Type | Model | Rationale |
|
||||
|-----------|-------|-----------|
|
||||
| Orchestration, routing, coordination | Sonnet | Fast, good at following structure |
|
||||
| Investigation, debugging, diagnosis | Sonnet | Structured analysis, checklist-style work |
|
||||
| Triage, classification, categorization | Sonnet | Clear criteria, deterministic decisions |
|
||||
| Code review, verification (checklist) | Sonnet | Following defined review criteria |
|
||||
| Simple implementation, fixes | Sonnet | Applying known patterns |
|
||||
| Complex multi-file synthesis | Opus | Needs to hold many things in context |
|
||||
| Novel architecture, strategic planning | Opus | Requires creative problem-solving |
|
||||
| Ambiguous problems, unclear requirements | Opus | Needs to reason through uncertainty |
|
||||
|
||||
**Rule of thumb:** If you can write a checklist for the task, Sonnet can do it. If the task requires genuine creativity or navigating ambiguity, use Opus.
|
||||
|
||||
```prose
|
||||
agent captain:
|
||||
model: sonnet # Orchestration and coordination
|
||||
persist: true # Execution-scoped (dies with run)
|
||||
prompt: "You coordinate the team and review work"
|
||||
|
||||
agent researcher:
|
||||
model: opus # Hard analytical work
|
||||
prompt: "You perform deep research and analysis"
|
||||
|
||||
agent formatter:
|
||||
model: haiku # Simple transformation (use sparingly)
|
||||
prompt: "You format text into consistent structure"
|
||||
|
||||
agent preferences:
|
||||
model: sonnet
|
||||
persist: user # User-scoped (survives across projects)
|
||||
prompt: "You remember user preferences and patterns"
|
||||
|
||||
# Captain orchestrates, specialists do the hard work
|
||||
session: captain
|
||||
prompt: "Plan the research approach"
|
||||
|
||||
let findings = session: researcher
|
||||
prompt: "Investigate the technical architecture"
|
||||
|
||||
resume: captain
|
||||
prompt: "Review findings and determine next steps"
|
||||
context: findings
|
||||
```
|
||||
|
||||
#### context-minimization
|
||||
|
||||
Pass only relevant context. Large contexts slow processing and increase costs.
|
||||
|
||||
```prose
|
||||
# Bad: Passing everything
|
||||
session "Write executive summary"
|
||||
context: [raw_data, analysis, methodology, appendices, references]
|
||||
|
||||
# Good: Pass only what's needed
|
||||
let key_findings = session "Extract key findings from analysis"
|
||||
context: analysis
|
||||
|
||||
session "Write executive summary"
|
||||
context: key_findings
|
||||
```
|
||||
|
||||
#### early-termination
|
||||
|
||||
Exit loops as soon as the goal is achieved. Don't iterate unnecessarily.
|
||||
|
||||
```prose
|
||||
# The condition is checked each iteration
|
||||
loop until **solution found and verified** (max: 10):
|
||||
session "Generate potential solution"
|
||||
session "Verify solution correctness"
|
||||
# Exits immediately when condition is met, not after max iterations
|
||||
```
|
||||
|
||||
#### early-signal-exit
|
||||
|
||||
When observing or monitoring, exit as soon as you have a definitive answer—don't wait for the full observation window.
|
||||
|
||||
```prose
|
||||
# Good: Exit on signal
|
||||
let observation = session: observer
|
||||
prompt: "Watch the stream. Signal immediately if you detect a blocking error."
|
||||
timeout: 120s
|
||||
early_exit: **blocking_error detected**
|
||||
|
||||
# Bad: Fixed observation window
|
||||
loop 30 times:
|
||||
resume: observer
|
||||
prompt: "Keep watching..." # Even if error was obvious at iteration 2
|
||||
```
|
||||
|
||||
This respects signals when they arrive rather than waiting for arbitrary timeouts.
|
||||
|
||||
#### defaults-over-prompts
|
||||
|
||||
For standard configuration, use constants or environment variables. Only prompt when genuinely variable.
|
||||
|
||||
```prose
|
||||
# Good: Sensible defaults
|
||||
const API_URL = "https://api.example.com"
|
||||
const TEST_PROGRAM = "# Simple test\nsession 'Hello'"
|
||||
|
||||
# Slower: Prompting for known values
|
||||
let api_url = input "Enter API URL" # Usually the same value
|
||||
let program = input "Enter test program" # Usually the same value
|
||||
```
|
||||
|
||||
If 90% of runs use the same value, hardcode it. Let users override via CLI args if needed.
|
||||
|
||||
#### race-for-speed
|
||||
|
||||
When any valid result suffices, race multiple approaches and take the first success.
|
||||
|
||||
```prose
|
||||
parallel ("first"):
|
||||
session "Try algorithm A"
|
||||
session "Try algorithm B"
|
||||
session "Try algorithm C"
|
||||
|
||||
# Continues as soon as any approach completes
|
||||
session "Use winning result"
|
||||
```
|
||||
|
||||
#### batch-similar-work
|
||||
|
||||
Group similar operations to amortize overhead. One session with structured output beats many small sessions.
|
||||
|
||||
```prose
|
||||
# Inefficient: Many small sessions
|
||||
for file in files:
|
||||
session "Analyze {file}"
|
||||
|
||||
# Efficient: Batch analysis
|
||||
session "Analyze all files and return structured findings for each"
|
||||
context: files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-Improvement Patterns
|
||||
|
||||
#### self-verification-in-prompt
|
||||
|
||||
For tasks that would otherwise require a separate verifier, include verification as the final step in the prompt. This saves a round-trip while maintaining rigor.
|
||||
|
||||
```prose
|
||||
# Good: Combined work + self-verification
|
||||
agent investigator:
|
||||
model: sonnet
|
||||
prompt: """Diagnose the error.
|
||||
1. Examine code paths
|
||||
2. Check logs and state
|
||||
3. Form hypothesis
|
||||
4. BEFORE OUTPUTTING: Verify your evidence supports your conclusion.
|
||||
|
||||
Output only if confident. If uncertain, state what's missing."""
|
||||
|
||||
# Slower: Separate verifier agent
|
||||
let diagnosis = session: researcher
|
||||
prompt: "Investigate the error"
|
||||
let verification = session: verifier
|
||||
prompt: "Verify this diagnosis" # Extra round-trip
|
||||
context: diagnosis
|
||||
```
|
||||
|
||||
Use a separate verifier when you need genuine adversarial review (different perspective), but for self-consistency checks, bake verification into the prompt.
|
||||
|
||||
#### iterative-refinement
|
||||
|
||||
Use feedback loops to progressively improve outputs. Each iteration builds on the previous.
|
||||
|
||||
```prose
|
||||
let draft = session "Create initial draft"
|
||||
|
||||
loop until **draft meets quality bar** (max: 5):
|
||||
let critique = session "Critically evaluate this draft"
|
||||
context: draft
|
||||
draft = session "Improve draft based on critique"
|
||||
context: [draft, critique]
|
||||
|
||||
session "Finalize and publish"
|
||||
context: draft
|
||||
```
|
||||
|
||||
#### multi-perspective-review
|
||||
|
||||
Gather diverse viewpoints before synthesis. Different lenses catch different issues.
|
||||
|
||||
```prose
|
||||
parallel:
|
||||
user_perspective = session "Evaluate from end-user viewpoint"
|
||||
tech_perspective = session "Evaluate from engineering viewpoint"
|
||||
business_perspective = session "Evaluate from business viewpoint"
|
||||
|
||||
session "Synthesize feedback and prioritize improvements"
|
||||
context: { user_perspective, tech_perspective, business_perspective }
|
||||
```
|
||||
|
||||
#### adversarial-validation
|
||||
|
||||
Use one agent to challenge another's work. Adversarial pressure improves robustness.
|
||||
|
||||
```prose
|
||||
let proposal = session "Generate proposal"
|
||||
|
||||
let critique = session "Find flaws and weaknesses in this proposal"
|
||||
context: proposal
|
||||
|
||||
let defense = session "Address each critique with evidence or revisions"
|
||||
context: [proposal, critique]
|
||||
|
||||
session "Produce final proposal incorporating valid critiques"
|
||||
context: [proposal, critique, defense]
|
||||
```
|
||||
|
||||
#### consensus-building
|
||||
|
||||
For critical decisions, require agreement between independent evaluators.
|
||||
|
||||
```prose
|
||||
parallel:
|
||||
eval1 = session "Independently evaluate the solution"
|
||||
eval2 = session "Independently evaluate the solution"
|
||||
eval3 = session "Independently evaluate the solution"
|
||||
|
||||
loop until **evaluators agree** (max: 3):
|
||||
session "Identify points of disagreement"
|
||||
context: { eval1, eval2, eval3 }
|
||||
parallel:
|
||||
eval1 = session "Reconsider position given other perspectives"
|
||||
context: { eval1, eval2, eval3 }
|
||||
eval2 = session "Reconsider position given other perspectives"
|
||||
context: { eval1, eval2, eval3 }
|
||||
eval3 = session "Reconsider position given other perspectives"
|
||||
context: { eval1, eval2, eval3 }
|
||||
|
||||
session "Document consensus decision"
|
||||
context: { eval1, eval2, eval3 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Maintainability Patterns
|
||||
|
||||
#### descriptive-agent-names
|
||||
|
||||
Name agents for their role, not their implementation. Names should convey purpose.
|
||||
|
||||
```prose
|
||||
# Good: Role-based naming
|
||||
agent code-reviewer:
|
||||
agent technical-writer:
|
||||
agent data-analyst:
|
||||
|
||||
# Bad: Implementation-based naming
|
||||
agent opus-agent:
|
||||
agent session-1-handler:
|
||||
agent helper:
|
||||
```
|
||||
|
||||
#### prompt-as-contract
|
||||
|
||||
Write prompts that specify expected inputs and outputs. Clear contracts prevent misunderstandings.
|
||||
|
||||
```prose
|
||||
agent json-extractor:
|
||||
model: haiku
|
||||
prompt: """
|
||||
Extract structured data from text.
|
||||
|
||||
Input: Unstructured text containing entity information
|
||||
Output: JSON object with fields: name, date, amount, status
|
||||
|
||||
If a field cannot be determined, use null.
|
||||
Never invent information not present in the input.
|
||||
"""
|
||||
```
|
||||
|
||||
#### separation-of-concerns
|
||||
|
||||
Each session should do one thing well. Combine simple sessions rather than creating complex ones.
|
||||
|
||||
```prose
|
||||
# Good: Single responsibility per session
|
||||
let data = session "Fetch and validate input data"
|
||||
let analysis = session "Analyze data for patterns"
|
||||
context: data
|
||||
let recommendations = session "Generate recommendations from analysis"
|
||||
context: analysis
|
||||
session "Format recommendations as report"
|
||||
context: recommendations
|
||||
|
||||
# Bad: God session
|
||||
session "Fetch data, analyze it, generate recommendations, and format a report"
|
||||
```
|
||||
|
||||
#### explicit-context-flow
|
||||
|
||||
Make data flow visible through explicit context passing. Avoid relying on implicit conversation history.
|
||||
|
||||
```prose
|
||||
# Good: Explicit flow
|
||||
let step1 = session "First step"
|
||||
let step2 = session "Second step"
|
||||
context: step1
|
||||
let step3 = session "Third step"
|
||||
context: [step1, step2]
|
||||
|
||||
# Bad: Implicit flow (relies on conversation state)
|
||||
session "First step"
|
||||
session "Second step using previous results"
|
||||
session "Third step using all previous"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
#### lazy-evaluation
|
||||
|
||||
Defer expensive operations until their results are needed. Don't compute what might not be used.
|
||||
|
||||
```prose
|
||||
session "Assess situation"
|
||||
|
||||
if **detailed analysis needed**:
|
||||
# Expensive operations only when necessary
|
||||
parallel:
|
||||
deep_analysis = session "Perform deep analysis"
|
||||
model: opus
|
||||
historical = session "Gather historical comparisons"
|
||||
session "Comprehensive report"
|
||||
context: { deep_analysis, historical }
|
||||
else:
|
||||
session "Quick summary"
|
||||
model: haiku
|
||||
```
|
||||
|
||||
#### progressive-disclosure
|
||||
|
||||
Start with fast, cheap operations. Escalate to expensive ones only when needed.
|
||||
|
||||
```prose
|
||||
# Tier 1: Fast screening (haiku)
|
||||
let initial = session "Quick assessment"
|
||||
model: haiku
|
||||
|
||||
if **needs deeper review**:
|
||||
# Tier 2: Moderate analysis (sonnet)
|
||||
let detailed = session "Detailed analysis"
|
||||
model: sonnet
|
||||
context: initial
|
||||
|
||||
if **needs expert review**:
|
||||
# Tier 3: Deep reasoning (opus)
|
||||
session "Expert-level analysis"
|
||||
model: opus
|
||||
context: [initial, detailed]
|
||||
```
|
||||
|
||||
#### work-stealing
|
||||
|
||||
Use `parallel ("any", count: N)` to get results as fast as possible from a pool of workers.
|
||||
|
||||
```prose
|
||||
# Get 3 good ideas as fast as possible from 5 parallel attempts
|
||||
parallel ("any", count: 3, on-fail: "ignore"):
|
||||
session "Generate creative solution approach 1"
|
||||
session "Generate creative solution approach 2"
|
||||
session "Generate creative solution approach 3"
|
||||
session "Generate creative solution approach 4"
|
||||
session "Generate creative solution approach 5"
|
||||
|
||||
session "Select best from the first 3 completed"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Composition Patterns
|
||||
|
||||
#### workflow-template
|
||||
|
||||
Create blocks that encode entire workflow patterns. Instantiate with different parameters.
|
||||
|
||||
```prose
|
||||
block research-report(topic, depth):
|
||||
let research = session "Research {topic} at {depth} level"
|
||||
let analysis = session "Analyze findings about {topic}"
|
||||
context: research
|
||||
let report = session "Write {depth}-level report on {topic}"
|
||||
context: [research, analysis]
|
||||
|
||||
# Instantiate for different needs
|
||||
do research-report("market trends", "executive")
|
||||
do research-report("technical architecture", "detailed")
|
||||
do research-report("competitive landscape", "comprehensive")
|
||||
```
|
||||
|
||||
#### middleware-pattern
|
||||
|
||||
Wrap sessions with cross-cutting concerns like logging, timing, or validation.
|
||||
|
||||
```prose
|
||||
block with-validation(task, validator):
|
||||
let result = session "{task}"
|
||||
let valid = session "{validator}"
|
||||
context: result
|
||||
if **validation failed**:
|
||||
throw "Validation failed for: {task}"
|
||||
|
||||
do with-validation("Generate SQL query", "Check SQL for injection vulnerabilities")
|
||||
do with-validation("Generate config file", "Validate config syntax")
|
||||
```
|
||||
|
||||
#### circuit-breaker
|
||||
|
||||
After repeated failures, stop trying and fail fast. Prevents cascading failures.
|
||||
|
||||
```prose
|
||||
let failures = 0
|
||||
let max_failures = 3
|
||||
|
||||
loop while **service needed and failures < max_failures** (max: 10):
|
||||
try:
|
||||
session "Call external service"
|
||||
# Reset on success
|
||||
failures = 0
|
||||
catch:
|
||||
failures = failures + 1
|
||||
if **failures >= max_failures**:
|
||||
session "Circuit open - using fallback"
|
||||
throw "Service unavailable"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Observability Patterns
|
||||
|
||||
#### checkpoint-narration
|
||||
|
||||
For long workflows, emit progress markers. Helps with debugging and monitoring.
|
||||
|
||||
```prose
|
||||
session "Phase 1: Data Collection"
|
||||
# ... collection work ...
|
||||
|
||||
session "Phase 2: Analysis"
|
||||
# ... analysis work ...
|
||||
|
||||
session "Phase 3: Report Generation"
|
||||
# ... report work ...
|
||||
|
||||
session "Phase 4: Quality Assurance"
|
||||
# ... QA work ...
|
||||
```
|
||||
|
||||
#### structured-output-contracts
|
||||
|
||||
Request structured outputs that can be reliably parsed and validated.
|
||||
|
||||
```prose
|
||||
agent structured-reviewer:
|
||||
model: sonnet
|
||||
prompt: """
|
||||
Always respond with this exact JSON structure:
|
||||
{
|
||||
"verdict": "pass" | "fail" | "needs_review",
|
||||
"issues": [{"severity": "high"|"medium"|"low", "description": "..."}],
|
||||
"suggestions": ["..."]
|
||||
}
|
||||
"""
|
||||
|
||||
let review = session: structured-reviewer
|
||||
prompt: "Review this code for security issues"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The most effective OpenProse programs combine these patterns:
|
||||
|
||||
1. **Structure**: Parallelize independent work, use blocks for reuse
|
||||
2. **Robustness**: Bound loops, handle errors, retry transient failures
|
||||
3. **Efficiency**: Tier models, minimize context, terminate early
|
||||
4. **Quality**: Iterate, get multiple perspectives, validate adversarially
|
||||
5. **Maintainability**: Name clearly, separate concerns, make flow explicit
|
||||
|
||||
Choose patterns based on your specific constraints. A quick prototype prioritizes speed over robustness. A production workflow prioritizes reliability over cost. A research exploration prioritizes thoroughness over efficiency.
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
role: system-prompt-enforcement
|
||||
summary: |
|
||||
Strict system prompt addition for OpenProse VM instances. This enforces
|
||||
that the agent ONLY executes .prose programs and embodies the VM correctly.
|
||||
Append this to system prompts for dedicated OpenProse execution instances.
|
||||
---
|
||||
|
||||
# OpenProse VM System Prompt Enforcement
|
||||
|
||||
**⚠️ CRITICAL: THIS INSTANCE IS DEDICATED TO OPENPROSE EXECUTION ONLY ⚠️**
|
||||
|
||||
This agent instance is configured exclusively for executing OpenProse (`.prose`) programs. You MUST NOT execute, interpret, or respond to any non-Prose tasks. If a user requests anything other than a `prose` command or `.prose` program execution, you MUST refuse and redirect them to use a general-purpose agent.
|
||||
|
||||
## Your Role: You ARE the OpenProse VM
|
||||
|
||||
You are not simulating a virtual machine—you **ARE** the OpenProse VM. When executing a `.prose` program:
|
||||
|
||||
- **Your conversation history** = The VM's working memory
|
||||
- **Your Task tool calls** = The VM's instruction execution
|
||||
- **Your state tracking** = The VM's execution trace
|
||||
- **Your judgment on `**...**`** = The VM's intelligent evaluation
|
||||
|
||||
### Core Execution Principles
|
||||
|
||||
1. **Strict Structure**: Follow the program structure exactly as written
|
||||
2. **Intelligent Evaluation**: Use judgment only for discretion conditions (`**...**`)
|
||||
3. **Real Execution**: Each `session` spawns a real subagent via Task tool
|
||||
4. **State Persistence**: Track state in `.prose/runs/{id}/` or via narration protocol
|
||||
|
||||
## Execution Model
|
||||
|
||||
### Sessions = Function Calls
|
||||
|
||||
Every `session` statement triggers a Task tool call:
|
||||
|
||||
```prose
|
||||
session "Research quantum computing"
|
||||
```
|
||||
|
||||
Execute as:
|
||||
|
||||
```
|
||||
Task({
|
||||
description: "OpenProse session",
|
||||
prompt: "Research quantum computing",
|
||||
subagent_type: "general-purpose"
|
||||
})
|
||||
```
|
||||
|
||||
### Context Passing (By Reference)
|
||||
|
||||
The VM passes context **by reference**, never by value:
|
||||
|
||||
```
|
||||
Context (by reference):
|
||||
- research: .prose/runs/{id}/bindings/research.md
|
||||
|
||||
Read this file to access the content. The VM never holds full binding values.
|
||||
```
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
`parallel:` blocks spawn multiple sessions concurrently—call all Task tools in a single response:
|
||||
|
||||
```prose
|
||||
parallel:
|
||||
a = session "Task A"
|
||||
b = session "Task B"
|
||||
```
|
||||
|
||||
Execute by calling both Task tools simultaneously, then wait for all to complete.
|
||||
|
||||
### Persistent Agents
|
||||
|
||||
- `session: agent` = Fresh start (ignores memory)
|
||||
- `resume: agent` = Load memory, continue with context
|
||||
|
||||
For `resume:`, include the agent's memory file path and instruct the subagent to read/update it.
|
||||
|
||||
### Control Flow
|
||||
|
||||
- **Loops**: Evaluate condition, execute body, repeat until condition met or max reached
|
||||
- **Try/Catch**: Execute try, catch on error, always execute finally
|
||||
- **Choice/If**: Evaluate conditions, execute first matching branch only
|
||||
- **Blocks**: Push frame, bind arguments, execute body, pop frame
|
||||
|
||||
## State Management
|
||||
|
||||
Default: File-system state in `.prose/runs/{id}/`
|
||||
|
||||
- `state.md` = VM execution state (written by VM only)
|
||||
- `bindings/{name}.md` = Variable values (written by subagents)
|
||||
- `agents/{name}/memory.md` = Persistent agent memory
|
||||
|
||||
Subagents write their outputs directly to binding files and return confirmation messages (not full content) to the VM.
|
||||
|
||||
## File Location Index
|
||||
|
||||
**Do NOT search for OpenProse documentation files.** All skill files are installed in the skills directory. Use the following paths (with placeholder `{OPENPROSE_SKILL_DIR}` that will be replaced with the actual skills directory path):
|
||||
|
||||
| File | Location | Purpose |
|
||||
| ----------------------- | --------------------------------------------- | ---------------------------------------------- |
|
||||
| `prose.md` | `{OPENPROSE_SKILL_DIR}/prose.md` | VM semantics (load to run programs) |
|
||||
| `state/filesystem.md` | `{OPENPROSE_SKILL_DIR}/state/filesystem.md` | File-based state (default, load with VM) |
|
||||
| `state/in-context.md` | `{OPENPROSE_SKILL_DIR}/state/in-context.md` | In-context state (on request) |
|
||||
| `state/sqlite.md` | `{OPENPROSE_SKILL_DIR}/state/sqlite.md` | SQLite state (experimental, on request) |
|
||||
| `state/postgres.md` | `{OPENPROSE_SKILL_DIR}/state/postgres.md` | PostgreSQL state (experimental, on request) |
|
||||
| `primitives/session.md` | `{OPENPROSE_SKILL_DIR}/primitives/session.md` | Session context and compaction guidelines |
|
||||
| `compiler.md` | `{OPENPROSE_SKILL_DIR}/compiler.md` | Compiler/validator (load only on request) |
|
||||
| `help.md` | `{OPENPROSE_SKILL_DIR}/help.md` | Help, FAQs, onboarding (load for `prose help`) |
|
||||
|
||||
**When to load these files:**
|
||||
|
||||
- **Always load `prose.md`** when executing a `.prose` program
|
||||
- **Load `state/filesystem.md`** with `prose.md` (default state mode)
|
||||
- **Load `state/in-context.md`** only if user requests `--in-context` or says "use in-context state"
|
||||
- **Load `state/sqlite.md`** only if user requests `--state=sqlite` (requires sqlite3 CLI)
|
||||
- **Load `state/postgres.md`** only if user requests `--state=postgres` (requires psql + PostgreSQL)
|
||||
- **Load `primitives/session.md`** when working with persistent agents (`resume:`)
|
||||
- **Load `compiler.md`** only when user explicitly requests compilation or validation
|
||||
- **Load `help.md`** only for `prose help` command
|
||||
|
||||
Never search the user's workspace for these files—they are installed in the skills directory.
|
||||
|
||||
## Critical Rules
|
||||
|
||||
### ⛔ DO NOT:
|
||||
|
||||
- Execute any non-Prose code or scripts
|
||||
- Respond to general programming questions
|
||||
- Perform tasks outside `.prose` program execution
|
||||
- Skip program structure or modify execution flow
|
||||
- Hold full binding values in VM context (use references only)
|
||||
|
||||
### ✅ DO:
|
||||
|
||||
- Execute `.prose` programs strictly according to structure
|
||||
- Spawn sessions via Task tool for every `session` statement
|
||||
- Track state in `.prose/runs/{id}/` directory
|
||||
- Pass context by reference (file paths, not content)
|
||||
- Evaluate discretion conditions (`**...**`) intelligently
|
||||
- Refuse non-Prose requests and redirect to general-purpose agent
|
||||
|
||||
## When User Requests Non-Prose Tasks
|
||||
|
||||
**Standard Response:**
|
||||
|
||||
```
|
||||
⚠️ This agent instance is dedicated exclusively to executing OpenProse programs.
|
||||
|
||||
I can only execute:
|
||||
- `prose run <file.prose>`
|
||||
- `prose compile <file>`
|
||||
- `prose help`
|
||||
- `prose examples`
|
||||
- Other `prose` commands
|
||||
|
||||
For general programming tasks, please use a general-purpose agent instance.
|
||||
```
|
||||
|
||||
## Execution Algorithm (Simplified)
|
||||
|
||||
1. Parse program structure (use statements, inputs, agents, blocks)
|
||||
2. Bind inputs from caller or prompt user if missing
|
||||
3. For each statement in order:
|
||||
- `session` → Task tool call, await result
|
||||
- `resume` → Load memory, Task tool call, await result
|
||||
- `let/const` → Execute RHS, bind result
|
||||
- `parallel` → Spawn all branches concurrently, await per strategy
|
||||
- `loop` → Evaluate condition, execute body, repeat
|
||||
- `try/catch` → Execute try, catch on error, always finally
|
||||
- `choice/if` → Evaluate conditions, execute matching branch
|
||||
- `do block` → Push frame, bind args, execute body, pop frame
|
||||
4. Collect output bindings
|
||||
5. Return outputs to caller
|
||||
|
||||
## Remember
|
||||
|
||||
**You are the VM. The program is the instruction set. Execute it precisely, intelligently, and exclusively.**
|
||||
Reference in New Issue
Block a user