Module 8.5 — Project: Resume Analyzer Agent

A complete working AI agent that analyzes resumes and matches them to job descriptions


What This Project Does

Input:  Resume text + Job Description
Output: Skill match score, gap analysis, improvement suggestions

Agent workflow:
1. Extract skills from resume
2. Extract requirements from job description  
3. Match skills against requirements
4. Score the candidate
5. Generate specific improvement suggestions

This is a real, portfolio-worthy project. You could turn this into a product.


Project Setup


    mkdir resume-analyzer
    cd resume-analyzer
    npm init -y

Update package.json:


  {
    "name": "resume-analyzer",
    "version": "1.0.0",
    "type": "module"
  }


  npm install @langchain/langgraph @langchain/openai @langchain/core langchain zod dotenv

Create .env:


    OPENAI_API_KEY=sk-proj-your-key-here


Project Structure

resume-analyzer/
├── .env
├── package.json
├── src/
│   ├── tools.js        ← all tools the agent can use
│   ├── agent.js        ← agent setup and configuration
│   └── index.js        ← entry point — run the analyzer
├── data/
│   ├── resume.txt      ← sample resume
│   └── job.txt         ← sample job description

Step 1 — Sample Data

Create data/resume.txt:

Name: Sofia Sharma
Location: Bangalore, India
Email: sofia@example.com

EXPERIENCE:
Full Stack Developer — TechStartup Pvt Ltd (2022 - Present)
- Built and maintained MERN stack applications (MongoDB, Express, React, Node.js)
- Developed REST APIs serving 50,000+ daily active users
- Implemented JWT authentication and role-based access control
- Used Git for version control and GitHub Actions for CI/CD
- Worked with AWS S3 for file storage

Junior Developer — FreelanceProjects (2021 - 2022)
- Built responsive websites using HTML, CSS, JavaScript
- Integrated third-party APIs (Stripe, Twilio, SendGrid)
- Deployed applications on Vercel and Netlify

SKILLS:
JavaScript, TypeScript, React, Node.js, Express.js, MongoDB, MySQL
HTML, CSS, Tailwind CSS, REST APIs, Git, GitHub
AWS S3, Vercel, Docker (basic), Redis (basic)
Agile methodology, Code reviews

EDUCATION:
B.Tech Computer Science — VIT University (2018 - 2022) — GPA: 8.4/10

CERTIFICATIONS:
- AWS Cloud Practitioner (2023)
- MongoDB Developer Associate (2022)

PROJECTS:
1. E-commerce Platform — Built with MERN stack, integrated Stripe payments
2. Real-time Chat App — Socket.io, React, Node.js, deployed on AWS
3. Portfolio Website — Next.js, deployed on Vercel

Create data/job.txt:

Position: Senior Full Stack Engineer
Company: FinTech Solutions Ltd
Location: Remote / Bangalore

REQUIREMENTS:
- 3+ years experience with React and Node.js
- Strong TypeScript skills (required)
- Experience with PostgreSQL or MySQL (required)
- REST API design and development
- Experience with cloud services (AWS preferred)
- Knowledge of Docker and containerization
- Understanding of microservices architecture
- CI/CD pipeline experience (GitHub Actions, Jenkins)
- Experience with Redis for caching (preferred)
- System design knowledge
- Good communication skills

NICE TO HAVE:
- Next.js experience
- GraphQL knowledge
- Kubernetes experience
- Experience with AI/ML integrations
- Previous fintech experience

ABOUT THE ROLE:
This role requires building and scaling financial applications that process millions of transactions.
The ideal candidate has strong backend skills and understands performance optimization.

Step 2 — Tools

Create src/tools.js:


  import { tool } from "@langchain/core/tools";
  // tool from "@langchain/core/tools" — correct 2025 import location
  import { z } from "zod";


  // ─────────────────────────────────────────
  // TOOL 1 — Extract Skills from Resume
  // Parses resume text and pulls out skills
  // ─────────────────────────────────────────

  export const extractResumeSkilllsTool = tool(
    async ({ resumeText }) => {
      // resumeText = the full resume as a plain text string
      // This tool does text processing to extract skills
      // In this implementation we do basic keyword extraction
      // In production you could use a dedicated NLP model

      const skillKeywords = [
        // Programming Languages
        "javascript", "typescript", "python", "java", "golang", "rust", "c++", "php", "ruby",
        // Frontend
        "react", "vue", "angular", "next.js", "svelte", "html", "css", "tailwind",
        // Backend
        "node.js", "express", "fastapi", "django", "spring boot", "laravel",
        // Databases
        "mongodb", "postgresql", "mysql", "redis", "elasticsearch", "sqlite",
        // Cloud
        "aws", "gcp", "azure", "docker", "kubernetes", "terraform",
        // Tools
        "git", "github", "ci/cd", "github actions", "jenkins", "agile", "scrum",
        // Other
        "rest api", "graphql", "microservices", "socket.io", "jwt", "oauth",
      ];

      const resumeLower = resumeText.toLowerCase();
      // convert to lowercase for case-insensitive matching
      // "React" and "react" should both match

      const foundSkills = skillKeywords.filter(skill =>
        resumeLower.includes(skill)
      );
      // filter = keep only skills that appear in the resume text
      // example foundSkills: ["javascript", "typescript", "react", "node.js", ...]

      // Extract years of experience from resume text
      const experienceMatch = resumeText.match(/(\d+)\s*(?:year|yr)/gi);
      // regex matches patterns like "3 years" or "5yr" or "2 Years"
      // example match: ["2022 - Present", "2021 - 2022"]

      const yearsMatch = resumeText.match(/\((\d{4})\s*-\s*(?:Present|\d{4})\)/gi);
      // matches date ranges like "(2022 - Present)" or "(2021 - 2022)"
      let totalExperience = 0;

      if (yearsMatch) {
        yearsMatch.forEach(match => {
          const years = match.match(/\d{4}/g);
          // extract 4-digit years from each match
          if (years) {
            const startYear = parseInt(years[0]);
            const endYear = years[1] === "Present" ? 2025 : parseInt(years[1]);
            totalExperience += endYear - startYear;
            // add duration of this job to total experience
          }
        });
      }

      return JSON.stringify({
        skills: foundSkills,
        // array of detected skills
        // example: ["javascript", "typescript", "react", "node.js", "mongodb"]

        skillCount: foundSkills.length,
        // total number of skills found
        // example: 18

        estimatedYearsExperience: Math.min(totalExperience, 15),
        // total years across all jobs (capped at 15)
        // example: 4 (2 years at TechStartup + 1 year freelance + rounding)

        rawSkillsText: resumeText.substring(0, 500),
        // first 500 chars for additional context
      });
      // returns JSON string — LLM parses this to understand the resume
    },

    {
      name: "extract_resume_skills",
      description: `Extracts and analyzes skills, technologies, and experience from a resume.
  Use this as the FIRST step when analyzing a resume.
  Returns a structured list of detected skills and estimated years of experience.`,
      schema: z.object({
        resumeText: z.string().describe("the complete resume text to analyze"),
      }),
    }
  );


  // ─────────────────────────────────────────
  // TOOL 2 — Extract Requirements from Job Description
  // Parses JD and pulls out what the company wants
  // ─────────────────────────────────────────

  export const extractJobRequirementsTool = tool(
    async ({ jobText }) => {
      // jobText = the full job description as plain text

      const requirementKeywords = [
        "javascript", "typescript", "python", "java", "react", "node.js", "vue", "angular",
        "mongodb", "postgresql", "mysql", "redis", "aws", "gcp", "azure",
        "docker", "kubernetes", "microservices", "rest api", "graphql",
        "ci/cd", "git", "agile", "system design", "next.js", "express",
      ];

      const jobLower = jobText.toLowerCase();

      // Separate required vs nice-to-have skills
      const requiredSection = extractSection(jobText, ["REQUIREMENTS", "REQUIRED", "MUST HAVE"]);
      const preferredSection = extractSection(jobText, ["NICE TO HAVE", "PREFERRED", "BONUS"]);
      // extractSection finds the relevant section of the JD

      const requiredSkills = requirementKeywords.filter(skill =>
        requiredSection.toLowerCase().includes(skill)
      );
      // skills mentioned in the REQUIREMENTS section
      // example: ["typescript", "react", "node.js", "postgresql"]

      const preferredSkills = requirementKeywords.filter(skill =>
        preferredSection.toLowerCase().includes(skill) &&
        !requiredSkills.includes(skill)
        // exclude skills already in required list
      );
      // skills mentioned in NICE TO HAVE section
      // example: ["next.js", "graphql", "kubernetes"]

      // Extract minimum years of experience
      const yearsMatch = jobText.match(/(\d+)\+?\s*years?\s*(?:of\s*)?experience/gi);
      const minYears = yearsMatch
        ? Math.max(...yearsMatch.map(m => parseInt(m)))
        : 0;
      // take the maximum years mentioned — that's likely the senior requirement
      // example: "3+ years" → 3

      return JSON.stringify({
        requiredSkills,
        // skills that are REQUIRED (must have)
        // example: ["typescript", "mysql", "aws", "docker"]

        preferredSkills,
        // skills that are NICE TO HAVE (bonus)
        // example: ["next.js", "graphql", "kubernetes"]

        minimumYearsRequired: minYears,
        // minimum years of experience required
        // example: 3

        totalRequirements: requiredSkills.length + preferredSkills.length,
        // total number of requirements found
      });
    },

    {
      name: "extract_job_requirements",
      description: `Extracts required skills, preferred skills, and experience requirements from a job description.
  Use this as the SECOND step — after extracting resume skills.
  Returns required vs preferred skills separately.`,
      schema: z.object({
        jobText: z.string().describe("the complete job description text to analyze"),
      }),
    }
  );

  function extractSection(text, sectionNames) {
    // Helper to find a specific section in the job description text
    // sectionNames = array of possible headings to look for

    for (const name of sectionNames) {
      const index = text.toUpperCase().indexOf(name);
      // find where this section heading appears

      if (index !== -1) {
        const afterHeading = text.substring(index);
        // text from this heading onwards

        const nextSectionMatch = afterHeading.slice(name.length).match(/\n[A-Z\s]{3,}:/);
        // find the next section heading (all caps text followed by colon)

        if (nextSectionMatch) {
          return afterHeading.slice(name.length, name.length + nextSectionMatch.index);
          // return text between this heading and the next
        }
        return afterHeading.slice(name.length, name.length + 500);
        // if no next section, return next 500 chars
      }
    }
    return text;
    // if section not found, return full text
  }


  // ─────────────────────────────────────────
  // TOOL 3 — Match Skills and Calculate Score
  // Compares resume skills vs job requirements
  // ─────────────────────────────────────────

  export const calculateMatchScoreTool = tool(
    async ({ resumeSkillsJson, jobRequirementsJson }) => {
      // resumeSkillsJson    = JSON string from extract_resume_skills tool
      // jobRequirementsJson = JSON string from extract_job_requirements tool

      const resumeData = JSON.parse(resumeSkillsJson);
      const jobData    = JSON.parse(jobRequirementsJson);
      // parse both JSON strings back into JavaScript objects

      const resumeSkills   = resumeData.skills || [];
      const requiredSkills = jobData.requiredSkills || [];
      const preferredSkills = jobData.preferredSkills || [];
      const minYears       = jobData.minimumYearsRequired || 0;
      const candidateYears = resumeData.estimatedYearsExperience || 0;

      // Find matched and missing required skills
      const matchedRequired = requiredSkills.filter(skill =>
        resumeSkills.includes(skill)
      );
      // skills that are required AND present in resume
      // example: ["typescript", "mysql", "aws"]

      const missingRequired = requiredSkills.filter(skill =>
        !resumeSkills.includes(skill)
      );
      // required skills that are NOT in the resume
      // example: ["docker", "microservices"]

      // Find matched preferred skills (bonus points)
      const matchedPreferred = preferredSkills.filter(skill =>
        resumeSkills.includes(skill)
      );
      // example: ["next.js"]

      // Calculate score components
      const requiredScore = requiredSkills.length > 0
        ? (matchedRequired.length / requiredSkills.length) * 70
        : 70;
      // required skills worth 70% of total score
      // example: 4/5 required matched = 0.8 × 70 = 56 points

      const preferredScore = preferredSkills.length > 0
        ? (matchedPreferred.length / preferredSkills.length) * 20
        : 20;
      // preferred skills worth 20% of total score
      // example: 1/3 preferred matched = 0.33 × 20 = 6.6 points

      const experienceScore = candidateYears >= minYears ? 10 : (candidateYears / minYears) * 10;
      // experience worth 10% of total score
      // example: 4 years vs 3 required = full 10 points

      const totalScore = Math.round(requiredScore + preferredScore + experienceScore);
      // final score out of 100
      // example: 56 + 6.6 + 10 = 72.6 → 73

      const verdict =
        totalScore >= 80 ? "STRONG MATCH — Highly recommended to apply" :
        totalScore >= 60 ? "GOOD MATCH — Apply with confidence" :
        totalScore >= 40 ? "PARTIAL MATCH — Apply but address skill gaps" :
                          "WEAK MATCH — Significant skill development needed first";

      return JSON.stringify({
        totalScore,
        // overall match score 0-100
        // example: 73

        requiredMatchPercent: requiredSkills.length > 0
          ? Math.round((matchedRequired.length / requiredSkills.length) * 100)
          : 100,
        // what % of required skills are matched
        // example: 80 (means 80% of required skills are in resume)

        matchedRequired,
        // example: ["typescript", "mysql", "aws", "rest api"]

        missingRequired,
        // example: ["docker", "microservices"]

        matchedPreferred,
        // example: ["next.js"]

        experienceMatch: {
          candidateYears,
          required: minYears,
          meets: candidateYears >= minYears,
          // example: { candidateYears: 4, required: 3, meets: true }
        },

        verdict,
        // human readable overall assessment
      });
    },

    {
      name: "calculate_match_score",
      description: `Calculates how well a candidate's skills match the job requirements.
  Use this THIRD — after extracting both resume skills and job requirements.
  Returns a detailed score breakdown with matched and missing skills.`,
      schema: z.object({
        resumeSkillsJson:    z.string().describe("JSON output from extract_resume_skills tool"),
        jobRequirementsJson: z.string().describe("JSON output from extract_job_requirements tool"),
      }),
    }
  );


  // ─────────────────────────────────────────
  // TOOL 4 — Generate Improvement Suggestions
  // Gives specific, actionable advice
  // ─────────────────────────────────────────

  export const generateSuggestionsTool = tool(
    async ({ missingSkills, candidateBackground, targetRole }) => {
      // missingSkills       = skills the candidate is missing (comma-separated string)
      // candidateBackground = brief description of current background
      // targetRole          = job title they are targeting

      const missing = missingSkills.split(",").map(s => s.trim()).filter(Boolean);
      // convert comma-separated string to array and clean whitespace
      // example: "docker, microservices, kubernetes" → ["docker", "microservices", "kubernetes"]

      // Learning path suggestions per skill
      const learningPaths = {
        "docker": {
          timeToLearn: "2-3 weeks",
          resources: ["Docker official docs", "Docker for Developers course on Udemy", "Play with Docker (free sandbox)"],
          project: "Containerize your existing MERN application",
        },
        "kubernetes": {
          timeToLearn: "4-6 weeks",
          resources: ["Kubernetes official docs", "CKA exam prep course", "Killercoda free labs"],
          project: "Deploy your Dockerized app on a local K8s cluster using minikube",
        },
        "microservices": {
          timeToLearn: "3-4 weeks",
          resources: ["Microservices.io patterns site", "Building Microservices book by Sam Newman", "YouTube: TechWorld with Nana"],
          project: "Split your monolithic app into 2-3 microservices communicating via REST",
        },
        "graphql": {
          timeToLearn: "1-2 weeks",
          resources: ["GraphQL official docs", "How to GraphQL tutorial", "Apollo GraphQL docs"],
          project: "Add a GraphQL API alongside your existing REST API",
        },
        "postgresql": {
          timeToLearn: "1-2 weeks",
          resources: ["PostgreSQL Tutorial website", "pgexercises.com for practice", "Supabase docs"],
          project: "Migrate one of your MongoDB collections to PostgreSQL",
        },
        "system design": {
          timeToLearn: "4-8 weeks",
          resources: ["System Design Primer on GitHub", "Designing Data-Intensive Applications book", "ByteByteGo newsletter"],
          project: "Design and document the architecture of your most complex project",
        },
      };

      const suggestions = missing.map(skill => {
        const path = learningPaths[skill.toLowerCase()];
        if (path) {
          return `${skill.toUpperCase()}:
    Time to learn: ${path.timeToLearn}
    Resources: ${path.resources.join(", ")}
    Hands-on project: ${path.project}`;
        }
        return `${skill.toUpperCase()}: Start with official documentation and build a small demo project to practice.`;
      });

      const priorityOrder = missing.slice(0, 3);
      // top 3 missing skills — focus on these first

      return `IMPROVEMENT PLAN FOR: ${targetRole}
  Background: ${candidateBackground}

  PRIORITY SKILLS TO LEARN (top 3 missing required skills):
  ${priorityOrder.map((s, i) => `${i + 1}. ${s}`).join("\n")}

  DETAILED LEARNING PATH:
  ${suggestions.join("\n\n")}

  GENERAL ADVICE:
  1. Focus on the top 3 priority skills before applying
  2. Build projects that use these new skills — employers verify skills through code
  3. Update your GitHub with these new projects before applying
  4. Estimated time to be competitive: ${missing.length <= 2 ? "2-4 weeks" : missing.length <= 4 ? "4-8 weeks" : "2-3 months"}`;
    },

    {
      name: "generate_suggestions",
      description: `Generates a detailed, actionable improvement plan based on skill gaps.
  Use this LAST — after calculating the match score.
  Returns specific learning resources and project ideas for each missing skill.`,
      schema: z.object({
        missingSkills:       z.string().describe("comma-separated list of missing skills"),
        candidateBackground: z.string().describe("brief description of candidate's current background"),
        targetRole:          z.string().describe("the job title the candidate is applying for"),
      }),
    }
  );


Step 3 — Agent Setup

Create src/agent.js:


    import { createReactAgent } from "@langchain/langgraph/prebuilt";
    // createReactAgent from langgraph/prebuilt — 2025 standard import
    // creates a ReAct agent: think → act → observe loop

    import { ChatOpenAI } from "@langchain/openai";
    import { MemorySaver } from "@langchain/langgraph";
    import {
        extractResumeSkilllsTool,
        extractJobRequirementsTool,
        calculateMatchScoreTool,
        generateSuggestionsTool,
    } from "./tools.js";


    // ─────────────────────────────────────────
    // CREATE THE RESUME ANALYZER AGENT
    // ─────────────────────────────────────────

    export function createResumeAnalyzerAgent() {

        const llm = new ChatOpenAI({
            model: "gpt-4o",
            temperature: 0,
            // temperature 0 = fully deterministic
            // analysis tasks need consistency — same resume always gets same score
        });

        const checkpointer = new MemorySaver();
        // enables conversation memory
        // agent remembers previous analyses in same thread

        const agent = createReactAgent({
            llm,
            // the language model

            tools: [
                extractResumeSkilllsTool,
                extractJobRequirementsTool,
                calculateMatchScoreTool,
                generateSuggestionsTool,
            ],
            // all four analysis tools — agent decides order

            checkpointer,
            // enables memory via thread_id

            prompt: `You are an expert technical recruiter and career coach AI.
    Your job is to analyze resumes against job descriptions and provide detailed, actionable feedback.

    ANALYSIS WORKFLOW — always follow this exact order:
    1. Call extract_resume_skills with the resume text
    2. Call extract_job_requirements with the job description text
    3. Call calculate_match_score with both outputs from steps 1 and 2
    4. Call generate_suggestions with the missing skills from step 3
    5. Compile everything into a final comprehensive report

    FINAL REPORT FORMAT:
    ================================
    📊 RESUME ANALYSIS REPORT
    ================================

    🎯 OVERALL MATCH SCORE: [X]/100
    Verdict: [verdict from calculate_match_score]

    📋 SKILLS BREAKDOWN:
    ✅ Matched Required Skills: [list]
    ❌ Missing Required Skills: [list]
    ⭐ Matched Bonus Skills: [list]

    📅 EXPERIENCE:
    Candidate: X years | Required: Y years | [MEETS/FALLS SHORT]

    🚀 IMPROVEMENT PLAN:
    [content from generate_suggestions]

    💡 FINAL RECOMMENDATION:
    [Your expert opinion on whether to apply now or after skill development]
    ================================

    Be specific, honest, and encouraging. Always provide actionable next steps.`,
        });

        return agent;
    }


Step 4 — Entry Point

Create src/index.js:


    import { createResumeAnalyzerAgent } from "./agent.js";
    import { readFileSync } from "fs";
    import * as dotenv from "dotenv";
    dotenv.config();


    // ─────────────────────────────────────────
    // LOAD RESUME AND JOB DESCRIPTION
    // ─────────────────────────────────────────

    function loadFile(filePath) {
        try {
            return readFileSync(filePath, "utf-8");
            // readFileSync = reads file synchronously
            // "utf-8" = treat file as text string (not binary)
            // returns the full file content as a string
        } catch (err) {
            console.error(`Could not read file: ${filePath}`);
            process.exit(1);
            // stop the program if file not found
        }
    }


    // ─────────────────────────────────────────
    // MAIN — Run the analyzer
    // ─────────────────────────────────────────

    async function main() {
        console.log("\n" + "=".repeat(55));
        console.log("🤖 RESUME ANALYZER AGENT");
        console.log("=".repeat(55) + "\n");

        // Load files
        const resume = loadFile("./data/resume.txt");
        const jobDescription = loadFile("./data/job.txt");
        // load both text files from disk

        console.log("📄 Resume loaded:", resume.split("\n")[0]);
        // print first line of resume (usually the name)

        console.log("💼 Job loaded:", jobDescription.split("\n")[0]);
        // print first line of job description (usually the title)

        console.log("\n🔄 Running analysis...\n");

        // Create agent
        const agent = createResumeAnalyzerAgent();

        // Build the analysis request
        const analysisRequest = `Please analyze this resume against the job description and provide a complete assessment.

    RESUME:
    ${resume}

    JOB DESCRIPTION:
    ${jobDescription}

    Please follow the complete analysis workflow and provide the final report.`;

        // Run the agent
        const result = await agent.invoke(
            {
                messages: [{ role: "user", content: analysisRequest }],
                // send resume + JD as user message
            },
            {
                configurable: { thread_id: "analysis_001" },
                // thread_id for this analysis session
            }
        );

        // Extract and print the final answer
        const lastMessage = result.messages[result.messages.length - 1];
        // last message = agent's final compiled report

        console.log("\n" + "=".repeat(55));
        console.log("📊 ANALYSIS COMPLETE");
        console.log("=".repeat(55));
        console.log(lastMessage.content);


        // ── INTERACTIVE MODE ──────────────────────────────────────────
        // After the initial analysis, allow follow-up questions
        console.log("\n" + "=".repeat(55));
        console.log("💬 FOLLOW-UP QUESTIONS");
        console.log('Type a follow-up question or "exit" to quit');
        console.log("=".repeat(55) + "\n");

        const { createInterface } = await import("readline");
        const rl = createInterface({ input: process.stdin, output: process.stdout });

        const question = (q) => new Promise(resolve => rl.question(q, resolve));

        while (true) {
            const userInput = await question("You: ");
            if (userInput.toLowerCase() === "exit") {
                console.log("\n👋 Goodbye!\n");
                rl.close();
                break;
            }

            const followUp = await agent.invoke(
                { messages: [{ role: "user", content: userInput }] },
                { configurable: { thread_id: "analysis_001" } }
                // SAME thread_id = agent remembers the analysis
                // agent can answer "what was my score?" without re-analyzing
            );

            const followUpMsg = followUp.messages[followUp.messages.length - 1];
            console.log("\nAgent:", followUpMsg.content, "\n");
        }
    }

    main().catch(console.error);


Step 5 — Run the Project


    node src/index.js


Expected Output

=======================================================
🤖 RESUME ANALYZER AGENT
=======================================================

📄 Resume loaded: Name: Sofia Sharma
💼 Job loaded: Position: Senior Full Stack Engineer

🔄 Running analysis...

=======================================================
📊 ANALYSIS COMPLETE
=======================================================

================================
📊 RESUME ANALYSIS REPORT
================================

🎯 OVERALL MATCH SCORE: 74/100
Verdict: GOOD MATCH — Apply with confidence

📋 SKILLS BREAKDOWN:
✅ Matched Required Skills: typescript, mysql, aws, rest api, ci/cd, git
❌ Missing Required Skills: docker, microservices
⭐ Matched Bonus Skills: next.js

📅 EXPERIENCE:
Candidate: 4 years | Required: 3 years | MEETS ✅

🚀 IMPROVEMENT PLAN:
PRIORITY SKILLS TO LEARN (top 2 missing):
1. docker
2. microservices

DOCKER:
  Time to learn: 2-3 weeks
  Resources: Docker official docs, Docker for Developers...
  Project: Containerize your existing MERN application

MICROSERVICES:
  Time to learn: 3-4 weeks
  Resources: Microservices.io, Building Microservices book...
  Project: Split your app into 2-3 services

💡 FINAL RECOMMENDATION:
Sofia is a strong candidate with solid MERN experience and TypeScript skills.
The 2 missing skills (Docker, Microservices) are learnable in 4-6 weeks.
Recommendation: Learn Docker first (2-3 weeks), apply immediately after.
================================

=======================================================
💬 FOLLOW-UP QUESTIONS
Type a follow-up question or "exit" to quit
=======================================================

You: What should I learn first to improve my score?
Agent: Based on your analysis, Docker is the highest-priority skill to learn...

You: exit
👋 Goodbye!

3-Line Summary

  1. The Resume Analyzer uses four tools in a strict sequence — extract resume skills, extract job requirements, calculate match score, generate suggestions — the agent follows this pipeline automatically based on the system prompt instructions.
  2. createReactAgent from @langchain/langgraph/prebuilt with a checkpointer enables follow-up questions after the initial analysis — the agent remembers the full analysis in the same thread so users can ask "what should I learn first?" without re-running everything.
  3. This project pattern — extract structured data from text, compare two datasets, score the match, generate actionable recommendations — applies to dozens of real use cases beyond resumes: product matching, candidate screening, document comparison, gap analysis.

Module 8.5 — Complete ✅

Coming up — Module 8.6 — Project: Research Agent

An agent that takes a topic, searches multiple sources, synthesizes information, and produces a structured research report. Uses everything from Phase 8 — tool calling, planning, memory, and multi-step reasoning.

No comments:

Post a Comment

Module 8.5 — Project: Resume Analyzer Agent

A complete working AI agent that analyzes resumes and matches them to job descriptions What This Project Does Input: Resume text + Job Desc...