Tutorial How To Write 80 20 Tools - 80-20-Human-In-The-Loop/Community GitHub Wiki
How To Write Tools That Embody the 80-20 Human in The Loop Philosophy
Build tools that make humans smarter, not obsolete. Automate the mundane, preserve the meaningful.
Table of Contents
- Core Philosophy
- Three Audiences Pattern
- Design Principles
- Implementation Patterns
- Real-World Examples
- Technical Implementation
- Testing Your Tool
- Common Pitfalls
- Community Integration
Core Philosophy
The 80-20 Human in The Loop philosophy recognizes that effective tools must balance automation with human learning and decision-making.
The 80-20 Balance
80% Automation: Let tools handle:
- Repetitive analysis
- Pattern detection
- Data collection
- Formatting and structure
- Known problem identification
20% Human Intelligence: Preserve human involvement for:
- Understanding context
- Making architectural decisions
- Learning from patterns
- Ethical considerations
- Creative problem-solving
Why This Matters
When we over-automate, we create developers who cannot debug their own systems. When we under-automate, we waste human potential on repetitive tasks. The 80-20 balance creates tools that enhance human capability without replacing human understanding.
Three Audiences Pattern
When building tools within this ecosystem, you must design for three distinct audiences:
1. Beginners/Students (--edu flag)
Characteristics:
- Need additional guidance and explanation
- Learn through tool usage
- Start with slower, more educational workflow
- Build understanding progressively
Implementation:
# Example: Educational mode with detailed explanations
$ your-tool analyze --edu
š Educational Mode Active
ā Analyzing your code for performance issues...
ā Found 3 issues. Let's understand each one:
Issue 1: N+1 Query Problem
š What this means: Your code makes 1 query to get a list,
then 1 query for each item. With 100 items = 101 queries!
š” Why it matters: Each query takes time. More queries = slower app.
š§ How to fix: Use select_related() or prefetch_related()
š Learn more: https://docs.djangoproject.com/en/stable/n+1-queries
2. Experts/Professionals (Default)
Characteristics:
- Expect tools to work fast and efficiently
- Already understand the concepts
- Need concise, actionable output
- Use advanced features regularly
Implementation:
# Example: Professional mode - direct and efficient
$ your-tool analyze
ā Analysis complete (0.3s)
- N+1 queries: views.py:45, views.py:78
- Missing indexes: models.py:23 (User.email)
- Slow query: queries.py:112 (avg 342ms)
Run with --details for more information
3. AI Agents/LLMs (--agent flag, MCP servers)
Characteristics:
- Need structured, parseable output
- Can process large amounts of data
- Require clear action items
- Must preserve human decision points
Implementation:
# Example: Agent mode - structured JSON output
$ your-tool analyze --agent
{
"status": "complete",
"issues": [
{
"type": "n_plus_one",
"severity": "high",
"location": "views.py:45",
"suggestion": "Add select_related('author')",
"requires_human_review": true,
"reason": "Architectural decision needed"
}
],
"metrics": {
"total_queries": 156,
"slow_queries": 3,
"optimization_potential": "68%"
}
}
Design Principles
1. Progressive Disclosure
Start simple, reveal complexity as needed:
class PerformanceTool:
def analyze(self, edu_mode=False, detail_level=1):
issues = self.detect_issues()
if edu_mode:
# Educational: Full explanation
return self.format_educational(issues)
elif detail_level > 2:
# Expert: Detailed technical output
return self.format_detailed(issues)
else:
# Default: Concise summary
return self.format_summary(issues)
2. Educational Opportunities
Embed learning in normal usage:
def format_issue(self, issue, edu_mode=False):
output = f"Issue: {issue.type} at {issue.location}"
if edu_mode:
output += f"\nš Explanation: {issue.get_explanation()}"
output += f"\nš” Why this matters: {issue.get_impact()}"
output += f"\nš§ How to fix: {issue.get_solution()}"
output += f"\nš Learn more: {issue.get_resources()}"
return output
3. Preserve Human Decision Points
Never fully automate critical decisions:
def suggest_optimization(self, issue):
suggestion = self.generate_suggestion(issue)
# Always require human review for architectural changes
if issue.impacts_architecture:
return {
"suggestion": suggestion,
"auto_fix": False,
"reason": "Architectural decision requires human review",
"considerations": [
"Will this affect other components?",
"Is this aligned with system design?",
"Are there performance trade-offs?"
]
}
return {
"suggestion": suggestion,
"auto_fix": issue.is_trivial,
"confidence": self.calculate_confidence(issue)
}
4. Clear Feedback Loops
Provide immediate, understandable feedback:
def run_with_feedback(self, task):
# Visual progress for long operations
with self.progress_bar() as progress:
progress.update("Analyzing code structure...")
structure = self.analyze_structure()
progress.update("Detecting patterns...")
patterns = self.detect_patterns()
progress.update("Generating recommendations...")
recommendations = self.generate_recommendations()
# Clear result presentation
self.display_results(
structure,
patterns,
recommendations,
verbosity=self.get_verbosity_level()
)
Implementation Patterns
Command-Line Interface Pattern
Design flexible CLI that serves all audiences:
import argparse
def create_parser():
parser = argparse.ArgumentParser()
# Audience-specific flags
parser.add_argument('--edu', action='store_true',
help='Educational mode with detailed explanations')
parser.add_argument('--agent', action='store_true',
help='Agent mode with structured JSON output')
# Progressive complexity
parser.add_argument('--detail', type=int, default=1,
help='Detail level (1-5, default: 1)')
# Learning features
parser.add_argument('--explain', action='store_true',
help='Explain the analysis process')
parser.add_argument('--tutorial', action='store_true',
help='Run in tutorial mode')
return parser
Configuration Pattern
Support different workflows through configuration:
class ToolConfig:
PROFILES = {
'student': {
'explanations': True,
'pace': 'slow',
'hints': True,
'auto_fix': False,
'teach_concepts': True
},
'professional': {
'explanations': False,
'pace': 'fast',
'hints': False,
'auto_fix': True,
'teach_concepts': False
},
'agent': {
'format': 'json',
'explanations': False,
'decisions': 'defer_to_human',
'batch_mode': True
}
}
@classmethod
def load_profile(cls, profile_name):
return cls.PROFILES.get(profile_name, cls.PROFILES['professional'])
Output Formatting Pattern
Adapt output to audience needs:
class OutputFormatter:
def format(self, data, mode='default'):
if mode == 'educational':
return self._format_educational(data)
elif mode == 'agent':
return self._format_json(data)
else:
return self._format_concise(data)
def _format_educational(self, data):
output = []
output.append("="*50)
output.append("š LEARNING MOMENT")
output.append("="*50)
for item in data:
output.append(f"\nšÆ {item.title}")
output.append(f"š What: {item.description}")
output.append(f"š” Why: {item.importance}")
output.append(f"š§ How: {item.solution}")
output.append(f"š Learn: {item.resources}")
return "\n".join(output)
def _format_concise(self, data):
return "\n".join([
f"⢠{item.title}: {item.location}"
for item in data
])
def _format_json(self, data):
return json.dumps({
'issues': [item.to_dict() for item in data],
'metadata': {
'tool_version': self.version,
'timestamp': datetime.now().isoformat(),
'requires_human_review': any(
item.requires_human for item in data
)
}
}, indent=2)
Real-World Examples
Example 1: Storm Checker Pattern
Storm Checker demonstrates the three-audience pattern for type checking:
class StormChecker:
def check_types(self, path, edu=False, agent=False):
"""Check Python type annotations with audience-appropriate output."""
issues = self.run_mypy(path)
categorized = self.categorize_by_complexity(issues)
if edu:
# Educational mode: Teach type concepts
self.display_educational(categorized)
self.offer_interactive_fixing(categorized)
self.show_type_tutorial_progress()
elif agent:
# Agent mode: Structured data for automation
return {
'fixable_automatically': categorized['simple'],
'requires_human': categorized['complex'],
'learning_opportunities': categorized['educational']
}
else:
# Professional mode: Efficient summary
self.display_summary(categorized)
if categorized['simple']:
self.offer_quick_fix(categorized['simple'])
Example 2: Django Mercury Pattern
Django Mercury shows progressive enhancement in performance testing:
class DjangoMercuryTestCase:
def run_performance_test(self, test_func):
"""Run test with performance monitoring."""
# Collect metrics
metrics = self.collect_metrics(test_func)
# Adapt output to user level
if self.educational_mode:
self.explain_metrics(metrics)
self.teach_optimization(metrics)
self.suggest_learning_path(metrics)
elif self.agent_mode:
return self.format_for_agent(metrics)
else:
self.display_grade(metrics)
if metrics.grade < 'B':
self.suggest_improvements(metrics)
Example 3: MCP Server Pattern
MCP servers enable AI agent integration while preserving human control:
class ToolMCPServer:
@tool()
def analyze_code(self, path: str, auto_fix: bool = False):
"""Analyze code with human-in-the-loop safeguards."""
issues = self.detect_issues(path)
results = []
for issue in issues:
result = {
'issue': issue.description,
'severity': issue.severity,
'location': issue.location
}
if issue.can_auto_fix and auto_fix:
if issue.requires_human_review:
result['action'] = 'requires_human_review'
result['reason'] = issue.human_review_reason
else:
result['action'] = 'auto_fixed'
result['fix'] = self.apply_fix(issue)
else:
result['action'] = 'suggestion'
result['suggestion'] = issue.suggested_fix
results.append(result)
return results
Technical Implementation
Error Messages That Teach
Transform errors into learning opportunities:
class EducationalError(Exception):
def __init__(self, message, explanation=None, suggestion=None, resources=None):
self.message = message
self.explanation = explanation
self.suggestion = suggestion
self.resources = resources
def display(self, edu_mode=False):
output = f"ā Error: {self.message}"
if edu_mode and self.explanation:
output += f"\n\nš What happened: {self.explanation}"
output += f"\nš” Why: This usually occurs when {self.get_common_cause()}"
output += f"\nš§ Fix: {self.suggestion}"
output += f"\nš Learn more: {', '.join(self.resources)}"
return output
Progress Indicators That Educate
Make waiting time valuable:
class EducationalProgress:
def __init__(self, edu_mode=False):
self.edu_mode = edu_mode
self.tips = [
"Did you know? Indexing can improve query speed by 100x",
"Tip: Use prefetch_related() for many-to-many relationships",
"Fun fact: The first database query is often the slowest (cold cache)"
]
def update(self, task, percentage):
if self.edu_mode and percentage % 20 == 0:
# Show educational tips during processing
tip = random.choice(self.tips)
print(f"š” {tip}")
# Standard progress update
self.display_bar(task, percentage)
Metrics Collection Pattern
Gather data to support all three audiences:
class MetricsCollector:
def collect(self, operation):
metrics = {
'duration': self.measure_duration(operation),
'memory': self.measure_memory(operation),
'queries': self.count_queries(operation),
'complexity': self.calculate_complexity(operation)
}
# Add educational context
metrics['educational'] = {
'is_optimal': metrics['queries'] < 5,
'bottleneck': self.identify_bottleneck(metrics),
'improvement_potential': self.calculate_potential(metrics)
}
# Add agent context
metrics['agent'] = {
'auto_optimizable': self.can_auto_optimize(metrics),
'confidence': self.optimization_confidence(metrics),
'human_review_needed': metrics['complexity'] > 7
}
return metrics
Testing Your Tool
Validate Educational Value
Test that your tool teaches effectively:
def test_educational_mode():
"""Ensure educational mode provides learning value."""
tool = YourTool(edu_mode=True)
output = tool.analyze(sample_code)
# Check for educational elements
assert "What this means" in output
assert "Why it matters" in output
assert "How to fix" in output
assert "Learn more" in output
# Verify progressive learning
assert tool.tracks_user_progress()
assert tool.adjusts_to_skill_level()
Ensure Expert Efficiency
Test that professional mode is fast and focused:
def test_professional_mode():
"""Ensure professional mode is efficient."""
tool = YourTool(edu_mode=False)
start_time = time.time()
output = tool.analyze(large_codebase)
duration = time.time() - start_time
# Should be fast
assert duration < 5.0 # seconds
# Should be concise
assert len(output.split('\n')) < 50
# Should be actionable
assert tool.provides_quick_fixes()
Test AI Agent Integration
Verify agent mode provides appropriate automation:
def test_agent_mode():
"""Ensure agent mode preserves human decision points."""
tool = YourTool(agent_mode=True)
result = tool.analyze(complex_code)
# Should return structured data
assert isinstance(result, dict)
assert 'requires_human_review' in result
# Should not auto-fix critical issues
critical_issues = [i for i in result['issues'] if i['severity'] == 'critical']
for issue in critical_issues:
assert not issue['auto_fixed']
assert issue['requires_human_review']
Common Pitfalls
Pitfall 1: Over-Automation
Problem: Tool does everything automatically, users learn nothing.
Solution: Always require human understanding for important decisions:
# Bad: Full automation
def fix_all_issues(code):
for issue in detect_issues(code):
apply_fix(issue) # User learns nothing
# Good: Human in the loop
def fix_issues_with_review(code, auto_fix_trivial=True):
for issue in detect_issues(code):
if issue.is_trivial and auto_fix_trivial:
apply_fix(issue)
explain_what_was_fixed(issue)
else:
present_issue_to_human(issue)
teach_fix_strategy(issue)
if user_approves():
apply_fix(issue)
Pitfall 2: Information Overload
Problem: Educational mode drowns users in information.
Solution: Progressive disclosure based on user engagement:
# Bad: Dump everything
def explain_issue(issue):
print(issue.full_technical_explanation) # 500 lines of text
# Good: Progressive depth
def explain_issue(issue, depth=1):
print(issue.summary) # One line
if user_wants_more():
print(issue.explanation) # Paragraph
if user_wants_even_more():
print(issue.technical_details) # Full details
offer_interactive_tutorial()
Pitfall 3: Unclear Audience Separation
Problem: Mixing output styles confuses users.
Solution: Clear mode separation with consistent behavior:
# Bad: Mixed signals
def display_results(results, flags):
print("Issue found!") # Casual
print(json.dumps(results)) # Technical
print("š” Did you know...") # Educational
# Good: Clear separation
def display_results(results, mode):
formatter = get_formatter(mode)
print(formatter.format(results))
# Each formatter is consistent within itself
Pitfall 4: Neglecting Human Growth
Problem: Tool doesn't help users improve over time.
Solution: Track progress and adapt:
class GrowthTracker:
def track_user_progress(self, user_id, issue_fixed):
# Record what user learned
self.record_learning(user_id, issue_fixed.concept)
# Adjust future guidance
if self.user_has_mastered(user_id, issue_fixed.concept):
self.reduce_explanation_detail(user_id, issue_fixed.concept)
self.introduce_advanced_concepts(user_id)
Community Integration
Documentation Standards
Follow the community's writing principles:
- Write for Translation: Simple, clear language
- Progressive Complexity: Start simple, add depth
- Global Accessibility: Consider bandwidth and hardware limitations
- Educational Focus: Every tool should teach something
Creating Tutorials
Build tutorials that serve all audiences:
# Tutorial: Using YourTool
## Quick Start (Everyone)
```bash
$ yourtool analyze mycode.py
For Students
Want to understand what's happening? Use educational mode:
$ yourtool analyze mycode.py --edu
This will explain each issue and teach you how to fix it.
For Professionals
Need fast results? YourTool works efficiently by default:
$ yourtool analyze . --fix
For AI Integration
Building an AI workflow? Use agent mode:
$ yourtool analyze --agent --output json
### Building MCP Servers
Create MCP servers that preserve human control:
```python
from mcp import tool, server
@server(name="yourtool-mcp")
class YourToolMCP:
@tool()
def analyze(self, path: str, education_level: str = "normal"):
"""Analyze code while preserving human learning."""
# Never fully automate critical decisions
results = self.tool.analyze(path)
return {
"automated_fixes": self.get_safe_fixes(results),
"requires_human": self.get_critical_issues(results),
"learning_opportunities": self.get_educational_items(results)
}
Contributing to the Ecosystem
When contributing tools:
- Provide Multiple Interfaces: CLI, API, and MCP
- Document Educational Value: What will users learn?
- Include Examples: Show all three modes in action
- Test Accessibility: Ensure tools work on limited hardware
- Share Knowledge: Write about your design decisions
Conclusion
Building 80-20 tools requires thoughtful balance. Your tool should:
- Empower beginners to learn while doing
- Enable experts to work efficiently
- Allow AI agents to automate safely
- Preserve human wisdom in critical decisions
- Create opportunities for growth and understanding
Remember: The goal is not to replace human intelligence but to augment it. Build tools that make developers smarter, not more dependent.
Resources
- 80-20 Human in The Loop Organization
- Storm Checker Example
- Django Mercury Example
- Community Discussions
Building tools that preserve human wisdom while embracing AI efficiency - together, we create a better future for development.