Skip to content

Self-Improvement Guide

Learning from execution results to improve orchestration.

Execution Metrics to Track

Monitor key metrics from LionAGI workflow executions.

from lionagi import Session, Builder, Branch, iModel
import time
import asyncio

async def track_execution_metrics():
    """Track key metrics for improvement"""
    session = Session()
    builder = Builder("tracked_workflow")

    branch = Branch(
        chat_model=iModel(provider="openai", model="gpt-4o-mini")
    )
    session.include_branches([branch])

    # Add operations
    op1 = builder.add_operation("communicate", branch=branch, instruction="Task 1")
    op2 = builder.add_operation("communicate", branch=branch, instruction="Task 2")
    synthesis = builder.add_aggregation(
        "communicate", branch=branch,
        source_node_ids=[op1, op2],
        instruction="Synthesize results"
    )

    # Execute and track metrics
    start_time = time.time()
    result = await session.flow(builder.get_graph(), max_concurrent=2, verbose=True)
    execution_time = time.time() - start_time

    # Extract metrics
    metrics = {
        "execution_time": execution_time,
        "completed_operations": len(result["completed_operations"]),
        "failed_operations": len(result["skipped_operations"]),
        "success_rate": len(result["completed_operations"]) / (len(result["completed_operations"]) + len(result["skipped_operations"])),
        "parallel_efficiency": len(result["completed_operations"]) / execution_time,
        "pattern_used": "builder_graph"
    }

    print(f"Metrics: {metrics}")
    return metrics

asyncio.run(track_execution_metrics())

Pattern Performance Analysis

Compare different orchestration patterns for similar tasks.

async def pattern_comparison():
    """Compare patterns for the same task set"""

    tasks = ["Analyze market", "Research competitors", "Assess risks"]
    results = {}

    # Pattern 1: Sequential execution
    start_time = time.time()
    branch = Branch(chat_model=iModel(provider="openai", model="gpt-4o-mini"))
    sequential_results = []
    for task in tasks:
        result = await branch.communicate(task)
        sequential_results.append(result)

    results["sequential"] = {
        "time": time.time() - start_time,
        "pattern": "sequential",
        "results": len(sequential_results)
    }

    # Pattern 2: Parallel execution
    start_time = time.time()
    parallel_results = await asyncio.gather(*[
        branch.communicate(task) for task in tasks
    ])

    results["parallel"] = {
        "time": time.time() - start_time,
        "pattern": "asyncio_gather", 
        "results": len(parallel_results)
    }

    # Pattern 3: Builder graph
    start_time = time.time()
    session = Session()
    builder = Builder("comparison")
    session.include_branches([branch])

    ops = [builder.add_operation("communicate", branch=branch, instruction=task) for task in tasks]
    synthesis = builder.add_aggregation("communicate", branch=branch, source_node_ids=ops, instruction="Combine")

    graph_result = await session.flow(builder.get_graph(), max_concurrent=3)

    results["builder"] = {
        "time": time.time() - start_time,
        "pattern": "builder_graph",
        "results": len(graph_result["completed_operations"])
    }

    # Analysis
    print("Pattern Performance Comparison:")
    for pattern, metrics in results.items():
        print(f"{pattern}: {metrics['time']:.2f}s, {metrics['results']} results")

    # Best pattern for this task type
    fastest = min(results.items(), key=lambda x: x[1]["time"])
    print(f"Best pattern: {fastest[0]} ({fastest[1]['time']:.2f}s)")

    return results

asyncio.run(pattern_comparison())

Failure Analysis and Learning

Learn from execution failures to improve future orchestration.

async def failure_analysis():
    """Analyze failures to improve future executions"""

    failure_log = []

    # Simulate different failure scenarios
    test_scenarios = [
        {"pattern": "direct", "task": "Complex multi-step analysis"},
        {"pattern": "gather", "task": "Sequential dependent tasks"},
        {"pattern": "builder", "task": "Simple single question"}
    ]

    for scenario in test_scenarios:
        try:
            if scenario["pattern"] == "direct":
                branch = Branch(chat_model=iModel(provider="openai", model="gpt-4o-mini"))
                # This might fail for complex tasks
                result = await branch.communicate(scenario["task"])

            elif scenario["pattern"] == "gather":
                # This might fail for dependent tasks
                branch = Branch(chat_model=iModel(provider="openai", model="gpt-4o-mini"))
                results = await asyncio.gather(*[
                    branch.communicate("Step 1 of analysis"),
                    branch.communicate("Step 2 that depends on Step 1")  # Problem: no dependency
                ])

            elif scenario["pattern"] == "builder":
                # This might be overkill for simple tasks
                session = Session()
                builder = Builder("simple")
                branch = Branch(chat_model=iModel(provider="openai", model="gpt-4o-mini"))
                session.include_branches([branch])

                op = builder.add_operation("communicate", branch=branch, instruction="What is 2+2?")
                result = await session.flow(builder.get_graph())

            print(f"✓ {scenario['pattern']} pattern worked for: {scenario['task']}")

        except Exception as e:
            failure_info = {
                "pattern": scenario["pattern"],
                "task": scenario["task"], 
                "error": str(e),
                "lesson": self.analyze_failure(scenario["pattern"], scenario["task"], str(e))
            }
            failure_log.append(failure_info)
            print(f"✗ {scenario['pattern']} failed: {e}")

    return failure_log

def analyze_failure(pattern, task, error):
    """Extract lessons from failures"""
    lessons = {
        ("direct", "multi-step"): "Use Builder for complex workflows with multiple steps",
        ("gather", "dependent"): "Use Builder with dependencies for sequential tasks",
        ("builder", "simple"): "Use direct execution for simple single tasks"
    }

    # Simple pattern matching for lessons
    for key, lesson in lessons.items():
        if key[0] in pattern.lower() and any(word in task.lower() for word in key[1].split("-")):
            return lesson

    return "Review pattern selection guide"

asyncio.run(failure_analysis())

Optimization Loop

Implement continuous improvement based on execution history.

class ExecutionOptimizer:
    """Learn from execution patterns and optimize future choices"""

    def __init__(self):
        self.execution_history = []
        self.pattern_performance = {}

    def record_execution(self, task_type: str, pattern: str, metrics: dict):
        """Record execution results for learning"""
        execution = {
            "task_type": task_type,
            "pattern": pattern,
            "execution_time": metrics.get("execution_time", 0),
            "success_rate": metrics.get("success_rate", 0),
            "efficiency": metrics.get("parallel_efficiency", 0)
        }

        self.execution_history.append(execution)

        # Update pattern performance
        key = (task_type, pattern)
        if key not in self.pattern_performance:
            self.pattern_performance[key] = []
        self.pattern_performance[key].append(execution)

    def recommend_pattern(self, task_type: str) -> str:
        """Recommend best pattern based on historical performance"""

        # Find all patterns used for this task type
        relevant_patterns = {}
        for (stored_task, pattern), executions in self.pattern_performance.items():
            if stored_task == task_type:
                avg_time = sum(e["execution_time"] for e in executions) / len(executions)
                avg_success = sum(e["success_rate"] for e in executions) / len(executions)

                # Score: balance speed and success rate
                score = avg_success * 0.7 + (1/avg_time) * 0.3
                relevant_patterns[pattern] = score

        if relevant_patterns:
            best_pattern = max(relevant_patterns.items(), key=lambda x: x[1])
            return best_pattern[0]

        # Default fallback
        return "direct"

    def get_insights(self) -> dict:
        """Generate insights from execution history"""
        if not self.execution_history:
            return {"insight": "No execution history available"}

        # Pattern usage frequency
        pattern_usage = {}
        for execution in self.execution_history:
            pattern = execution["pattern"]
            pattern_usage[pattern] = pattern_usage.get(pattern, 0) + 1

        # Average performance by pattern
        pattern_performance = {}
        for execution in self.execution_history:
            pattern = execution["pattern"]
            if pattern not in pattern_performance:
                pattern_performance[pattern] = []
            pattern_performance[pattern].append(execution["execution_time"])

        avg_performance = {
            pattern: sum(times) / len(times) 
            for pattern, times in pattern_performance.items()
        }

        return {
            "most_used_pattern": max(pattern_usage.items(), key=lambda x: x[1])[0],
            "fastest_pattern": min(avg_performance.items(), key=lambda x: x[1])[0],
            "total_executions": len(self.execution_history),
            "pattern_usage": pattern_usage,
            "avg_performance": avg_performance
        }

async def optimization_example():
    """Example of using the optimization loop"""
    optimizer = ExecutionOptimizer()

    # Simulate some executions
    optimizer.record_execution("analysis", "direct", {"execution_time": 2.0, "success_rate": 1.0})
    optimizer.record_execution("analysis", "gather", {"execution_time": 1.5, "success_rate": 0.9})
    optimizer.record_execution("multi_step", "builder", {"execution_time": 3.0, "success_rate": 1.0})
    optimizer.record_execution("multi_step", "gather", {"execution_time": 2.0, "success_rate": 0.6})

    # Get recommendations
    print(f"Recommended pattern for 'analysis': {optimizer.recommend_pattern('analysis')}")
    print(f"Recommended pattern for 'multi_step': {optimizer.recommend_pattern('multi_step')}")

    # Get insights
    insights = optimizer.get_insights()
    print(f"Insights: {insights}")

    return optimizer

asyncio.run(optimization_example())

Knowledge Persistence

Save and retrieve learned orchestration patterns.

import json
import os

class PatternKnowledge:
    """Persist learned orchestration knowledge"""

    def __init__(self, knowledge_file: str = "orchestration_knowledge.json"):
        self.knowledge_file = knowledge_file
        self.knowledge = self.load_knowledge()

    def save_pattern_success(self, task_pattern: str, orchestration_pattern: str, metrics: dict):
        """Save successful pattern combination"""
        if "successful_patterns" not in self.knowledge:
            self.knowledge["successful_patterns"] = {}

        key = f"{task_pattern}:{orchestration_pattern}"
        if key not in self.knowledge["successful_patterns"]:
            self.knowledge["successful_patterns"][key] = []

        self.knowledge["successful_patterns"][key].append(metrics)
        self.save_knowledge()

    def save_pattern_failure(self, task_pattern: str, orchestration_pattern: str, error: str):
        """Save failed pattern combination to avoid repeating"""
        if "failed_patterns" not in self.knowledge:
            self.knowledge["failed_patterns"] = {}

        key = f"{task_pattern}:{orchestration_pattern}"
        if key not in self.knowledge["failed_patterns"]:
            self.knowledge["failed_patterns"][key] = []

        self.knowledge["failed_patterns"][key].append(error)
        self.save_knowledge()

    def get_best_pattern(self, task_pattern: str) -> str:
        """Get best orchestration pattern for task type"""
        successful = self.knowledge.get("successful_patterns", {})

        # Find all successful patterns for this task type
        candidates = {}
        for key, results in successful.items():
            stored_task, pattern = key.split(":", 1)
            if stored_task == task_pattern:
                # Average success metrics
                avg_time = sum(r.get("execution_time", 10) for r in results) / len(results)
                avg_success = sum(r.get("success_rate", 0) for r in results) / len(results)

                score = avg_success * 0.8 + (1/avg_time) * 0.2
                candidates[pattern] = score

        if candidates:
            return max(candidates.items(), key=lambda x: x[1])[0]

        return "direct"  # Default

    def should_avoid_pattern(self, task_pattern: str, orchestration_pattern: str) -> bool:
        """Check if pattern combination should be avoided"""
        failed = self.knowledge.get("failed_patterns", {})
        key = f"{task_pattern}:{orchestration_pattern}"

        # Avoid if it has failed multiple times
        return len(failed.get(key, [])) >= 3

    def load_knowledge(self) -> dict:
        """Load knowledge from file"""
        if os.path.exists(self.knowledge_file):
            with open(self.knowledge_file, "r") as f:
                return json.load(f)
        return {}

    def save_knowledge(self):
        """Save knowledge to file"""
        with open(self.knowledge_file, "w") as f:
            json.dump(self.knowledge, f, indent=2)

async def knowledge_example():
    """Example of using persistent knowledge"""
    knowledge = PatternKnowledge()

    # Save some successful patterns
    knowledge.save_pattern_success(
        "code_review", "gather", 
        {"execution_time": 1.5, "success_rate": 0.95}
    )

    knowledge.save_pattern_success(
        "research_workflow", "builder",
        {"execution_time": 3.0, "success_rate": 1.0}
    )

    # Save a failure
    knowledge.save_pattern_failure(
        "simple_question", "builder",
        "Overkill for simple task"
    )

    # Get recommendations
    print(f"Best for code_review: {knowledge.get_best_pattern('code_review')}")
    print(f"Best for research_workflow: {knowledge.get_best_pattern('research_workflow')}")
    print(f"Should avoid simple_question+builder: {knowledge.should_avoid_pattern('simple_question', 'builder')}")

    return knowledge

asyncio.run(knowledge_example())

Best Practices for Self-Improvement

1. Track Key Metrics

  • Execution time
  • Success/failure rates
  • Pattern effectiveness
  • Resource usage

2. Analyze Patterns

  • Compare different approaches for similar tasks
  • Identify what makes patterns successful
  • Learn from failures and mistakes

3. Build Feedback Loops

  • Record execution results
  • Update recommendations based on performance
  • Continuously refine pattern selection

4. Persist Learning

  • Save successful pattern combinations
  • Remember failed approaches to avoid repeating
  • Build knowledge base over time

Self-improvement in LionAGI orchestration comes from systematically tracking execution results, analyzing pattern effectiveness, and building persistent knowledge to make better orchestration decisions over time.