Project 2: Preference-Aligned Chatbot Using DPO
Project Goal
In this capstone project, you will build a preference-aligned chatbot using Direct Preference Optimization (DPO). This project represents a significant evolution from the first capstone, where you learned to create specialized models through instruction tuning. While that approach taught models what to say about specific topics, this project teaches models how to say it—optimizing for qualities like helpfulness, clarity, and appropriateness that distinguish truly useful AI assistants from merely knowledgeable ones.
Unlike standard supervised fine-tuning, which learns from single correct responses, DPO allows a model to learn human preferences between alternative responses. This distinction is crucial: when you train with individual examples, the model learns to reproduce those responses, but it doesn't understand why one response might be better than another. By contrast, when you train with preference pairs—showing the model both a preferred response and a rejected one for the same prompt—you're teaching it to recognize and reproduce the qualities that make responses valuable. This approach enables models to produce answers that are not only correct, but also more helpful, polite, and aligned with user expectations.
Consider a simple example. If a user asks "How do I learn Python?", a technically correct but unhelpful response might be "Read books and write code." A preference-aligned model would instead provide structured guidance: "Start with Python's official tutorial to learn syntax basics, then build small projects like a calculator or to-do list to practice. Focus on understanding one concept thoroughly before moving to the next." Both responses are accurate, but the second demonstrates the qualities users actually value—specificity, actionability, and thoughtful organization.
Preference alignment has become a key technique in modern AI systems because it allows developers to shape model behavior without training complex reinforcement learning pipelines. Traditional reinforcement learning from human feedback (RLHF) requires maintaining multiple models simultaneously, computing complex reward signals, and carefully balancing exploration versus exploitation. DPO simplifies this dramatically by framing preference learning as a classification problem: given two responses, learn to favor the better one. This elegant reformulation makes preference alignment accessible to practitioners without requiring specialized RL expertise or massive computational resources.
By completing this project, you will:
- create a preference dataset with chosen and rejected responses, learning to identify and codify the subtle qualities that distinguish excellent responses from mediocre ones
- train a model using DPO, understanding how the algorithm uses contrastive learning to shift model behavior toward preferred patterns
- evaluate how alignment changes model behavior through systematic comparison, measuring improvements in helpfulness, clarity, and appropriateness
- test the chatbot through an interactive interface, experiencing firsthand how preference alignment creates more satisfying user interactions
This workflow closely mirrors how many modern conversational AI systems are aligned before deployment. Companies like Anthropic, OpenAI, and others use variations of preference learning to ensure their models respond in ways that users find genuinely helpful rather than merely technically correct. The techniques you'll practice here—from curating preference pairs to evaluating subjective quality improvements—represent the current state of the art in making AI systems that people actually want to use.
What makes this project particularly valuable is that it addresses a challenge that pure instruction tuning cannot solve: the gap between correctness and usefulness. You can train a model to know everything about a domain, but without preference alignment, it might respond curtly, miss important context, or fail to anticipate what users actually need. By the end of this project, you'll understand how to bridge that gap, creating chatbots that don't just answer questions, but do so in ways that genuinely serve user needs.
Step 1: Prepare a Preference Dataset
The foundation of successful DPO training lies in creating a high-quality preference dataset. Unlike standard instruction datasets where each example stands alone, a DPO dataset contains pairs of responses for the same prompt—teaching the model through comparison rather than imitation.
Each entry in your dataset must include:
- a preferred (chosen) answer that exemplifies the qualities you want the model to exhibit
- a less desirable (rejected) answer that represents patterns you want the model to avoid
This paired structure is what enables contrastive learning. When the model sees both responses during training, it learns to recognize the specific qualities that distinguish helpful responses from unhelpful ones. The rejected response isn't necessarily wrong—it might be technically accurate but lack helpfulness, clarity, or appropriate detail.
Consider this example dataset entry:
{ "prompt": "How do I improve my Python programming skills?", "chosen": "Practice writing small programs daily, read high-quality documentation, and study well-written open source projects.", "rejected": "Just keep coding and hope you get better."}Notice that the rejected response isn't factually incorrect—practice does improve skills. However, it fails on multiple dimensions: it's vague, unhelpful, and lacks actionable guidance. The chosen response, by contrast, provides concrete steps the user can immediately implement. This contrast teaches the model to favor specificity and actionability.
Here's another example demonstrating preference for clarity over casualness:
{ "prompt": "Explain machine learning in simple terms.", "chosen": "Machine learning is a technique that allows computers to learn patterns from data and improve their predictions without being explicitly programmed.", "rejected": "Machine learning is when computers magically learn things."}The rejected response uses imprecise language ("magically") that obscures rather than clarifies. While it attempts simplicity, it sacrifices accuracy. The chosen response balances accessibility with precision—it's understandable to beginners while remaining technically sound.
When creating your preference dataset, consider these principles:
- Identify specific quality dimensions: What makes one response better? Is it more detailed? More structured? More empathetic? Being explicit about these qualities helps you create consistent training signals.
- Ensure meaningful contrast: The difference between chosen and rejected responses should illustrate the behaviors you want to reinforce. Subtle differences are fine, but they should represent real improvements in usefulness.
- Maintain realism in rejected responses: The rejected examples should represent plausible model outputs, not obviously bad responses. This ensures the model learns to make fine-grained distinctions rather than just avoiding egregiously poor answers.
- Cover diverse scenarios: Include examples across different types of questions, response lengths, and stylistic requirements to ensure broad behavioral improvement.
Once you've created your preference pairs, save the dataset in JSON format:
preference_dataset.jsonThe file should contain an array of objects, each with the three required fields: prompt, chosen, and rejected. For this project, aim for at least 50-100 high-quality preference pairs. While this might seem small compared to instruction tuning datasets, preference learning is remarkably sample-efficient—each pair provides rich training signal by explicitly teaching the model what to favor.
Step 2: Load the Dataset
With your preference dataset prepared, the next step is loading it into a format compatible with the DPO trainer. The Hugging Face datasets library provides convenient tools for this.
from datasets import load_dataset dataset = load_dataset("json", data_files="preference_dataset.json") print(dataset["train"][0])This code loads your JSON file and automatically structures it as a Hugging Face Dataset object. By default, the data is placed in a split called "train". The print statement lets you verify that the dataset loaded correctly—you should see a dictionary with your prompt, chosen, and rejected fields.
The dataset structure is critical for DPO training. The trainer expects exactly these three fields:
prompt: The user's input or questionchosen: The preferred model responserejected: The less desirable model response
If your dataset uses different field names, you'll need to rename them or configure the trainer accordingly. These standardized fields allow the DPO algorithm to construct the appropriate training pairs during optimization.
Before proceeding to training, it's wise to inspect several examples from your loaded dataset. Verify that the contrasts between chosen and rejected responses are clear, that prompts are well-formed, and that there are no formatting artifacts from the JSON loading process. Quality control at this stage prevents subtle issues that could undermine training effectiveness.
You can also perform basic dataset statistics to understand your data distribution:
print(f"Dataset size: {len(dataset['train'])} examples") # Check average response lengthschosen_lengths = [len(ex['chosen'].split()) for ex in dataset['train']]rejected_lengths = [len(ex['rejected'].split()) for ex in dataset['train']] print(f"Average chosen response length: {sum(chosen_lengths)/len(chosen_lengths):.1f} words")print(f"Average rejected response length: {sum(rejected_lengths)/len(rejected_lengths):.1f} words")These statistics help you understand whether your preference pairs have consistent patterns. For instance, if chosen responses are systematically much longer than rejected ones, the model might simply learn to generate longer text rather than genuinely better content. Ideally, your preference pairs should vary in length, with quality differences stemming from content rather than quantity.
With your dataset loaded and validated, you're ready to proceed to loading the base model that you'll align using these preference pairs.
Step 3: Load the Base Model
For this project, you will start with an instruction-tuned model rather than a base pre-trained model. This choice is deliberate and important: instruction-tuned models already understand how to follow prompts and generate coherent responses, which provides a solid foundation for preference alignment. Starting from this higher baseline means your DPO training can focus specifically on refining response quality rather than teaching basic instruction-following behavior from scratch.
The model you'll use is Mistral-7B-Instruct-v0.2, a capable open-source instruction-tuned model that balances performance with accessibility. Its 7-billion parameter size makes it practical to fine-tune on consumer hardware while still producing high-quality conversational responses.
Load the model and tokenizer using the following code:
from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "mistralai/Mistral-7B-Instruct-v0.2" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, device_map="auto")The device_map="auto" parameter automatically distributes the model across available hardware—whether that's a single GPU, multiple GPUs, or a combination of GPU and CPU memory. This automatic device placement is particularly valuable when working with larger models that might not fit entirely in GPU memory.
Once loaded, this model is already capable of generating reasonable responses to user queries. What it lacks, however, is the refined behavior that preference alignment provides—the subtle qualities that distinguish truly helpful responses from merely adequate ones. The DPO training you'll perform in subsequent steps will teach it to consistently exhibit these desirable characteristics.
Step 4: Install the TRL Library
To perform DPO training, you'll need the TRL (Transformer Reinforcement Learning) library, which provides a clean, high-level implementation of the DPO algorithm.While you could implement DPO from scratch using the mathematical formulation, TRL abstracts away the complex mechanics—handling the contrastive loss computation, reference model management, and training loop orchestration—allowing you to focus on the dataset and training configuration.
The library integrates seamlessly with the Hugging Face ecosystem, working directly with the transformers models and datasets you've already loaded. This integration means you can apply preference alignment using the same familiar APIs you've used throughout this book.
Install TRL using pip:
pip install trlAfter installation, you'll have access to the DPOTrainer class, which handles all the complexity of preference optimization. Behind the scenes, DPO maintains a reference copy of your base model (frozen and unchanged) and uses it to compute how much your training model's behavior diverges from the original. This reference comparison ensures that preference learning improves response quality without causing the model to forget its general capabilities—a balance that's crucial for maintaining stable, useful behavior.
With both your base model and the TRL library ready, you now have all the components needed to configure and execute preference alignment training.
Step 5: Configure the DPO Trainer
With your model loaded and the TRL library installed, you're now ready to configure the training process. The configuration phase is where you make critical decisions about how the preference optimization will proceed—decisions that affect both the quality of the alignment and the computational resources required.
Begin by importing the necessary components:
from trl import DPOTrainerfrom transformers import TrainingArgumentsThe DPOTrainer class encapsulates the entire preference optimization algorithm, while TrainingArguments provides a standardized way to specify training hyperparameters—the same interface you've used for instruction tuning and other fine-tuning tasks throughout this book.
Next, define the training configuration:
training_args = TrainingArguments( output_dir="./dpo_chatbot", per_device_train_batch_size=2, num_train_epochs=3, learning_rate=1e-5, logging_steps=10, save_strategy="epoch")Let's examine each parameter and understand its role in the training process:
output_dir: Specifies where the trained model and checkpoints will be saved. After training completes, you'll find your aligned model in this directory, ready for inference or further fine-tuning.per_device_train_batch_size: Controls how many preference pairs are processed simultaneously on each GPU. A batch size of 2 is deliberately conservative—DPO training requires maintaining both the training model and a frozen reference model in memory, which effectively doubles memory requirements compared to standard fine-tuning. If you have ample GPU memory, you can increase this value to speed up training.num_train_epochs: Determines how many complete passes through the dataset the training will make. Three epochs is typically sufficient for preference alignment, especially with smaller datasets. Unlike pre-training or initial instruction tuning, which benefit from extensive iteration, DPO achieves its behavioral improvements relatively quickly.learning_rate: Sets how aggressively the model's parameters are updated during training. The value of 1e-5 (0.00001) is smaller than typical fine-tuning rates, reflecting the fact that you're making subtle behavioral adjustments rather than teaching entirely new capabilities. Too high a learning rate can cause the model to overfit to your preference examples or forget its general knowledge; too low a rate may result in insufficient alignment.logging_steps: Controls how frequently training metrics are recorded. Every 10 steps, you'll see updates about loss values and training progress, allowing you to monitor whether training is proceeding smoothly.save_strategy="epoch": Instructs the trainer to save model checkpoints at the end of each epoch. This gives you multiple snapshots of the model at different stages of alignment, which can be valuable if you want to compare how behavior evolves or if you need to roll back to an earlier checkpoint.
These hyperparameters represent reasonable defaults for DPO training on a moderately-sized preference dataset. However, you should view them as a starting point rather than absolute rules. Depending on your specific dataset size, hardware capabilities, and alignment goals, you may need to adjust these values. For instance, if you notice that training loss hasn't converged after three epochs, you might increase num_train_epochs to allow more optimization cycles.
Step 6: Initialize the DPO Trainer
With your training arguments configured, you can now instantiate the DPO trainer itself. This object will orchestrate the entire preference optimization process, managing the interactions between your training model, the reference model, and the preference dataset.
trainer = DPOTrainer( model=model, args=training_args, train_dataset=dataset["train"], tokenizer=tokenizer)The DPOTrainer initialization is remarkably concise, but substantial complexity operates beneath this simple interface. When you create the trainer, several important things happen:
First, the trainer creates an internal copy of your model to serve as the reference model. This reference remains frozen throughout training—its parameters never change. During each training step, the DPO algorithm compares the training model's behavior against this fixed reference, computing how much the preferences are shifting the model's probability distribution. This comparison prevents the model from drifting too far from its original behavior, maintaining its general capabilities while improving its alignment.
Second, the trainer configures the loss computation mechanism. Unlike standard language modeling, which simply maximizes the probability of target tokens, DPO uses a contrastive objective. For each preference pair, it increases the probability of the chosen response while decreasing the probability of the rejected response—but crucially, it does so relative to what the reference model would have produced. This relative formulation is what allows DPO to refine behavior without requiring explicit reward models or complex reinforcement learning machinery.
Third, the trainer sets up the data processing pipeline. Your raw preference pairs need to be tokenized, formatted, and batched appropriately for training. The trainer handles these transformations automatically, ensuring that prompts and responses are encoded correctly and that the chosen and rejected responses are properly paired during optimization.
The parameters you've passed tell the trainer everything it needs to know: which model to optimize (model), what training configuration to use (args), what preference data to learn from (train_dataset), and how to convert text into tokens (tokenizer). This explicit parameterization gives you full control over the training process while the trainer manages the algorithmic details.
Step 7: Train the Model
With everything configured and initialized, you're ready to begin the actual preference optimization. The training process itself requires just a single line of code:
trainer.train()This simple command initiates an iterative optimization process that will continue for the number of epochs you specified in your training arguments. But what exactly happens during this training?
On each training step, the trainer selects a batch of preference pairs from your dataset. For each pair, it performs a forward pass through both the training model and the reference model, computing the probability each assigns to both the chosen and rejected responses. The DPO loss function then compares these probabilities, creating a training signal that encourages the model to increase the relative likelihood of chosen responses compared to rejected ones.
Crucially, the optimization is not about making the model exactly reproduce the chosen responses word-for-word. Instead, it's about shifting the model's internal preferences—teaching it to recognize and favor the qualities that distinguish good responses from less good ones. The model learns patterns: that specificity is better than vagueness, that structured answers are more helpful than rambling ones, that appropriate tone matters for user experience.
As training progresses, you'll see periodic log outputs showing the loss decreasing. A decreasing loss indicates that the model is successfully learning to distinguish between your preferred and non-preferred responses. The loss won't reach zero—nor should it. DPO includes a regularization term (controlled by a beta parameter that's set to a reasonable default) that prevents the model from deviating too dramatically from the reference model's behavior. This regularization ensures that preference learning improves quality without compromising the model's general knowledge or causing distribution shift that would make outputs unpredictable.
The training will periodically save checkpoints to your output directory, creating a snapshot of the model's state at each epoch. These checkpoints serve as insurance—if something goes wrong during training, you won't lose all progress. They also enable experimentation: you can load different checkpoints and compare their behavior to determine which stage of training produced the best alignment for your use case.
During training, the model gradually internalizes the behavioral patterns encoded in your preference pairs. It's learning what makes responses genuinely helpful—not just accurate, but actionable, clear, and appropriately detailed. These subtle qualities are difficult to capture with simple instruction examples alone, which is why preference optimization has become essential for creating chatbots that feel genuinely helpful rather than merely functional.
The training process typically takes anywhere from minutes to hours, depending on your dataset size, batch size, and hardware. For a dataset of 50-100 preference pairs with the configuration we've specified, you might expect training to complete in 10-30 minutes on a single GPU. This is remarkably efficient compared to the original training of the base model, which required vast computational resources and massive datasets. Preference alignment is a targeted refinement—costly enough to matter, but accessible enough to be practical.
After training completes, the aligned model will be saved in:
./dpo_chatbotStep 8: Test the Aligned Chatbot
After training completes, the most important question is whether the alignment process actually worked. Did your preference data successfully shift the model's behavior in the intended direction? The only way to answer this is through systematic testing.
Begin by loading your newly aligned model and testing it with representative prompts:
prompt = "How can I stay motivated while learning programming?" inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=200, temperature=0.7) print(tokenizer.decode(outputs[0], skip_special_tokens=True))This test prompt is deliberately open-ended, asking for practical advice rather than factual information. Such prompts reveal whether the model has internalized the behavioral patterns from your preference data—whether it provides concrete, actionable guidance rather than generic platitudes.
Pay attention to the generation parameters. The temperature of 0.7 introduces controlled randomness, making outputs more natural and varied than greedy decoding would produce. The max_new_tokens limit of 200 prevents the model from generating excessively long responses while giving it enough space to provide substantive answers.
Compare this response carefully to what the base model would have produced. The differences may be subtle but meaningful. An aligned model typically exhibits several qualitative improvements:
- Clearer explanations: Instead of abstract or academic language, the model provides concrete examples and step-by-step guidance that's immediately applicable.
- More helpful guidance: Responses address not just what the user asked, but anticipate related concerns and provide comprehensive support.
- Improved tone and politeness: The model strikes a balance between being informative and being conversational, avoiding both sterile formality and inappropriate casualness.
These improvements reflect the essence of preference alignment. You're not teaching the model new facts—the base model already possessed relevant knowledge about programming motivation. Instead, you're teaching it how to communicate that knowledge in ways that users find genuinely helpful.
Document your observations methodically. Save both the prompts and the generated responses, noting specific phrases or structural patterns that demonstrate improvement. This documentation becomes valuable when you need to justify the alignment process to stakeholders or when planning future iterations of your preference dataset.
Step 9: Compare Base vs Aligned Model
Single-example testing provides initial impressions, but rigorous evaluation requires systematic comparison across multiple prompts. This step transforms subjective observations into quantifiable evidence of alignment effectiveness.
To conduct meaningful comparisons, you need to maintain both your base model and aligned model in memory simultaneously, or carefully manage loading and unloading them for each test. Create a diverse set of evaluation prompts that span the range of conversations your chatbot will encounter:
Explain recursion to a beginner.How should someone start learning machine learning?What are good habits for becoming a better programmer?These prompts are strategically chosen. The first tests the model's ability to explain complex technical concepts accessibly. The second evaluates whether it provides structured, practical guidance for beginners. The third assesses whether responses contain actionable advice rather than vague generalities.
For each prompt, generate responses from both models using identical parameters. This controlled comparison ensures that any differences you observe stem from alignment rather than sampling variation. Side-by-side evaluation reveals patterns that single examples might obscure.
Evaluate each response pair across multiple dimensions:
- Helpfulness: Does the response actually address what the user needs to know? Does it provide actionable next steps? An unhelpful response might be technically accurate but fail to serve the user's underlying goal.
- Clarity: Is the explanation structured logically? Are complex ideas broken down into digestible components? Clarity isn't about simplification—it's about appropriate scaffolding that matches the user's expertise level.
- Politeness: Does the tone convey respect for the user's question? Does it avoid condescension or excessive informality? Politeness in this context means establishing an appropriate relationship—professional but warm, informative but approachable.
- Reasoning quality: Does the response demonstrate coherent logical flow? Are claims supported by explanations? Reasoning quality distinguishes responses that teach understanding from those that merely provide answers.
Many organizations implement formal evaluation protocols using human reviewers or automated evaluation models. Human evaluation provides nuanced feedback but scales poorly and introduces subjective variance. Automated evaluation using specialized judge models offers consistency and scale but may miss subtle quality differences that humans easily detect.
A practical approach combines both methods. Use human reviewers to evaluate a representative sample—perhaps 50-100 response pairs. This establishes ground truth about what constitutes improvement in your specific context. Then train or adapt an automated evaluation model on these human judgments, enabling scalable assessment of larger test sets.
Consider implementing a structured scoring rubric. For each dimension, define a 1-5 scale with concrete criteria for each level. For example, a helpfulness score of 1 might indicate "response does not address the question," while a score of 5 indicates "response fully addresses the question and anticipates related needs." Such rubrics reduce subjective variance and enable aggregate analysis across multiple evaluators.
Document not just scores, but specific examples of where alignment helped or where it fell short. These qualitative insights guide your next iteration. If aligned models consistently struggle with certain prompt types, that signals a gap in your preference dataset that you should address.
Step 10: Build an Interactive Chat Interface
Testing with individual prompts provides essential evaluation data, but the true test of a conversational AI system is sustained interaction. Multi-turn conversations reveal capabilities and failure modes that single exchanges cannot expose. This final step transforms your aligned model into an interactive chatbot that you can converse with naturally.
The implementation is straightforward:
while True: user_input = input("User: ") prompt = f"User: {user_input}\nAssistant:" inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=200 ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) print("Assistant:", response)This simple loop creates a REPL (Read-Eval-Print Loop) interface. Each iteration reads user input, generates a model response, prints that response, and waits for the next input. The conversational format—explicitly labeling inputs as "User:" and outputs as "Assistant:"—helps the model understand its role in the dialogue.
Notice what this basic implementation doesn't include: conversation history. Each exchange is treated independently, with no memory of previous turns. This limitation is deliberate at this stage—it allows you to test the model's single-turn behavior without the complexity of context management.
However, for a production chatbot, conversation history is essential. Users expect the system to remember what they've said and maintain coherent context across turns. Implementing this requires concatenating previous exchanges into each new prompt:
conversation_history = [] while True: user_input = input("User: ") conversation_history.append(f"User: {user_input}") prompt = "\n".join(conversation_history) + "\nAssistant:" inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=200) response = tokenizer.decode(outputs[0], skip_special_tokens=True) assistant_response = response.split("Assistant:")[-1].strip() conversation_history.append(f"Assistant: {assistant_response}") print("Assistant:", assistant_response)This extended version maintains a growing conversation history, providing the model with full context for each response. Be mindful of context length limits—most models have maximum sequence lengths, and very long conversations will eventually exceed these limits. Production systems typically implement context window management, keeping recent exchanges and summarizing or truncating older ones.
Through this interface, you can conduct exploratory testing. Try edge cases: ambiguous questions, requests for clarification, follow-up questions that reference previous exchanges. Observe how alignment affects not just isolated responses but conversational flow. Does the model maintain appropriate consistency? Does it handle clarifying questions gracefully? Does it acknowledge when it doesn't understand rather than generating plausible-sounding nonsense?
Interactive testing often reveals alignment successes and failures that structured evaluations miss. You might discover that your preference data successfully taught the model to be more helpful, but inadvertently made it overly verbose. Or you might find that alignment improved technical explanations but reduced creativity in open-ended discussions. These insights inform how you'll refine your preference dataset for future alignment iterations.
Now you have a complete preference-aligned chatbot system—one that has been systematically trained to produce responses that match human quality expectations.
What You Learned
This project synthesized multiple techniques into a coherent workflow for building aligned conversational AI:
- You built preference datasets that capture nuanced distinctions between good and better responses, moving beyond simple correctness to behavioral quality.
- You trained models with Direct Preference Optimization, applying a modern alignment algorithm that achieves the benefits of reinforcement learning from human feedback without its computational complexity.
- You aligned chatbot behavior systematically, teaching models to communicate knowledge in ways that users find genuinely helpful rather than merely accurate.
- You evaluated alignment improvements through both qualitative observation and structured comparison, developing intuition for what makes conversational AI effective.
- You created an interactive conversational interface that transforms a static model into a dynamic system capable of sustained dialogue.
Preference alignment has become foundational to modern conversational AI development. Organizations ranging from major technology companies to specialized AI startups now treat alignment as an essential phase in model deployment, not an optional refinement.
The techniques you've practiced enable you to guide model behavior toward responses that are more useful, responsible, and aligned with human expectations—taking language models from impressive but unreliable systems to practical tools that users can trust and rely upon.