Back to roadmaps langgraph Course

Project: Autonomous Coding Agent with Self-Correction

In this project, we will build an Autonomous Coding Agent. The agent generates a JavaScript function, executes it in a virtual sandbox container, captures any console run errors, and loops back to self-correct and rewrite the code until it runs successfully.


1. Agent Workflow

graph TD
    A[Start: User coding task] --> B[Node: GenerateCode]
    B --> C[Node: RunCodeSandbox]
    C -->|Conditional Edge| D{Did it throw errors?}
    D -->|Yes: Feed error log| B
    D -->|No: Successful execution| E[__end__]

2. Implementing the Self-Correction Agent

Create the full StateGraph setup:

// src/services/debuggerAgent.ts
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, Annotation } from "@langchain/langgraph";
import { StringOutputParser } from "@langchain/core/output_parsers";
import * as vm from "vm";

// 1. Define Shared Graph State
const CodeStateAnnotation = Annotation.Root({
  taskDescription: Annotation<string>(),
  generatedCode: Annotation<string>(),
  errorLog: Annotation<string>(),
  runsSuccessfully: Annotation<boolean>(),
});

// 2. Node: Generate or fix code based on errorLog status
async function generateCodeNode(state: typeof CodeStateAnnotation.State) {
  const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0.1 });
  const parser = new StringOutputParser();

  let prompt = `Write a JavaScript function resolving this task: ${state.taskDescription}.
  You must output ONLY raw executable JavaScript code inside standard backticks. No markdown.`;

  if (state.errorLog) {
    prompt += `\n\nYour previous code failed with this error: ${state.errorLog}. Please fix the code and return the corrected version.`;
  }

  const codeReply = await model.pipe(parser).invoke(prompt);
  
  // Clean potential markdown wrap strings
  const cleanedCode = codeReply.replace(/```javascript/g, "").replace(/```/g, "").trim();

  return {
    generatedCode: cleanedCode,
  };
}

// 3. Node: Run code in Node Node.js VM Sandbox
async function runCodeNode(state: typeof CodeStateAnnotation.State) {
  console.log("Running generated code in sandbox...");
  
  try {
    const sandboxContext = { console };
    vm.createContext(sandboxContext);
    
    // Execute code block in sandbox
    vm.runInContext(state.generatedCode, sandboxContext, { timeout: 1000 });
    
    return {
      runsSuccessfully: true,
      errorLog: "",
    };
  } catch (error: any) {
    console.log("Sandbox error caught:", error.message);
    return {
      runsSuccessfully: false,
      errorLog: error.message,
    };
  }
}

// 4. Compose the StateGraph workflow
const workflow = new StateGraph(CodeStateAnnotation)
  .addNode("generate", generateCodeNode)
  .addNode("run", runCodeNode)
  .setEntryPoint("generate")
  .addEdge("generate", "run")
  
  // 5. Add conditional routing edges
  .addConditionalEdges("run", (state) => {
    if (state.runsSuccessfully) {
      return "success";
    }
    return "fix";
  }, {
    success: "__end__",
    fix: "generate", // Loop back to generate node for debugging rewrite
  });

export const debuggerApp = workflow.compile();

3. Verifying the Loop

Invoke the debugger agent with a coding task:

const result = await debuggerApp.invoke({
  taskDescription: "Create a function returning the sum of numbers, but intentionally divide by zero first.",
});

console.log("Successfully generated code:", result.generatedCode);
Published on Last updated: