A comprehensive learning path covering both LangChain and LangGraph, featuring 100+ practical implementations progressing from basic concepts to advanced agent architectures.
This repository provides a structured approach to learning modern LLM application development using both LangChain and LangGraph frameworks. The collection begins with fundamental concepts and gradually progresses to complex, specialized agent implementations and graph-based workflows. It serves as both an educational resource and a reference for implementing AI agents in real-world scenarios.
- Python 3.9+
- OpenAI API key
- Basic understanding of Python and API concepts
-
Clone this repository:
git clone https://github.com/timeless-residents/handson-langchain.git cd langchain-tutorial
This command creates a local copy of the repository. We use HTTPS cloning for broader compatibility and easier setup compared to SSH, especially for users behind corporate firewalls.
-
Create a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
Virtual environments are crucial for project isolation. This prevents dependency conflicts between different projects and ensures reproducible environments. The
venv
module is chosen over alternatives likevirtualenv
because it's included in Python's standard library since Python 3.3. -
Install common dependencies:
pip install langchain langchain-openai langchain-community langgraph python-dotenv
We install these specific packages because:
langchain
: Core framework for building LLM applicationslangchain-openai
: OpenAI-specific implementationslangchain-community
: Community-contributed componentslanggraph
: Graph-based workflow managementpython-dotenv
: Secure environment variable management
-
Set up your OpenAI API key: Create a
.env
file in the root directory with the following content:OPENAI_API_KEY=your_api_key_here
We use environment variables instead of hardcoding API keys for security best practices. The
.env
file is included in.gitignore
to prevent accidental exposure of sensitive credentials.
The repository follows a progressive learning path, with each directory serving a specific educational purpose:
langchain-tutorial/
├── step1.py # Basic LLM usage with LangChain
├── step2.py # Multi-tool agent implementation
├── steps/ # Additional introductory steps (optional)
│ ├── step3.py
│ └── ...
├── usecase-001/ # Basic Calculator Agent (LangChain)
│ ├── main.py
│ ├── README.md
│ └── requirements.txt
└── ...
This structure is designed for incremental learning, with each subsequent directory building upon concepts introduced in previous sections.
from langchain_openai import OpenAI
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Create OpenAI LLM instance
llm = OpenAI()
# Query the LLM
prompt = "What's the weather like today?"
response = llm.invoke(prompt)
print("LLM Response:")
print(response)
This code demonstrates several key concepts:
- Environment Setup:
load_dotenv()
loads environment variables securely, a crucial practice for managing API keys and sensitive data. - LLM Initialization:
OpenAI()
creates an LLM instance with default parameters. We use the default settings initially for simplicity, but these can be customized for temperature, max tokens, etc. - Synchronous Invocation:
llm.invoke(prompt)
sends a synchronous request to the LLM. We use synchronous calls here for clarity, though asynchronous operations are available for production scenarios.
from langchain.agents import initialize_agent, Tool
from langchain.tools import DuckDuckGoSearchRun
from langchain_openai import OpenAI
from datetime import datetime
# Initialize tools
search = DuckDuckGoSearchRun()
calculator = Tool(
name="Calculator",
func=lambda x: eval(x),
description="Useful for mathematical calculations"
)
time_tool = Tool(
name="Time",
func=lambda _: datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
description="Returns the current time"
)
# Create and initialize the agent
llm = OpenAI(temperature=0)
agent = initialize_agent(
tools=[search, calculator, time_tool],
llm=llm,
agent="zero-shot-react-description",
verbose=True
)
This implementation showcases several advanced concepts:
-
Tool Integration: Each tool is encapsulated with a clear name and description, helping the agent understand when to use each tool.
- The calculator uses
eval()
for simple calculations (Note: In production, use safer evaluation methods) - The time tool provides formatted current time
- DuckDuckGoSearchRun enables web searches without API keys
- The calculator uses
-
Agent Configuration:
temperature=0
: Set to 0 for deterministic responses, crucial for tool-using agentszero-shot-react-description
: This agent type is chosen because it:- Requires no examples (zero-shot)
- Uses ReAct (Reasoning and Acting) framework
- Can choose tools based on their descriptions
-
Verbose Mode: Enabled for learning purposes, allowing observation of the agent's decision-making process.
Each use case demonstrates specific patterns and techniques:
Each implementation is carefully structured to demonstrate specific capabilities:
-
001: Basic Calculator Agent
from langchain.agents import create_react_agent from langchain.tools import Tool def safe_eval(expression: str) -> float: """ Safely evaluate mathematical expressions. Args: expression (str): Mathematical expression to evaluate Returns: float: Result of the evaluation Safety: - Uses ast.literal_eval instead of eval() - Validates input format - Handles division by zero """ import ast try: # Convert string to abstract syntax tree tree = ast.parse(expression, mode='eval') # Validate node types for node in ast.walk(tree): if not isinstance(node, (ast.Expression, ast.Num, ast.BinOp, ast.UnaryOp, ast.Add, ast.Sub, ast.Mult, ast.Div)): raise ValueError("Invalid expression") # Evaluate if safe return float(eval(compile(tree, '<string>', 'eval'))) except ZeroDivisionError: raise ValueError("Division by zero") except Exception as e: raise ValueError(f"Invalid expression: {str(e)}")
This implementation demonstrates:
- Security: Uses AST parsing instead of direct eval()
- Error Handling: Comprehensive error cases
- Type Safety: Explicit return type
- Documentation: Detailed docstring with Args, Returns, and Safety sections
[Additional use cases would follow with similar detailed explanations...]
from typing import List, Dict, Optional
def process_data(input_data: List[Dict[str, any]],
config: Optional[Dict[str, str]] = None) -> Dict[str, any]:
"""
Process input data according to optional configuration.
Args:
input_data: List of dictionaries containing data to process
config: Optional configuration parameters
Returns:
Processed data as a dictionary
"""
# Implementation
Type hints are used throughout the codebase because they:
- Enable better IDE support
- Facilitate early error detection
- Serve as inline documentation
- Support static type checking
class CustomError(Exception):
"""Base class for custom exceptions"""
pass
def handle_api_request(url: str) -> Dict[str, any]:
"""
Handle external API requests with comprehensive error handling.
Args:
url: API endpoint URL
Returns:
API response data
Raises:
CustomError: When API request fails
"""
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise CustomError(f"API request failed: {str(e)}")
except json.JSONDecodeError as e:
raise CustomError(f"Invalid JSON response: {str(e)}")
This pattern demonstrates:
- Custom exception classes
- Specific exception handling
- Detailed error messages
- Proper error propagation
- API Costs: The implementations rely on OpenAI's API, which incurs usage costs. This may limit scalability for high-volume applications.
- Rate Limiting: OpenAI's API has rate limits that may affect performance in production environments.
- Latency: API calls introduce network latency, which can impact real-time applications.
-
Memory Management:
- Conversation history can grow large, impacting performance
- Token limits restrict context window size
- Memory implementations may not persist across sessions by default
-
Tool Integration:
- Tools must be pre-defined and cannot be dynamically created during runtime
- Complex tools may require significant error handling
- Tool descriptions must be carefully crafted to ensure proper agent usage
-
Error Handling Challenges:
- LLM responses can be unpredictable
- Tool execution may fail in unexpected ways
- Error recovery strategies may need manual intervention
-
Synchronous vs Asynchronous:
- Examples use synchronous calls for clarity
- Production environments may need async implementations for better performance
- Async implementations add complexity to error handling
-
Security Considerations:
- Safe evaluation of expressions limits mathematical capabilities
- API key management requires careful handling
- Input validation adds processing overhead
-
Development Complexity:
- Debugging LLM-based systems can be challenging
- Testing requires mock implementations of LLM responses
- Maintaining consistent behavior across different LLM versions
-
LangChain:
- Documentation may lag behind rapid development
- Some features may be experimental or unstable
- Community tools may have varying levels of maintenance
-
LangGraph:
- Graph-based workflows add complexity
- State management can become complicated
- Learning curve for graph-based thinking
-
Scalability:
- Cost increases linearly with usage
- Parallel processing may be limited by API constraints
- State management becomes complex at scale
-
Monitoring:
- LLM behavior can be difficult to monitor
- Tool usage patterns may need custom logging
- Performance metrics require careful definition
-
Maintenance:
- Regular updates needed for API changes
- Tool integrations may break with external changes
- Prompt engineering may need ongoing refinement
These limitations and trade-offs should be carefully considered when implementing these patterns in production environments. Mitigation strategies should be developed based on specific use case requirements.
This project is licensed under the MIT License - see the LICENSE file for details.
- The LangChain and LangGraph teams for creating excellent frameworks
- OpenAI for providing the underlying language models
- All contributors who have helped improve this collection