When I build an LLM application, I avoid judging it by just asking a few questions and seeing if the answers seem right. That method is fine for a demo, but it stops being reliable once I start changing prompts, models, retrieval logic, or the code. That’s why I prefer to build a small LLM evaluation dashboard with Python as I work on the application.
In this article, I’ll show you how to build one using Python, Ollama, Pandas, and Streamlit. Everything runs on your own machine, so you won’t need a paid API or evaluation tool.
The dashboard lets you compare different prompt versions by response quality and speed, and it saves your evaluation data and results as CSV files.
Build an LLM Evaluation Dashboard With Python: What We’re Building
The workflow is simple:
Evaluation dataset → Local LLM → Evaluation → Results → Streamlit dashboard.
I’ll use a small dataset with questions, context, and expected answers.
Then I’ll run the same questions through two different prompts and compare the results.
The key is to keep the LLM application and the evaluation pipeline separate. This way, it’s much easier to test changes without touching the main application.
Make sure to download Ollama on your system and install these libraries:
pip install pandas ollama streamlit
Also, pull the Llama 3 model:
ollama pull llama3
Step 1: Create an Evaluation Dataset
I always begin evaluation with a fixed dataset. The idea is simple: if I change something in my LLM application, I want to run the same questions again to see if the results really improved.
Here is the evaluation dataset:
import pandas as pd
import ollama
eval_data = pd.DataFrame({
"question": [
"What is overfitting?",
"What is RAG?",
"Why do we use a validation set?",
"What is an AI agent?"
],
"context": [
"Overfitting occurs when a model learns training data too closely and performs poorly on unseen data.",
"RAG combines information retrieval with generation so an LLM can answer using external knowledge.",
"A validation set is used to tune model choices and hyperparameters without using the final test set.",
"An AI agent is a system that can reason about a task and use tools or actions to accomplish it."
],
"ground_truth": [
"Overfitting happens when a model fits training data too closely and fails to generalize.",
"RAG retrieves relevant information and provides it to an LLM to generate a grounded response.",
"A validation set helps tune the model while keeping the test set unseen.",
"An AI agent can reason about a task and use tools to take actions."
]
})
eval_data.to_csv("evaluation_dataset.csv", index=False)Each example contains three things:
- Question: what I ask the LLM.
- Context: information the LLM should use.
- Ground truth: the expected answer or reference answer.
For a real application, I’d make this dataset much larger. Four examples are enough to show how the pipeline works, but not enough to properly evaluate a production system.
Step 2: Connect the Application to Ollama
Instead of using a paid API, I’ll use Ollama to run the model on my own machine. I’ll create a small function that just handles talking to the model:
def local_model(prompt):
response = ollama.chat(
model="llama3",
messages=[
{
"role": "user",
"content": prompt
}
]
)
return response["message"]["content"]I keep this function separate because it makes the app easier to update later. If I want to switch models, I just change the model settings instead of rewriting the whole evaluation pipeline.
Step 3: Build the LLM Application
Now I’ll build the actual LLM application. I want to test two different prompt versions.
The first prompt tells the model to answer using the context. The second prompt adds an important instruction: if the context doesn’t support the answer, the model should say it doesn’t have enough information.
def llm_application(question, context, prompt_version="v1"):
if prompt_version == "v1":
prompt = f"""
Answer the question using the provided context.
Context:
{context}
Question:
{question}
"""
else:
prompt = f"""
Answer the question using only the provided context.
If the answer is not supported by the context, say you
don't have enough information.
Context:
{context}
Question:
{question}
"""
answer = local_model(prompt)
return answerThis is where evaluation really helps. Instead of just guessing that prompt v2 is better because it sounds more polished, I can actually test both prompts on the same dataset.
Step 4: Measure Latency
Response quality isn’t the only thing that matters. If one version gives better answers but takes much longer, that can be a problem in a real application.
I measure latency using Python’s time module:
import time
def run_with_latency(question, context, prompt_version):
start = time.perf_counter()
answer = llm_application(
question,
context,
prompt_version
)
latency = time.perf_counter() - start
return answer, latencyNow, each evaluation gives me both the generated answer and how long it took to produce.
Step 5: Calculate a Quality Score
For this small demo, I’ll use SequenceMatcher to get a basic similarity score between the generated response and the reference answer:
from difflib import SequenceMatcher
def similarity_score(prediction, reference):
return SequenceMatcher(
None,
prediction.lower(),
reference.lower()
).ratio()This gives me a simple score I can use to compare different prompts. Still, I wouldn’t rely on this alone to measure LLM quality.
Two answers might use very different wording and still both be correct. For a real evaluation system, I’d add human review, task-specific checks, or a local evaluation model.
Step 6: Run the Evaluation Pipeline
Now I can run the first prompt against the complete evaluation dataset:
results = []
for _, row in eval_data.iterrows():
answer, latency = run_with_latency(
row["question"],
row["context"],
prompt_version="v1"
)
score = similarity_score(
answer,
row["ground_truth"]
)
results.append({
"question": row["question"],
"answer": answer,
"quality_score": score,
"latency": latency
})
results_df = pd.DataFrame(results)
results_df.to_csv(
"evaluation_results.csv",
index=False
)Now I have a structured evaluation result, not just a bunch of answers in my terminal.
This difference matters more as you add more test cases.
Preparing for AI/ML Interviews?
If you are building projects like this while preparing for AI or ML interviews, I recommend my book Cracking Your First AI/ML Interview. I wrote it to help you turn the concepts you are learning into practical interview knowledge.
Step 7: Compare Different Prompt Versions
Next, I’ll turn the evaluation process into a reusable function:
def evaluate_prompt(prompt_version):
results = []
for _, row in eval_data.iterrows():
answer, latency = run_with_latency(
row["question"],
row["context"],
prompt_version
)
score = similarity_score(
answer,
row["ground_truth"]
)
results.append({
"prompt": prompt_version,
"quality": score,
"latency": latency
})
return pd.DataFrame(results)Now I can evaluate both prompts:
v1_results = evaluate_prompt("v1")
v2_results = evaluate_prompt("v2")
comparison = pd.concat(
[v1_results, v2_results]
)This is the part I find most helpful. Whenever I update an LLM application, I can run the same evaluation dataset and compare the new version to the old one.
Step 8: Build the Streamlit Dashboard
Finally, I’ll create a simple dashboard:
import streamlit as st
st.title("LLM Evaluation Dashboard")
st.metric(
"Average Quality",
f"{comparison['quality'].mean():.2f}"
)
st.metric(
"Average Latency",
f"{comparison['latency'].mean():.2f}s"
)
st.subheader("Quality by Prompt")
st.bar_chart(
comparison.groupby("prompt")["quality"].mean()
)
st.subheader("Latency by Prompt")
st.bar_chart(
comparison.groupby("prompt")["latency"].mean()
)
st.dataframe(comparison)Save the code as app.py and run:
streamlit run app.py




The dashboard gives me a quick look at the average quality and speed, and lets me compare the two prompt versions.
The Takeaway
When I build an LLM application, I make sure evaluation isn’t an afterthought. The app shows what the model can do, but the evaluation pipeline shows if it’s still doing it well after any changes.
Even a small Python project like this gives me the basics for that workflow: a fixed dataset, repeatable evaluations, ways to measure quality, track speed, compare prompts, and a dashboard.
As I work on more advanced LLM, RAG, and agentic applications, I’ll keep building on this system instead of going back to manual testing.
I hope you enjoyed this article about building an LLM evaluation dashboard with Python. For more AI and machine learning tips, you can follow me on Instagram.





