# Enhance Your Coding Skills with the Interview Simulator Tool URL: https://madhudadi.in/blog/posts/interview-simulator-personalized-mock-interviews-tool Published: 2026-06-15 Tags: AI, Architecture, FastAPI, Production Read time: 14 min Difficulty: advanced > AI-powered mock interviews that generate questions from a user's reading history, scale difficulty based on performance, and provide structured feedback with scoring.# The Interview Simulator The Interview Simulator generates personalized mock interviews based on what the user has read. If you've completed posts about Python classes and FastAPI routes, the simulator asks you Python OOP questions and API design questions — at a difficulty level that adapts to your performance. --- ## Data Model ```python class InterviewSession(Base): __tablename__ = "interview_sessions" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id")) status: Mapped[str] = mapped_column(default="in_progress") overall_score: Mapped[float | None] question_count: Mapped[int] = mapped_column(default=0) current_question: Mapped[int] = mapped_column(default=0) difficulty: Mapped[str] = mapped_column(default="medium") topics: Mapped[list[str]] = mapped_column(JSONB, default=list) created_at: Mapped[datetime] = mapped_column(default=func.now()) completed_at: Mapped[datetime | None] ``` --- ## Question Generation When a user starts an interview, the system selects topics based on their reading history: ```python async def generate_interview(user_id: str) -> InterviewSession: completed_posts = await db.execute( select(Post.title, Post.tags).join( UserProgress, UserProgress.post_id == Post.id ).where( UserProgress.user_id == user_id, UserProgress.completed_at.isnot(None), ) ) topic_counts: dict[str, int] = {} for title, tags in completed_posts: for tag in tags: topic_counts[tag.name] = topic_counts.get(tag.name, 0) + 1 # Select top 3 topics the user has read most top_topics = sorted(topic_counts, key=topic_counts.get, reverse=True)[:3] session = InterviewSession( user_id=user_id, topics=top_topics, difficulty="medium", ) db.add(session) await generate_next_question(session, db) return session ``` --- ## Adaptive Difficulty After each answer, the difficulty adjusts based on the answer's quality score: ```python async def adjust_difficulty(session: InterviewSession, score: float): diffs = ["easy", "medium", "hard"] current_idx = diffs.index(session.difficulty) if score >= 0.8: session.difficulty = diffs[min(current_idx + 1, len(diffs) - 1)] elif score <= 0.4: session.difficulty = diffs[max(current_idx - 1, 0)] # Otherwise, stay at current difficulty ``` The scoring rubric is simple: | Score | Criteria | |-------|----------| | 0.0 - 0.3 | No relevant concepts mentioned, major errors | | 0.3 - 0.5 | Some relevant concepts but incomplete | | 0.5 - 0.7 | Correct concepts, partial explanation | | 0.7 - 0.9 | Complete, correct answer with good explanation | | 0.9 - 1.0 | Comprehensive answer with examples and edge cases | --- ## The Prompt Chain Instead of one large prompt, the system uses a chain of smaller prompts: ### Step 1: Generate Question ``` Context: The user is being interviewed on {topic} at {difficulty} level. They have read: {list_of_posts} Generate one technical interview question. Output: {{"question": "..."}} ``` ### Step 2: Evaluate Answer ``` Question: {question} User's answer: {answer} Expected knowledge areas: {topic_keywords} Evaluate the answer on: 1. Correctness (0-1): Is the answer technically correct? 2. Completeness (0-1): Does it cover all relevant aspects? 3. Clarity (0-1): Is it well-structured and explained? Output: {{"correctness": 0.8, "completeness": 0.6, "clarity": 0.9, "feedback": "..."}} ``` ### Step 3: Generate Follow-Up ``` Context: The user answered a {difficulty} question about {topic}. They scored {score}. Generate a follow-up question that tests deeper understanding. Output: {{"question": "..."}} ``` Each prompt is independent and stateless. The session state (current question, difficulty, topic) is stored in the database, not in the prompt. --- ## Frontend The interview UI is a multi-step form: ```tsx function InterviewSimulator() { const [session, setSession] = useState(null); const [currentQuestion, setCurrentQuestion] = useState(null); const [answer, setAnswer] = useState(""); const [feedback, setFeedback] = useState(null); if (!session) return ; return (
{currentQuestion && !feedback && (

Question {session.current_question} of {session.question_count}

{currentQuestion.text}