Skip to content

Commit 5dee5db

Browse files
committed
Add advanced RAG
1 parent 6fa15cc commit 5dee5db

25 files changed

+571
-53
lines changed

apps/12_langgraph_agent/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,31 @@ $ source .venv/bin/activate
3131

3232
# Install dependencies
3333
$ pip install -r requirements.txt
34+
```
35+
36+
### Examples
37+
38+
#### [reflection_agent](./reflection_agent/main.py)
39+
40+
#### [react_agent](./react_agent/main.py)
41+
42+
#### [advanced_rag_flows](./advanced_rag_flows/main.py)
43+
44+
```shell
45+
# create vector store
46+
python apps/12_langgraph_agent/advanced_rag_flows/ingestion.py
3447

35-
# Run the script
36-
$ python apps/12_langgraph_agent/reflection_agent/main.py
48+
# run main.py
49+
python apps/12_langgraph_agent/advanced_rag_flows/main.py
3750
```
3851

39-
### Example
52+
![Advanced RAG Flows](../../docs/images/12_langgraph_agent_graph.png)
4053

4154
## References
4255

4356
- [LangGraph](https://langchain-ai.github.io/langgraph/)
4457
- [Udemy > LangGraph- Develop LLM powered agents with LangGraph](https://www.udemy.com/course/langgraph)
58+
- [emarco177/langgaph-course](https://github.com/emarco177/langgaph-course)
4559
- [Prompt flow > Tracing](https://microsoft.github.io/promptflow/how-to-guides/tracing/index.html)
4660
- [Reflection Agents](https://blog.langchain.dev/reflection-agents/)
4761
- [LangChain > Reflexion](https://langchain-ai.github.io/langgraph/tutorials/reflexion/reflexion/)

apps/12_langgraph_agent/advanced_rag_flows/graph/__init__.py

Whitespace-only changes.

apps/12_langgraph_agent/advanced_rag_flows/graph/chains/__init__.py

Whitespace-only changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from os import getenv
2+
3+
from langchain_core.prompts import ChatPromptTemplate
4+
from langchain_core.runnables import RunnableSequence
5+
from langchain_openai import AzureChatOpenAI
6+
from pydantic import BaseModel, Field
7+
8+
9+
class GradeAnswer(BaseModel):
10+
11+
binary_score: bool = Field(description="Answer addresses the question, 'yes' or 'no'")
12+
13+
14+
llm = AzureChatOpenAI(
15+
temperature=0,
16+
api_key=getenv("AZURE_OPENAI_API_KEY"),
17+
api_version=getenv("AZURE_OPENAI_API_VERSION"),
18+
azure_endpoint=getenv("AZURE_OPENAI_ENDPOINT"),
19+
model=getenv("AZURE_OPENAI_GPT_MODEL"),
20+
)
21+
22+
structured_llm_grader = llm.with_structured_output(GradeAnswer)
23+
24+
system = """You are a grader assessing whether an answer addresses / resolves a question \n
25+
Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question."""
26+
answer_prompt = ChatPromptTemplate.from_messages(
27+
[
28+
("system", system),
29+
("human", "User question: \n\n {question} \n\n LLM generation: {generation}"),
30+
]
31+
)
32+
33+
answer_grader: RunnableSequence = answer_prompt | structured_llm_grader
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from os import getenv
2+
3+
from langchain import hub
4+
from langchain_core.output_parsers import StrOutputParser
5+
from langchain_openai import AzureChatOpenAI
6+
7+
llm = AzureChatOpenAI(
8+
temperature=0,
9+
api_key=getenv("AZURE_OPENAI_API_KEY"),
10+
api_version=getenv("AZURE_OPENAI_API_VERSION"),
11+
azure_endpoint=getenv("AZURE_OPENAI_ENDPOINT"),
12+
model=getenv("AZURE_OPENAI_GPT_MODEL"),
13+
)
14+
prompt = hub.pull("rlm/rag-prompt")
15+
16+
generation_chain = prompt | llm | StrOutputParser()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from os import getenv
2+
3+
from langchain_core.prompts import ChatPromptTemplate
4+
from langchain_core.runnables import RunnableSequence
5+
from langchain_openai import AzureChatOpenAI
6+
from pydantic import BaseModel, Field
7+
8+
llm = AzureChatOpenAI(
9+
temperature=0,
10+
api_key=getenv("AZURE_OPENAI_API_KEY"),
11+
api_version=getenv("AZURE_OPENAI_API_VERSION"),
12+
azure_endpoint=getenv("AZURE_OPENAI_ENDPOINT"),
13+
model=getenv("AZURE_OPENAI_GPT_MODEL"),
14+
)
15+
16+
17+
class GradeHallucinations(BaseModel):
18+
"""Binary score for hallucination present in generation answer."""
19+
20+
binary_score: bool = Field(description="Answer is grounded in the facts, 'yes' or 'no'")
21+
22+
23+
structured_llm_grader = llm.with_structured_output(GradeHallucinations)
24+
25+
system = """You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \n
26+
Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts.""" # noqa
27+
hallucination_prompt = ChatPromptTemplate.from_messages(
28+
[
29+
("system", system),
30+
("human", "Set of facts: \n\n {documents} \n\n LLM generation: {generation}"),
31+
]
32+
)
33+
34+
hallucination_grader: RunnableSequence = hallucination_prompt | structured_llm_grader
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from os import getenv
2+
3+
from langchain_core.prompts import ChatPromptTemplate
4+
from langchain_openai import AzureChatOpenAI
5+
from pydantic import BaseModel, Field
6+
7+
llm = AzureChatOpenAI(
8+
temperature=0,
9+
api_key=getenv("AZURE_OPENAI_API_KEY"),
10+
api_version=getenv("AZURE_OPENAI_API_VERSION"),
11+
azure_endpoint=getenv("AZURE_OPENAI_ENDPOINT"),
12+
model=getenv("AZURE_OPENAI_GPT_MODEL"),
13+
)
14+
15+
16+
class GradeDocuments(BaseModel):
17+
"""Binary score for relevance check on retrieved documents."""
18+
19+
binary_score: str = Field(description="Documents are relevant to the question, 'yes' or 'no'")
20+
21+
22+
structured_llm_grader = llm.with_structured_output(GradeDocuments)
23+
24+
system = """You are a grader assessing relevance of a retrieved document to a user question. \n
25+
If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. \n
26+
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""
27+
grade_prompt = ChatPromptTemplate.from_messages(
28+
[
29+
("system", system),
30+
("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
31+
]
32+
)
33+
34+
retrieval_grader = grade_prompt | structured_llm_grader
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from os import getenv
2+
from typing import Literal
3+
4+
from langchain_core.prompts import ChatPromptTemplate
5+
from langchain_openai import AzureChatOpenAI
6+
from pydantic import BaseModel, Field
7+
8+
9+
class RouteQuery(BaseModel):
10+
"""Route a user query to the most relevant datasource."""
11+
12+
datasource: Literal["vectorstore", "websearch"] = Field(
13+
...,
14+
description="Given a user question choose to route it to web search or a vectorstore.",
15+
)
16+
17+
18+
llm = AzureChatOpenAI(
19+
temperature=0,
20+
api_key=getenv("AZURE_OPENAI_API_KEY"),
21+
api_version=getenv("AZURE_OPENAI_API_VERSION"),
22+
azure_endpoint=getenv("AZURE_OPENAI_ENDPOINT"),
23+
model=getenv("AZURE_OPENAI_GPT_MODEL"),
24+
)
25+
structured_llm_router = llm.with_structured_output(RouteQuery)
26+
27+
system = """You are an expert at routing a user question to a vectorstore or web search.
28+
The vectorstore contains documents related to agents, prompt engineering, and adversarial attacks.
29+
Use the vectorstore for questions on these topics. For all else, use web-search."""
30+
route_prompt = ChatPromptTemplate.from_messages(
31+
[
32+
("system", system),
33+
("human", "{question}"),
34+
]
35+
)
36+
37+
question_router = route_prompt | structured_llm_router

apps/12_langgraph_agent/advanced_rag_flows/graph/chains/tests/__init__.py

Whitespace-only changes.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from pprint import pprint
2+
3+
from dotenv import load_dotenv
4+
from ingestion import retriever
5+
6+
from graph.chains.generation import generation_chain
7+
from graph.chains.hallucination_grader import GradeHallucinations, hallucination_grader
8+
from graph.chains.retrieval_grader import GradeDocuments, retrieval_grader
9+
from graph.chains.router import RouteQuery, question_router
10+
11+
load_dotenv()
12+
13+
14+
def test_generation_chain() -> None:
15+
question = "agent memory"
16+
docs = retriever.invoke(question)
17+
generation = generation_chain.invoke({"context": docs, "question": question})
18+
pprint(generation)
19+
20+
21+
def test_retrival_grader_answer_yes() -> None:
22+
question = "agent memory"
23+
docs = retriever.invoke(question)
24+
doc_txt = docs[1].page_content
25+
26+
res: GradeDocuments = retrieval_grader.invoke({"question": question, "document": doc_txt})
27+
28+
assert res.binary_score == "yes"
29+
30+
31+
def test_retrival_grader_answer_no() -> None:
32+
question = "agent memory"
33+
docs = retriever.invoke(question)
34+
doc_txt = docs[1].page_content
35+
36+
res: GradeDocuments = retrieval_grader.invoke({"question": "how to make pizaa", "document": doc_txt})
37+
38+
assert res.binary_score == "no"
39+
40+
41+
def test_hallucination_grader_answer_yes() -> None:
42+
question = "agent memory"
43+
docs = retriever.invoke(question)
44+
45+
generation = generation_chain.invoke({"context": docs, "question": question})
46+
res: GradeHallucinations = hallucination_grader.invoke({"documents": docs, "generation": generation})
47+
assert res.binary_score
48+
49+
50+
def test_hallucination_grader_answer_no() -> None:
51+
question = "agent memory"
52+
docs = retriever.invoke(question)
53+
54+
res: GradeHallucinations = hallucination_grader.invoke(
55+
{
56+
"documents": docs,
57+
"generation": "In order to make pizza we need to first start with the dough",
58+
}
59+
)
60+
assert not res.binary_score
61+
62+
63+
def test_router_to_vectorstore() -> None:
64+
question = "agent memory"
65+
66+
res: RouteQuery = question_router.invoke({"question": question})
67+
assert res.datasource == "vectorstore"
68+
69+
70+
def test_router_to_websearch() -> None:
71+
question = "how to make pizza"
72+
73+
res: RouteQuery = question_router.invoke({"question": question})
74+
assert res.datasource == "websearch"

0 commit comments

Comments
 (0)