Skip to content

Latest commit

 

History

History
534 lines (381 loc) · 12.4 KB

README.md

File metadata and controls

534 lines (381 loc) · 12.4 KB

Resume Builder - AI-Powered Resume Optimization Platform

An intelligent resume optimization platform that leverages MiniMax M3 API for dynamic LaTeX resume generation and ATS-optimized PDF outputs.

Features

  • AI-Powered Resume Optimization: Uses MiniMax M3 (Anthropic-compatible API) for intelligent resume tailoring
  • Dynamic LaTeX Generation: Automatically generates ATS-optimized LaTeX resumes
  • Conversational Interface: Natural language interaction for resume refinement
  • Local File Storage: No database required - uses local JSON storage
  • Real-time PDF Generation: Convert LaTeX to professional PDFs using Puppeteer

Setup

1. Environment Configuration

Create a .env file in the root directory with your MiniMax API credentials:

# MiniMax M3 API Configuration (Anthropic-compatible)
MINIMAX_API_KEY=your_minimax_api_key_here
ANTHROPIC_API_KEY=your_minimax_api_key_here

# API Base URLs (Anthropic-compatible for M3)
MINIMAX_BASE_URL=https://api.minimax.io/anthropic
ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic

# Model Configuration
MINIMAX_MODEL=MiniMax-M3
MINIMAX_MODEL_FALLBACK=MiniMax-M2

# Server Configuration
PORT=3001
NODE_ENV=development

2. Get MiniMax API Key

  1. Visit MiniMax API
  2. Sign up for an account
  3. Generate an API key
  4. Add the key to your .env file

3. Install Dependencies

npm install
cd frontend && npm install

4. Build and Run

# Build the backend
npm run build

# Run the backend
npm run dev

# In another terminal, run the frontend
cd frontend && npm run dev

API Configuration

MiniMax M3 Integration

The system uses MiniMax's Anthropic-compatible API for the M3 model:

  • Base URL: https://api.minimax.io/anthropic
  • Model: MiniMax-M3 (primary), MiniMax-M2 (fallback)
  • API Format: Anthropic-compatible with /messages endpoint

Alternative OpenAI-Compatible API

If needed, you can switch to OpenAI-compatible format:

MINIMAX_BASE_URL=https://api.minimax.io/v1
OPENAI_API_KEY=your_minimax_api_key_here

Usage

  1. Upload Resume: Upload your resume (PDF or DOCX)
  2. Enter Job Description: Paste the target job description
  3. AI Optimization: Use natural language to request resume optimizations
  4. Generate PDF: Download your ATS-optimized resume as PDF

Architecture

  • Backend: Node.js + TypeScript + Express
  • Frontend: Next.js + React + Tailwind CSS
  • AI: MiniMax M3 API (Anthropic-compatible)
  • Storage: Local JSON files (no database required)
  • PDF Generation: Puppeteer for HTML-to-PDF conversion

Key Components

  • src/services/minimaxService.ts: MiniMax M3 API integration
  • src/services/resumeParser.ts: Resume parsing and LaTeX generation
  • src/routes/conversation.ts: Conversation management with LaTeX optimization
  • src/routes/pdf.ts: PDF generation from LaTeX templates

Testing

npm test

Environment Variables

Variable Description Default
MINIMAX_API_KEY Your MiniMax API key -
MINIMAX_BASE_URL API base URL https://api.minimax.io/anthropic
MINIMAX_MODEL Primary model MiniMax-M3
PORT Server port 3001

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Yes. Given your constraints—OpenAI APIs only, separate RAG/Data prompts, low latency—the production design I would use is this:

USER │ ▼ [0] Edge / Auth / Tenant │ ▼ [1] Input Safety ───────────────┐ │ │ ▼ │ parallel when safe [2] Context Builder │ │ │ ▼ │ [3] ONE Orchestrator LLM ◄──────┘ │ ├──── direct answer ├──── clarification ├──── RAG agent ├──── Data/MCP agent └──── RAG + Data in parallel │ ▼ [4] Specialist execution │ ▼ [5] Synthesis only if required │ ▼ [6] Selective output checks │ ▼ STREAM

The important optimization is that not every box is an LLM.

Layer Production implementation Extra LLM? Optimization 0. Edge auth, tenant, RBAC, rate limit, request ID No pure code

  1. Safety injection/basic abuse/tool-risk Usually no / optional parallel for reads; blocking for writes
  2. Context session state, entities, previous intent No deterministic state
  3. Orchestration follow-up + rewrite + route + clarification Yes, one call tiny structured output
  4. RAG/Data specialist prompt + specialist tools Yes when selected only selected agent runs
  5. Synthesis combine multi-agent evidence Sometimes skip for single-agent
  6. Validation schema/security/high-risk checks Usually code LLM only for uncertain/high-risk cases
  7. Observability traces, timings, route accuracy No async logging

Now optimize each layer.

  1. Edge layer

Do all known facts here:

JWT company_id user_id role permissions request_id rate limit

Never ask an LLM:

"Can Sai access company 123?"

Application code decides that.

OpenAI recommends unique client request IDs for production debugging.

⸻

  1. Prompt-injection / bad-input layer

Do not put another full GPT call before every request.

Use:

cheap deterministic checks + optional guardrail

For normal read-only requests:

         ┌─ guardrail

request ─────┤ └─ orchestrator run concurrently

For destructive/side-effect operations:

request ↓ BLOCKING security check ↓ authorization ↓ tool execution

The Agents SDK explicitly supports both parallel and blocking guardrails; parallel is lower latency, while blocking prevents downstream execution before the check completes.

⸻

  1. Conversation / follow-up context

This should not be another LLM.

Maintain state such as:

{ "company_id": "123", "previous_intent": "revenue_comparison", "entities": { "metric": "revenue", "period": "Q2" } }

Then:

User: "what about Q3?"

you send the orchestrator:

previous intent = revenue comparison metric = revenue previous period = Q2 current query = "what about Q3?"

Do not run:

followup_detector LLM → rewrite LLM → router LLM

That is exactly the latency explosion you’re trying to eliminate.

⸻

  1. Orchestrator — this is the one important routing call

Make one small structured call responsible for:

follow-up resolution + intent detection + query rewrite + clarification decision + agent selection + parallel/sequential decision

Output should be tiny:

{ "route": ["data"], "query": "Q3 revenue for company 123", "clarify": false, "parallel": false }

Not paragraphs of reasoning.

Your static prompt should stay stable:

SYSTEM agent contracts routing rules output schema security rules ──── stable/cached prefix ──── session context user query

OpenAI currently provides prompt-cache controls such as prompt_cache_key; keeping stable prefixes improves cache reuse.

Also version your orchestration prompt instead of modifying production prompts ad hoc. OpenAI’s current Responses architecture explicitly separates versioned prompts from application-side orchestration.

⸻

  1. Specialist agents

This is where your existing design makes sense.

              Orchestrator
              /          \
             /            \
      RAG Agent          Data Agent
      own prompt         own prompt
      own tools          own MCP tools
      own retrieval      own DB logic

But avoid giving each specialist the entire conversation.

Send only:

normalized task relevant entities tenant/company required context

not 40 previous turns.

If using handoffs, OpenAI supports filtering what history the receiving agent sees specifically for this reason.

⸻

  1. Multi-agent execution

If both are independent:

"Compare revenue and explain policy impact" ┌─ Data Agent ────┐ Orchestrator ─┤ ├─ synthesis └─ RAG Agent ─────┘

Run them concurrently.

Do NOT do:

Data Agent ↓ wait RAG Agent

unless the RAG query actually depends on Data’s result.

This is one of the biggest real latency wins.

⸻

  1. Synthesis

This is where people often accidentally add another unnecessary LLM call.

If only Data Agent ran and its response is already final:

Data Agent → stream user

No synthesis call.

Same for RAG:

RAG Agent → stream user

Only use:

Data + RAG ↓ synthesis LLM

when multiple specialist outputs genuinely need combining.

That means:

single-agent request ≈ orchestrator + specialist multi-agent request ≈ orchestrator + parallel specialists + synthesis

instead of forcing synthesis everywhere.

⸻

  1. Validation

Do not use:

answer ↓ GPT validator ↓ answer

for every request.

First use deterministic validation:

required_fields_present JSON_schema_valid citations_present tenant_ids_match tool_status == success no forbidden data

LLM validation only for cases like:

conflicting agent outputs low-confidence answer high-risk response complex synthesis

Tool input/output guardrails can also protect individual custom tools instead of globally validating every final response.

⸻

  1. Failure handling

Don’t blindly restart the whole graph.

Example:

Data Agent timeout

should become:

retry Data tool once ↓ still fails ↓ return controlled partial/error

Not:

Router → Data agent → failure → router again → another agent → validator

Set hard:

tool timeout max agent turns max retries max total tool calls

The current Agents runner itself supports bounded turns, and tools support explicit timeouts.

⸻

  1. Observability

Every request should produce something resembling:

trace_id: abc preflight 4 ms orchestrator 610 ms route DATA data_agent 2100 ms MCP calls 340 ms TTFT 2840 ms total 3900 ms tokens 1850

Also log:

expected route selected route route corrected? tool failures clarification rate p50 / p95 TTFT p50 / p95 total

OpenAI tracing records model generations, tools, handoffs and guardrails, so you can inspect exactly where latency is accumulating.

⸻

The final architecture I’d build for your system is therefore:

                     REQUEST
                        │
                deterministic code
                        │
           ┌────────────▼────────────┐
           │ Auth / RBAC / Tenant    │
           │ basic validation/state  │
           └────────────┬────────────┘
                        │
              ┌─────────┴──────────┐
              │                    │
         Safety check       ORCHESTRATOR
           parallel          one small LLM
              │                    │
              └─────────┬──────────┘
                        │
            ┌───────────┼───────────┐
            │           │           │
          DIRECT       RAG        DATA
                        │           │
                   RAG Agent    Data Agent
                        │           │
                   Vector DB       MCP
                        │           │
                        └─────┬─────┘
                              │
                    if >1 result only
                              │
                          SYNTHESIS
                              │
                     deterministic checks
                              │
                            STREAM

This is much closer to a production system than router → agent → validator → replanner everywhere.

One thing I want you to answer before we design the exact orchestrator schema:

For a normal request that routes only to your Data Agent, do you want the Data Agent itself to produce the final user-facing answer, or do you currently send its result back through the main agent again?