Course Correction - Meta-Agent Monitoring
Level: Enhancement (beyond minimal agent) Prerequisites: Agent loop, tool system, subagents
What This Adds
A meta-agent architecture where a secondary LLM monitors the primary agent and injects corrective guidance when it detects problems.
This is essentially automated code review at inference time.
Key Concept
| Question | Answer |
|---|---|
| What monitors the agent? | Gemini 3 Pro Preview (external observer) |
| When does it trigger? | Only on end_turn, and only if 5+ tool calls and a file edit occurred (plus whitelist + non-free mode) |
| How does it intervene? | Injects a user message that triggers new inference |
| Is it visible to users? | Yes - displayed as correction |
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ COURSE CORRECTION FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ User Request │
│ │ │
│ ▼ │
│ Main Agent (Claude/GPT) │
│ │ │
│ ├── Tools Execute │
│ │ (Read/Edit/Bash/etc.) │
│ │ │
│ ▼ │
│ Agent Claims "Done" (stop_reason: end_turn) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ COURSE CORRECTION CHECK (Gemini 3 Pro Preview) │ │
│ │ │ │
│ │ Gate: │ │
│ │ - 5+ tools since last user message │ │
│ │ - At least one file edit │ │
│ │ - Whitelisted + not free mode │ │
│ └────────────────────────────┬────────────────────────────┘ │
│ │ │
│ ▼ │
│ needsCorrection? │
│ ├── true → Inject message, re-run │
│ └── false → Continue normally │
│ │
└─────────────────────────────────────────────────────────────────┘
When to Intervene
Course correction triggers ONLY for clear problems:
Intervene (needsCorrection: true)
- False Completion - Agent claimed "done" but skipped explicit requirements
- Wrong Direction - Agent is doing something different from requested
- Hallucinated Success - Agent says "fixed" but output shows error
- Clear Mistake - Syntax error, missed import, logic bug
- Missing Obvious Steps - Didn't update related tests
Stay Silent (needsCorrection: false)
- Agent is still working (even slowly)
- Agent is exploring or debugging
- Minor suboptimal choice
- Agent completed what was asked
- Uncertain whether intervention needed
Default to silence. Over-correction degrades performance.
The Course Correction Prompt
# Your Role
You are an EXTERNAL OBSERVER watching a conversation between a human
user and an AI coding agent. You are NOT the agent. You are a separate
system that monitors the agent's work.
Your job: Intervene only when the agent clearly goes off track.
When you intervene, write as if you are the user speaking to the agent.
# Critical Rules
**NEVER intervene to do or suggest anything the user didn't explicitly ask for.**
**NEVER run git operations (commit, push, pull, etc.)**
**NEVER suggest git operations**
# When to Stay Silent
Stay silent in ALL of these cases:
- Agent is still working (even if slowly or taking detours)
- Agent is exploring, debugging, or investigating
- Agent made a minor suboptimal choice but is still heading toward the goal
- You're unsure whether intervention is needed
- Agent completed exactly what was asked
**Default to silence. When in doubt, do not intervene.**
# When to Intervene
Intervene ONLY for clear, obvious problems:
1. Agent claimed "done" but skipped something explicitly requested
2. Agent is doing something completely different from what was requested
3. Agent says it succeeded but the output clearly shows failure
4. Agent made a mistake visible from the code or tool output
# How to Write the Message
Write like the user would - short, casual, direct.
Good examples:
- "I asked you to also run the tests"
- "wait, I wanted TypeScript, not JavaScript"
- "that's not what I meant - I want X, not Y"
Bad examples:
- "Please ensure you complete the testing phase"
- "I notice you have deviated from the specification"
The Tool Schema
Course correction uses a single tool with forced calling:
const courseCorrectionTool = {
name: "course_correct",
description: "Report whether the agent needs course correction and provide guidance if so.",
inputSchema: {
type: "object",
properties: {
needsCorrection: {
type: "boolean",
description: "True if intervention is required, false otherwise."
},
message: {
type: "string",
nullable: true,
description: "Guidance message when correction is needed, otherwise null."
}
},
required: ["needsCorrection"],
additionalProperties: false
}
};
Implementation
Trigger Conditions
const FILE_EDIT_TOOLS = new Set([
"edit_file",
"create_file",
"format_file",
"delete_file",
"undo_edit"
]);
function shouldTriggerCourseCorrection(params: {
toolCallsSinceLastUser: ToolCall[];
lastUserWasCourseCorrection: boolean;
agentMode: AgentMode;
whitelisted: boolean;
hasFileEdits: boolean;
}): boolean {
if (!params.whitelisted) return false;
if (isFreeMode(params.agentMode)) return false;
if (params.lastUserWasCourseCorrection) return false;
const toolCount = params.toolCallsSinceLastUser.length;
const hasFileEdit = params.hasFileEdits ||
params.toolCallsSinceLastUser.some(call => FILE_EDIT_TOOLS.has(call.name));
return toolCount >= 5 && hasFileEdit;
}
Running the Assessment
async function runCourseCorrection(
thread: Thread,
signal: AbortSignal
): Promise<{ needsCorrection: boolean; message?: string }> {
// 1. Extract AGENTS.md content from thread
const guidanceContent = extractGuidanceFiles(thread);
// 2. Build system prompt
const systemPrompt = buildCourseCorrectPrompt(guidanceContent);
// 3. Get recent conversation (since last user message)
const recentMessages = getMessagesSinceLastUser(thread);
// 4. Convert to markdown for the monitor
const conversationMarkdown = threadToMarkdown(recentMessages);
// 5. Call the monitoring model (end_turn only)
const response = await inference({
model: "gemini-3-pro-preview",
system: systemPrompt,
messages: [{
role: "user",
content: [{ type: "text", text: conversationMarkdown }]
}],
tools: [courseCorrectionTool],
toolConfig: { mode: "ANY" }, // FORCE tool call
temperature: 0.1, // Very low for consistency
maxTokens: 1024
});
// 6. Extract the tool call result
const toolCall = response.content.find(c => c.type === "tool_use");
return toolCall?.args;
}
Integration with Agent Loop
class AgentLoop {
async onEndTurn(toolCallsSinceLastUser: ToolCall[]) {
const shouldRun = shouldTriggerCourseCorrection({
toolCallsSinceLastUser,
lastUserWasCourseCorrection: this.lastUserSource === "course-correction",
agentMode: this.mode,
whitelisted: this.isWhitelisted,
hasFileEdits: this.editedFiles.size > 0
});
if (!shouldRun) return;
const result = await this.runCourseCorrection();
if (result?.needsCorrection && result.message) {
this.injectCorrectionMessage(result.message);
}
}
injectCorrectionMessage(message: string) {
// Add as user message with special source
this.thread.messages.push({
role: "user",
content: [{ type: "text", text: message }],
source: { type: "course-correction" }
});
// Trigger new inference (with skipCourseCorrection to prevent recursion)
this.runInference({ skipCourseCorrection: true });
}
}
End-Turn Assessment
async function onInferenceComplete(response: Response) {
if (response.stopReason !== "end_turn") return;
await this.onEndTurn(this.toolCallsSinceLastUser);
}
Model Choice
Using a different model for monitoring is deliberate:
- Prevents self-bias - Different model catches different errors
- Cost effective - Gemini 3 Pro Preview for monitoring, Claude for work
- Large context - 1M token context handles long conversations
- Reasoning - Built-in reasoning for analysis
Configuration
const COURSE_CORRECTION_CONFIG = {
enabled: false, // Experimental feature
model: "gemini-3-pro-preview",
temperature: 0.1,
maxTokens: 1024,
timeoutMs: 1000,
requireWhitelist: true,
skipFreeModes: true
};
Cost Tracking
Course correction costs are tracked separately:
interface UsageEvent {
operationType: "inference" | "course-correction";
model: string;
tokens: { input: number; output: number };
credits: number;
}
Even when no correction is applied, usage is tracked for billing.
When to Add Course Correction
Add this enhancement when:
- Agent reliability matters - Production use cases
- AGENTS.md compliance - Project rules must be enforced
- False completion is costly - Users shouldn't verify everything
- Budget allows - Additional model calls per interaction
Skip if:
- Development/testing only
- Cost is a primary concern
- Agent tasks are simple/short
Limitations
- Latency - Adds ~1 second to critical path
- Cost - Additional model API calls
- False Positives - Could interrupt valid exploratory work
- Conservative - May miss subtle issues
Enhancement based on Amp Code v0.0.1769212917 patterns