Build a refund support agent, trace its tool calls, and evaluate both its answers and its behavior with seven DeepEval metrics.
A correct final answer is only one part of a successful agent run. Did the agent identify the customer, check refund eligibility, select the right tools, and refund only the requested items?
This repository is a hands-on learning lab for answering those questions. It pairs a LangChain refund agent with eight evaluation scenarios, seven metrics, and small examples that isolate individual evaluation concepts. Start with the examples, follow the complete evaluation flow, then adapt it to your own agent.
- What you will learn
- How the agent works
- Quick start
- Run evaluations
- Evaluation metrics
- Evaluation dataset
- Read the results
- Try the chat demo
- Explore and extend
- Project map
- Limitations and troubleshooting
- Build a tool-using agent with a system prompt, conversation memory, and a structured response.
- Define evaluation cases with inputs, expected answers, and expected tools.
- Capture LangChain execution traces with DeepEval's
CallbackHandler. - Evaluate task completion, efficiency, planning, tool selection, tool arguments, and answer correctness.
- Use per-case explanations to decide whether to improve the agent, the dataset, or the evaluation itself.
The repository has two learning tracks:
| Track | Start here | Focus |
|---|---|---|
| Metric fundamentals | understanding_metrics/ |
Small multiplication agents that demonstrate each metric with minimal business logic |
| Refund agent evaluation | evaluations/evaluate_all.py |
A complete evaluation flow over customer support scenarios |
The agent handles refund requests against the sample purchase records in agent/data.py. Its system prompt requires it to identify the customer, check that purchases are no more than 30 days old and are not in the future, and refund only the items requested.
| Tool | Purpose |
|---|---|
get_user_purchase_history |
Look up a customer's purchases by username |
get_today_date |
Read the current date for eligibility checks |
calculate_right_refund |
Add the prices of the requested items |
issue_refund_ticket |
Return a simulated ticket confirmation for the refund amount |
The tools use in-memory sample data. Issuing a ticket returns a string; it does not contact a payment provider or create an external ticket. Eligibility checks are directed by the prompt rather than enforced inside the refund tools.
agent/agent.py creates the agent with an InMemorySaver checkpointer and the Pydantic Answer schema:
class Answer(BaseModel):
answer: str
conversation: bool = Trueanswer contains the user-facing reply. conversation indicates whether the agent needs another user response. Evaluation cases each receive a new thread ID to keep their conversation histories separate.
flowchart TD
Dataset["Eight refund scenarios"] --> Runner["evaluations/evaluate_all.py"]
Runner --> Agent["LangChain refund agent"]
Agent <--> Tools["Purchase history · date · refund calculation · ticket"]
Agent --> Response["Structured answer + messages and tool calls"]
Agent -. "CallbackHandler" .-> Trace["Execution trace"]
Trace --> TraceMetrics["Task completion · step efficiency<br/>plan quality · plan adherence"]
Response --> TestCase["LLMTestCase + expected answer and tools"]
TestCase --> OutputMetrics["Argument correctness · tool correctness<br/>answer correctness"]
TraceMetrics --> Report["Scores, pass rates, and reasons"]
OutputMetrics --> Report
You need Python 3.12+, uv, and an OpenAI API key with access to the models configured in the code.
git clone https://github.com/Mohamad-Hachem/LangChain_Agent_Evaluation_System.git
cd LangChain_Agent_Evaluation_System
uv sync --lockedCopy .env.example to .env in the repository root, then set:
OPENAI_API_KEY=your_openai_api_key_hereThe .env file is ignored by Git. Keep your real key there.
The refund agent and its evaluator currently both use the model identifier gpt-6-luna. Check these two configuration files before running:
| Role | Configuration |
|---|---|
| Refund agent | agent/models.py |
| DeepEval judge | evaluations/model.py |
If that identifier is unavailable to your account, change each file to a model supported by its respective library and your API account. The standalone multiplication examples specify their own models inside their scripts.
Run commands from the repository root:
uv run --env-file .env python evaluations/evaluate_all.pyThis runs the refund agent once per scenario, evaluates all seven metrics, and prints reports in the terminal. Agent generation and LLM-based judging make API calls and incur usage costs.
Check the dates before interpreting scores. The purchase records use fixed dates, while
get_today_datereads your machine's current date. The expected answers are static. Align the sample dates and expected outcomes for your experiment; see Evaluation dataset.
evaluations/evaluate_all.py is the main entry point. It performs evaluation in two stages:
dataset.evals_iterator(...)runs four metrics against the execution trace captured byCallbackHandler.- The runner collects the same run's structured answer and tool calls into an
LLMTestCase.evaluate(...)then applies the remaining three metrics.
Reusing the response avoids generating a different answer for each metric. The runner disables cache reads and writes and sets inspect_after_run=False, so it does not automatically launch the inspector. The terminal ends with:
Completed all 7 metrics for 8 test cases.
Completion means the evaluation finished; individual metrics can still fail. The script produces two evaluation reports, one for each stage, rather than a single combined export.
For small examples without the refund workflow:
uv run --env-file .env python understanding_metrics/tool_correctness.py
uv run --env-file .env python understanding_metrics/argument_correctness.py
uv run --env-file .env python understanding_metrics/all_metrics.pyFor an individual metric applied to the refund agent:
uv run --env-file .env python evaluations/task_completion.py
uv run --env-file .env python evaluations/tool_correctness.py
uv run --env-file .env python evaluations/answer_correctness.pyEach individual script invokes the agent again. Its configuration can differ from the combined runner. In particular, evaluations/argument_correctness.py collects tool calls but omits them from its LLMTestCase; use the combined runner for refund argument evaluation.
These thresholds are defined in build_metrics() in evaluations/evaluate_all.py. Scores run from 0 to 1; a score at or above the threshold passes.
| Metric | DeepEval implementation | What it checks | Evidence | Threshold |
|---|---|---|---|---|
| Task completion | TaskCompletionMetric |
Whether the agent achieved the task under the stated refund rules | Execution trace + task description | 0.80 |
| Step efficiency | StepEfficiencyMetric |
Whether the agent took unnecessary or redundant steps | Execution trace | 0.80 |
| Plan quality | PlanQualityMetric |
Whether the agent's plan is suitable for the task | Execution trace | 0.80 |
| Plan adherence | PlanAdherenceMetric |
Whether execution follows the agent's plan | Execution trace | 0.80 |
| Argument correctness | ArgumentCorrectnessMetric |
Whether tool inputs are appropriate for the request | User input + actual tool calls and arguments | 0.90 |
| Tool correctness | ToolCorrectnessMetric |
Whether the agent called the expected tools | Actual tool calls + expected tools | 0.90 |
| Answer correctness | GEval |
Whether the answer reaches the expected logical conclusion | Input + actual answer + expected answer | 0.80 |
The refund dataset specifies expected tool names, without reference arguments. The combined runner also leaves exact matching and tool ordering at their defaults. Its tool correctness score therefore does not establish that arguments or execution order were correct. Inspect those separately.
The GEval rubric checks the logical conclusion against the reference answer. Read the reasons alongside the score to see whether details such as the refund amount were assessed as intended.
evaluations/dataset.py defines eight DeepEval Golden cases. A golden describes the desired behavior before the agent runs; an LLMTestCase adds the actual output and observed tool calls afterward.
| Scenario | Expected behavior encoded in the dataset |
|---|---|
user130: refund headphones |
Issue a $35 refund ticket |
user123: refund book |
Deny the refund because the purchase is over 30 days old |
user131: refund only the mouse |
Issue a $20 ticket for the mouse only |
user132: refund paintbrush and canvas |
Issue a $34 ticket for both requested items |
user129: ask about returning the USB drive |
Quote $16 without issuing a ticket |
| Username missing | Ask for the username |
Unknown customer user999 |
Explain that no account was found; issue no ticket |
user131: request an unpurchased keyboard |
Explain that the item was not found; issue no ticket |
These expected outcomes need a consistent date reference. Most sample purchases are dated October 2026. For example, on October 15, 2026, the October 22 book purchase is in the future, while its reference answer calls it more than 30 days old. A correct policy decision can therefore disagree with the reference answer.
For a meaningful experiment, align agent/data.py and the expected answers with the date you intend to test. For repeatable experiments, adapt the date tool to return a fixed reference date and record it with your results.
The screenshots below show an example run over eight cases. They illustrate how to diagnose failures; they are not a reproducible benchmark or a guarantee of current performance.
| Metric | Average score | Pass rate | Passed / total |
|---|---|---|---|
| Task completion | 0.92 | 87.50% | 7 / 8 |
| Step efficiency | 0.97 | 87.50% | 7 / 8 |
| Plan quality | 0.28 | 0.00% | 0 / 8 |
| Plan adherence | 0.88 | 87.50% | 7 / 8 |
| Argument correctness | 0.62 | 25.00% | 2 / 8 |
| Tool correctness | 0.77 | 50.00% | 4 / 8 |
| Answer correctness | 0.51 | 50.00% | 4 / 8 |
Average score and pass rate answer different questions. Argument correctness averages 0.62, but only two cases meet its 0.90 threshold. High step efficiency also does not establish that the agent made a good plan: inspect the plan quality failures and their traces together.
In this case, the agent reports that the customer could not be found. Tool correctness and answer correctness pass, while argument correctness fails. Read the captured arguments and the judge's reason before changing the agent: the failure may expose an agent mistake, missing evaluation evidence, or a rubric that needs refinement.
LLM judgments and agent responses can vary between runs. Keep the model choices, date reference, prompts, dataset, and thresholds with any results you compare.
agent/streamlit_ui.py provides a conversational refund interface with a reset button. Streamlit is not declared in pyproject.toml; add it for this command with uv:
uv run --env-file .env --with streamlit python -m streamlit run agent/streamlit_ui.pyOpen the local URL printed in the terminal. The app begins with “I would like to request a refund.” Reply with a sample username and the items you want to return, such as I am user131. Please refund only my mouse. The conversation ends when the agent returns conversation=False.
The UI builds its own agent with three tools and does not include get_today_date. Use agent/agent.py and the evaluation runner when studying the four-tool agent's date checks. The UI displays chat messages; evaluation reports appear in the terminal.
A suggested reading order:
- Read
understanding_metrics/tool_correctness.pyto see inputs, expected tools, actual tool calls, and scoring in one small example. - Explore
agent/prompts.py,agent/tools.py, andagent/agent.pyto understand the refund workflow. - Read
evaluations/dataset.pyto connect each business rule to an expected outcome. - Follow
evaluations/evaluate_all.pyfrom trace capture to test case construction and scoring. - Inspect a failed case, change one part of the system, and rerun with the same evaluation settings.
To evaluate your own agent, replace the sample data and tools, rewrite the system prompt, and define goldens for your task. Update SUCCESSFUL_TASK_DESCRIPTION and the GEval rubric in the combined runner so the judges assess your intended behavior.
Useful additions to the dataset include purchases exactly 30 days old, future purchases, mixed eligible and ineligible items, duplicate item requests, and attempts to bypass the refund policy. Add expected tool arguments when you want stronger reference checks, and enable order or exact-match checks only when those requirements are part of your task.
| Path | Purpose |
|---|---|
agent/agent.py |
Four-tool agent, memory, structured response, and interactive conversation helper |
agent/tools.py |
Purchase lookup, date lookup, refund calculation, and simulated ticket tools |
agent/data.py |
Sample purchases for ten customers |
agent/prompts.py |
Refund policy and conversation instructions |
agent/objects.py |
Answer and PurchaseItem Pydantic schemas |
agent/models.py |
Agent model configuration |
agent/streamlit_ui.py |
Three-tool chat demo |
evaluations/dataset.py |
Eight refund goldens with expected answers and tools |
evaluations/evaluate_all.py |
Combined seven-metric evaluation runner |
evaluations/model.py |
Judge model configuration |
evaluations/ |
Individual refund metric scripts |
understanding_metrics/ |
Standalone multiplication examples; the answer example is named asnwer_correctness.py |
screenshots/ |
Example terminal evaluation reports |
pyproject.toml, uv.lock |
Project dependencies and locked environment |
.env.example |
API key template |
This is an educational prototype with a small, single-turn evaluation dataset. The interactive agent has memory, but the eight goldens do not test complete multi-turn conversations. Refund eligibility is prompt-driven, and the tools do not provide production validation or persistent storage. Unknown users or items passed directly to the calculation tool can raise lookup errors.
| Symptom | What to check |
|---|---|
| Authentication or model access error | Set OPENAI_API_KEY in the root .env and verify both model configurations |
| Unexpected date-related failures | Align purchase dates, today's date, and the expected answers |
No module named streamlit |
Use the demo command with --with streamlit |
| Import errors when launching scripts | Run the documented file commands from the repository root; the scripts use local imports |
| Argument correctness has missing evidence | Use evaluations/evaluate_all.py, which passes tools_called into each test case |
| Different scores on a rerun | Compare model settings, dates, traces, and judge reasons before drawing conclusions |
Start with the documented Python script commands. The package entry point declared in pyproject.toml points to a main function that is not currently implemented.


