Files
Claude-Code-Workflow/.claude/skills/skill-tuning/phases/orchestrator.md

13 KiB
Raw Blame History

Orchestrator

Autonomous orchestrator for skill-tuning workflow. Reads current state and selects the next action based on diagnosis progress and quality gates.

Role

Drive the tuning workflow by:

  1. Reading current session state
  2. Selecting the appropriate next action
  3. Executing the action via sub-agent
  4. Updating state with results
  5. Repeating until termination conditions met

State Management

Read State

const state = JSON.parse(Read(`${workDir}/state.json`));

Update State

function updateState(updates) {
  const state = JSON.parse(Read(`${workDir}/state.json`));
  const newState = {
    ...state,
    ...updates,
    updated_at: new Date().toISOString()
  };
  Write(`${workDir}/state.json`, JSON.stringify(newState, null, 2));
  return newState;
}

Decision Logic

function selectNextAction(state) {
  // === Termination Checks ===

  // User exit
  if (state.status === 'user_exit') return null;

  // Completed
  if (state.status === 'completed') return null;

  // Error limit exceeded
  if (state.error_count >= state.max_errors) {
    return 'action-abort';
  }

  // Max iterations exceeded
  if (state.iteration_count >= state.max_iterations) {
    return 'action-complete';
  }

  // === Action Selection ===

  // 1. Not initialized yet
  if (state.status === 'pending') {
    return 'action-init';
  }

  // 1.5. Requirement analysis (在 init 后diagnosis 前)
  if (state.status === 'running' &&
      state.completed_actions.includes('action-init') &&
      !state.completed_actions.includes('action-analyze-requirements')) {
    return 'action-analyze-requirements';
  }

  // 1.6. 如果需求分析发现歧义需要澄清,暂停等待用户
  if (state.requirement_analysis?.status === 'needs_clarification') {
    return null;  // 等待用户澄清后继续
  }

  // 1.7. 如果需求分析覆盖度不足,优先触发 Gemini 深度分析
  if (state.requirement_analysis?.coverage?.status === 'unsatisfied' &&
      !state.completed_actions.includes('action-gemini-analysis')) {
    return 'action-gemini-analysis';
  }

  // 2. Check if Gemini analysis is requested or needed
  if (shouldTriggerGeminiAnalysis(state)) {
    return 'action-gemini-analysis';
  }

  // 3. Check if Gemini analysis is running
  if (state.gemini_analysis?.status === 'running') {
    // Wait for Gemini analysis to complete
    return null;  // Orchestrator will be re-triggered when CLI completes
  }

  // 4. Run diagnosis in order (only if not completed)
  const diagnosisOrder = ['context', 'memory', 'dataflow', 'agent', 'docs', 'token_consumption'];

  for (const diagType of diagnosisOrder) {
    if (state.diagnosis[diagType] === null) {
      // Check if user wants to skip this diagnosis
      if (!state.focus_areas.length || state.focus_areas.includes(diagType)) {
        return `action-diagnose-${diagType}`;
      }
      // For docs diagnosis, also check 'all' focus_area
      if (diagType === 'docs' && state.focus_areas.includes('all')) {
        return 'action-diagnose-docs';
      }
    }
  }

  // 5. All diagnosis complete, generate report if not done
  const allDiagnosisComplete = diagnosisOrder.every(
    d => state.diagnosis[d] !== null || !state.focus_areas.includes(d)
  );

  if (allDiagnosisComplete && !state.completed_actions.includes('action-generate-report')) {
    return 'action-generate-report';
  }

  // 6. Report generated, propose fixes if not done
  if (state.completed_actions.includes('action-generate-report') &&
      state.proposed_fixes.length === 0 &&
      state.issues.length > 0) {
    return 'action-propose-fixes';
  }

  // 7. Fixes proposed, check if user wants to apply
  if (state.proposed_fixes.length > 0 && state.pending_fixes.length > 0) {
    return 'action-apply-fix';
  }

  // 8. Fixes applied, verify
  if (state.applied_fixes.length > 0 &&
      state.applied_fixes.some(f => f.verification_result === 'pending')) {
    return 'action-verify';
  }

  // 9. Quality gate check
  if (state.quality_gate === 'pass') {
    return 'action-complete';
  }

  // 10. More iterations needed
  if (state.iteration_count < state.max_iterations &&
      state.quality_gate !== 'pass' &&
      state.issues.some(i => i.severity === 'critical' || i.severity === 'high')) {
    // Reset diagnosis for re-evaluation
    return 'action-diagnose-context';  // Start new iteration
  }

  // 11. Default: complete
  return 'action-complete';
}

/**
 * 判断是否需要触发 Gemini CLI 分析
 */
function shouldTriggerGeminiAnalysis(state) {
  // 已完成 Gemini 分析,不再触发
  if (state.gemini_analysis?.status === 'completed') {
    return false;
  }

  // 用户显式请求
  if (state.gemini_analysis_requested === true) {
    return true;
  }

  // 发现 critical 问题且未进行深度分析
  if (state.issues.some(i => i.severity === 'critical') &&
      !state.completed_actions.includes('action-gemini-analysis')) {
    return true;
  }

  // 用户指定了需要 Gemini 分析的 focus_areas
  const geminiAreas = ['architecture', 'prompt', 'performance', 'custom'];
  if (state.focus_areas.some(area => geminiAreas.includes(area))) {
    return true;
  }

  // 标准诊断完成但问题未得到解决,需要深度分析
  const diagnosisComplete = ['context', 'memory', 'dataflow', 'agent', 'docs'].every(
    d => state.diagnosis[d] !== null
  );
  if (diagnosisComplete &&
      state.issues.length > 0 &&
      state.iteration_count > 0 &&
      !state.completed_actions.includes('action-gemini-analysis')) {
    // 第二轮迭代如果问题仍存在,触发 Gemini 分析
    return true;
  }

  return false;
}

Execution Loop

async function runOrchestrator(workDir) {
  console.log('=== Skill Tuning Orchestrator Started ===');

  let iteration = 0;
  const MAX_LOOP_ITERATIONS = 50;  // Safety limit

  while (iteration < MAX_LOOP_ITERATIONS) {
    iteration++;

    // 1. Read current state
    const state = JSON.parse(Read(`${workDir}/state.json`));
    console.log(`[Loop ${iteration}] Status: ${state.status}, Action: ${state.current_action}`);

    // 2. Select next action
    const actionId = selectNextAction(state);

    if (!actionId) {
      console.log('No action selected, terminating orchestrator.');
      break;
    }

    console.log(`[Loop ${iteration}] Executing: ${actionId}`);

    // 3. Update state: current action
    // FIX CTX-001: sliding window for action_history (keep last 10)
    updateState({
      current_action: actionId,
      action_history: [...state.action_history, {
        action: actionId,
        started_at: new Date().toISOString(),
        completed_at: null,
        result: null,
        output_files: []
      }].slice(-10)  // Sliding window: prevent unbounded growth
    });

    // 4. Execute action
    try {
      const actionPrompt = Read(`phases/actions/${actionId}.md`);
      // FIX CTX-003: Pass state path + key fields only instead of full state
      const stateKeyInfo = {
        status: state.status,
        iteration_count: state.iteration_count,
        issues_by_severity: state.issues_by_severity,
        quality_gate: state.quality_gate,
        current_action: state.current_action,
        completed_actions: state.completed_actions,
        user_issue_description: state.user_issue_description,
        target_skill: { name: state.target_skill.name, path: state.target_skill.path }
      };
      const stateKeyJson = JSON.stringify(stateKeyInfo, null, 2);

      const result = await Task({
        subagent_type: 'universal-executor',
        run_in_background: false,
        prompt: `
[CONTEXT]
You are executing action "${actionId}" for skill-tuning workflow.
Work directory: ${workDir}

[STATE KEY INFO]
${stateKeyJson}

[FULL STATE PATH]
${workDir}/state.json
(Read full state from this file if you need additional fields)

[ACTION INSTRUCTIONS]
${actionPrompt}

[OUTPUT REQUIREMENT]
After completing the action:
1. Write any output files to the work directory
2. Return a JSON object with:
   - stateUpdates: object with state fields to update
   - outputFiles: array of files created
   - summary: brief description of what was done
`
      });

      // 5. Parse result and update state
      let actionResult;
      try {
        actionResult = JSON.parse(result);
      } catch (e) {
        actionResult = {
          stateUpdates: {},
          outputFiles: [],
          summary: result
        };
      }

      // 6. Update state: action complete
      const updatedHistory = [...state.action_history];
      updatedHistory[updatedHistory.length - 1] = {
        ...updatedHistory[updatedHistory.length - 1],
        completed_at: new Date().toISOString(),
        result: 'success',
        output_files: actionResult.outputFiles || []
      };

      updateState({
        current_action: null,
        completed_actions: [...state.completed_actions, actionId],
        action_history: updatedHistory,
        ...actionResult.stateUpdates
      });

      console.log(`[Loop ${iteration}] Completed: ${actionId}`);

    } catch (error) {
      console.log(`[Loop ${iteration}] Error in ${actionId}: ${error.message}`);

      // Error handling
      // FIX CTX-002: sliding window for errors (keep last 5)
      updateState({
        current_action: null,
        errors: [...state.errors, {
          action: actionId,
          message: error.message,
          timestamp: new Date().toISOString(),
          recoverable: true
        }].slice(-5),  // Sliding window: prevent unbounded growth
        error_count: state.error_count + 1
      });
    }
  }

  console.log('=== Skill Tuning Orchestrator Finished ===');
}

Action Catalog

Action Purpose Preconditions Effects
action-init Initialize tuning session status === 'pending' Creates work dirs, backup, sets status='running'
action-analyze-requirements Analyze user requirements init completed Sets requirement_analysis, optimizes focus_areas
action-diagnose-context Analyze context explosion status === 'running' Sets diagnosis.context
action-diagnose-memory Analyze long-tail forgetting status === 'running' Sets diagnosis.memory
action-diagnose-dataflow Analyze data flow issues status === 'running' Sets diagnosis.dataflow
action-diagnose-agent Analyze agent coordination status === 'running' Sets diagnosis.agent
action-diagnose-docs Analyze documentation structure status === 'running', focus includes 'docs' Sets diagnosis.docs
action-gemini-analysis Deep analysis via Gemini CLI User request OR critical issues Sets gemini_analysis, adds issues
action-generate-report Generate consolidated report All diagnoses complete Creates tuning-report.md
action-propose-fixes Generate fix proposals Report generated, issues > 0 Sets proposed_fixes
action-apply-fix Apply selected fix pending_fixes > 0 Updates applied_fixes
action-verify Verify applied fixes applied_fixes with pending verification Updates verification_result
action-complete Finalize session quality_gate='pass' OR max_iterations Sets status='completed'
action-abort Abort on errors error_count >= max_errors Sets status='failed'

Termination Conditions

  • status === 'completed': Normal completion
  • status === 'user_exit': User requested exit
  • status === 'failed': Unrecoverable error
  • requirement_analysis.status === 'needs_clarification': Waiting for user clarification (暂停,非终止)
  • error_count >= max_errors: Too many errors (default: 3)
  • iteration_count >= max_iterations: Max iterations reached (default: 5)
  • quality_gate === 'pass': All quality criteria met

Error Recovery

Error Type Recovery Strategy
Action execution failed Retry up to 3 times, then skip
State parse error Restore from backup
File write error Retry with alternative path
User abort Save state and exit gracefully

User Interaction Points

The orchestrator pauses for user input at these points:

  1. action-init: Confirm target skill and describe issue
  2. action-propose-fixes: Select which fixes to apply
  3. action-verify: Review verification results, decide to continue or stop
  4. action-complete: Review final summary