🔥 0
0
Lesson 2 of 8 20 min +125 XP

Task 1: Create a Basic Interview Chat

In this lesson, we'll create our first LangChain chain - an interviewer that asks technical questions and responds to answers.

What We're Building

┌─────────────────────────────────────────────────────────┐
│                    Interview Chain                       │
│                                                          │
│  ┌──────────┐    ┌─────────┐    ┌────────────────┐      │
│  │  Prompt  │ -> │   LLM   │ -> │ String Output  │      │
│  │ Template │    │ (GPT-4) │    │                │      │
│  └──────────┘    └─────────┘    └────────────────┘      │
│                                                          │
└─────────────────────────────────────────────────────────┘

Step 1: Create the Interviewer Prompt

The prompt defines the interviewer's behavior and personality.

# chains/interviewer.py
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

INTERVIEWER_SYSTEM_PROMPT = """You are an expert technical interviewer conducting a {interview_type} interview.

Your role:
- Ask one clear, focused question at a time
- Questions should be appropriate for a {level} position
- Be professional but encouraging
- After the candidate answers, provide brief acknowledgment before the next question

Interview focus: {focus_area}

Current question number: {question_number} of {total_questions}
"""

interviewer_prompt = ChatPromptTemplate.from_messages([
    ("system", INTERVIEWER_SYSTEM_PROMPT),
    MessagesPlaceholder(variable_name="history", optional=True),
    ("human", "{input}")
])

Step 2: Build the Basic Chain

# chains/interviewer.py (continued)
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

def create_interviewer_chain(
    model: str = "gpt-4o-mini",
    temperature: float = 0.7
):
    """Create a basic interviewer chain."""

    llm = ChatOpenAI(model=model, temperature=temperature)

    chain = interviewer_prompt | llm | StrOutputParser()

    return chain

Step 3: Using the Chain

# main.py
from dotenv import load_dotenv
from chains.interviewer import create_interviewer_chain

load_dotenv()

# Create the chain
interviewer = create_interviewer_chain()

# Start the interview
response = interviewer.invoke({
    "interview_type": "technical Python",
    "level": "senior",
    "focus_area": "Python fundamentals and best practices",
    "question_number": 1,
    "total_questions": 5,
    "input": "Please start the interview with your first question."
})

print(f"Interviewer: {response}")
Output:
Interviewer: Welcome! Let's begin with a fundamental Python concept.
Can you explain the difference between a list and a tuple in Python,
and describe a scenario where you would prefer one over the other?

Step 4: Interactive Interview Loop

# main.py (extended)
def run_basic_interview():
    interviewer = create_interviewer_chain()

    config = {
        "interview_type": "technical Python",
        "level": "senior",
        "focus_area": "Python fundamentals, OOP, and best practices",
        "total_questions": 5,
    }

    print("=" * 50)
    print("AI Interview Coach - Basic Mode")
    print("=" * 50)
    print("Type 'quit' to exit\n")

    # Get first question
    response = interviewer.invoke({
        **config,
        "question_number": 1,
        "input": "Start the interview with your first question."
    })
    print(f"\nInterviewer: {response}\n")

    question_num = 1
    while question_num < config["total_questions"]:
        # Get candidate's answer
        answer = input("You: ")
        if answer.lower() == 'quit':
            break

        question_num += 1

        # Get next question (acknowledging previous answer)
        response = interviewer.invoke({
            **config,
            "question_number": question_num,
            "input": f"The candidate answered: {answer}\n\nAcknowledge briefly and ask question {question_num}."
        })
        print(f"\nInterviewer: {response}\n")

    print("\nInterview complete! Thank you for participating.")

if __name__ == "__main__":
    run_basic_interview()

Understanding LCEL (LangChain Expression Language)

The pipe operator | chains components together:

# This:
chain = prompt | llm | output_parser

# Is equivalent to:
def chain(input):
    prompt_result = prompt.invoke(input)
    llm_result = llm.invoke(prompt_result)
    return output_parser.invoke(llm_result)

Chain Components

ComponentPurposeExample
ChatPromptTemplateFormat input into messagesSystem + Human messages
ChatOpenAICall the LLMGPT-4, GPT-3.5
StrOutputParserExtract string from responseGets .content

Different Interview Types

Modify the prompt for different interview styles:

INTERVIEW_TYPES = {
    "behavioral": """Focus on STAR method questions.
        Ask about past experiences, challenges, teamwork.""",

    "system_design": """Ask about architecture, scalability,
        trade-offs. Start high-level, then dive deep.""",

    "coding": """Present coding problems. Ask for approach first,
        then implementation details. Probe for edge cases.""",

    "technical": """Test domain knowledge. Mix conceptual
        questions with practical scenarios."""
}

# Usage
config["interview_type"] = "system_design"
config["focus_area"] = INTERVIEW_TYPES["system_design"]

Exercise: Add Interview Styles

Extend the interviewer to support different personalities:

INTERVIEWER_STYLES = {
    "friendly": "Be warm, encouraging, help candidates feel comfortable.",
    "challenging": "Push back on answers, ask follow-ups, test depth.",
    "neutral": "Professional and straightforward, minimal feedback."
}

# Modify the prompt to include style

What's Missing?

Our current implementation has a problem - it doesn't remember previous exchanges!

Each invocation is independent. Try this:

# First question
response1 = interviewer.invoke({...})
# Second question - interviewer doesn't remember first!
response2 = interviewer.invoke({...})

In the next lesson, we'll add conversation memory to fix this.

Key Takeaways

  • Chains combine prompts, LLMs, and output parsers
  • LCEL (|) creates readable, composable pipelines
  • Prompt templates make chains reusable with different inputs
  • Basic chains are stateless - no memory between calls

Next Up

Task 2: Add Conversation Memory - Make the interviewer remember the entire conversation.
Introduction & Project Setup