An advanced, self-correcting Retrieval-Augmented Generation (RAG) pipeline built with LangGraph and LangChain.
Instead of a naive "retrieve → generate" flow, this project models RAG as a stateful graph where each step can grade its own output and decide what to do next: route the question, grade retrieved documents, fall back to web search, and check the final answer for hallucinations and relevance before returning it.
The graph orchestrates the following logic:
- Route question — A question router decides whether the query should hit the local vectorstore (topics about agents, prompt engineering, and adversarial attacks) or go straight to web search for everything else.
- Retrieve — Relevant documents are fetched from the Chroma vectorstore.
- Grade documents — Each retrieved document is graded for relevance. If any document is not relevant, the graph flags the need for a web search.
- Web search — When documents are insufficient (or the router chose this path), Tavily fetches fresh results from the web and appends them to the context.
- Generate — The LLM produces an answer grounded in the collected context.
- Grade generation — The answer is double-checked:
- Hallucination grader: is the generation grounded in the documents? If not (
not supported), regenerate. - Answer grader: does the generation actually address the question? If yes (
useful), finish. If not (not useful), route to web search for more context.
- Hallucination grader: is the generation grounded in the documents? If not (
langgraph-agentic-rag/
├── main.py # Entry point: invokes the compiled graph
├── ingestion.py # Loads, splits, and indexes documents into Chroma
├── graph/
│ ├── graph.py # Graph definition: nodes, edges, conditional routing
│ ├── state.py # GraphState (question, generation, web_search, documents)
│ ├── consts.py # Node name constants
│ ├── nodes/ # Node implementations (retrieve, grade_documents, generate, web_search)
│ └── chains/ # LLM chains
│ ├── router.py # Routes a question to vectorstore or web search
│ ├── retrieval_grader.py # Grades document relevance
│ ├── generation.py # Generates the final answer
│ ├── hallucination_grader.py # Checks the answer is grounded in documents
│ ├── answer_grader.py # Checks the answer addresses the question
│ └── tests/ # Pytest tests for the chains
└── graph.png # Auto-generated visualization of the graph
The vectorstore is seeded (via ingestion.py) with the following blog posts by Lilian Weng:
- LangGraph — stateful graph orchestration
- LangChain — LLM chains and prompts
- OpenAI — embeddings and chat model (
gpt-5-nano) - Chroma — local vector store
- Tavily — web search tool
- uv — dependency and environment management
This project uses uv for dependency management.
uv syncCreate a .env file in the project root with your API keys:
OPENAI_API_KEY=your_openai_key
TAVILY_API_KEY=your_tavily_key
LANGCHAIN_API_KEY=your_langsmith_key # optional, for tracing
LANGCHAIN_TRACING_V2=true # optionalFirst, ingest the documents into the vectorstore (uncomment the Chroma.from_documents block in ingestion.py for the initial run):
uv run python ingestion.pyThen run the agent:
uv run python main.pyThe LLM chains are covered by pytest:
uv run pytest