mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
removing empty cells (#1624)
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -47,7 +47,9 @@
|
||||
"id": "568c84d6-9df6-4b7b-b50d-476c0a64a04b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph tavily-python"]
|
||||
"source": [
|
||||
"! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph tavily-python"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -63,7 +65,11 @@
|
||||
"id": "74710419-158d-4270-931c-de83db7b580d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import os\n\nos.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -81,7 +87,9 @@
|
||||
"id": "c3ac6e65-2d4e-48dd-9fff-40047373332d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["os.environ[\"TAVILY_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"os.environ[\"TAVILY_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -99,7 +107,11 @@
|
||||
"id": "e205f57e-5218-478b-ad8e-1723bdb0d45e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -117,7 +129,34 @@
|
||||
"id": "3a566a30-cf0e-4330-ad4d-9bf994bdfa86",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import WebBaseLoader\nfrom langchain_community.vectorstores import Chroma\nfrom langchain_openai import OpenAIEmbeddings\n\nurls = [\n \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n]\n\ndocs = [WebBaseLoader(url).load() for url in urls]\ndocs_list = [item for sublist in docs for item in sublist]\n\ntext_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n chunk_size=250, chunk_overlap=0\n)\ndoc_splits = text_splitter.split_documents(docs_list)\n\n# Add to vectorDB\nvectorstore = Chroma.from_documents(\n documents=doc_splits,\n collection_name=\"rag-chroma\",\n embedding=OpenAIEmbeddings(),\n)\nretriever = vectorstore.as_retriever()"]
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"urls = [\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"docs = [WebBaseLoader(url).load() for url in urls]\n",
|
||||
"docs_list = [item for sublist in docs for item in sublist]\n",
|
||||
"\n",
|
||||
"text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n",
|
||||
" chunk_size=250, chunk_overlap=0\n",
|
||||
")\n",
|
||||
"doc_splits = text_splitter.split_documents(docs_list)\n",
|
||||
"\n",
|
||||
"# Add to vectorDB\n",
|
||||
"vectorstore = Chroma.from_documents(\n",
|
||||
" documents=doc_splits,\n",
|
||||
" collection_name=\"rag-chroma\",\n",
|
||||
" embedding=OpenAIEmbeddings(),\n",
|
||||
")\n",
|
||||
"retriever = vectorstore.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -141,7 +180,44 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Retrieval Grader\n\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.pydantic_v1 import BaseModel, Field\nfrom langchain_openai import ChatOpenAI\n\n\n# Data model\nclass GradeDocuments(BaseModel):\n \"\"\"Binary score for relevance check on retrieved documents.\"\"\"\n\n binary_score: str = Field(\n description=\"Documents are relevant to the question, 'yes' or 'no'\"\n )\n\n\n# LLM with function call\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\nstructured_llm_grader = llm.with_structured_output(GradeDocuments)\n\n# Prompt\nsystem = \"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. \\n\n Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\"\"\"\ngrade_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\"human\", \"Retrieved document: \\n\\n {document} \\n\\n User question: {question}\"),\n ]\n)\n\nretrieval_grader = grade_prompt | structured_llm_grader\nquestion = \"agent memory\"\ndocs = retriever.get_relevant_documents(question)\ndoc_txt = docs[1].page_content\nprint(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"]
|
||||
"source": [
|
||||
"### Retrieval Grader\n",
|
||||
"\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Data model\n",
|
||||
"class GradeDocuments(BaseModel):\n",
|
||||
" \"\"\"Binary score for relevance check on retrieved documents.\"\"\"\n",
|
||||
"\n",
|
||||
" binary_score: str = Field(\n",
|
||||
" description=\"Documents are relevant to the question, 'yes' or 'no'\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeDocuments)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n",
|
||||
" If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. \\n\n",
|
||||
" Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\"\"\"\n",
|
||||
"grade_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\"human\", \"Retrieved document: \\n\\n {document} \\n\\n User question: {question}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"retrieval_grader = grade_prompt | structured_llm_grader\n",
|
||||
"question = \"agent memory\"\n",
|
||||
"docs = retriever.get_relevant_documents(question)\n",
|
||||
"doc_txt = docs[1].page_content\n",
|
||||
"print(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -157,7 +233,31 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Generate\n\nfrom langchain import hub\nfrom langchain_core.output_parsers import StrOutputParser\n\n# Prompt\nprompt = hub.pull(\"rlm/rag-prompt\")\n\n# LLM\nllm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n\n\n# Post-processing\ndef format_docs(docs):\n return \"\\n\\n\".join(doc.page_content for doc in docs)\n\n\n# Chain\nrag_chain = prompt | llm | StrOutputParser()\n\n# Run\ngeneration = rag_chain.invoke({\"context\": docs, \"question\": question})\nprint(generation)"]
|
||||
"source": [
|
||||
"### Generate\n",
|
||||
"\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Post-processing\n",
|
||||
"def format_docs(docs):\n",
|
||||
" return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Chain\n",
|
||||
"rag_chain = prompt | llm | StrOutputParser()\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n",
|
||||
"print(generation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -176,7 +276,28 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Question Re-writer\n\n# LLM\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n\n# Prompt\nsystem = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n for web search. Look at the input and try to reason about the underlying semantic intent / meaning.\"\"\"\nre_write_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\n \"human\",\n \"Here is the initial question: \\n\\n {question} \\n Formulate an improved question.\",\n ),\n ]\n)\n\nquestion_rewriter = re_write_prompt | llm | StrOutputParser()\nquestion_rewriter.invoke({\"question\": question})"]
|
||||
"source": [
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n",
|
||||
" for web search. Look at the input and try to reason about the underlying semantic intent / meaning.\"\"\"\n",
|
||||
"re_write_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\n",
|
||||
" \"human\",\n",
|
||||
" \"Here is the initial question: \\n\\n {question} \\n Formulate an improved question.\",\n",
|
||||
" ),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"question_rewriter = re_write_prompt | llm | StrOutputParser()\n",
|
||||
"question_rewriter.invoke({\"question\": question})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -192,7 +313,13 @@
|
||||
"id": "46d51b53-54a9-4e0a-9f14-e39998f5b340",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["### Search\n\nfrom langchain_community.tools.tavily_search import TavilySearchResults\n\nweb_search_tool = TavilySearchResults(k=3)"]
|
||||
"source": [
|
||||
"### Search\n",
|
||||
"\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"\n",
|
||||
"web_search_tool = TavilySearchResults(k=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -212,7 +339,28 @@
|
||||
"id": "94b3945f-ef0f-458d-a443-f763903550b0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from typing import List\n\nfrom typing_extensions import TypedDict\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n question: question\n generation: LLM generation\n web_search: whether to add search\n documents: list of documents\n \"\"\"\n\n question: str\n generation: str\n web_search: str\n documents: List[str]"]
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from typing_extensions import TypedDict\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" question: question\n",
|
||||
" generation: LLM generation\n",
|
||||
" web_search: whether to add search\n",
|
||||
" documents: list of documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question: str\n",
|
||||
" generation: str\n",
|
||||
" web_search: str\n",
|
||||
" documents: List[str]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -220,7 +368,155 @@
|
||||
"id": "efd639c5-82e2-45e6-a94a-6a4039646ef5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langchain.schema import Document\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.get_relevant_documents(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n web_search = \"No\"\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score.binary_score\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n web_search = \"Yes\"\n continue\n return {\"documents\": filtered_docs, \"question\": question, \"web_search\": web_search}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}\n\n\ndef web_search(state):\n \"\"\"\n Web search based on the re-phrased question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with appended web results\n \"\"\"\n\n print(\"---WEB SEARCH---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Web search\n docs = web_search_tool.invoke({\"query\": question})\n web_results = \"\\n\".join([d[\"content\"] for d in docs])\n web_results = Document(page_content=web_results)\n documents.append(web_results)\n\n return {\"documents\": documents, \"question\": question}\n\n\n### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n web_search = state[\"web_search\"]\n state[\"documents\"]\n\n if web_search == \"Yes\":\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\""]
|
||||
"source": [
|
||||
"from langchain.schema import Document\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def retrieve(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve documents\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, documents, that contains retrieved documents\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---RETRIEVE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
"\n",
|
||||
" # Retrieval\n",
|
||||
" documents = retriever.get_relevant_documents(question)\n",
|
||||
" return {\"documents\": documents, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate answer\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation, that contains LLM generation\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---GENERATE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # RAG generation\n",
|
||||
" generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_documents(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the retrieved documents are relevant to the question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with only filtered relevant documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Score each doc\n",
|
||||
" filtered_docs = []\n",
|
||||
" web_search = \"No\"\n",
|
||||
" for d in documents:\n",
|
||||
" score = retrieval_grader.invoke(\n",
|
||||
" {\"question\": question, \"document\": d.page_content}\n",
|
||||
" )\n",
|
||||
" grade = score.binary_score\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---GRADE: DOCUMENT RELEVANT---\")\n",
|
||||
" filtered_docs.append(d)\n",
|
||||
" else:\n",
|
||||
" print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n",
|
||||
" web_search = \"Yes\"\n",
|
||||
" continue\n",
|
||||
" return {\"documents\": filtered_docs, \"question\": question, \"web_search\": web_search}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transform_query(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Transform the query to produce a better question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates question key with a re-phrased question\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---TRANSFORM QUERY---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Re-write question\n",
|
||||
" better_question = question_rewriter.invoke({\"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": better_question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def web_search(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Web search based on the re-phrased question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with appended web results\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---WEB SEARCH---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Web search\n",
|
||||
" docs = web_search_tool.invoke({\"query\": question})\n",
|
||||
" web_results = \"\\n\".join([d[\"content\"] for d in docs])\n",
|
||||
" web_results = Document(page_content=web_results)\n",
|
||||
" documents.append(web_results)\n",
|
||||
"\n",
|
||||
" return {\"documents\": documents, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Edges\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to generate an answer, or re-generate a question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Binary decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---ASSESS GRADED DOCUMENTS---\")\n",
|
||||
" state[\"question\"]\n",
|
||||
" web_search = state[\"web_search\"]\n",
|
||||
" state[\"documents\"]\n",
|
||||
"\n",
|
||||
" if web_search == \"Yes\":\n",
|
||||
" # All documents have been filtered check_relevance\n",
|
||||
" # We will re-generate a new query\n",
|
||||
" print(\n",
|
||||
" \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n",
|
||||
" )\n",
|
||||
" return \"transform_query\"\n",
|
||||
" else:\n",
|
||||
" # We have relevant documents, so generate answer\n",
|
||||
" print(\"---DECISION: GENERATE---\")\n",
|
||||
" return \"generate\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -238,7 +534,36 @@
|
||||
"id": "dedae17a-98c6-474d-90a7-9234b7c8cea0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langgraph.graph import END, StateGraph, START\n\nworkflow = StateGraph(GraphState)\n\n# Define the nodes\nworkflow.add_node(\"retrieve\", retrieve) # retrieve\nworkflow.add_node(\"grade_documents\", grade_documents) # grade documents\nworkflow.add_node(\"generate\", generate) # generatae\nworkflow.add_node(\"transform_query\", transform_query) # transform_query\nworkflow.add_node(\"web_search_node\", web_search) # web search\n\n# Build graph\nworkflow.add_edge(START, \"retrieve\")\nworkflow.add_edge(\"retrieve\", \"grade_documents\")\nworkflow.add_conditional_edges(\n \"grade_documents\",\n decide_to_generate,\n {\n \"transform_query\": \"transform_query\",\n \"generate\": \"generate\",\n },\n)\nworkflow.add_edge(\"transform_query\", \"web_search_node\")\nworkflow.add_edge(\"web_search_node\", \"generate\")\nworkflow.add_edge(\"generate\", END)\n\n# Compile\napp = workflow.compile()"]
|
||||
"source": [
|
||||
"from langgraph.graph import END, StateGraph, START\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"workflow.add_node(\"retrieve\", retrieve) # retrieve\n",
|
||||
"workflow.add_node(\"grade_documents\", grade_documents) # grade documents\n",
|
||||
"workflow.add_node(\"generate\", generate) # generatae\n",
|
||||
"workflow.add_node(\"transform_query\", transform_query) # transform_query\n",
|
||||
"workflow.add_node(\"web_search_node\", web_search) # web search\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.add_edge(START, \"retrieve\")\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
" decide_to_generate,\n",
|
||||
" {\n",
|
||||
" \"transform_query\": \"transform_query\",\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"workflow.add_edge(\"transform_query\", \"web_search_node\")\n",
|
||||
"workflow.add_edge(\"web_search_node\", \"generate\")\n",
|
||||
"workflow.add_edge(\"generate\", END)\n",
|
||||
"\n",
|
||||
"# Compile\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -281,7 +606,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["from pprint import pprint\n\n# Run\ninputs = {\"question\": \"What are the types of agent memory?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n # Optional: print full state at each node\n # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"]
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"inputs = {\"question\": \"What are the types of agent memory?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" # Optional: print full state at each node\n",
|
||||
" # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -326,7 +666,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["from pprint import pprint\n\n# Run\ninputs = {\"question\": \"How does the AlphaCodium paper work?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n # Optional: print full state at each node\n # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"]
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"inputs = {\"question\": \"How does the AlphaCodium paper work?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" # Optional: print full state at each node\n",
|
||||
" # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -339,14 +694,6 @@
|
||||
"\n",
|
||||
"* https://smith.langchain.com/public/497c8ed9-d9e2-429e-8ada-e64de3ec26c9/r"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6ce65be5-fd12-4ffc-984c-34c132693e69",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [""]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n%pip install -U langchain_community tiktoken langchainhub scikit-learn langchain langgraph tavily-python nomic[local] langchain-nomic langchain_openai"
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install -U langchain_community tiktoken langchainhub scikit-learn langchain langgraph tavily-python nomic[local] langchain-nomic langchain_openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -80,7 +81,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Embedding (optional)\nos.environ[\"OPENAI_API_KEY\"] = \"xxx\""
|
||||
"# Embedding (optional)\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"xxx\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -90,7 +92,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tracing and testing (optional)\nos.environ[\"LANGCHAIN_API_KEY\"] = \"xxx\"\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_PROJECT\"] = \"corrective-rag-agent-testing\""
|
||||
"# Tracing and testing (optional)\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"xxx\"\n",
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_PROJECT\"] = \"corrective-rag-agent-testing\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -110,7 +116,9 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"local_llm = \"llama3\"\nmodel_tested = \"llama3-8b\"\nmetadata = f\"CRAG, {model_tested}\""
|
||||
"local_llm = \"llama3\"\n",
|
||||
"model_tested = \"llama3-8b\"\n",
|
||||
"metadata = f\"CRAG, {model_tested}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -204,7 +212,45 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"### Retrieval Grader\n\nfrom langchain.prompts import PromptTemplate\nfrom langchain_community.chat_models import ChatOllama\nfrom langchain_core.output_parsers import JsonOutputParser\nfrom langchain_mistralai.chat_models import ChatMistralAI\n\n# LLM\nllm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n\n# Prompt\nprompt = PromptTemplate(\n template=\"\"\"You are a teacher grading a quiz. You will be given: \n 1/ a QUESTION\n 2/ A FACT provided by the student\n \n You are grading RELEVANCE RECALL:\n A score of 1 means that ANY of the statements in the FACT are relevant to the QUESTION. \n A score of 0 means that NONE of the statements in the FACT are relevant to the QUESTION. \n 1 is the highest (best) score. 0 is the lowest score you can give. \n \n Explain your reasoning in a step-by-step manner. Ensure your reasoning and conclusion are correct. \n \n Avoid simply stating the correct answer at the outset.\n \n Question: {question} \\n\n Fact: \\n\\n {documents} \\n\\n\n \n Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question. \\n\n Provide the binary score as a JSON with a single key 'score' and no premable or explanation.\n \"\"\",\n input_variables=[\"question\", \"documents\"],\n)\n\nretrieval_grader = prompt | llm | JsonOutputParser()\nquestion = \"agent memory\"\ndocs = retriever.invoke(question)\ndoc_txt = docs[1].page_content\nprint(retrieval_grader.invoke({\"question\": question, \"documents\": doc_txt}))"
|
||||
"### Retrieval Grader\n",
|
||||
"\n",
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain_community.chat_models import ChatOllama\n",
|
||||
"from langchain_core.output_parsers import JsonOutputParser\n",
|
||||
"from langchain_mistralai.chat_models import ChatMistralAI\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You are a teacher grading a quiz. You will be given: \n",
|
||||
" 1/ a QUESTION\n",
|
||||
" 2/ A FACT provided by the student\n",
|
||||
" \n",
|
||||
" You are grading RELEVANCE RECALL:\n",
|
||||
" A score of 1 means that ANY of the statements in the FACT are relevant to the QUESTION. \n",
|
||||
" A score of 0 means that NONE of the statements in the FACT are relevant to the QUESTION. \n",
|
||||
" 1 is the highest (best) score. 0 is the lowest score you can give. \n",
|
||||
" \n",
|
||||
" Explain your reasoning in a step-by-step manner. Ensure your reasoning and conclusion are correct. \n",
|
||||
" \n",
|
||||
" Avoid simply stating the correct answer at the outset.\n",
|
||||
" \n",
|
||||
" Question: {question} \\n\n",
|
||||
" Fact: \\n\\n {documents} \\n\\n\n",
|
||||
" \n",
|
||||
" Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question. \\n\n",
|
||||
" Provide the binary score as a JSON with a single key 'score' and no premable or explanation.\n",
|
||||
" \"\"\",\n",
|
||||
" input_variables=[\"question\", \"documents\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"retrieval_grader = prompt | llm | JsonOutputParser()\n",
|
||||
"question = \"agent memory\"\n",
|
||||
"docs = retriever.invoke(question)\n",
|
||||
"doc_txt = docs[1].page_content\n",
|
||||
"print(retrieval_grader.invoke({\"question\": question, \"documents\": doc_txt}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -222,7 +268,35 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"### Generate\n\nfrom langchain_core.output_parsers import StrOutputParser\n\n# Prompt\nprompt = PromptTemplate(\n template=\"\"\"You are an assistant for question-answering tasks. \n \n Use the following documents to answer the question. \n \n If you don't know the answer, just say that you don't know. \n \n Use three sentences maximum and keep the answer concise:\n Question: {question} \n Documents: {documents} \n Answer: \n \"\"\",\n input_variables=[\"question\", \"documents\"],\n)\n\n# LLM\nllm = ChatOllama(model=local_llm, temperature=0)\n\n# Chain\nrag_chain = prompt | llm | StrOutputParser()\n\n# Run\ngeneration = rag_chain.invoke({\"documents\": docs, \"question\": question})\nprint(generation)"
|
||||
"### Generate\n",
|
||||
"\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You are an assistant for question-answering tasks. \n",
|
||||
" \n",
|
||||
" Use the following documents to answer the question. \n",
|
||||
" \n",
|
||||
" If you don't know the answer, just say that you don't know. \n",
|
||||
" \n",
|
||||
" Use three sentences maximum and keep the answer concise:\n",
|
||||
" Question: {question} \n",
|
||||
" Documents: {documents} \n",
|
||||
" Answer: \n",
|
||||
" \"\"\",\n",
|
||||
" input_variables=[\"question\", \"documents\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, temperature=0)\n",
|
||||
"\n",
|
||||
"# Chain\n",
|
||||
"rag_chain = prompt | llm | StrOutputParser()\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"generation = rag_chain.invoke({\"documents\": docs, \"question\": question})\n",
|
||||
"print(generation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -232,7 +306,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"### Search\n\nfrom langchain_community.tools.tavily_search import TavilySearchResults\n\nweb_search_tool = TavilySearchResults(k=3)"
|
||||
"### Search\n",
|
||||
"\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"\n",
|
||||
"web_search_tool = TavilySearchResults(k=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -263,7 +341,175 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from typing import List\nfrom typing_extensions import TypedDict\nfrom IPython.display import Image, display\nfrom langchain.schema import Document\nfrom langgraph.graph import START, END, StateGraph\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n question: question\n generation: LLM generation\n search: whether to add search\n documents: list of documents\n \"\"\"\n\n question: str\n generation: str\n search: str\n documents: List[str]\n steps: List[str]\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n question = state[\"question\"]\n documents = retriever.invoke(question)\n steps = state[\"steps\"]\n steps.append(\"retrieve_documents\")\n return {\"documents\": documents, \"question\": question, \"steps\": steps}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = rag_chain.invoke({\"documents\": documents, \"question\": question})\n steps = state[\"steps\"]\n steps.append(\"generate_answer\")\n return {\n \"documents\": documents,\n \"question\": question,\n \"generation\": generation,\n \"steps\": steps,\n }\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n question = state[\"question\"]\n documents = state[\"documents\"]\n steps = state[\"steps\"]\n steps.append(\"grade_document_retrieval\")\n filtered_docs = []\n search = \"No\"\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"documents\": d.page_content}\n )\n grade = score[\"score\"]\n if grade == \"yes\":\n filtered_docs.append(d)\n else:\n search = \"Yes\"\n continue\n return {\n \"documents\": filtered_docs,\n \"question\": question,\n \"search\": search,\n \"steps\": steps,\n }\n\n\ndef web_search(state):\n \"\"\"\n Web search based on the re-phrased question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with appended web results\n \"\"\"\n\n question = state[\"question\"]\n documents = state.get(\"documents\", [])\n steps = state[\"steps\"]\n steps.append(\"web_search\")\n web_results = web_search_tool.invoke({\"query\": question})\n documents.extend(\n [\n Document(page_content=d[\"content\"], metadata={\"url\": d[\"url\"]})\n for d in web_results\n ]\n )\n return {\"documents\": documents, \"question\": question, \"steps\": steps}\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n search = state[\"search\"]\n if search == \"Yes\":\n return \"search\"\n else:\n return \"generate\"\n\n\n# Graph\nworkflow = StateGraph(GraphState)\n\n# Define the nodes\nworkflow.add_node(\"retrieve\", retrieve) # retrieve\nworkflow.add_node(\"grade_documents\", grade_documents) # grade documents\nworkflow.add_node(\"generate\", generate) # generatae\nworkflow.add_node(\"web_search\", web_search) # web search\n\n# Build graph\nworkflow.add_edge(START, \"retrieve\")\nworkflow.add_edge(\"retrieve\", \"grade_documents\")\nworkflow.add_conditional_edges(\n \"grade_documents\",\n decide_to_generate,\n {\n \"search\": \"web_search\",\n \"generate\": \"generate\",\n },\n)\nworkflow.add_edge(\"web_search\", \"generate\")\nworkflow.add_edge(\"generate\", END)\n\ncustom_graph = workflow.compile()\n\ndisplay(Image(custom_graph.get_graph(xray=True).draw_mermaid_png()))"
|
||||
"from typing import List\n",
|
||||
"from typing_extensions import TypedDict\n",
|
||||
"from IPython.display import Image, display\n",
|
||||
"from langchain.schema import Document\n",
|
||||
"from langgraph.graph import START, END, StateGraph\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" question: question\n",
|
||||
" generation: LLM generation\n",
|
||||
" search: whether to add search\n",
|
||||
" documents: list of documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question: str\n",
|
||||
" generation: str\n",
|
||||
" search: str\n",
|
||||
" documents: List[str]\n",
|
||||
" steps: List[str]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def retrieve(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve documents\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, documents, that contains retrieved documents\n",
|
||||
" \"\"\"\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = retriever.invoke(question)\n",
|
||||
" steps = state[\"steps\"]\n",
|
||||
" steps.append(\"retrieve_documents\")\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"steps\": steps}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate answer\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation, that contains LLM generation\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
" generation = rag_chain.invoke({\"documents\": documents, \"question\": question})\n",
|
||||
" steps = state[\"steps\"]\n",
|
||||
" steps.append(\"generate_answer\")\n",
|
||||
" return {\n",
|
||||
" \"documents\": documents,\n",
|
||||
" \"question\": question,\n",
|
||||
" \"generation\": generation,\n",
|
||||
" \"steps\": steps,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_documents(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the retrieved documents are relevant to the question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with only filtered relevant documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
" steps = state[\"steps\"]\n",
|
||||
" steps.append(\"grade_document_retrieval\")\n",
|
||||
" filtered_docs = []\n",
|
||||
" search = \"No\"\n",
|
||||
" for d in documents:\n",
|
||||
" score = retrieval_grader.invoke(\n",
|
||||
" {\"question\": question, \"documents\": d.page_content}\n",
|
||||
" )\n",
|
||||
" grade = score[\"score\"]\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" filtered_docs.append(d)\n",
|
||||
" else:\n",
|
||||
" search = \"Yes\"\n",
|
||||
" continue\n",
|
||||
" return {\n",
|
||||
" \"documents\": filtered_docs,\n",
|
||||
" \"question\": question,\n",
|
||||
" \"search\": search,\n",
|
||||
" \"steps\": steps,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def web_search(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Web search based on the re-phrased question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with appended web results\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state.get(\"documents\", [])\n",
|
||||
" steps = state[\"steps\"]\n",
|
||||
" steps.append(\"web_search\")\n",
|
||||
" web_results = web_search_tool.invoke({\"query\": question})\n",
|
||||
" documents.extend(\n",
|
||||
" [\n",
|
||||
" Document(page_content=d[\"content\"], metadata={\"url\": d[\"url\"]})\n",
|
||||
" for d in web_results\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"steps\": steps}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to generate an answer, or re-generate a question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Binary decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
" search = state[\"search\"]\n",
|
||||
" if search == \"Yes\":\n",
|
||||
" return \"search\"\n",
|
||||
" else:\n",
|
||||
" return \"generate\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Graph\n",
|
||||
"workflow = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"workflow.add_node(\"retrieve\", retrieve) # retrieve\n",
|
||||
"workflow.add_node(\"grade_documents\", grade_documents) # grade documents\n",
|
||||
"workflow.add_node(\"generate\", generate) # generatae\n",
|
||||
"workflow.add_node(\"web_search\", web_search) # web search\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.add_edge(START, \"retrieve\")\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
" decide_to_generate,\n",
|
||||
" {\n",
|
||||
" \"search\": \"web_search\",\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"workflow.add_edge(\"web_search\", \"generate\")\n",
|
||||
"workflow.add_edge(\"generate\", END)\n",
|
||||
"\n",
|
||||
"custom_graph = workflow.compile()\n",
|
||||
"\n",
|
||||
"display(Image(custom_graph.get_graph(xray=True).draw_mermaid_png()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -339,7 +585,39 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langsmith import Client\n\nclient = Client()\n\n# Create a dataset\nexamples = [\n (\n \"How does the ReAct agent use self-reflection? \",\n \"ReAct integrates reasoning and acting, performing actions - such tools like Wikipedia search API - and then observing / reasoning about the tool outputs.\",\n ),\n (\n \"What are the types of biases that can arise with few-shot prompting?\",\n \"The biases that can arise with few-shot prompting include (1) Majority label bias, (2) Recency bias, and (3) Common token bias.\",\n ),\n (\n \"What are five types of adversarial attacks?\",\n \"Five types of adversarial attacks are (1) Token manipulation, (2) Gradient based attack, (3) Jailbreak prompting, (4) Human red-teaming, (5) Model red-teaming.\",\n ),\n (\n \"Who did the Chicago Bears draft first in the 2024 NFL draft”?\",\n \"The Chicago Bears drafted Caleb Williams first in the 2024 NFL draft.\",\n ),\n (\"Who won the 2024 NBA finals?\", \"The Boston Celtics on the 2024 NBA finals\"),\n]\n\n# Save it\ndataset_name = \"Corrective RAG Agent Testing\"\nif not client.has_dataset(dataset_name=dataset_name):\n dataset = client.create_dataset(dataset_name=dataset_name)\n inputs, outputs = zip(\n *[({\"input\": text}, {\"output\": label}) for text, label in examples]\n )\n client.create_examples(inputs=inputs, outputs=outputs, dataset_id=dataset.id)"
|
||||
"from langsmith import Client\n",
|
||||
"\n",
|
||||
"client = Client()\n",
|
||||
"\n",
|
||||
"# Create a dataset\n",
|
||||
"examples = [\n",
|
||||
" (\n",
|
||||
" \"How does the ReAct agent use self-reflection? \",\n",
|
||||
" \"ReAct integrates reasoning and acting, performing actions - such tools like Wikipedia search API - and then observing / reasoning about the tool outputs.\",\n",
|
||||
" ),\n",
|
||||
" (\n",
|
||||
" \"What are the types of biases that can arise with few-shot prompting?\",\n",
|
||||
" \"The biases that can arise with few-shot prompting include (1) Majority label bias, (2) Recency bias, and (3) Common token bias.\",\n",
|
||||
" ),\n",
|
||||
" (\n",
|
||||
" \"What are five types of adversarial attacks?\",\n",
|
||||
" \"Five types of adversarial attacks are (1) Token manipulation, (2) Gradient based attack, (3) Jailbreak prompting, (4) Human red-teaming, (5) Model red-teaming.\",\n",
|
||||
" ),\n",
|
||||
" (\n",
|
||||
" \"Who did the Chicago Bears draft first in the 2024 NFL draft”?\",\n",
|
||||
" \"The Chicago Bears drafted Caleb Williams first in the 2024 NFL draft.\",\n",
|
||||
" ),\n",
|
||||
" (\"Who won the 2024 NBA finals?\", \"The Boston Celtics on the 2024 NBA finals\"),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"# Save it\n",
|
||||
"dataset_name = \"Corrective RAG Agent Testing\"\n",
|
||||
"if not client.has_dataset(dataset_name=dataset_name):\n",
|
||||
" dataset = client.create_dataset(dataset_name=dataset_name)\n",
|
||||
" inputs, outputs = zip(\n",
|
||||
" *[({\"input\": text}, {\"output\": label}) for text, label in examples]\n",
|
||||
" )\n",
|
||||
" client.create_examples(inputs=inputs, outputs=outputs, dataset_id=dataset.id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -551,16 +829,6 @@
|
||||
"\n",
|
||||
"However, the answer accuracy performance lags the larger models with `custom agent` implementations."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "79295798-0181-417e-abad-11dddb6ff05e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -59,7 +59,9 @@
|
||||
"id": "a384cc48-0425-4e8f-aafc-cfb8e56025c9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["! pip install -U langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph"]
|
||||
"source": [
|
||||
"! pip install -U langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -75,7 +77,11 @@
|
||||
"id": "f18b63c7-d0d3-41c1-ae6b-5a0f1b8ccf0f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import os\n\nos.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -93,7 +99,11 @@
|
||||
"id": "ccc3dae5-1df6-48ca-af8a-50f0e6128876",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -111,7 +121,34 @@
|
||||
"id": "565a6d44-2c9f-4fff-b1ec-eea05df9350d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import WebBaseLoader\nfrom langchain_community.vectorstores import Chroma\nfrom langchain_openai import OpenAIEmbeddings\n\nurls = [\n \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n]\n\ndocs = [WebBaseLoader(url).load() for url in urls]\ndocs_list = [item for sublist in docs for item in sublist]\n\ntext_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n chunk_size=250, chunk_overlap=0\n)\ndoc_splits = text_splitter.split_documents(docs_list)\n\n# Add to vectorDB\nvectorstore = Chroma.from_documents(\n documents=doc_splits,\n collection_name=\"rag-chroma\",\n embedding=OpenAIEmbeddings(),\n)\nretriever = vectorstore.as_retriever()"]
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"urls = [\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"docs = [WebBaseLoader(url).load() for url in urls]\n",
|
||||
"docs_list = [item for sublist in docs for item in sublist]\n",
|
||||
"\n",
|
||||
"text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n",
|
||||
" chunk_size=250, chunk_overlap=0\n",
|
||||
")\n",
|
||||
"doc_splits = text_splitter.split_documents(docs_list)\n",
|
||||
"\n",
|
||||
"# Add to vectorDB\n",
|
||||
"vectorstore = Chroma.from_documents(\n",
|
||||
" documents=doc_splits,\n",
|
||||
" collection_name=\"rag-chroma\",\n",
|
||||
" embedding=OpenAIEmbeddings(),\n",
|
||||
")\n",
|
||||
"retriever = vectorstore.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -143,7 +180,46 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Retrieval Grader\n\n\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.pydantic_v1 import BaseModel, Field\nfrom langchain_openai import ChatOpenAI\n\n\n# Data model\nclass GradeDocuments(BaseModel):\n \"\"\"Binary score for relevance check on retrieved documents.\"\"\"\n\n binary_score: str = Field(\n description=\"Documents are relevant to the question, 'yes' or 'no'\"\n )\n\n\n# LLM with function call\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\nstructured_llm_grader = llm.with_structured_output(GradeDocuments)\n\n# Prompt\nsystem = \"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \\n\n If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \\n\n Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\"\"\"\ngrade_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\"human\", \"Retrieved document: \\n\\n {document} \\n\\n User question: {question}\"),\n ]\n)\n\nretrieval_grader = grade_prompt | structured_llm_grader\nquestion = \"agent memory\"\ndocs = retriever.get_relevant_documents(question)\ndoc_txt = docs[1].page_content\nprint(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"]
|
||||
"source": [
|
||||
"### Retrieval Grader\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Data model\n",
|
||||
"class GradeDocuments(BaseModel):\n",
|
||||
" \"\"\"Binary score for relevance check on retrieved documents.\"\"\"\n",
|
||||
"\n",
|
||||
" binary_score: str = Field(\n",
|
||||
" description=\"Documents are relevant to the question, 'yes' or 'no'\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeDocuments)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n",
|
||||
" It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \\n\n",
|
||||
" If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \\n\n",
|
||||
" Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\"\"\"\n",
|
||||
"grade_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\"human\", \"Retrieved document: \\n\\n {document} \\n\\n User question: {question}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"retrieval_grader = grade_prompt | structured_llm_grader\n",
|
||||
"question = \"agent memory\"\n",
|
||||
"docs = retriever.get_relevant_documents(question)\n",
|
||||
"doc_txt = docs[1].page_content\n",
|
||||
"print(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -159,7 +235,31 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Generate\n\nfrom langchain import hub\nfrom langchain_core.output_parsers import StrOutputParser\n\n# Prompt\nprompt = hub.pull(\"rlm/rag-prompt\")\n\n# LLM\nllm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n\n\n# Post-processing\ndef format_docs(docs):\n return \"\\n\\n\".join(doc.page_content for doc in docs)\n\n\n# Chain\nrag_chain = prompt | llm | StrOutputParser()\n\n# Run\ngeneration = rag_chain.invoke({\"context\": docs, \"question\": question})\nprint(generation)"]
|
||||
"source": [
|
||||
"### Generate\n",
|
||||
"\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Post-processing\n",
|
||||
"def format_docs(docs):\n",
|
||||
" return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Chain\n",
|
||||
"rag_chain = prompt | llm | StrOutputParser()\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n",
|
||||
"print(generation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -178,7 +278,36 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Hallucination Grader\n\n\n# Data model\nclass GradeHallucinations(BaseModel):\n \"\"\"Binary score for hallucination present in generation answer.\"\"\"\n\n binary_score: str = Field(\n description=\"Answer is grounded in the facts, 'yes' or 'no'\"\n )\n\n\n# LLM with function call\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\nstructured_llm_grader = llm.with_structured_output(GradeHallucinations)\n\n# Prompt\nsystem = \"\"\"You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \\n \n Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts.\"\"\"\nhallucination_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\"human\", \"Set of facts: \\n\\n {documents} \\n\\n LLM generation: {generation}\"),\n ]\n)\n\nhallucination_grader = hallucination_prompt | structured_llm_grader\nhallucination_grader.invoke({\"documents\": docs, \"generation\": generation})"]
|
||||
"source": [
|
||||
"### Hallucination Grader\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Data model\n",
|
||||
"class GradeHallucinations(BaseModel):\n",
|
||||
" \"\"\"Binary score for hallucination present in generation answer.\"\"\"\n",
|
||||
"\n",
|
||||
" binary_score: str = Field(\n",
|
||||
" description=\"Answer is grounded in the facts, 'yes' or 'no'\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeHallucinations)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \\n \n",
|
||||
" Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts.\"\"\"\n",
|
||||
"hallucination_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\"human\", \"Set of facts: \\n\\n {documents} \\n\\n LLM generation: {generation}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"hallucination_grader = hallucination_prompt | structured_llm_grader\n",
|
||||
"hallucination_grader.invoke({\"documents\": docs, \"generation\": generation})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -197,7 +326,36 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Answer Grader\n\n\n# Data model\nclass GradeAnswer(BaseModel):\n \"\"\"Binary score to assess answer addresses question.\"\"\"\n\n binary_score: str = Field(\n description=\"Answer addresses the question, 'yes' or 'no'\"\n )\n\n\n# LLM with function call\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\nstructured_llm_grader = llm.with_structured_output(GradeAnswer)\n\n# Prompt\nsystem = \"\"\"You are a grader assessing whether an answer addresses / resolves a question \\n \n Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question.\"\"\"\nanswer_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\"human\", \"User question: \\n\\n {question} \\n\\n LLM generation: {generation}\"),\n ]\n)\n\nanswer_grader = answer_prompt | structured_llm_grader\nanswer_grader.invoke({\"question\": question, \"generation\": generation})"]
|
||||
"source": [
|
||||
"### Answer Grader\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Data model\n",
|
||||
"class GradeAnswer(BaseModel):\n",
|
||||
" \"\"\"Binary score to assess answer addresses question.\"\"\"\n",
|
||||
"\n",
|
||||
" binary_score: str = Field(\n",
|
||||
" description=\"Answer addresses the question, 'yes' or 'no'\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# LLM with function call\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"structured_llm_grader = llm.with_structured_output(GradeAnswer)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You are a grader assessing whether an answer addresses / resolves a question \\n \n",
|
||||
" Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question.\"\"\"\n",
|
||||
"answer_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\"human\", \"User question: \\n\\n {question} \\n\\n LLM generation: {generation}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"answer_grader = answer_prompt | structured_llm_grader\n",
|
||||
"answer_grader.invoke({\"question\": question, \"generation\": generation})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -216,7 +374,28 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Question Re-writer\n\n# LLM\nllm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n\n# Prompt\nsystem = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n for vectorstore retrieval. Look at the input and try to reason about the underlying semantic intent / meaning.\"\"\"\nre_write_prompt = ChatPromptTemplate.from_messages(\n [\n (\"system\", system),\n (\n \"human\",\n \"Here is the initial question: \\n\\n {question} \\n Formulate an improved question.\",\n ),\n ]\n)\n\nquestion_rewriter = re_write_prompt | llm | StrOutputParser()\nquestion_rewriter.invoke({\"question\": question})"]
|
||||
"source": [
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"system = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n",
|
||||
" for vectorstore retrieval. Look at the input and try to reason about the underlying semantic intent / meaning.\"\"\"\n",
|
||||
"re_write_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" (\n",
|
||||
" \"human\",\n",
|
||||
" \"Here is the initial question: \\n\\n {question} \\n Formulate an improved question.\",\n",
|
||||
" ),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"question_rewriter = re_write_prompt | llm | StrOutputParser()\n",
|
||||
"question_rewriter.invoke({\"question\": question})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -236,7 +415,26 @@
|
||||
"id": "f1617e9e-66a8-4c1a-a1fe-cc936284c085",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from typing import List\n\nfrom typing_extensions import TypedDict\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n question: question\n generation: LLM generation\n documents: list of documents\n \"\"\"\n\n question: str\n generation: str\n documents: List[str]"]
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from typing_extensions import TypedDict\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" question: question\n",
|
||||
" generation: LLM generation\n",
|
||||
" documents: list of documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question: str\n",
|
||||
" generation: str\n",
|
||||
" documents: List[str]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -244,7 +442,167 @@
|
||||
"id": "add509d8-6682-4127-8d95-13dd37d79702",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["### Nodes\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.get_relevant_documents(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score.binary_score\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n continue\n return {\"documents\": filtered_docs, \"question\": question}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}\n\n\n### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n filtered_documents = state[\"documents\"]\n\n if not filtered_documents:\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\"\n\n\ndef grade_generation_v_documents_and_question(state):\n \"\"\"\n Determines whether the generation is grounded in the document and answers question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Decision for next node to call\n \"\"\"\n\n print(\"---CHECK HALLUCINATIONS---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = state[\"generation\"]\n\n score = hallucination_grader.invoke(\n {\"documents\": documents, \"generation\": generation}\n )\n grade = score.binary_score\n\n # Check hallucination\n if grade == \"yes\":\n print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n # Check question-answering\n print(\"---GRADE GENERATION vs QUESTION---\")\n score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n grade = score.binary_score\n if grade == \"yes\":\n print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n return \"useful\"\n else:\n print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n return \"not useful\"\n else:\n pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n return \"not supported\""]
|
||||
"source": [
|
||||
"### Nodes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def retrieve(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve documents\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, documents, that contains retrieved documents\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---RETRIEVE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
"\n",
|
||||
" # Retrieval\n",
|
||||
" documents = retriever.get_relevant_documents(question)\n",
|
||||
" return {\"documents\": documents, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate answer\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation, that contains LLM generation\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---GENERATE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # RAG generation\n",
|
||||
" generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_documents(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the retrieved documents are relevant to the question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with only filtered relevant documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Score each doc\n",
|
||||
" filtered_docs = []\n",
|
||||
" for d in documents:\n",
|
||||
" score = retrieval_grader.invoke(\n",
|
||||
" {\"question\": question, \"document\": d.page_content}\n",
|
||||
" )\n",
|
||||
" grade = score.binary_score\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---GRADE: DOCUMENT RELEVANT---\")\n",
|
||||
" filtered_docs.append(d)\n",
|
||||
" else:\n",
|
||||
" print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n",
|
||||
" continue\n",
|
||||
" return {\"documents\": filtered_docs, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transform_query(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Transform the query to produce a better question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates question key with a re-phrased question\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---TRANSFORM QUERY---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Re-write question\n",
|
||||
" better_question = question_rewriter.invoke({\"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": better_question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Edges\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to generate an answer, or re-generate a question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Binary decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---ASSESS GRADED DOCUMENTS---\")\n",
|
||||
" state[\"question\"]\n",
|
||||
" filtered_documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" if not filtered_documents:\n",
|
||||
" # All documents have been filtered check_relevance\n",
|
||||
" # We will re-generate a new query\n",
|
||||
" print(\n",
|
||||
" \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n",
|
||||
" )\n",
|
||||
" return \"transform_query\"\n",
|
||||
" else:\n",
|
||||
" # We have relevant documents, so generate answer\n",
|
||||
" print(\"---DECISION: GENERATE---\")\n",
|
||||
" return \"generate\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_generation_v_documents_and_question(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the generation is grounded in the document and answers question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK HALLUCINATIONS---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
" generation = state[\"generation\"]\n",
|
||||
"\n",
|
||||
" score = hallucination_grader.invoke(\n",
|
||||
" {\"documents\": documents, \"generation\": generation}\n",
|
||||
" )\n",
|
||||
" grade = score.binary_score\n",
|
||||
"\n",
|
||||
" # Check hallucination\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n",
|
||||
" # Check question-answering\n",
|
||||
" print(\"---GRADE GENERATION vs QUESTION---\")\n",
|
||||
" score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n",
|
||||
" grade = score.binary_score\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n",
|
||||
" return \"useful\"\n",
|
||||
" else:\n",
|
||||
" print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n",
|
||||
" return \"not useful\"\n",
|
||||
" else:\n",
|
||||
" pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n",
|
||||
" return \"not supported\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -262,7 +620,42 @@
|
||||
"id": "0e09ca9f-e36d-4ef4-a0d5-79fdbada9fe0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langgraph.graph import END, StateGraph, START\n\nworkflow = StateGraph(GraphState)\n\n# Define the nodes\nworkflow.add_node(\"retrieve\", retrieve) # retrieve\nworkflow.add_node(\"grade_documents\", grade_documents) # grade documents\nworkflow.add_node(\"generate\", generate) # generatae\nworkflow.add_node(\"transform_query\", transform_query) # transform_query\n\n# Build graph\nworkflow.add_edge(START, \"retrieve\")\nworkflow.add_edge(\"retrieve\", \"grade_documents\")\nworkflow.add_conditional_edges(\n \"grade_documents\",\n decide_to_generate,\n {\n \"transform_query\": \"transform_query\",\n \"generate\": \"generate\",\n },\n)\nworkflow.add_edge(\"transform_query\", \"retrieve\")\nworkflow.add_conditional_edges(\n \"generate\",\n grade_generation_v_documents_and_question,\n {\n \"not supported\": \"generate\",\n \"useful\": END,\n \"not useful\": \"transform_query\",\n },\n)\n\n# Compile\napp = workflow.compile()"]
|
||||
"source": [
|
||||
"from langgraph.graph import END, StateGraph, START\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"workflow.add_node(\"retrieve\", retrieve) # retrieve\n",
|
||||
"workflow.add_node(\"grade_documents\", grade_documents) # grade documents\n",
|
||||
"workflow.add_node(\"generate\", generate) # generatae\n",
|
||||
"workflow.add_node(\"transform_query\", transform_query) # transform_query\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.add_edge(START, \"retrieve\")\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
" decide_to_generate,\n",
|
||||
" {\n",
|
||||
" \"transform_query\": \"transform_query\",\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"workflow.add_edge(\"transform_query\", \"retrieve\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"generate\",\n",
|
||||
" grade_generation_v_documents_and_question,\n",
|
||||
" {\n",
|
||||
" \"not supported\": \"generate\",\n",
|
||||
" \"useful\": END,\n",
|
||||
" \"not useful\": \"transform_query\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Compile\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -301,7 +694,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["from pprint import pprint\n\n# Run\ninputs = {\"question\": \"Explain how the different types of agent memory work?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n # Optional: print full state at each node\n # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"]
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"inputs = {\"question\": \"Explain how the different types of agent memory work?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" # Optional: print full state at each node\n",
|
||||
" # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -341,7 +749,19 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["inputs = {\"question\": \"Explain how chain of thought prompting works?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n # Optional: print full state at each node\n # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"]
|
||||
"source": [
|
||||
"inputs = {\"question\": \"Explain how chain of thought prompting works?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" # Optional: print full state at each node\n",
|
||||
" # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -354,14 +774,6 @@
|
||||
"\n",
|
||||
"* https://smith.langchain.com/public/1c6bf654-61b2-4fc5-9889-054b020c78aa/r"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "42369ab8-322d-434a-b5dd-2266e4cb2903",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [""]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -59,7 +59,10 @@
|
||||
"id": "d7f9cc6d-a70c-433a-b0ad-ea47c5a0717e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["%capture --no-stderr\n%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph nomic[local]"]
|
||||
"source": [
|
||||
"%capture --no-stderr\n",
|
||||
"%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph nomic[local]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -91,7 +94,10 @@
|
||||
"id": "bedffc73-6b10-42c8-8768-2085c8ed3398",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["# Ollama model name\nlocal_llm = \"mistral\""]
|
||||
"source": [
|
||||
"# Ollama model name\n",
|
||||
"local_llm = \"mistral\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -109,7 +115,13 @@
|
||||
"id": "2208f342-8163-4af3-8dc0-aa70f5e06143",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["import os\n\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\nos.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""]
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
|
||||
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -127,7 +139,34 @@
|
||||
"id": "c3bb9060-ad74-4470-9991-2ba167b6b8d8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import WebBaseLoader\nfrom langchain_community.vectorstores import Chroma\nfrom langchain_nomic.embeddings import NomicEmbeddings\n\nurls = [\n \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n]\n\ndocs = [WebBaseLoader(url).load() for url in urls]\ndocs_list = [item for sublist in docs for item in sublist]\n\ntext_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n chunk_size=250, chunk_overlap=0\n)\ndoc_splits = text_splitter.split_documents(docs_list)\n\n# Add to vectorDB\nvectorstore = Chroma.from_documents(\n documents=doc_splits,\n collection_name=\"rag-chroma\",\n embedding=NomicEmbeddings(model=\"nomic-embed-text-v1.5\", inference_mode=\"local\"),\n)\nretriever = vectorstore.as_retriever()"]
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_nomic.embeddings import NomicEmbeddings\n",
|
||||
"\n",
|
||||
"urls = [\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n",
|
||||
" \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"docs = [WebBaseLoader(url).load() for url in urls]\n",
|
||||
"docs_list = [item for sublist in docs for item in sublist]\n",
|
||||
"\n",
|
||||
"text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n",
|
||||
" chunk_size=250, chunk_overlap=0\n",
|
||||
")\n",
|
||||
"doc_splits = text_splitter.split_documents(docs_list)\n",
|
||||
"\n",
|
||||
"# Add to vectorDB\n",
|
||||
"vectorstore = Chroma.from_documents(\n",
|
||||
" documents=doc_splits,\n",
|
||||
" collection_name=\"rag-chroma\",\n",
|
||||
" embedding=NomicEmbeddings(model=\"nomic-embed-text-v1.5\", inference_mode=\"local\"),\n",
|
||||
")\n",
|
||||
"retriever = vectorstore.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -151,7 +190,33 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Retrieval Grader\n\nfrom langchain.prompts import PromptTemplate\nfrom langchain_community.chat_models import ChatOllama\nfrom langchain_core.output_parsers import JsonOutputParser\n\n# LLM\nllm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n\nprompt = PromptTemplate(\n template=\"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n Here is the retrieved document: \\n\\n {document} \\n\\n\n Here is the user question: {question} \\n\n If the document contains keywords related to the user question, grade it as relevant. \\n\n It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \\n\n Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question. \\n\n Provide the binary score as a JSON with a single key 'score' and no premable or explanation.\"\"\",\n input_variables=[\"question\", \"document\"],\n)\n\nretrieval_grader = prompt | llm | JsonOutputParser()\nquestion = \"agent memory\"\ndocs = retriever.get_relevant_documents(question)\ndoc_txt = docs[1].page_content\nprint(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"]
|
||||
"source": [
|
||||
"### Retrieval Grader\n",
|
||||
"\n",
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain_community.chat_models import ChatOllama\n",
|
||||
"from langchain_core.output_parsers import JsonOutputParser\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n",
|
||||
"\n",
|
||||
"prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n \n",
|
||||
" Here is the retrieved document: \\n\\n {document} \\n\\n\n",
|
||||
" Here is the user question: {question} \\n\n",
|
||||
" If the document contains keywords related to the user question, grade it as relevant. \\n\n",
|
||||
" It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \\n\n",
|
||||
" Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question. \\n\n",
|
||||
" Provide the binary score as a JSON with a single key 'score' and no premable or explanation.\"\"\",\n",
|
||||
" input_variables=[\"question\", \"document\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"retrieval_grader = prompt | llm | JsonOutputParser()\n",
|
||||
"question = \"agent memory\"\n",
|
||||
"docs = retriever.get_relevant_documents(question)\n",
|
||||
"doc_txt = docs[1].page_content\n",
|
||||
"print(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -167,7 +232,31 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["### Generate\n\nfrom langchain import hub\nfrom langchain_core.output_parsers import StrOutputParser\n\n# Prompt\nprompt = hub.pull(\"rlm/rag-prompt\")\n\n# LLM\nllm = ChatOllama(model=local_llm, temperature=0)\n\n\n# Post-processing\ndef format_docs(docs):\n return \"\\n\\n\".join(doc.page_content for doc in docs)\n\n\n# Chain\nrag_chain = prompt | llm | StrOutputParser()\n\n# Run\ngeneration = rag_chain.invoke({\"context\": docs, \"question\": question})\nprint(generation)"]
|
||||
"source": [
|
||||
"### Generate\n",
|
||||
"\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = hub.pull(\"rlm/rag-prompt\")\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, temperature=0)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Post-processing\n",
|
||||
"def format_docs(docs):\n",
|
||||
" return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Chain\n",
|
||||
"rag_chain = prompt | llm | StrOutputParser()\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n",
|
||||
"print(generation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -186,7 +275,28 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Hallucination Grader\n\n# LLM\nllm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n\n# Prompt\nprompt = PromptTemplate(\n template=\"\"\"You are a grader assessing whether an answer is grounded in / supported by a set of facts. \\n \n Here are the facts:\n \\n ------- \\n\n {documents} \n \\n ------- \\n\n Here is the answer: {generation}\n Give a binary score 'yes' or 'no' score to indicate whether the answer is grounded in / supported by a set of facts. \\n\n Provide the binary score as a JSON with a single key 'score' and no preamble or explanation.\"\"\",\n input_variables=[\"generation\", \"documents\"],\n)\n\nhallucination_grader = prompt | llm | JsonOutputParser()\nhallucination_grader.invoke({\"documents\": docs, \"generation\": generation})"]
|
||||
"source": [
|
||||
"### Hallucination Grader\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You are a grader assessing whether an answer is grounded in / supported by a set of facts. \\n \n",
|
||||
" Here are the facts:\n",
|
||||
" \\n ------- \\n\n",
|
||||
" {documents} \n",
|
||||
" \\n ------- \\n\n",
|
||||
" Here is the answer: {generation}\n",
|
||||
" Give a binary score 'yes' or 'no' score to indicate whether the answer is grounded in / supported by a set of facts. \\n\n",
|
||||
" Provide the binary score as a JSON with a single key 'score' and no preamble or explanation.\"\"\",\n",
|
||||
" input_variables=[\"generation\", \"documents\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"hallucination_grader = prompt | llm | JsonOutputParser()\n",
|
||||
"hallucination_grader.invoke({\"documents\": docs, \"generation\": generation})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -205,7 +315,28 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Answer Grader\n\n# LLM\nllm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n\n# Prompt\nprompt = PromptTemplate(\n template=\"\"\"You are a grader assessing whether an answer is useful to resolve a question. \\n \n Here is the answer:\n \\n ------- \\n\n {generation} \n \\n ------- \\n\n Here is the question: {question}\n Give a binary score 'yes' or 'no' to indicate whether the answer is useful to resolve a question. \\n\n Provide the binary score as a JSON with a single key 'score' and no preamble or explanation.\"\"\",\n input_variables=[\"generation\", \"question\"],\n)\n\nanswer_grader = prompt | llm | JsonOutputParser()\nanswer_grader.invoke({\"question\": question, \"generation\": generation})"]
|
||||
"source": [
|
||||
"### Answer Grader\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You are a grader assessing whether an answer is useful to resolve a question. \\n \n",
|
||||
" Here is the answer:\n",
|
||||
" \\n ------- \\n\n",
|
||||
" {generation} \n",
|
||||
" \\n ------- \\n\n",
|
||||
" Here is the question: {question}\n",
|
||||
" Give a binary score 'yes' or 'no' to indicate whether the answer is useful to resolve a question. \\n\n",
|
||||
" Provide the binary score as a JSON with a single key 'score' and no preamble or explanation.\"\"\",\n",
|
||||
" input_variables=[\"generation\", \"question\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"answer_grader = prompt | llm | JsonOutputParser()\n",
|
||||
"answer_grader.invoke({\"question\": question, \"generation\": generation})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -224,7 +355,23 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": ["### Question Re-writer\n\n# LLM\nllm = ChatOllama(model=local_llm, temperature=0)\n\n# Prompt\nre_write_prompt = PromptTemplate(\n template=\"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n for vectorstore retrieval. Look at the initial and formulate an improved question. \\n\n Here is the initial question: \\n\\n {question}. Improved question with no preamble: \\n \"\"\",\n input_variables=[\"generation\", \"question\"],\n)\n\nquestion_rewriter = re_write_prompt | llm | StrOutputParser()\nquestion_rewriter.invoke({\"question\": question})"]
|
||||
"source": [
|
||||
"### Question Re-writer\n",
|
||||
"\n",
|
||||
"# LLM\n",
|
||||
"llm = ChatOllama(model=local_llm, temperature=0)\n",
|
||||
"\n",
|
||||
"# Prompt\n",
|
||||
"re_write_prompt = PromptTemplate(\n",
|
||||
" template=\"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n \n",
|
||||
" for vectorstore retrieval. Look at the initial and formulate an improved question. \\n\n",
|
||||
" Here is the initial question: \\n\\n {question}. Improved question with no preamble: \\n \"\"\",\n",
|
||||
" input_variables=[\"generation\", \"question\"],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"question_rewriter = re_write_prompt | llm | StrOutputParser()\n",
|
||||
"question_rewriter.invoke({\"question\": question})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -244,7 +391,26 @@
|
||||
"id": "90fb1dc6-c482-483a-8441-39965c401beb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from typing import List\n\nfrom typing_extensions import TypedDict\n\n\nclass GraphState(TypedDict):\n \"\"\"\n Represents the state of our graph.\n\n Attributes:\n question: question\n generation: LLM generation\n documents: list of documents\n \"\"\"\n\n question: str\n generation: str\n documents: List[str]"]
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from typing_extensions import TypedDict\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GraphState(TypedDict):\n",
|
||||
" \"\"\"\n",
|
||||
" Represents the state of our graph.\n",
|
||||
"\n",
|
||||
" Attributes:\n",
|
||||
" question: question\n",
|
||||
" generation: LLM generation\n",
|
||||
" documents: list of documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" question: str\n",
|
||||
" generation: str\n",
|
||||
" documents: List[str]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -252,7 +418,167 @@
|
||||
"id": "5324ea49-5745-47b5-a0a5-bf58c8babe46",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["### Nodes\n\n\ndef retrieve(state):\n \"\"\"\n Retrieve documents\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, documents, that contains retrieved documents\n \"\"\"\n print(\"---RETRIEVE---\")\n question = state[\"question\"]\n\n # Retrieval\n documents = retriever.get_relevant_documents(question)\n return {\"documents\": documents, \"question\": question}\n\n\ndef generate(state):\n \"\"\"\n Generate answer\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): New key added to state, generation, that contains LLM generation\n \"\"\"\n print(\"---GENERATE---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # RAG generation\n generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n return {\"documents\": documents, \"question\": question, \"generation\": generation}\n\n\ndef grade_documents(state):\n \"\"\"\n Determines whether the retrieved documents are relevant to the question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates documents key with only filtered relevant documents\n \"\"\"\n\n print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Score each doc\n filtered_docs = []\n for d in documents:\n score = retrieval_grader.invoke(\n {\"question\": question, \"document\": d.page_content}\n )\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---GRADE: DOCUMENT RELEVANT---\")\n filtered_docs.append(d)\n else:\n print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n continue\n return {\"documents\": filtered_docs, \"question\": question}\n\n\ndef transform_query(state):\n \"\"\"\n Transform the query to produce a better question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n state (dict): Updates question key with a re-phrased question\n \"\"\"\n\n print(\"---TRANSFORM QUERY---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n\n # Re-write question\n better_question = question_rewriter.invoke({\"question\": question})\n return {\"documents\": documents, \"question\": better_question}\n\n\n### Edges\n\n\ndef decide_to_generate(state):\n \"\"\"\n Determines whether to generate an answer, or re-generate a question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Binary decision for next node to call\n \"\"\"\n\n print(\"---ASSESS GRADED DOCUMENTS---\")\n state[\"question\"]\n filtered_documents = state[\"documents\"]\n\n if not filtered_documents:\n # All documents have been filtered check_relevance\n # We will re-generate a new query\n print(\n \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n )\n return \"transform_query\"\n else:\n # We have relevant documents, so generate answer\n print(\"---DECISION: GENERATE---\")\n return \"generate\"\n\n\ndef grade_generation_v_documents_and_question(state):\n \"\"\"\n Determines whether the generation is grounded in the document and answers question.\n\n Args:\n state (dict): The current graph state\n\n Returns:\n str: Decision for next node to call\n \"\"\"\n\n print(\"---CHECK HALLUCINATIONS---\")\n question = state[\"question\"]\n documents = state[\"documents\"]\n generation = state[\"generation\"]\n\n score = hallucination_grader.invoke(\n {\"documents\": documents, \"generation\": generation}\n )\n grade = score[\"score\"]\n\n # Check hallucination\n if grade == \"yes\":\n print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n # Check question-answering\n print(\"---GRADE GENERATION vs QUESTION---\")\n score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n grade = score[\"score\"]\n if grade == \"yes\":\n print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n return \"useful\"\n else:\n print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n return \"not useful\"\n else:\n print(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n return \"not supported\""]
|
||||
"source": [
|
||||
"### Nodes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def retrieve(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve documents\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, documents, that contains retrieved documents\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---RETRIEVE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
"\n",
|
||||
" # Retrieval\n",
|
||||
" documents = retriever.get_relevant_documents(question)\n",
|
||||
" return {\"documents\": documents, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate answer\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): New key added to state, generation, that contains LLM generation\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"---GENERATE---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # RAG generation\n",
|
||||
" generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_documents(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the retrieved documents are relevant to the question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates documents key with only filtered relevant documents\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Score each doc\n",
|
||||
" filtered_docs = []\n",
|
||||
" for d in documents:\n",
|
||||
" score = retrieval_grader.invoke(\n",
|
||||
" {\"question\": question, \"document\": d.page_content}\n",
|
||||
" )\n",
|
||||
" grade = score[\"score\"]\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---GRADE: DOCUMENT RELEVANT---\")\n",
|
||||
" filtered_docs.append(d)\n",
|
||||
" else:\n",
|
||||
" print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n",
|
||||
" continue\n",
|
||||
" return {\"documents\": filtered_docs, \"question\": question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transform_query(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Transform the query to produce a better question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" state (dict): Updates question key with a re-phrased question\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---TRANSFORM QUERY---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" # Re-write question\n",
|
||||
" better_question = question_rewriter.invoke({\"question\": question})\n",
|
||||
" return {\"documents\": documents, \"question\": better_question}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Edges\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decide_to_generate(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether to generate an answer, or re-generate a question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Binary decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---ASSESS GRADED DOCUMENTS---\")\n",
|
||||
" state[\"question\"]\n",
|
||||
" filtered_documents = state[\"documents\"]\n",
|
||||
"\n",
|
||||
" if not filtered_documents:\n",
|
||||
" # All documents have been filtered check_relevance\n",
|
||||
" # We will re-generate a new query\n",
|
||||
" print(\n",
|
||||
" \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n",
|
||||
" )\n",
|
||||
" return \"transform_query\"\n",
|
||||
" else:\n",
|
||||
" # We have relevant documents, so generate answer\n",
|
||||
" print(\"---DECISION: GENERATE---\")\n",
|
||||
" return \"generate\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def grade_generation_v_documents_and_question(state):\n",
|
||||
" \"\"\"\n",
|
||||
" Determines whether the generation is grounded in the document and answers question.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (dict): The current graph state\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" str: Decision for next node to call\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" print(\"---CHECK HALLUCINATIONS---\")\n",
|
||||
" question = state[\"question\"]\n",
|
||||
" documents = state[\"documents\"]\n",
|
||||
" generation = state[\"generation\"]\n",
|
||||
"\n",
|
||||
" score = hallucination_grader.invoke(\n",
|
||||
" {\"documents\": documents, \"generation\": generation}\n",
|
||||
" )\n",
|
||||
" grade = score[\"score\"]\n",
|
||||
"\n",
|
||||
" # Check hallucination\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n",
|
||||
" # Check question-answering\n",
|
||||
" print(\"---GRADE GENERATION vs QUESTION---\")\n",
|
||||
" score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n",
|
||||
" grade = score[\"score\"]\n",
|
||||
" if grade == \"yes\":\n",
|
||||
" print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n",
|
||||
" return \"useful\"\n",
|
||||
" else:\n",
|
||||
" print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n",
|
||||
" return \"not useful\"\n",
|
||||
" else:\n",
|
||||
" print(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n",
|
||||
" return \"not supported\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -270,7 +596,42 @@
|
||||
"id": "5605dee4-b2df-46ae-a640-cc2ed90c21a6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["from langgraph.graph import END, StateGraph, START\n\nworkflow = StateGraph(GraphState)\n\n# Define the nodes\nworkflow.add_node(\"retrieve\", retrieve) # retrieve\nworkflow.add_node(\"grade_documents\", grade_documents) # grade documents\nworkflow.add_node(\"generate\", generate) # generatae\nworkflow.add_node(\"transform_query\", transform_query) # transform_query\n\n# Build graph\nworkflow.add_edge(START, \"retrieve\")\nworkflow.add_edge(\"retrieve\", \"grade_documents\")\nworkflow.add_conditional_edges(\n \"grade_documents\",\n decide_to_generate,\n {\n \"transform_query\": \"transform_query\",\n \"generate\": \"generate\",\n },\n)\nworkflow.add_edge(\"transform_query\", \"retrieve\")\nworkflow.add_conditional_edges(\n \"generate\",\n grade_generation_v_documents_and_question,\n {\n \"not supported\": \"generate\",\n \"useful\": END,\n \"not useful\": \"transform_query\",\n },\n)\n\n# Compile\napp = workflow.compile()"]
|
||||
"source": [
|
||||
"from langgraph.graph import END, StateGraph, START\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(GraphState)\n",
|
||||
"\n",
|
||||
"# Define the nodes\n",
|
||||
"workflow.add_node(\"retrieve\", retrieve) # retrieve\n",
|
||||
"workflow.add_node(\"grade_documents\", grade_documents) # grade documents\n",
|
||||
"workflow.add_node(\"generate\", generate) # generatae\n",
|
||||
"workflow.add_node(\"transform_query\", transform_query) # transform_query\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.add_edge(START, \"retrieve\")\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
" decide_to_generate,\n",
|
||||
" {\n",
|
||||
" \"transform_query\": \"transform_query\",\n",
|
||||
" \"generate\": \"generate\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"workflow.add_edge(\"transform_query\", \"retrieve\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"generate\",\n",
|
||||
" grade_generation_v_documents_and_question,\n",
|
||||
" {\n",
|
||||
" \"not supported\": \"generate\",\n",
|
||||
" \"useful\": END,\n",
|
||||
" \"not useful\": \"transform_query\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Compile\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -324,7 +685,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": ["from pprint import pprint\n\n# Run\ninputs = {\"question\": \"Explain how the different types of agent memory work?\"}\nfor output in app.stream(inputs):\n for key, value in output.items():\n # Node\n pprint(f\"Node '{key}':\")\n # Optional: print full state at each node\n # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n pprint(\"\\n---\\n\")\n\n# Final generation\npprint(value[\"generation\"])"]
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"# Run\n",
|
||||
"inputs = {\"question\": \"Explain how the different types of agent memory work?\"}\n",
|
||||
"for output in app.stream(inputs):\n",
|
||||
" for key, value in output.items():\n",
|
||||
" # Node\n",
|
||||
" pprint(f\"Node '{key}':\")\n",
|
||||
" # Optional: print full state at each node\n",
|
||||
" # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
|
||||
" pprint(\"\\n---\\n\")\n",
|
||||
"\n",
|
||||
"# Final generation\n",
|
||||
"pprint(value[\"generation\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -335,14 +711,6 @@
|
||||
"\n",
|
||||
"https://smith.langchain.com/public/4163a342-5260-4852-8602-bda3f95177e7/r"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "953143c2-2f2a-4361-a36b-87db7cf21d63",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [""]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
Reference in New Issue
Block a user