Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions langchain-tutorial/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
66 changes: 66 additions & 0 deletions langchain-tutorial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# LangChain Tutorial: Build Your First Chains and Agents

Sample code for the Real Python tutorial [LangChain Tutorial: Build Your First Chains and Agents](https://realpython.com/langchain-tutorial/).

This is the `langchain_intro` project you build throughout the tutorial: a chat model, reusable prompt templates, an LCEL chain, a ChromaDB-backed review retriever (RAG), and a tool-calling agent that answers questions about patient reviews and hospital wait times.

## Project layout

```
langchain-tutorial/
├── data/
│ └── reviews.csv
├── langchain_intro/
│ ├── chatbot.py # final chat model + prompt templates + RAG chain + agent
│ ├── create_retriever.py # builds the ChromaDB vector database from reviews.csv
│ └── tools.py # get_current_wait_time() tool
├── .env.example
├── requirements.txt
└── README.md
```

## Setup

1. Create and activate a virtual environment (Python 3.10 or later), then install the dependencies:

```console
(venv) $ python -m pip install -r requirements.txt
```

2. Copy `.env.example` to `.env` and add your OpenAI API key:

```console
(venv) $ cp .env.example .env
```

```dotenv
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
```

3. Build the ChromaDB vector database from the reviews. Run this from the project root; it creates a `chroma_data/` directory with the embedded reviews:

```console
(venv) $ python langchain_intro/create_retriever.py
```

## Try it out

Start a Python REPL **from the project root** so the `langchain_intro` package is importable and `dotenv` finds your `.env`:

```pycon
>>> from langchain_intro.chatbot import review_chain
>>> review_chain.invoke("Has anyone complained about communication with the hospital staff?")

>>> from langchain_intro.chatbot import hospital_agent_executor
>>> response = hospital_agent_executor.invoke(
... {
... "messages": [
... {"role": "user", "content": "What is the current wait time at hospital C?"}
... ]
... }
... )
>>> response["messages"][-1].text
```
1,006 changes: 1,006 additions & 0 deletions langchain-tutorial/data/reviews.csv

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions langchain-tutorial/langchain_intro/chatbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import dotenv
from langchain.agents import create_agent
from langchain_chroma import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
PromptTemplate,
SystemMessagePromptTemplate,
)
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import Tool
from langchain_openai import ChatOpenAI
from langchain_openai import OpenAIEmbeddings

from langchain_intro.tools import get_current_wait_time

REVIEWS_CHROMA_PATH = "chroma_data/"

dotenv.load_dotenv()

review_template_str = """Your job is to use patient
reviews to answer questions about their experience at
a hospital. Use the following context to answer questions.
Be as detailed as possible, but don't make up any information
that's not from the context. If you don't know an answer, say
you don't know.

{context}
"""

review_system_prompt = SystemMessagePromptTemplate(
prompt=PromptTemplate(
input_variables=["context"],
template=review_template_str,
)
)

review_human_prompt = HumanMessagePromptTemplate(
prompt=PromptTemplate(
input_variables=["question"],
template="{question}",
)
)
messages = [review_system_prompt, review_human_prompt]

review_prompt_template = ChatPromptTemplate(
input_variables=["context", "question"],
messages=messages,
)

chat_model = ChatOpenAI(model="gpt-5.6-luna", output_version="responses/v1")

output_parser = StrOutputParser()

reviews_vector_db = Chroma(
persist_directory=REVIEWS_CHROMA_PATH,
embedding_function=OpenAIEmbeddings(),
)

reviews_retriever = reviews_vector_db.as_retriever(search_kwargs={"k": 10})

review_chain = (
{"context": reviews_retriever, "question": RunnablePassthrough()}
| review_prompt_template
| chat_model
| output_parser
)

tools = [
Tool(
name="Reviews",
func=review_chain.invoke,
description="""Useful when you need to answer questions
about patient reviews or experiences at the hospital.
Not useful for answering questions about specific visit
details such as payer, billing, treatment, diagnosis,
chief complaint, hospital, or physician information.
Pass the entire question as input to the tool. For instance,
if the question is "What do patients think about the triage system?",
the input should be "What do patients think about the triage system?"
""",
),
Tool(
name="Waits",
func=get_current_wait_time,
description="""Use when asked about current wait times
at a specific hospital. This tool can only get the current
wait time at a hospital and does not have any information about
aggregate or historical wait times. This tool returns wait times in
minutes. Do not pass the word "hospital" as input,
only the hospital name itself. For instance, if the question is
"What is the wait time at hospital A?", the input should be "A".
""",
),
]

hospital_agent_executor = create_agent(
model=chat_model,
tools=tools,
system_prompt="You're a helpful assistant.",
)
32 changes: 32 additions & 0 deletions langchain-tutorial/langchain_intro/create_retriever.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import csv

import dotenv
from langchain_chroma import Chroma
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings

REVIEWS_CSV_PATH = "data/reviews.csv"
REVIEWS_CHROMA_PATH = "chroma_data"

dotenv.load_dotenv()


def load_reviews(csv_path):
with open(csv_path, newline="", encoding="utf-8") as csv_file:
reader = csv.DictReader(csv_file)
return [
Document(
page_content="\n".join(
f"{column}: {value}" for column, value in row.items()
),
metadata={"source": row["review"], "row": index},
)
for index, row in enumerate(reader)
]


reviews = load_reviews(REVIEWS_CSV_PATH)

reviews_vector_db = Chroma.from_documents(
reviews, OpenAIEmbeddings(), persist_directory=REVIEWS_CHROMA_PATH
)
14 changes: 14 additions & 0 deletions langchain-tutorial/langchain_intro/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import random
import time


def get_current_wait_time(hospital: str) -> int | str:
"""Dummy function to generate fake wait times"""

if hospital not in ["A", "B", "C", "D"]:
return f"Hospital {hospital} does not exist"

# Simulate API call delay
time.sleep(1)

return random.randint(0, 10000)
5 changes: 5 additions & 0 deletions langchain-tutorial/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
langchain==1.3.12
langchain-openai==1.3.4
python-dotenv==1.2.2
chromadb==1.5.9
langchain-chroma==1.1.0
Loading