Master LangChain in 2025: From RAG to Tools (Complete Guide)
Last Updated on September 29, 2025 by Editorial Team
Author(s): Debasish Das
Originally published on Towards AI.
Models → Prompt →Output → Chain →Runnable →RAG → Documents Loaders → Text Splitter → Vector Store → Retriver → Creating Tools → Tools Calling → AI Agents
I have provided an example and a link to the code on GitHub.
What is Langchain ?
LangChain is an open-source framework that simplifies the development of applications powered by large language models (LLMS). It provides tools and abstractions for connecting LLMS to external data sources, creating chains of model calls, and building various AI applications like chatbots and virtual agents. LangChain is available in both Python and JavaScript/TypeScript, making it accessible to a wide range of developers.
Models :
These models are essentially the “brains” of your application, capable of generating text, completing prompts, or engaging in conversational interactions. LangChain provides a unified interface to interact with various LLMS and chat models from different providers, making building applications that leverage their capabilities easier.
In Langchain, we utilise two types of models: 1) Language Model 2) Embedding Model
- Language Models:
There are two main categories of language models: Large Language Models (LLMS) and Chat Models. Nowadays, many companies prefer using Chat Models over LLMs. It’s essential to understand the distinctions between these two types.
- LLM:
Large Language Models (LLMs) are pure text-generating models. They do not interact like a human. So that's why we use Chat models instead of LLMs.An example is OpenAI’s GPT-3, which takes a string prompt as input and generates a text completion as output.
2. Chat Model:
Chat Models are often based on LLMs but are specifically designed for conversational interactions like humans. They accept a sequence of chat messages as input and return a chat message as output. Examples of Chat Models include GPT-4 and Anthropic’s Claude-2.
3. Embedding Models
Embedding models are algorithms that transform text into numerical representations, or embeddings, which are then used by machine learning algorithms for various natural language processing (NLP) tasks. These embeddings capture the semantic meaning of text, allowing for tasks like similarity search,etc.
from langchain_openai import ChatOpenAI # Chatmodels
from dotenv import load_dotenv
#load api key from .env file
load_dotenv()
# 1. model: The model name to use. For example, "gpt-3.5-turbo" or "gpt-4".
# 2. temperature: Controls the randomness of the output. Lower values make the output more deterministic, while higher values make it more random.
# 3. max_tokens: The maximum number of tokens to generate in the response.
model=ChatOpenAI(model="gpt-4", temperature=0.5, max_tokens=50)
results=model.invoke("What is Capital of the India?")
print(results)
print(results.content)
Prompts:
Prompts are the structured inputs provided to Large Language Models (LLMs) to guide their responses. They are essentially instructions or questions designed to extract a specific output from the model. Prompt engineering focuses on crafting these prompts effectively to ensure the LLM generates accurate, relevant, and helpful responses.
- Basic Prompt
from langchain.prompts import PromptTemplate,load_prompt
input=PromptTemplate(template="My name Is {name} and I am from {country}. I have done my {education} ",
input_variables=['name', 'country','education'],validate_template=True)
input.format(name= 'Debasish Das', education= 'Master', country='India')
# Output
'My name Is Debasish Das and I am from India. I have done my Master '
- JSON Prompt
input=PromptTemplate(template="My name Is {name} and I am from {country}. I have done my {education} ",
input_variables=['name', 'country','education'],validate_template=True)
input.format(name= 'Debasish Das', education= 'Master', country='India')
input.save('temp.json')
- Load Prompt from Jason File
from langchain.prompts import PromptTemplate,load_prompt
input=load_prompt('temp.json')
input.format(name= 'Debasish Das', education= 'Master', country='India')
# Output
'My name Is Debasish Das and I am from India. I have done my Master '
Output:
- Structure Output
In LangChain, structured output refers to the practice of having language models return responses in a well-defined data format (for example, JSON), rather than free-form text. This makes the model output easier to parse and work with programmatically.
Most new language models have the ability to provide structured output. When using certain libraries, they can produce JSON-type or dictionary-type data. This is extremely useful for integrating multiple components into one pipeline.
Here we are converting LLM Response to Pydantic Data Format :
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from typing import TypedDict, Annotated, Optional, Literal
from pydantic import BaseModel, Field
load_dotenv()
model = ChatOpenAI()
# schema
class Review(BaseModel):
key_themes: list[str] = Field(description="Write down all the key themes discussed in the review in a list")
summary: str = Field(description="A brief summary of the review")
sentiment: Literal["pos", "neg"] = Field(description="Return sentiment of the review either negative, positive or neutral")
pros: Optional[list[str]] = Field(default=None, description="Write down all the pros inside a list")
cons: Optional[list[str]] = Field(default=None, description="Write down all the cons inside a list")
name: Optional[str] = Field(default=None, description="Write the name of the reviewer")
structured_model = model.with_structured_output(Review)
result = structured_model.invoke("""I recently upgraded to the Samsung Galaxy S24 Ultra, and I must say, it’s an absolute powerhouse! The Snapdragon 8 Gen 3 processor makes everything lightning fast—whether I’m gaming, multitasking, or editing photos. The 5000mAh battery easily lasts a full day even with heavy use, and the 45W fast charging is a lifesaver.
The S-Pen integration is a great touch for note-taking and quick sketches, though I don't use it often. What really blew me away is the 200MP camera—the night mode is stunning, capturing crisp, vibrant images even in low light. Zooming up to 100x actually works well for distant objects, but anything beyond 30x loses quality.
However, the weight and size make it a bit uncomfortable for one-handed use. Also, Samsung’s One UI still comes with bloatware—why do I need five different Samsung apps for things Google already provides? The $1,300 price tag is also a hard pill to swallow.
Pros:
Insanely powerful processor (great for gaming and productivity)
Stunning 200MP camera with incredible zoom capabilities
Long battery life with fast charging
S-Pen support is unique and useful
Review by Nitish Singh
""")
print(result)
In the same way, we can convert into JSON and Dict format.
- Output Parser
Output Parsers in Lang Chain help convert raw LLM responses into structured formats like JSON, CSV, Pydantic models, and more. They ensure consistency, validation, and ease of use in applications.
Old LLMs cannot provide output in a structured format; therefore, we use an output parser to convert the LLM response into structured formats like JSON, dictionaries, Pydantic, Stroutput Parser, etc.
Converting LLM Response to Pydantic Format :
from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint
from dotenv import load_dotenv
from langchain_core.prompts import PromptTemplate
from langchain_groq import ChatGroq
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
load_dotenv()
# # Define the model
# llm = HuggingFaceEndpoint(
# repo_id="google/gemma-2-2b-it",
# task="text-generation"
# )
# model = ChatHuggingFace(llm=llm)
model=ChatGroq(model="llama-3.1-8b-instant")
class Person(BaseModel):
name: str = Field(description='Name of the person')
age: int = Field(gt=18, description='Age of the person')
city: str = Field(description='Name of the city the person belongs to')
parser = PydanticOutputParser(pydantic_object=Person)
template = PromptTemplate(
template='Generate the name, age and city of a fictional {place} person \n {format_instruction}',
input_variables=['place'],
partial_variables={'format_instruction':parser.get_format_instructions()}
)
chain = template | model | parser
final_result = chain.invoke({'place':'sri lankan'})
print(final_result)
In the same way, we can convert the response to String, JSON, Dict, CSV, Structure output parser, and Pydantic Output parser format.
Difference between string, JSON, Structure, Pydantic :

Chain:
A chain refers to a sequence of calls or actions executed in a predefined order. These actions can involve interacting with large language models (LLMs), external tools, or data preprocessing steps. Chains are fundamental to LangChain because they allow you to build more complex and powerful applications by combining different components and functionalities.
- Sequential Chain
from langchain_groq import ChatGroq
from dotenv import load_dotenv
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
load_dotenv()
# Define the model
model= ChatGroq(model="llama-3.1-8b-instant")
# Prompt for model
prompt=PromptTemplate(template="Give me 5 line summary of thr {topic}", input_variables=["topic"])
# Output parser
parser=StrOutputParser()
# Chains
chain= prompt | model | parser
resluts=chain.invoke({"topic":"Grobal Warming"})
# Print summary
print(resluts)
# Pipeline Printing
chain.get_graph().print_ascii()
- Parallel Chain
from langchain_groq import ChatGroq
from dotenv import load_dotenv
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.schema.runnable import RunnableParallel
load_dotenv()
model=ChatGroq(model="llama-3.1-8b-instant")
# Define the prompts
prompt1=PromptTemplate(template="Can U tell me the future of {job_role} in this sector and tell about pro and cons", input_variables=["job_role"]
)
prompt2=PromptTemplate(template="Can U tell me the current Job market trends for {job_role}", input_variables=["job_role"]
)
prompt3=PromptTemplate(template="Analysing the {future} and {market_trend} this job role good for me or not",input_variables=["future","market_trend"])
# Define the output parsers
parser=StrOutputParser()
# Create Parallel pipe line
sequence=RunnableParallel({
"future": prompt1 | model | parser,
"market_trend": prompt2| model | parser}
)
# Define the final prompt
main= prompt3 | model | parser
# Test the pipeline
chain= sequence | main | parser
results=chain.invoke({"job_role": "Data Science"})
print(results)
chain.get_graph().print_ascii()
- Conditional Chain
from langchain_groq import ChatGroq
from dotenv import load_dotenv
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.schema.runnable import RunnableParallel, RunnableBranch, RunnableLambda
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import Literal
load_dotenv()
model=ChatGroq(model="llama-3.1-8b-instant")
parser = StrOutputParser()
class Feedback(BaseModel):
sentiment: Literal['positive', 'negative'] = Field(description='Give the sentiment of the feedback')
parser2 = PydanticOutputParser(pydantic_object=Feedback)
prompt1 = PromptTemplate(
template='Classify the sentiment of the following feedback text into postive or negative \n {feedback} \n {format_instruction}',
input_variables=['feedback'],
partial_variables={'format_instruction':parser2.get_format_instructions()}
)
classifier_chain = prompt1 | model | parser2
prompt2 = PromptTemplate(
template='Write an appropriate response to this positive feedback \n {feedback}',
input_variables=['feedback']
)
prompt3 = PromptTemplate(
template='Write an appropriate response to this negative feedback \n {feedback}',
input_variables=['feedback']
)
branch_chain = RunnableBranch(
(lambda x:x.sentiment == 'positive', prompt2 | model | parser),
(lambda x:x.sentiment == 'negative', prompt3 | model | parser),
RunnableLambda(lambda x: "could not find sentiment")
)
chain = classifier_chain | branch_chain
print(chain.invoke({'feedback': 'This is a beautiful phone'}))
chain.get_graph().print_ascii()
Runnables:
The Runnable interface in LangChain is essential for simplifying the creation, execution, and customisation of workflows involving large language models (LLMS). It standardises how chains are defined and executed, offering methods like invoke, batch, and stream to handle inputs and outputs efficiently, whether synchronously or asynchronously. This interface supports advanced features like concurrent processing, intermediate result access, and seamless integration with other LangChain components, such as memory modules and retrieval systems. By enabling flexibility, scalability, and ease of use, the Runnable interface helps developers transition smoothly from prototyping to production while building sophisticated applications with features like streaming outputs, debugging capabilities, and multi-agent orchestration.
- Task Specfic Runnables
These core Lanchain components have been converted into Runnables for pipeline use. It performs task-specific operations like LLM calls, prompting and retrieval.
Example:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
model = ChatOpenAI() # Connect LLM to local system
parser = StrOutputParser() # Formats Prompts Dynamically
- Primitives Runnables
These are foundational building blocks for structuring execution logic in AI Workflows. They help orchestrate execution by defining runnables. People mostly use Primitive Runnables.
Code Examples:
''' Here an end-to-end project implementing a customer service assistant
using LangChain components '''
from langchain_core.runnables import (
RunnableSequence,
RunnableParallel,
RunnablePassthrough,
RunnableLambda,
RunnableBranch
)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
# 1. Define components
llm = ChatOpenAI(model="gpt-3.5-turbo")
output_parser = StrOutputParser()
# Prompt templates
main_prompt = PromptTemplate.from_template(
"As customer service, respond to this query: {query}\n"
"Context:\n- User status: {user_status}\n- Order history: {order_history}"
)
urgent_prompt = PromptTemplate.from_template(
"URGENT QUERY: {query}\n"
"Immediately escalate to L2 support with this summary:"
)
# 2. Custom functions
def check_urgency(query: str) -> bool:
return any(word in query.lower() for word in ["urgent", "emergency", "critical"])
def fetch_user_data(query: str):
# Simulated database lookup
return {"user_status": "VIP", "order_history": "3 orders in past month"}
# 3. Create runnables
route_checker = RunnableLambda(check_urgency)
data_fetcher = RunnableLambda(fetch_user_data)
# 4. Define processing branches
urgent_chain = (
urgent_prompt
| llm
| output_parser
)
normal_chain = (
RunnableParallel(
query=RunnablePassthrough(),
user_status=lambda x: x["user_status"],
order_history=lambda x: x["order_history"]
)
| main_prompt
| llm
| output_parser
)
# 5. Build main sequence
full_chain = RunnableSequence(
RunnablePassthrough.assign(
is_urgent=route_checker
)
| RunnableBranch(
(lambda x: x["is_urgent"], urgent_chain),
normal_chain
)
)
# 6. Add parallel data processing
full_chain = RunnableParallel(
original_input=RunnablePassthrough(),
user_data=data_fetcher
) | {
"query": lambda x: x["original_input"],
"user_status": lambda x: x["user_data"]["user_status"],
"order_history": lambda x: x["user_data"]["order_history"]
} | full_chain
# Example usage
print(full_chain.invoke("My order is missing! This is urgent!"))
# Output: Escalating to L2 support: Customer reports missing urgent order...
print(full_chain.invoke("When will my package arrive?"))
# Output: As a valued VIP customer, your package with 3 recent orders...
RAG:
- Document Loaders
- Text Splitter
- Vector Store
- Retrievers
Documents Loaders :
Document loaders are components in Langchain used to load data from various sources into a standardised format ( usually as Document Objects), which can then be used for chunking, embedding, retrieval and Generation.
- Textloader: It converts a text (.txt) file to a Langchain Document Object.
- PyPDFLoader: It is a Langchain Documents Loader that converts a PDF file to a Langchain Documents Object. One limitation is that it is not useful for a complex layout of a PDF.
- DirectoryLoaders: It loads multiple documents from a Directory (Folder).
- Load vs Lazy Load

- WebBaseLoader: It loads and extracts text content from web pages(URLS).
Text Splitting :
Whenever we work with any LLM or embedding model, they operate within a limited token capacity. The model may produce incorrect results if you provide input exceeding this token limit. This is where text splitters come into play. Text splitters solve this problem by dividing the text into smaller chunks that fit within the model’s token limit. Instead of using the entire text, we use a text splitter to segment it and then feed these chunks to the model. This approach is particularly useful when creating embeddings.
Text splitters are highly effective in NLP-related tasks as they reduce computational costs, improve results, and optimise the parallel processing of text.
Parameters: Chunk_Size , Chunk_Overlap
- Length-Based Text Splitter
==> A length-based text splitter divides text into chunks based on a specified number of characters, words, or other length units. This straightforward approach ensures consistent chunk sizes, making it suitable for various applications.
Key aspects of length-based splitting:
- Chunk Size:
==> The maximum length of each chunk is defined, ensuring that no chunk exceeds this limit.
- Overlap:
==> An optional overlap between chunks can be added to maintain context.
2. Text Structure-Based Text Splitter
==> Text Structure-Based text splitters utilize the natural hierarchy of text (paragraphs, sentences, words) to create chunks that maintain semantic coherence and natural language flow. LangChain’s RecursiveCharacterTextSplitter is a prime example, which recursively attempts to split at the highest level possible (paragraphs, sentences, then words) while keeping chunks within a specified size. This approach aims to preserve the original meaning and context of the text.
3. Document Structure-Based Text Splitter
==> A Document Structure-Based Text Splitter in LangChain is a type of text splitter that breaks down documents into smaller, manageable chunks based on their inherent structure, such as headers, paragraphs, or sections. This approach benefits documents with a natural hierarchy, like HTML, Markdown, or JSON files. Splitting along structural boundaries helps preserve the document's logical organisation and semantic context, making it more effective for downstream tasks like retrieval and summarisation.
4. Semantic Meaning-Based Test Splitter
==> A Semantic Meaning-Based Test Splitter is a tool that divides text into chunks based on semantic similarity, ensuring that each chunk contains sentences with related meanings. This approach is beneficial for tasks where maintaining the context and flow of information is essential, like in natural language processing or document summarization.
# Install required packages
# pip install langchain-text-splitters langchain-community sentence-transformers
from langchain_text_splitters import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter,
CharacterTextSplitter
)
from langchain_experimental.text_splitter import SemanticChunker
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.document_loaders import PyPDFLoader
# Sample text for demonstration
text = """# LangChain Documentation
## Text Splitting
Effective text splitting is crucial for working with large documents.
There are several approaches to split text while preserving meaning.
### Methods
1. Length-based splitting
2. Structure-aware splitting
3. Document-type specific splitting
4. Semantic splitting"""
# 1. Length-based splitter (Recursive Character Text Splitter)
length_splitter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=20,
separators=["\n\n", "\n", " ", ""])
length_chunks = length_splitter.split_text(text)
# 2. Structure-based splitter (Markdown Header Splitter)
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3")
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
structure_chunks = markdown_splitter.split_text(text)
# 3. Document-based splitter (PDF processing example)
document_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300,
chunk_overlap=50
)
loader = PyPDFLoader("example.pdf") # Replace with actual PDF path
docs = loader.load()
document_chunks = document_splitter.split_documents(docs)
# 4. Semantic splitter (Experimental)
embeddings = HuggingFaceEmbeddings()
semantic_splitter = SemanticChunker(
embeddings,
breakpoint_threshold_type="percentile",
buffer_size=3
)
semantic_chunks = semantic_splitter.split_text(text)
Vector Store
- Why do we need Vector Store instead of a Database?
==> Vector stores are crucial for AI and machine learning because they efficiently handle high-dimensional vector data and enable fast similarity searches, which traditional databases cannot manage effectively due to their limitations with complex, unstructured data. This makes vector stores indispensable for modern applications like semantic analysis and image recognition.
- What is Vector Store?
==>A vector store is a specialized system or library designed to efficiently store, manage, and retrieve vector embeddings, which are numerical data representations. These systems are beneficial for tasks like similarity search, pattern recognition, and machine learning in AI and data analytics.
Key Features and Functionality:
- Vector Embedding Storage:
==> Vector stores are specifically designed to handle vector embeddings, which are numerical representations of data points. These embeddings capture the semantic meaning of data, making them suitable for similarity searches.
- Similarity Search:
==> Vector stores enable efficient searching based on the similarity of data points. By comparing the vector embeddings of a query with those stored in the database, they can retrieve the most relevant results.
- Index and Retrieval:
==> Vector stores employ indexing techniques to optimize search performance. They can quickly locate and retrieve relevant vectors based on similarity criteria.
- High-Dimensional Data Handling:
==> Vector stores are well-suited for managing and querying high-dimensional data, which is common in machine learning and AI applications.
Retrievers
- What are Retrivers?
A Retriever is the component responsible for finding relevant information from a knowledge base or external source based on a user’s query. It acts as a search engine, returning the most relevant documents, passages, or chunks of text to the generator, which then uses this context to create a response.
- Types of Retrievers
==> Data Source, Search Strategy
- Wikipedia Retriever
==> Accessing real-time web content can be a valuable data source.
- Vector store Retriever
==> These store data as vector embeddings, allowing for semantic similarity searches.
- MMR
==> This strategy generates multiple queries from the original query to capture different perspectives and improve retrieval.
- MQR
==> This technique re-ranks results to balance relevance and diversity.
- CCR
==> This method reduces the size of retrieved documents by compressing irrelevant information, saving LLM costs.
Tools
- What are Tools?
==> A “Tool” is essentially a packaged-up Python function that an LLM can understand and request to be executed when needed. This allows the LLM to interact with the outside world, perform actions beyond just generating text.
or
These are Python functions (or similar functions in other languages) that are designed to perform specific tasks.
- How to fit Agents in Ecosystem
==> An AI agent is an LLM-powered system that can autonomously think, make decisions, and take actions to achieve a goal, often by using external tools or APIs. Unlike a chatbot, which simply responds to queries, an AI agent can plan, execute, and learn from its interactions with the environment.

- Built-in Tools
Built-in tools are tools which is provided by Langchain. You need to use it freely. No need for any external installation.
i. DuckDuckGoSearchRun
ii. GmailsendMessageTool
iii.WikipediaQueryRun
For more information on tools, Visit Here.
Now Our Mission is to Create Tools for Agents

- Custom Tools Using Tool Class:
A custom tool is a tool that you define yourself.
Use them when:
- You want to call your own APIs
- You want to encapsulate business logic
- You want the LLM to interact with your database
# Step 1 - create a function
# Step 2 - add type hints
# Step 3 - add tool decorator
from langchain_core.tools import tool
@tool
def multiply(a: int, b:int) -> int:
"""Multiply two numbers"""
return a*b
# Output
result = multiply.invoke({"a":3, "b":5})
print(result)
# Features
print(multiply.name)
print(multiply.description)
print(multiply.args)
- Structure Tools and Pydantic
A Stractured tool in Langchain is a special type of tool where the input to the tool follows a structured schema, typically defined using a Pydantic Model.
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
class MultiplyInput(BaseModel):
a: int = Field(required=True, description="The first number to add")
b: int = Field(required=True, description="The second number to add")
multiply_tool = StructuredTool.from_function(
func=multiply_func,
name="multiply",
description="Multiply two numbers",
args_schema=MultiplyInput
)
result = multiply_tool.invoke({'a':3, 'b':3})
print(result)
print(multiply_tool.name)
print(multiply_tool.description)
print(multiply_tool.args)
- Base model Class
Base Tool is the abstract base class for all tools in Langchain. It defines the core structure and interface that any tool must follow, whether it's a simple one-liner or fully customized functions.
All other tool types, like the tool and structured tool, are built on top of BaseTool.
from langchain.tools import BaseTool
from typing import Type
class MultiplyInput(BaseModel):
a: int = Field(required=True, description="The first number to add")
b: int = Field(required=True, description="The second number to add")
class MultiplyTool(BaseTool):
name: str = "multiply"
description: str = "Multiply two numbers"
args_schema: Type[BaseModel] = MultiplyInput
def _run(self, a: int, b: int) -> int:
return a * b
multiply_tool = MultiplyTool()
result = multiply_tool.invoke({'a':3, 'b':3})
print(result)
print(multiply_tool.name)
print(multiply_tool.description)
print(multiply_tool.args)
- Toolkit

from langchain_core.tools import tool
# Custom tools
@tool
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
return a * b
class MathToolkit:
def get_tools(self):
return [add, multiply]
toolkit = MathToolkit()
tools = toolkit.get_tools()
for tool in tools:
print(tool.name, "=>", tool.description)
# Output
"""add => Add two numbers
multiply => Multiply two numbers"""
Tools Calling
- Tool Binding

- Tool Calling

- Tool Execution

Small Project [ Currency Conversion ] use tool calling
# tool create for Currency Conversion
from langchain_core.tools import InjectedToolArg
from typing import Annotated
@tool
def get_conversion_factor(base_currency: str, target_currency: str) -> float:
"""
This function fetches the currency conversion factor between a given base currency and a target currency
"""
url = f'https://v6.exchangerate-api.com/v6/API/pair/{base_currency}/{target_currency}'
response = requests.get(url)
return response.json()
@tool
def convert(base_currency_value: int, conversion_rate: Annotated[float, InjectedToolArg]) -> float:
"""
given a currency conversion rate this function calculates the target currency value from a given base currency value
"""
return base_currency_value * conversion_rate
# Model
llm = ChatOpenAI()
# Tools Binding
llm_with_tools = llm.bind_tools([get_conversion_factor, convert])
# Queary message
messages = [HumanMessage('What is the conversion factor between INR and USD, and based on that can you convert 10 inr to usd')]
# Store the Meaasge
ai_message = llm_with_tools.invoke(messages)
messages.append(ai_message)
import json
# Atuomathed workflow for Currency Conversion
for tool_call in ai_message.tool_calls:
# execute the 1st tool and get the value of conversion rate
if tool_call['name'] == 'get_conversion_factor':
tool_message1 = get_conversion_factor.invoke(tool_call)
# fetch this conversion rate
conversion_rate = json.loads(tool_message1.content)['conversion_rate']
# append this tool message to messages list
messages.append(tool_message1)
# execute the 2nd tool using the conversion rate from tool 1
if tool_call['name'] == 'convert':
# fetch the current arg
tool_call['args']['conversion_rate'] = conversion_rate
tool_message2 = convert.invoke(tool_call)
messages.append(tool_message2)
# Output
llm_with_tools.invoke(messages).content
AI Agents
- What is an AI Agent?
An AI agent is a software program or system that uses artificial intelligence to interact with its environment and perform tasks to achieve specific goals. These agents have a degree of autonomy, meaning they can make decisions and act independently to accomplish their objectives. They rely on AI techniques like machine learning and natural language processing to understand and respond to user inputs, and they can learn and adapt over time.
- Characteristics of AI Agents

- What is ReAct?
“ReAct” stands for Reasoning and Acting. It’s a framework that enhances LLM agents by integrating reasoning with the ability to take actions and interact with external environments. Essentially, ReAct allows LLMs to think, then act, and then use the results of their actions to refine their thinking and further act.
- What are agents and Agent Executor?
An agent is a system that uses a language model (LLM) as a reasoning engine to determine which actions to take and what inputs to use for those actions. The agent itself doesn’t execute the actions; instead, it’s the AgentExecutor’s responsibility to manage the execution of those actions.
Agents:
- Reasoning Engine:
Agents use an LLM to decide what actions to take and the necessary inputs for those actions.
- Action Planning:
The agent determines the sequence of actions needed to achieve a goal, but it doesn’t execute them.
- Tool Interaction:
Agents can interact with various tools and environments to gather information, perform tasks, and generate outputs.
AgentExecutor:
- Execution Manager:
The AgentExecutor is responsible for managing the execution of the agent’s actions, controlling its lifecycle, and handling interactions with tools.
- Action Execution:
It executes the actions determined by the agent, potentially using tools to perform tasks.
- Iteration and Observation:
The AgentExecutor can iterate through actions, observe the results, and feed them back to the agent for further decision-making.
- Scratchpad:
It often uses a scratchpad (a list of messages) to keep track of the agent’s thoughts, actions, and observations during the execution.
— — — — — — — — — — — — — The End — — — — — — — — — — — —
I will be adding more content to this blog in the future. For now, this serves as a comprehensive resource for learning Langchain, from basic to advanced levels. I frequently reference Nitish Sir’s videos while creating this blog, as well as the Langchain documentation.
If you enjoy this blog, please follow me on Medium and LinkedIn. Thank you for your time!.
Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.
Published via Towards AI
Take our 90+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!
Towards AI has published Building LLMs for Production—our 470+ page guide to mastering LLMs with practical projects and expert insights!

Discover Your Dream AI Career at Towards AI Jobs
Towards AI has built a jobs board tailored specifically to Machine Learning and Data Science Jobs and Skills. Our software searches for live AI jobs each hour, labels and categorises them and makes them easily searchable. Explore over 40,000 live jobs today with Towards AI Jobs!
Note: Content contains the views of the contributing authors and not Towards AI.