Skip to content
Blog

Agent Change Management: Peer Review, Staging, and Rollback for Prompt Updates

Implement change management for AI agents. Review prompt updates in staging, test against evals, and rollback safely when production behavior degrades.

Published on September 8, 2026

AI Assistant

Changing a prompt in production is like deploying code without a test suite — it might work, or it might silently break your agent’s behavior. Agent change management brings software engineering discipline to prompt and configuration updates: peer review, staging environments, automated evals, and safe rollbacks.

The Problem with Ad-Hoc Prompt Changes

Without change management:

  • A developer tweaks a system prompt to fix one edge case
  • The fix works for that case but degrades performance elsewhere
  • No one notices until customers complain days later
  • Rolling back means frantically searching Slack for “what was the old prompt?”

Change management prevents this by treating agent configurations as code.

Version Control for Agent Configs

Structure Your Agent Config

# agent-config.yaml
version: "1.2.3"
model: "gpt-4o"
temperature: 0.7

system_prompt: |
  You are a customer support agent for Acme Corp.
  
  Guidelines:
  - Be helpful and concise
  - Always verify the customer's identity before sharing account details
  - Escalate billing disputes to a human agent
  
  Current date: {{current_date}}

tools:
  - name: lookup_order
    description: Look up an order by ID
    parameters:
      order_id:
        type: string
        description: The order ID
  
  - name: process_refund
    description: Process a refund for an order
    parameters:
      order_id:
        type: string
      reason:
        type: string

guardrails:
  max_tokens: 2000
  blocked_patterns:
    - "ignore previous"
    - "system prompt"
  required_disclaimers:
    - "Terms and conditions apply"

Git-Based Version Control

# Each change is a commit
git add agent-config.yaml
git commit -m "feat: add refund processing tool"

# Tags for releases
git tag v1.2.3
git tag -a v1.3.0 -m "Add order lookup tool"

# Branch for experiments
git checkout -b experiment/temperature-tuning

The Change Pipeline

Step 1: Propose Changes

class AgentChangeProposal:
    def __init__(self):
        self.proposals = []
    
    def create_proposal(
        self,
        author: str,
        current_version: str,
        changes: dict,
        rationale: str,
        eval_criteria: list[str]
    ) -> dict:
        proposal = {
            "id": f"proposal-{len(self.proposals) + 1}",
            "author": author,
            "current_version": current_version,
            "changes": changes,
            "rationale": rationale,
            "eval_criteria": eval_criteria,
            "status": "draft",
            "created_at": datetime.now().isoformat(),
        }
        self.proposals.append(proposal)
        return proposal

Step 2: Automated Evaluation

Run the proposed config against your eval suite:

class EvalRunner:
    def __init__(self, eval_suite: list[dict]):
        self.eval_suite = eval_suite
    
    async def run_evals(
        self,
        config: dict,
        baseline_config: dict = None
    ) -> dict:
        results = {"pass": 0, "fail": 0, "regressions": []}
        
        for eval_case in self.eval_suite:
            # Run with proposed config
            proposed_result = await self._run_eval(config, eval_case)
            
            # Run with baseline for comparison
            if baseline_config:
                baseline_result = await self._run_eval(baseline_config, eval_case)
                
                # Check for regressions
                if self._is_regression(baseline_result, proposed_result):
                    results["regressions"].append({
                        "eval": eval_case["name"],
                        "baseline_score": baseline_result["score"],
                        "proposed_score": proposed_result["score"],
                    })
            
            if proposed_result["passed"]:
                results["pass"] += 1
            else:
                results["fail"] += 1
        
        results["pass_rate"] = results["pass"] / len(self.eval_suite)
        results["regression_count"] = len(results["regressions"])
        
        return results
    
    def _is_regression(self, baseline: dict, proposed: dict) -> bool:
        return proposed["score"] < baseline["score"] * 0.95  # 5% threshold

Step 3: Peer Review

class ReviewSystem:
    def request_review(self, proposal_id: str, reviewers: list[str]):
        proposal = self._get_proposal(proposal_id)
        proposal["status"] = "review"
        proposal["reviewers"] = reviewers
        proposal["reviews"] = {}
        
        # Notify reviewers
        for reviewer in reviewers:
            self._send_notification(reviewer, proposal)
    
    def submit_review(
        self,
        proposal_id: str,
        reviewer: str,
        approved: bool,
        comments: str
    ):
        proposal = self._get_proposal(proposal_id)
        proposal["reviews"][reviewer] = {
            "approved": approved,
            "comments": comments,
            "timestamp": datetime.now().isoformat()
        }
        
        # Check if all reviewers have approved
        all_approved = all(
            r["approved"] for r in proposal["reviews"].values()
        )
        
        if all_approved:
            proposal["status"] = "approved"
            self._trigger_deployment(proposal_id)
        elif any(not r["approved"] for r in proposal["reviews"].values()):
            proposal["status"] = "changes_requested"

Step 4: Staging Deployment

class StagingEnvironment:
    def __init__(self):
        self.shadow_traffic_percentage = 0.1  # 10% of traffic
    
    async def deploy_to_staging(self, config: dict) -> str:
        # Deploy alongside production
        staging_endpoint = await self._provision_staging(config)
        
        # Route a percentage of traffic to staging
        await self._configure_traffic_split(
            production=self.production_endpoint,
            staging=staging_endpoint,
            split_ratio=self.shadow_traffic_percentage
        )
        
        # Monitor for issues
        monitor = StagingMonitor(staging_endpoint)
        await monitor.watch(duration_hours=24)
        
        return staging_endpoint
    
    async def promote_to_production(self, config: dict):
        # Validate staging results
        staging_results = await self._get_staging_results()
        
        if staging_results["error_rate"] > 0.01:  # 1% threshold
            raise PromotionError("Staging error rate too high")
        
        if staging_results["latency_p99"] > self.latency_budget:
            raise PromotionError("Staging latency exceeds budget")
        
        # Deploy to production
        await self._deploy_production(config)

Step 5: Safe Rollback

class RollbackManager:
    def __init__(self):
        self.version_history = []
    
    def deploy_version(self, config: dict, version: str):
        # Save current version for rollback
        current = self._get_current_config()
        self.version_history.append({
            "version": version,
            "config": current,
            "deployed_at": datetime.now().isoformat()
        })
        
        # Deploy new version
        self._apply_config(config)
    
    def rollback(self, target_version: str = None):
        if target_version:
            version = self._find_version(target_version)
        else:
            version = self.version_history[-1]
        
        self._apply_config(version["config"])
        
        # Log rollback
        logger.warning(
            f"Rolled back from {self._current_version()} "
            f"to {version['version']}"
        )
    
    def rollback_if_degraded(
        self,
        metric_name: str,
        threshold: float,
        check_interval: int = 60
    ):
        """Automatic rollback based on metrics."""
        while True:
            current_value = self._get_metric(metric_name)
            
            if current_value > threshold:
                logger.error(
                    f"Metric {metric_name} exceeded threshold: "
                    f"{current_value} > {threshold}. Rolling back."
                )
                self.rollback()
                break
            
            time.sleep(check_interval)

CI/CD Integration

GitHub Actions Workflow

name: Agent Config Deployment

on:
  push:
    branches: [main]
    paths: ['agent-config.yaml']

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run evals
        run: python run_evals.py --config agent-config.yaml
      
      - name: Check eval results
        run: |
          python check_eval_results.py \
            --min-pass-rate 0.95 \
            --max-regressions 0

  staging:
    needs: eval
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging
        run: python deploy.py --env staging
      
      - name: Run smoke tests
        run: python smoke_tests.py --env staging
      
      - name: Monitor staging (1 hour)
        run: python monitor.py --env staging --duration 3600

  production:
    needs: staging
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        run: python deploy.py --env production
      
      - name: Verify production
        run: python verify.py --env production --duration 1800
      
      - name: Auto-rollback on issues
        if: failure()
        run: python rollback.py --env production

Monitoring and Alerting

class AgentHealthMonitor:
    def __init__(self):
        self.baselines = {}
    
    def check_health(self, config_version: str) -> dict:
        metrics = self._collect_metrics()
        
        alerts = []
        baseline = self.baselines.get("stable")
        
        if baseline:
            # Check for degradation
            if metrics["success_rate"] < baseline["success_rate"] * 0.95:
                alerts.append({
                    "type": "degradation",
                    "metric": "success_rate",
                    "current": metrics["success_rate"],
                    "baseline": baseline["success_rate"],
                    "severity": "high"
                })
            
            if metrics["avg_latency"] > baseline["avg_latency"] * 1.5:
                alerts.append({
                    "type": "latency_spike",
                    "metric": "avg_latency",
                    "current": metrics["avg_latency"],
                    "baseline": baseline["avg_latency"],
                    "severity": "medium"
                })
        
        return {
            "healthy": len(alerts) == 0,
            "alerts": alerts,
            "metrics": metrics
        }

Best Practices

  • Every config change goes through the pipeline — No exceptions, no hotfixes
  • Keep eval suites updated — Add new test cases for each bug fix
  • Monitor after every deployment — At least 1 hour of heightened monitoring
  • Document rollbacks — Record why each rollback happened for future prevention
  • Use feature flags — For gradual rollouts and quick toggles

Conclusion

Agent change management brings engineering discipline to prompt and configuration updates. By versioning configs, running automated evals, requiring peer review, deploying to staging, and monitoring production, you catch issues before they affect users. When issues do slip through, automated rollback gets you back to a known-good state in minutes.