Getting Started With LangChain
LangChain is a framework for building applications with large language models (LLMs) through chaining different components together. Some of the applications of LangChain are chatbots, generative question-answering, summarization, data-augmented generation and more. LangChain can save time in building chatbots and other systems by providing a standard interface for chains, agents and memory, as well as integrations with other tools and end-to-end examples. We refer to “chains” as sequences of calls (to an LLMs and a different program utilities, cloud services, etc.) that go beyond just one LLM API call. LangChain provides a standard interface for chains, many integrations with other tools, and end-to-end chains for common applications. Often you will find existing chains already written that meet the requirements for your applications.
For example, one can create a chain that takes user input, formats it using a PromptTemplate, and then passes the formatted response to a Large Language Model (LLM) for processing.
While LLMs are very general in nature which means that while they can perform many tasks effectively, they often can not directly provide specific answers to questions or tasks that require deep domain knowledge or expertise. LangChain provides a standard interface for agents, a library of agents to choose from, and examples of end-to-end agents.
LangChain Memory is the concept of persisting state between calls of a chain or agent. LangChain provides a standard interface for memory, a collection of memory implementations, and examples of chains/agents that use memory. LangChain provides a large collection of common utils to use in your application. Chains go beyond just a single LLM call, and are sequences of calls (whether to an LLM or a different utility). LangChain provides a standard interface for chains, lots of integrations with other tools, and end-to-end chains for common applications.
LangChain can be integrated with one or more model providers, data stores, APIs, etc. LangChain can be used for in-depth question-and-answer chat sessions, API interaction, or action-taking. LangChain can be integrated with Zapier’s platform through a natural language API interface (we have an entire chapter dedicated to Zapier integrations).
Installing Necessary Packages
For the purposes of examples in this book, you might want to create a new Anaconda or other Python environment and install:
1 pip install -U langchain langchain-openai faiss-cpu tiktoken
2 pip install -U langchain llama_index langchain-openai
3 pip install -U langchain-community langchainhub
4 pip install -U kor pydrive pandas rdflib
5 pip install -U google-search-results SPARQLWrapper
For the rest of this chapter we will use the subdirectory langchain_getting_started and in the next chapter use llama-index_case_study in the GitHub repository for this book.
Creating a New LangChain Project
Simple LangChain projects are often just a very short Python script file. As you read this book, when any example looks interesting or useful, I suggest copying the requirements.txt and Python source files to a new directory and making your own GitHub private repository to work in. Please make the examples in this book “your code,” that is, freely reuse any code or ideas you find here.
Basic Usage and Examples
While I try to make the material in this book independent, something you can enjoy with no external references, you should also take advantage of the high quality Langchain Quickstart Guide documentation and the individual detailed guides for prompts, chat, document loading, indexes, etc.
As we work through some examples please keep in mind what it is like to use the ChatGPT web application: you enter text and get responses. The way you prompt ChatGPT is obviously important if you want to get useful responses. In code examples we automate and formalize this manual process.
You need to choose a LLM to use. We will usually choose the GPT-4 API from OpenAI because it is general purpose but is more expensive that the older GPT-3.5 APIs. You will need to sign up for an API key and set it as an environment variable:
1 export OPENAI_API_KEY="YOUR KEY GOES HERE"
Both the libraries openai and langchain will look for this environment variable and use it. We will look at a few simple examples in a Python REPL. We will start by just using OpenAI’s text prediction API:
1 $ python
2 >>> from langchain_openai import OpenAI
3 >>> llm = OpenAI(temperature=0.8)
4 >>> s = llm("John got into his new sports car, and he drove it")
5 >>> s
6 ' to work\n\nJohn started up his new sports car and drove it to work. He had a huge \
7 smile on his face as he drove, excited to show off his new car to his colleagues. Th
8 e wind blowing through his hair, and the sun on his skin, he felt a sense of freedom
9 and joy as he cruised along the road. He arrived at work in no time, feeling refres
10 hed and energized.'
11 >>> s = llm("John got into his new sports car, and he drove it")
12 >>> s
13 " around town\n\nJohn drove his new sports car around town, enjoying the feeling of \
14 the wind in his hair. He stopped to admire the view from a scenic lookout, and then
15 sped off to the mall to do some shopping. On the way home, he took a detour down a w
16 inding country road, admiring the scenery and enjoying the feel of the car's powerfu
17 l engine. By the time he arrived back home, he had a huge smile on his face."
Notice how when we ran the same input text prompt twice that we see different results. Setting the temperature in line 3 to a higher value increases the randomness.
Our next example is in the source file directions_template.py in the directory langchain_getting_started and uses the PromptTemplate class. A prompt template is a reproducible way to generate a prompt. It contains a text string (“the template”), that can take in a set of parameters from the end user and generate a prompt. The prompt template may contain language model instructions, few-shot examples to improve the model’s response, or specific questions for the model to answer.
1 from langchain.prompts import PromptTemplate
2 from langchain_openai import OpenAI
3 llm = OpenAI(temperature=0.9)
4
5 def get_directions(thing_to_do):
6 prompt = PromptTemplate(
7 input_variables=["thing_to_do"],
8 template="How do I {thing_to_do}?",
9 )
10 prompt_text = prompt.format(thing_to_do=thing_to_do)
11 print(f"\n{prompt_text}:")
12 return llm(prompt_text)
13
14 print(get_directions("get to the store"))
15 print(get_directions("hang a picture on the wall"))
You could just write Python string manipulation code to create a prompt but using the utility class PromptTemplate is more legible and works with any number of prompt input variables.
The output is:
1 $ python directions_template.py
2
3 How do I get to the store?:
4
5 To get to the store, you will need to use a mode of transportation such as walking, \
6 driving, biking, or taking public transportation. Depending on the location of the s
7 tore, you may need to look up directions or maps to determine the best route to take
8 .
9
10 How do I hang a picture on the wall?:
11
12 1. Find a stud in the wall, or use two or three wall anchors for heavier pictures.
13 2. Measure and mark the wall where the picture hanger will go.
14 3. Pre-drill the holes and place wall anchors if needed.
15 4. Hammer the picture hanger into the holes.
16 5. Hang the picture on the picture hanger.
The next example in the file country_information.py is derived from an example in the LangChain documentation. In this example we use PromptTemplate that contains the pattern we would like the LLM to use when returning a response.
1 from langchain.prompts import PromptTemplate
2 from langchain_openai import OpenAI
3 llm = OpenAI(temperature=0.9)
4
5 def get_country_information(country_name):
6 print(f"\nProcessing {country_name}:")
7 global prompt
8 if "prompt" not in globals():
9 print("Creating prompt...")
10 prompt = PromptTemplate(
11 input_variables=["country_name"],
12 template = """
13 Predict the capital and population of a country.
14
15 Country: {country_name}
16 Capital:
17 Population:""",
18 )
19 prompt_text = prompt.format(country_name=country_name)
20 print(prompt_text)
21 return llm(prompt_text)
22
23 print(get_country_information("Canada"))
24 print(get_country_information("Germany"))
You can use the ChatGPT web interface to experiment with prompts and when you find a pattern that works well then write a Python script like the last example, but changing the data you supply in the PromptTemplate instance.
The output of the last example is:
1 $ python country_information.py
2
3 Processing Canada:
4 Creating prompt...
5
6 Predict the capital and population of a country.
7
8 Country: Canada
9 Capital:
10 Population:
11
12
13 Capital: Ottawa
14 Population: 37,058,856 (as of July 2020)
15
16 Processing Germany:
17
18 Predict the capital and population of a country.
19
20 Country: Germany
21 Capital:
22 Population:
23
24
25 Capital: Berlin
26 Population: 83,02 million (est. 2019)
Creating Embeddings
We will reference the LangChain embeddings documentation. We can use a Python REPL to see what text to vector space embeddings might look like:
1 $ python
2 Python 3.10.8 (main, Nov 24 2022, 08:08:27) [Clang 14.0.6 ] on darwin
3 Type "help", "copyright", "credits" or "license" for more information.
4 >>> from langchain_openai import OpenAIEmbeddings
5 >>> embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
6 >>> text = "Mary has blond hair and John has brown hair. Mary lives in town and John\
7 lives in the country."
8 >>> doc_embeddings = embeddings.embed_documents([text])
9 >>> doc_embeddings
10 [[0.007727328687906265, 0.0009025644976645708, -0.0033224383369088173, -0.0179449208\
11 0807686, -0.017969949170947075, 0.028506645932793617, -0.013414892368018627, 0.00466
12 76816418766975, -0.0024965214543044567, -0.02662956342101097,
13 ...]]
14 >>> query_embedding = embeddings.embed_query("Does John live in the city?")
15 >>> query_embedding
16 [0.028048301115632057, 0.011499025858938694, -0.00944007933139801, -0.02080961130559\
17 4444, -0.023904507979750633, 0.018750663846731186, -0.01626438833773136, 0.018129095
18 435142517,
19 ...]
20 >>>
Notice that the doc_embeddings is a list where each list element is the embeddings for one input text document. The query_embedding is a single embedding. Please read the above linked embedding documentation.
We will use vector stores to store calculated embeddings for future use. In the next chapter we will see a document database search example using LangChain and Llama-Index.
Using LangChain Vector Stores to Query Documents: a Simple RAG Application
We will reference the LangChain Vector Stores documentation.
We use a utility in file read_text_files.py to convert text files in a directory to a list of strings:
1 import os
2
3 # Generated by Perplexity
4
5 def read_text_files(directory_path):
6 """
7 Reads all .txt files in the specified directory and returns their contents as a \
8 list of strings.
9
10 :param directory_path: The path to the directory containing .txt files
11 :return: A list of strings where each string is the content of a .txt file
12 """
13 txt_contents = []
14 # Check if the directory exists
15 if not os.path.isdir(directory_path):
16 print(f"The directory {directory_path} does not exist.")
17 return txt_contents
18
19 # Iterate over all files in the directory
20 for filename in os.listdir(directory_path):
21 # Check for .txt extension
22 if filename.endswith(".txt"):
23 # Construct full file path
24 file_path = os.path.join(directory_path, filename)
25 # Open and read the contents of the file
26 try:
27 with open(file_path, 'r') as file:
28 txt_contents.append(file.read())
29 except IOError as e:
30 print(f"Failed to read file {filename}: {e}")
31
32 return txt_contents
The example script is doc_search.py:
1 from langchain_community.vectorstores import FAISS
2 from langchain_core.output_parsers import StrOutputParser
3 from langchain_core.prompts import ChatPromptTemplate
4 from langchain_core.runnables import RunnablePassthrough
5 from langchain_openai import ChatOpenAI, OpenAIEmbeddings
6 from langchain_community.document_loaders import DirectoryLoader
7
8 model = ChatOpenAI()
9
10 from read_text_files import read_text_files
11
12 vectorstore = FAISS.from_texts(read_text_files("../data/"), embedding=OpenAIEmbeddin\
13 gs())
14
15 retriever = vectorstore.as_retriever()
16
17 template = """Answer the question based only on the following context:
18 {context}
19
20 Question: {question}
21 """
22 prompt = ChatPromptTemplate.from_template(template)
23
24 chain = (
25 {"context": retriever, "question": RunnablePassthrough()}
26 | prompt
27 | model
28 | StrOutputParser()
29 )
30
31 print(chain.invoke("who tried to define what Chemistry is?"))
32
33 print(chain.invoke("What kinds of equipment are in a chemistry laboratory?"))
34 print(chain.invoke("What is Austrian School of Economics?"))
35 print(chain.invoke("Why do people engage in sports?"))
36 print(chain.invoke("What is the effect of body chemistry on exercise?"))
The output is:
1 $ python doc_search.py
2 Georg Ernst Stahl, Jean-Baptiste Dumas, Linus Pauling, and Professor Raymond Chang t\
3 ried to define what Chemistry is.
4 Various forms of laboratory glassware are typically used in a chemistry laboratory. \
5 However, it is mentioned that a great deal of experimental and applied/industrial ch
6 emistry can also be done without the use of glassware.
7 The Austrian School of Economics is a school of economic thought that emphasizes the\
8 spontaneous organizing power of the price mechanism, advocates a laissez-faire appr
9 oach to the economy, and holds that commercial transactions should be subject to min
10 imal government intervention.
11 People engage in sports for physical athleticism, physical dexterity, and for leisur\
12 e activities that they find amusing or entertaining.
13 Body chemistry can affect exercise in terms of how energy is transferred from one su\
14 bstance to another, with heat being transferred more easily than other forms of ener
15 gy. Additionally, the body's ability to learn to fight specific infections after exp
16 osure to germs, known as adaptive immunity, can impact exercise performance and reco
17 very.
Example Using LangChain Integrations: Using Server APIs for Google Search
NOTE: This example is deprecated.
The example shown here is in the directory from_langchain_docs in the source file search_simple.py. The relevant LangChain Integrations documentation page is https://python.langchain.com/docs/integrations/tools/google_serper.
1 # make sure SERPER_API_KEY is set in your environment
2
3 from langchain_community.utilities import GoogleSerperAPIWrapper
4 search_helper = GoogleSerperAPIWrapper()
5
6 def search(query):
7 return search_helper.run(query)
8
9 print(search("What is the capital of Arizona?"))
10 #print(search("Sedona Arizona?"))
You will need a Server API key form https://serper.dev. Currently you can get a free key for 2500 API calls. After that the paid tier currently starts at $50 for 50K API calls and these credits must be used within a 6 month period.
OpenAI Model GPT-4o Example
Here we use the new OpenAI model GPT-4o released May 13, 2024. The example file from_langchain_docs/gpt_4o_test.py uses the latest (May 2024) LangChain APIs:
1 from langchain_core.messages import HumanMessage, SystemMessage
2 from langchain_openai import ChatOpenAI
3
4 llm = ChatOpenAI(model="gpt-4o")
5
6 messages = [
7 SystemMessage(content="You're a helpful assistant"),
8 HumanMessage(content="What is the purpose of model regularization? Be concise."),
9 ]
10
11 results = llm.invoke(messages)
12 print(results.content)
13 print("\n")
14 print(results)
The output is:
1 $ python gpt_4o_test.py
2 The purpose of model regularization is to prevent overfitting by adding a penalty fo\
3 r larger coefficients in the model, thereby improving its generalization to new, uns
4 een data.
5
6 content='The purpose of model regularization is to prevent overfitting by adding a p\
7 enalty for larger coefficients in the model, thereby improving its generalization to
8 new, unseen data.' response_metadata={'token_usage': {'completion_tokens': 34, 'pro
9 mpt_tokens': 27, 'total_tokens': 61}, 'model_name': 'gpt-4o', 'system_fingerprint':
10 'fp_729ea513f7', 'finish_reason': 'stop', 'logprobs': None} id='run-f2fa3e57-eeff-44
11 96-b6e4-d06af8f230d0-0'
This new (as of May 2024) model is half the cost and much lower API call latency than the previous GPT-4 API.
LangChain Overview Wrap Up
We will continue using LangChain for the rest of this book as well as the LlamaIndex library that we introduce in the next chapter.
I cover just the subset of LangChain that I use in my own projects in this book. I urge you to read the LangChain documentation and to explore public LangChain chains that users have written on Langchain-hub.