This repo is built for testing new agentic features. It is built from langchain and langgraph
agent_resources/
base_agent.py
agent_factory.py
agents/
conversational_agent/
react_agent/
tools/
tool_registry.py
retrieve_documents.py
Python 3.8 or higher: Ensure you have Python installed. You can check your Python version with:
python --version
Pip: Python package installer should be available.
OpenAI API Key: You need an API key from OpenAI. Sign up at OpenAI.
Tavily API Key (Optional): For web search capabilities, obtain a Tavily API key from Tavily.
Clone the Repository
git clone https://github.com/yourusername/llm-agents-project.git
cd llm-agents-project
Create a Virtual Environment (Optional but Recommended)
python -m venv venv
source venv/bin/activate
Install Dependencies
pip install -r requirements.txt
Copy the Template .env File
cp .env.template .env
Populate the .env File
Open the .env file and replace the placeholders with your actual API keys:
OPENAI_API_KEY=YOUR_OPENAI_API_KEY_HERE
TAVILY_API_KEY=YOUR_TAVILY_API_KEY_HERE
# OPTIONAL LANGSMITH FOR TRACING
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
# LANGCHAIN_API_KEY="YOUR_API_KEY_HERE"
# LANGCHAIN_PROJECT="PROJECT_NAME_HERE"
TAVILY_API_KEY, and don't plan to use the web search agent, you can leave it blank or remove it.Run the main.py script to start interacting with the agents (mainly for testing purposes):
python main.py
Upon running the script, you'll be prompted to select an agent type:
Available Agent Types:
1. conversational_agent
2. react_agent
Type the desired agent name and press Enter.
After selecting an agent, you can start the conversation:
Welcome to the Chatbot!
You've selected the web_search_agent agent. Type 'exit' to end the conversation.
You:
exit and press Enter to terminate the session.agent_resources/: Contains shared resources for agents, including the abstract base agent class (base_agent.py). This serves as the foundation for all agent implementations.
agents/: Houses individual agent implementations. Each agent is contained within its own folder, which includes:
<agent_name>.py: The main agent class file.nodes.py: Definitions of node functions specific to the agent.<agent_name>_workflow.png: A visual representation (DAG) of the agent's workflow.tools/: Contains tool implementations and a registry for managing and retrieving tools. This includes:
tool_registry.py: A registry/factory class for registering and retrieving tools.retrieve_documents.py: An example of a custom tool that can be integrated into agents.LangGraph is a library that enables the creation of complex workflows using graphs. It offers advanced state management and dynamic graph capabilities, making it ideal for building sophisticated agent workflows.
state and return updated state information.agent_resources/base_agent.py.Agent defines the structure and required methods.compile_graph(): Abstract method to compile the agent's graph.run(message): Abstract method to execute the agent with a given message.visualize_workflow(save_path): Visualizes the agent's workflow DAG.ChatOpenAI) are passed to agents for use within nodes.Tools extend the capabilities of agents, enabling them to perform actions like web searches or retrieval-augmented generation.
langchain.tools.BaseTool.agent_resources/tools/tool_registry.py.Usage Example:
from agent_resources.tools.tool_registry import ToolRegistry
# get the tavily_search tool from the list of tools within the ToolRegistry
web_search_tool = ToolRegistry.get_tool('tavily_search', max_results=1)
create_react_agent.In this section, we'll delve into the practical steps of creating agents using the LangGraph framework. We'll start by understanding the base agent class and then walk through the process of creating a new agent with custom nodes and workflows.
base_agent.py ClassThe base_agent.py file defines the abstract base class Agent, which serves as a foundation for all agents in the project. It ensures a consistent interface and enforces the implementation of essential methods.
from abc import ABC, abstractmethod
from langchain.schema import AIMessage
from IPython.display import display, Image
from langchain_core.runnables.graph import MermaidDrawMethod
class Agent(ABC):
"""
Abstract base class for all agents.
"""
def visualize_workflow(self, save_path: str = None):
"""
Visualize the agent's workflow. Optionally save the visualization as an image.
:param save_path: Optional path to save the image.
"""
graph_image = self.agent.get_graph().draw_mermaid_png(
draw_method=MermaidDrawMethod.API)
image = Image(graph_image)
# Display in the notebook (if in a Jupyter environment)
display(image)
# Save the image if a save_path is provided
if save_path:
with open(save_path, "wb") as f:
f.write(graph_image)
print(f"Workflow visualization saved at: {save_path}")
@abstractmethod
def build_graph(self):
"""
Abstract method for compiling the graph and creating an executable agent.
"""
pass
@abstractmethod
def run(self, message) -> AIMessage:
"""
Abstract method that all agents must implement.
Takes a BaseMessage as input and returns an AIMessage as the response.
"""
pass
build_graph(): Must be implemented to define and compile the agent's workflow graph.run(message): Must be implemented to define how the agent processes input messages.visualize_workflow(save_path): Generates a visual representation of the agent's workflow using Mermaid diagrams. Optionally saves the image to a specified path.Creating a new agent involves subclassing the Agent base class, defining custom nodes, and compiling the workflow graph.
Begin by creating a new class that inherits from Agent.
from agent_resources.base_agent import Agent
class MyAgent(Agent):
def __init__(self, llm, memory):
self.llm = llm
self.memory = memory
self.agent = self.compile_graph()
def build_graph(self):
# Define and compile the graph here
pass
def run(self, message):
# Implement the agent's execution logic here
pass
Create a State class using TypedDict to define the structure of the state that will be passed between nodes.
from typing import TypedDict, List
class State(TypedDict):
input_text: str
result: str
# Add other state variables as needed
Define node functions that perform specific tasks. Each node accepts a state and returns updated state information.
def my_node(state: State):
# Perform some operation
updated_result = state["input_text"].upper() # Example operation
return {"result": updated_result}
In the compile_graph() method, create a StateGraph, add nodes and edges, and compile it into an executable agent.
from langgraph.graph import StateGraph, END
def build_graph(self):
workflow = StateGraph(State)
# Add nodes to the graph
workflow.add_node("start_node", lambda state: state)
workflow.add_node("my_node", my_node)
workflow.add_node("end_node", lambda state: state)
# Set entry point
workflow.set_entry_point("start_node")
# Add edges to define the workflow
workflow.add_edge("start_node", "my_node")
workflow.add_edge("my_node", "end_node")
workflow.add_edge("end_node", END)
# Compile into an executable agent
agent = workflow.compile()
return agent
Define how the agent processes input messages and returns responses.
from langchain_core.messages import BaseMessage, AIMessage
def run(self, message: BaseMessage) -> AIMessage:
try:
state_input = {"input_text": message.content}
result = self.agent.invoke(state_input)
return AIMessage(content=result['result'])
except Exception as e:
# Handle exceptions
return AIMessage(content="An error occurred while processing your request.")
Use the visualize_workflow method to generate a diagram of the workflow.
agent = MyAgent(llm, memory)
agent.visualize_workflow(save_path="my_agent_workflow.png")
run method to manage exceptions gracefully.The repo currently has a few example agents to reference. To illustrate the concepts discussed, let's explore the ClassificationAgent in detail. This agent processes input text by classifying it, extracting entities, and summarizing the content. These nodes are run concurrently

classification_nodeentity_extraction_nodesummarization_nodecombine_node.classification_agent.pyImports and Setup:
import logging
from langgraph.graph import StateGraph, END
from langchain_core.messages import BaseMessage, AIMessage
from agent_resources.base_agent import Agent
from .nodes import State, classification_node, entity_extraction_node, summarization_node
logger = logging.getLogger(__name__)
Agent Class Definition:
class ClassificationAgent(Agent):
def __init__(self, llm, memory):
self.llm = llm
self.memory = memory
# Compile the graph
self.agent = self.compile_graph()
def compile_graph(self):
workflow = StateGraph(State)
# Add nodes to the graph
workflow.add_node("start_node", lambda state: state)
workflow.add_node("classification_node", classification_node)
workflow.add_node("entity_extraction", entity_extraction_node)
workflow.add_node("summarization", summarization_node)
workflow.add_node("combine_node", lambda state: state)
# Set entry point
workflow.set_entry_point("start_node")
# Add edges to create fan-out from start_node
workflow.add_edge("start_node", "classification_node")
workflow.add_edge("start_node", "entity_extraction")
workflow.add_edge("start_node", "summarization")
# Add edges to combine_node to create fan-in
workflow.add_edge("classification_node", "combine_node")
workflow.add_edge("entity_extraction", "combine_node")
workflow.add_edge("summarization", "combine_node")
# Add edge to END
workflow.add_edge("combine_node", END)
# Compile into an executable agent
agent = workflow.compile()
return agent
def run(self, message: BaseMessage) -> AIMessage:
try:
state_input = {"text": message.content}
result = self.agent.invoke(state_input)
return AIMessage(content=result['classification'])
except Exception as e:
logger.error("Error generating response", exc_info=True)
return AIMessage(content="Sorry, I encountered an error while processing your request.")
Explanation:
Initialization:
compile_graph() to build the workflow.Graph Compilation:
StateGraph with the defined State structure.start_node and a combine_node.start_node to the processing nodes.combine_node from the processing nodes.Run Method:
AIMessage.nodes.pyImports and Setup:
from typing import TypedDict, List
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
from dotenv import load_dotenv
load_dotenv()
State Definition:
class State(TypedDict):
text: str
classification: str
entities: List[str]
summary: str
LLM Instance:
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
Node Functions:
def classification_node(state: State):
''' Classify the text into one of the categories: News, Blog, Research, or Other '''
prompt = PromptTemplate(
input_variables=["text"],
template="Classify the following text into one of the categories: News, Blog, Research, or Other.\n\nText:{text}\n\nCategory:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
classification = llm.invoke([message]).content.strip()
return {"classification": classification}
def entity_extraction_node(state: State):
''' Extract all the entities (Person, Organization, Location) from the text '''
prompt = PromptTemplate(
input_variables=["text"],
template="Extract all the entities (Person, Organization, Location) from the following text. Provide the result as a comma-separated list.\n\nText:{text}\n\nEntities:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
entities = llm.invoke([message]).content.strip().split(", ")
return {"entities": entities}
def summarization_node(state: State):
''' Summarize the text in one short sentence '''
prompt = PromptTemplate(
input_variables=["text"],
template="Summarize the following text in one short sentence.\n\nText:{text}\n\nSummary:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
summary = llm.invoke([message]).content.strip()
return {"summary": summary}
Explanation:
state.PromptTemplate.llm instance is used across all nodes for consistency.Tools enhance agents by providing additional capabilities, such as web searching or data retrieval. The ToolRegistry facilitates the management and integration of these tools.
from typing import Dict, Type, List
from langchain.tools import BaseTool
class ToolRegistry:
"""
ToolRegistry manages the registration and retrieval of tools.
"""
tool_registry: Dict[str, Type[BaseTool]] = {
'tavily_search': TavilySearchResults,
'rag_tool': RAGTool,
}
@classmethod
def get_tool(cls, tool_name: str, **kwargs) -> BaseTool:
"""
Retrieve a single tool by name.
"""
tool_class = cls.tool_registry.get(tool_name)
if tool_class is None:
raise ValueError(f"Unknown tool: {tool_name}")
return tool_class(**kwargs)
@classmethod
def get_tools(cls, tool_names: List[str], **kwargs) -> List[BaseTool]:
"""
Retrieve multiple tools by their names.
"""
return [cls.get_tool(name, **kwargs) for name in tool_names]
ToolRegistry.tool_registry['my_custom_tool'] = MyCustomTool
my_tool = ToolRegistry.get_tool('my_custom_tool', param1=value1)
tools = ToolRegistry.get_tools(['tool1', 'tool2'], shared_param=shared_value)
The WebSearchAgent is another agent present in the examples, and it demonstrates how to integrate a tool using the ToolRegistry.
Agent Definition:
import logging
from langchain_core.messages import HumanMessage, AIMessage
from langgraph.prebuilt import create_react_agent
from agent_resources.tools.tool_registry import ToolRegistry
from agent_resources.base_agent import Agent
logger = logging.getLogger(__name__)
class WebSearchAgent(Agent):
"""
LangGraph-based WebSearch agent implementation with MemorySaver for persistence.
"""
def __init__(self, llm, memory):
"""
Initialize the WebSearch agent using LangGraph.
:param llm: The language model.
:param memory: Persistent memory for managing conversation history.
"""
# Retrieve the tavily_search tool from the registry
web_search_tool = ToolRegistry.get_tool('tavily_search', max_results=1)
self.tools = [web_search_tool]
self.llm = llm
self.memory = memory
# Create a prebuilt React agent with tools and memory
self.agent = self.compile_graph()
def compile_graph(self):
agent = create_react_agent(
self.llm,
tools=self.tools,
checkpointer=self.memory,
)
return agent
def run(self, message: HumanMessage) -> AIMessage:
"""
Process a HumanMessage and return an AIMessage response.
:param message: User's input message.
:return: AIMessage response.
"""
try:
thread_id = "default"
config = {"configurable": {"thread_id": thread_id}}
response = self.agent.invoke(
{"messages": [message]}, config=config)
ai_message = response["messages"][-1]
if isinstance(ai_message, AIMessage):
return ai_message
else:
logger.error("Unexpected message type in response.")
raise ValueError("Expected AIMessage in the response.")
except Exception as e:
logger.error("Error generating response", exc_info=True)
return AIMessage(content="Sorry, I encountered an error while processing your request.")
ToolRegistry.get_tool() to obtain the tavily_search tool.create_react_agent() to build an agent with the LLM, tools, and memory.Create a new tool by subclassing BaseTool.
from langchain.tools import BaseTool
class MyCustomTool(BaseTool):
def __init__(self, param1, param2):
super().__init__()
# Initialize tool-specific parameters
def _run(self, query: str):
# Implement the tool's functionality
result = perform_custom_operation(query)
return result
async def _arun(self, query: str):
# Implement asynchronous functionality if needed
result = await perform_custom_async_operation(query)
return result
Add the new tool to the ToolRegistry.
from agent_resources.tools.tool_registry import ToolRegistry
ToolRegistry.tool_registry['my_custom_tool'] = MyCustomTool
Retrieve and integrate the tool within your agent.
my_tool = ToolRegistry.get_tool('my_custom_tool', param1=value1, param2=value2)
self.tools = [my_tool]
This repo is built for testing new agentic features. It is built from langchain and langgraph
agent_resources/
base_agent.py
agent_factory.py
agents/
conversational_agent/
react_agent/
tools/
tool_registry.py
retrieve_documents.py
Python 3.8 or higher: Ensure you have Python installed. You can check your Python version with:
python --version
Pip: Python package installer should be available.
OpenAI API Key: You need an API key from OpenAI. Sign up at OpenAI.
Tavily API Key (Optional): For web search capabilities, obtain a Tavily API key from Tavily.
Clone the Repository
git clone https://github.com/yourusername/llm-agents-project.git
cd llm-agents-project
Create a Virtual Environment (Optional but Recommended)
python -m venv venv
source venv/bin/activate
Install Dependencies
pip install -r requirements.txt
Copy the Template .env File
cp .env.template .env
Populate the .env File
Open the .env file and replace the placeholders with your actual API keys:
OPENAI_API_KEY=YOUR_OPENAI_API_KEY_HERE
TAVILY_API_KEY=YOUR_TAVILY_API_KEY_HERE
# OPTIONAL LANGSMITH FOR TRACING
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
# LANGCHAIN_API_KEY="YOUR_API_KEY_HERE"
# LANGCHAIN_PROJECT="PROJECT_NAME_HERE"
TAVILY_API_KEY, and don't plan to use the web search agent, you can leave it blank or remove it.Run the main.py script to start interacting with the agents (mainly for testing purposes):
python main.py
Upon running the script, you'll be prompted to select an agent type:
Available Agent Types:
1. conversational_agent
2. react_agent
Type the desired agent name and press Enter.
After selecting an agent, you can start the conversation:
Welcome to the Chatbot!
You've selected the web_search_agent agent. Type 'exit' to end the conversation.
You:
exit and press Enter to terminate the session.agent_resources/: Contains shared resources for agents, including the abstract base agent class (base_agent.py). This serves as the foundation for all agent implementations.
agents/: Houses individual agent implementations. Each agent is contained within its own folder, which includes:
<agent_name>.py: The main agent class file.nodes.py: Definitions of node functions specific to the agent.<agent_name>_workflow.png: A visual representation (DAG) of the agent's workflow.tools/: Contains tool implementations and a registry for managing and retrieving tools. This includes:
tool_registry.py: A registry/factory class for registering and retrieving tools.retrieve_documents.py: An example of a custom tool that can be integrated into agents.LangGraph is a library that enables the creation of complex workflows using graphs. It offers advanced state management and dynamic graph capabilities, making it ideal for building sophisticated agent workflows.
state and return updated state information.agent_resources/base_agent.py.Agent defines the structure and required methods.compile_graph(): Abstract method to compile the agent's graph.run(message): Abstract method to execute the agent with a given message.visualize_workflow(save_path): Visualizes the agent's workflow DAG.ChatOpenAI) are passed to agents for use within nodes.Tools extend the capabilities of agents, enabling them to perform actions like web searches or retrieval-augmented generation.
langchain.tools.BaseTool.agent_resources/tools/tool_registry.py.Usage Example:
from agent_resources.tools.tool_registry import ToolRegistry
# get the tavily_search tool from the list of tools within the ToolRegistry
web_search_tool = ToolRegistry.get_tool('tavily_search', max_results=1)
create_react_agent.In this section, we'll delve into the practical steps of creating agents using the LangGraph framework. We'll start by understanding the base agent class and then walk through the process of creating a new agent with custom nodes and workflows.
base_agent.py ClassThe base_agent.py file defines the abstract base class Agent, which serves as a foundation for all agents in the project. It ensures a consistent interface and enforces the implementation of essential methods.
from abc import ABC, abstractmethod
from langchain.schema import AIMessage
from IPython.display import display, Image
from langchain_core.runnables.graph import MermaidDrawMethod
class Agent(ABC):
"""
Abstract base class for all agents.
"""
def visualize_workflow(self, save_path: str = None):
"""
Visualize the agent's workflow. Optionally save the visualization as an image.
:param save_path: Optional path to save the image.
"""
graph_image = self.agent.get_graph().draw_mermaid_png(
draw_method=MermaidDrawMethod.API)
image = Image(graph_image)
# Display in the notebook (if in a Jupyter environment)
display(image)
# Save the image if a save_path is provided
if save_path:
with open(save_path, "wb") as f:
f.write(graph_image)
print(f"Workflow visualization saved at: {save_path}")
@abstractmethod
def build_graph(self):
"""
Abstract method for compiling the graph and creating an executable agent.
"""
pass
@abstractmethod
def run(self, message) -> AIMessage:
"""
Abstract method that all agents must implement.
Takes a BaseMessage as input and returns an AIMessage as the response.
"""
pass
build_graph(): Must be implemented to define and compile the agent's workflow graph.run(message): Must be implemented to define how the agent processes input messages.visualize_workflow(save_path): Generates a visual representation of the agent's workflow using Mermaid diagrams. Optionally saves the image to a specified path.Creating a new agent involves subclassing the Agent base class, defining custom nodes, and compiling the workflow graph.
Begin by creating a new class that inherits from Agent.
from agent_resources.base_agent import Agent
class MyAgent(Agent):
def __init__(self, llm, memory):
self.llm = llm
self.memory = memory
self.agent = self.compile_graph()
def build_graph(self):
# Define and compile the graph here
pass
def run(self, message):
# Implement the agent's execution logic here
pass
Create a State class using TypedDict to define the structure of the state that will be passed between nodes.
from typing import TypedDict, List
class State(TypedDict):
input_text: str
result: str
# Add other state variables as needed
Define node functions that perform specific tasks. Each node accepts a state and returns updated state information.
def my_node(state: State):
# Perform some operation
updated_result = state["input_text"].upper() # Example operation
return {"result": updated_result}
In the compile_graph() method, create a StateGraph, add nodes and edges, and compile it into an executable agent.
from langgraph.graph import StateGraph, END
def build_graph(self):
workflow = StateGraph(State)
# Add nodes to the graph
workflow.add_node("start_node", lambda state: state)
workflow.add_node("my_node", my_node)
workflow.add_node("end_node", lambda state: state)
# Set entry point
workflow.set_entry_point("start_node")
# Add edges to define the workflow
workflow.add_edge("start_node", "my_node")
workflow.add_edge("my_node", "end_node")
workflow.add_edge("end_node", END)
# Compile into an executable agent
agent = workflow.compile()
return agent
Define how the agent processes input messages and returns responses.
from langchain_core.messages import BaseMessage, AIMessage
def run(self, message: BaseMessage) -> AIMessage:
try:
state_input = {"input_text": message.content}
result = self.agent.invoke(state_input)
return AIMessage(content=result['result'])
except Exception as e:
# Handle exceptions
return AIMessage(content="An error occurred while processing your request.")
Use the visualize_workflow method to generate a diagram of the workflow.
agent = MyAgent(llm, memory)
agent.visualize_workflow(save_path="my_agent_workflow.png")
run method to manage exceptions gracefully.The repo currently has a few example agents to reference. To illustrate the concepts discussed, let's explore the ClassificationAgent in detail. This agent processes input text by classifying it, extracting entities, and summarizing the content. These nodes are run concurrently

classification_nodeentity_extraction_nodesummarization_nodecombine_node.classification_agent.pyImports and Setup:
import logging
from langgraph.graph import StateGraph, END
from langchain_core.messages import BaseMessage, AIMessage
from agent_resources.base_agent import Agent
from .nodes import State, classification_node, entity_extraction_node, summarization_node
logger = logging.getLogger(__name__)
Agent Class Definition:
class ClassificationAgent(Agent):
def __init__(self, llm, memory):
self.llm = llm
self.memory = memory
# Compile the graph
self.agent = self.compile_graph()
def compile_graph(self):
workflow = StateGraph(State)
# Add nodes to the graph
workflow.add_node("start_node", lambda state: state)
workflow.add_node("classification_node", classification_node)
workflow.add_node("entity_extraction", entity_extraction_node)
workflow.add_node("summarization", summarization_node)
workflow.add_node("combine_node", lambda state: state)
# Set entry point
workflow.set_entry_point("start_node")
# Add edges to create fan-out from start_node
workflow.add_edge("start_node", "classification_node")
workflow.add_edge("start_node", "entity_extraction")
workflow.add_edge("start_node", "summarization")
# Add edges to combine_node to create fan-in
workflow.add_edge("classification_node", "combine_node")
workflow.add_edge("entity_extraction", "combine_node")
workflow.add_edge("summarization", "combine_node")
# Add edge to END
workflow.add_edge("combine_node", END)
# Compile into an executable agent
agent = workflow.compile()
return agent
def run(self, message: BaseMessage) -> AIMessage:
try:
state_input = {"text": message.content}
result = self.agent.invoke(state_input)
return AIMessage(content=result['classification'])
except Exception as e:
logger.error("Error generating response", exc_info=True)
return AIMessage(content="Sorry, I encountered an error while processing your request.")
Explanation:
Initialization:
compile_graph() to build the workflow.Graph Compilation:
StateGraph with the defined State structure.start_node and a combine_node.start_node to the processing nodes.combine_node from the processing nodes.Run Method:
AIMessage.nodes.pyImports and Setup:
from typing import TypedDict, List
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
from dotenv import load_dotenv
load_dotenv()
State Definition:
class State(TypedDict):
text: str
classification: str
entities: List[str]
summary: str
LLM Instance:
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
Node Functions:
def classification_node(state: State):
''' Classify the text into one of the categories: News, Blog, Research, or Other '''
prompt = PromptTemplate(
input_variables=["text"],
template="Classify the following text into one of the categories: News, Blog, Research, or Other.\n\nText:{text}\n\nCategory:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
classification = llm.invoke([message]).content.strip()
return {"classification": classification}
def entity_extraction_node(state: State):
''' Extract all the entities (Person, Organization, Location) from the text '''
prompt = PromptTemplate(
input_variables=["text"],
template="Extract all the entities (Person, Organization, Location) from the following text. Provide the result as a comma-separated list.\n\nText:{text}\n\nEntities:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
entities = llm.invoke([message]).content.strip().split(", ")
return {"entities": entities}
def summarization_node(state: State):
''' Summarize the text in one short sentence '''
prompt = PromptTemplate(
input_variables=["text"],
template="Summarize the following text in one short sentence.\n\nText:{text}\n\nSummary:"
)
message = HumanMessage(content=prompt.format(text=state["text"]))
summary = llm.invoke([message]).content.strip()
return {"summary": summary}
Explanation:
state.PromptTemplate.llm instance is used across all nodes for consistency.Tools enhance agents by providing additional capabilities, such as web searching or data retrieval. The ToolRegistry facilitates the management and integration of these tools.
from typing import Dict, Type, List
from langchain.tools import BaseTool
class ToolRegistry:
"""
ToolRegistry manages the registration and retrieval of tools.
"""
tool_registry: Dict[str, Type[BaseTool]] = {
'tavily_search': TavilySearchResults,
'rag_tool': RAGTool,
}
@classmethod
def get_tool(cls, tool_name: str, **kwargs) -> BaseTool:
"""
Retrieve a single tool by name.
"""
tool_class = cls.tool_registry.get(tool_name)
if tool_class is None:
raise ValueError(f"Unknown tool: {tool_name}")
return tool_class(**kwargs)
@classmethod
def get_tools(cls, tool_names: List[str], **kwargs) -> List[BaseTool]:
"""
Retrieve multiple tools by their names.
"""
return [cls.get_tool(name, **kwargs) for name in tool_names]
ToolRegistry.tool_registry['my_custom_tool'] = MyCustomTool
my_tool = ToolRegistry.get_tool('my_custom_tool', param1=value1)
tools = ToolRegistry.get_tools(['tool1', 'tool2'], shared_param=shared_value)
The WebSearchAgent is another agent present in the examples, and it demonstrates how to integrate a tool using the ToolRegistry.
Agent Definition:
import logging
from langchain_core.messages import HumanMessage, AIMessage
from langgraph.prebuilt import create_react_agent
from agent_resources.tools.tool_registry import ToolRegistry
from agent_resources.base_agent import Agent
logger = logging.getLogger(__name__)
class WebSearchAgent(Agent):
"""
LangGraph-based WebSearch agent implementation with MemorySaver for persistence.
"""
def __init__(self, llm, memory):
"""
Initialize the WebSearch agent using LangGraph.
:param llm: The language model.
:param memory: Persistent memory for managing conversation history.
"""
# Retrieve the tavily_search tool from the registry
web_search_tool = ToolRegistry.get_tool('tavily_search', max_results=1)
self.tools = [web_search_tool]
self.llm = llm
self.memory = memory
# Create a prebuilt React agent with tools and memory
self.agent = self.compile_graph()
def compile_graph(self):
agent = create_react_agent(
self.llm,
tools=self.tools,
checkpointer=self.memory,
)
return agent
def run(self, message: HumanMessage) -> AIMessage:
"""
Process a HumanMessage and return an AIMessage response.
:param message: User's input message.
:return: AIMessage response.
"""
try:
thread_id = "default"
config = {"configurable": {"thread_id": thread_id}}
response = self.agent.invoke(
{"messages": [message]}, config=config)
ai_message = response["messages"][-1]
if isinstance(ai_message, AIMessage):
return ai_message
else:
logger.error("Unexpected message type in response.")
raise ValueError("Expected AIMessage in the response.")
except Exception as e:
logger.error("Error generating response", exc_info=True)
return AIMessage(content="Sorry, I encountered an error while processing your request.")
ToolRegistry.get_tool() to obtain the tavily_search tool.create_react_agent() to build an agent with the LLM, tools, and memory.Create a new tool by subclassing BaseTool.
from langchain.tools import BaseTool
class MyCustomTool(BaseTool):
def __init__(self, param1, param2):
super().__init__()
# Initialize tool-specific parameters
def _run(self, query: str):
# Implement the tool's functionality
result = perform_custom_operation(query)
return result
async def _arun(self, query: str):
# Implement asynchronous functionality if needed
result = await perform_custom_async_operation(query)
return result
Add the new tool to the ToolRegistry.
from agent_resources.tools.tool_registry import ToolRegistry
ToolRegistry.tool_registry['my_custom_tool'] = MyCustomTool
Retrieve and integrate the tool within your agent.
my_tool = ToolRegistry.get_tool('my_custom_tool', param1=value1, param2=value2)
self.tools = [my_tool]