|
15 | 15 | "metadata": {}, |
16 | 16 | "outputs": [], |
17 | 17 | "source": [ |
18 | | - "! pip install -U langchain_community tiktoken langchainhub chromadb langchain langgraph tavily-python sentence-transformers" |
| 18 | + "! pip install -U langchain-huggingface langchain_community langchain-ollama tiktoken langchainhub chromadb langchain langgraph tavily-python sentence-transformers beautifulsoup4" |
19 | 19 | ] |
20 | 20 | }, |
21 | 21 | { |
|
99 | 99 | "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", |
100 | 100 | "os.environ['LANGCHAIN_API_KEY'] = 'LANGCHAIN_API_KEY'\n", |
101 | 101 | "\n", |
102 | | - "os.environ['TAVILY_API_KEY'] = 'TAVILY_API_KEY'" |
| 102 | + "os.environ['TAVILY_API_KEY'] = 'TAVILY_API_KEY'\n", |
| 103 | + "os.environ[\"USER_AGENT\"] = \"USER_AGENT_KEY\"" |
103 | 104 | ] |
104 | 105 | }, |
105 | 106 | { |
|
126 | 127 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", |
127 | 128 | "from langchain_community.document_loaders import WebBaseLoader\n", |
128 | 129 | "from langchain_community.vectorstores import Chroma\n", |
129 | | - "from langchain_community.embeddings import HuggingFaceEmbeddings\n", |
| 130 | + "from langchain_huggingface import HuggingFaceEmbeddings\n", |
130 | 131 | "\n", |
131 | 132 | "urls = [\n", |
132 | 133 | " \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n", |
|
146 | 147 | "vectorstore = Chroma.from_documents(\n", |
147 | 148 | " documents=doc_splits,\n", |
148 | 149 | " collection_name=\"rag-chroma\",\n", |
149 | | - " embedding=HuggingFaceEmbeddings(),\n", |
| 150 | + " embedding=HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-mpnet-base-v2\"),\n", |
150 | 151 | ")\n", |
151 | 152 | "retriever = vectorstore.as_retriever()" |
152 | 153 | ] |
|
158 | 159 | "metadata": {}, |
159 | 160 | "outputs": [], |
160 | 161 | "source": [ |
161 | | - "### Retrieval Grader \n", |
| 162 | + "### Retrieval Grader\n", |
162 | 163 | "\n", |
163 | 164 | "from langchain.prompts import PromptTemplate\n", |
164 | | - "from langchain_community.chat_models import ChatOllama\n", |
| 165 | + "from langchain_ollama import ChatOllama\n", |
165 | 166 | "from langchain_core.output_parsers import JsonOutputParser\n", |
166 | 167 | "\n", |
167 | 168 | "# LLM\n", |
168 | 169 | "llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n", |
169 | 170 | "\n", |
170 | 171 | "prompt = PromptTemplate(\n", |
171 | | - " template=\"\"\"You are a grader assessing relevance \n", |
172 | | - " of a retrieved document to a user question. If the document contains keywords related to the user question, \n", |
173 | | - " grade it as relevant. It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \n", |
174 | | - " \n", |
| 172 | + " template=\"\"\"You are a grader assessing relevance\n", |
| 173 | + " of a retrieved document to a user question. If the document contains keywords related to the user question,\n", |
| 174 | + " grade it as relevant. It does not need to be a stringent test. The goal is to filter out erroneous retrievals.\n", |
| 175 | + "\n", |
175 | 176 | " Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\n", |
176 | 177 | " Provide the binary score as a JSON with a single key 'score' and no premable or explanation.\n", |
177 | | - " \n", |
178 | | - " Here is the retrieved document: \n", |
| 178 | + "\n", |
| 179 | + " Here is the retrieved document:\n", |
179 | 180 | " {document}\n", |
180 | | - " \n", |
181 | | - " Here is the user question: \n", |
| 181 | + "\n", |
| 182 | + " Here is the user question:\n", |
182 | 183 | " {question}\n", |
183 | 184 | " \"\"\",\n", |
184 | 185 | " input_variables=[\"question\", \"document\"],\n", |
|
206 | 207 | "\n", |
207 | 208 | "# Prompt\n", |
208 | 209 | "prompt = PromptTemplate(\n", |
209 | | - " template=\"\"\"You are an assistant for question-answering tasks. \n", |
210 | | - " Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. \n", |
| 210 | + " template=\"\"\"You are an assistant for question-answering tasks.\n", |
| 211 | + " Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know.\n", |
211 | 212 | " Use three sentences maximum and keep the answer concise:\n", |
212 | | - " Question: {question} \n", |
213 | | - " Context: {context} \n", |
214 | | - " Answer: \n", |
| 213 | + " Question: {question}\n", |
| 214 | + " Context: {context}\n", |
| 215 | + " Answer:\n", |
215 | 216 | " \"\"\",\n", |
216 | 217 | " input_variables=[\"question\", \"document\"],\n", |
217 | 218 | ")\n", |
|
239 | 240 | "metadata": {}, |
240 | 241 | "outputs": [], |
241 | 242 | "source": [ |
242 | | - "### Hallucination Grader \n", |
| 243 | + "### Hallucination Grader\n", |
243 | 244 | "\n", |
244 | 245 | "# LLM\n", |
245 | 246 | "llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n", |
246 | 247 | "\n", |
247 | 248 | "# Prompt\n", |
248 | 249 | "prompt = PromptTemplate(\n", |
249 | | - " template=\"\"\"You are a grader assessing whether \n", |
250 | | - " an answer is grounded in / supported by a set of facts. Give a binary score 'yes' or 'no' score to indicate \n", |
251 | | - " whether the answer is grounded in / supported by a set of facts. Provide the binary score as a JSON with a \n", |
| 250 | + " template=\"\"\"You are a grader assessing whether\n", |
| 251 | + " an answer is grounded in / supported by a set of facts. Give a binary score 'yes' or 'no' score to indicate\n", |
| 252 | + " whether the answer is grounded in / supported by a set of facts. Provide the binary score as a JSON with a\n", |
252 | 253 | " single key 'score' and no preamble or explanation.\n", |
253 | | - " \n", |
| 254 | + "\n", |
254 | 255 | " Here are the facts:\n", |
255 | | - " {documents} \n", |
| 256 | + " {documents}\n", |
256 | 257 | "\n", |
257 | | - " Here is the answer: \n", |
| 258 | + " Here is the answer:\n", |
258 | 259 | " {generation}\n", |
259 | 260 | " \"\"\",\n", |
260 | | - " input_variables=[\"generation\", \"documents\"],\n", |
| 261 | + " input_variables=[\"generation\", \"documents\"]\n", |
261 | 262 | ")\n", |
262 | 263 | "\n", |
263 | 264 | "hallucination_grader = prompt | llm | JsonOutputParser()\n", |
|
271 | 272 | "metadata": {}, |
272 | 273 | "outputs": [], |
273 | 274 | "source": [ |
274 | | - "### Answer Grader \n", |
| 275 | + "### Answer Grader\n", |
275 | 276 | "\n", |
276 | 277 | "# LLM\n", |
277 | 278 | "llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n", |
278 | 279 | "\n", |
279 | 280 | "# Prompt\n", |
280 | 281 | "prompt = PromptTemplate(\n", |
281 | | - " template=\"\"\"You are a grader assessing whether an \n", |
282 | | - " answer is useful to resolve a question. Give a binary score 'yes' or 'no' to indicate whether the answer is \n", |
| 282 | + " template=\"\"\"You are a grader assessing whether an\n", |
| 283 | + " answer is useful to resolve a question. Give a binary score 'yes' or 'no' to indicate whether the answer is\n", |
283 | 284 | " useful to resolve a question. Provide the binary score as a JSON with a single key 'score' and no preamble or explanation.\n", |
284 | | - " \n", |
| 285 | + "\n", |
285 | 286 | " Here is the answer:\n", |
286 | | - " {generation} \n", |
| 287 | + " {generation}\n", |
287 | 288 | "\n", |
288 | 289 | " Here is the question: {question}\n", |
289 | 290 | " \"\"\",\n", |
|
304 | 305 | "### Router\n", |
305 | 306 | "\n", |
306 | 307 | "from langchain.prompts import PromptTemplate\n", |
307 | | - "from langchain_community.chat_models import ChatOllama\n", |
| 308 | + "from langchain_ollama import ChatOllama\n", |
308 | 309 | "from langchain_core.output_parsers import JsonOutputParser\n", |
309 | 310 | "\n", |
310 | 311 | "# LLM\n", |
311 | 312 | "llm = ChatOllama(model=local_llm, format=\"json\", temperature=0)\n", |
312 | 313 | "\n", |
313 | 314 | "prompt = PromptTemplate(\n", |
314 | | - " template=\"\"\"You are an expert at routing a \n", |
315 | | - " user question to a vectorstore or web search. Use the vectorstore for questions on LLM agents, \n", |
316 | | - " prompt engineering, and adversarial attacks. You do not need to be stringent with the keywords \n", |
317 | | - " in the question related to these topics. Otherwise, use web-search. Give a binary choice 'web_search' \n", |
318 | | - " or 'vectorstore' based on the question. Return the a JSON with a single key 'datasource' and \n", |
319 | | - " no premable or explanation. \n", |
320 | | - " \n", |
321 | | - " Question to route: \n", |
| 315 | + " template=\"\"\"You are an expert at routing a\n", |
| 316 | + " user question to a vectorstore or web search. Use the vectorstore for questions on LLM agents,\n", |
| 317 | + " prompt engineering, and adversarial attacks. You do not need to be stringent with the keywords\n", |
| 318 | + " in the question related to these topics. Otherwise, use web-search. Give a binary choice 'web_search'\n", |
| 319 | + " or 'vectorstore' based on the question. Return the a JSON with a single key 'datasource' and\n", |
| 320 | + " no premable or explanation.\n", |
| 321 | + "\n", |
| 322 | + " Question to route:\n", |
322 | 323 | " {question}\"\"\",\n", |
323 | 324 | " input_variables=[\"question\"],\n", |
324 | 325 | ")\n", |
325 | 326 | "\n", |
326 | 327 | "question_router = prompt | llm | JsonOutputParser()\n", |
327 | 328 | "question = \"llm agent memory\"\n", |
328 | | - "docs = retriever.get_relevant_documents(question)\n", |
| 329 | + "docs = retriever.invoke(question)\n", |
329 | 330 | "doc_txt = docs[1].page_content\n", |
330 | 331 | "print(question_router.invoke({\"question\": question}))" |
331 | 332 | ] |
|
371 | 372 | " question: question\n", |
372 | 373 | " generation: LLM generation\n", |
373 | 374 | " web_search: whether to add search\n", |
374 | | - " documents: list of documents \n", |
| 375 | + " documents: list of documents\n", |
375 | 376 | " \"\"\"\n", |
376 | 377 | " question : str\n", |
377 | 378 | " generation : str\n", |
|
412 | 413 | " print(\"---GENERATE---\")\n", |
413 | 414 | " question = state[\"question\"]\n", |
414 | 415 | " documents = state[\"documents\"]\n", |
415 | | - " \n", |
| 416 | + "\n", |
416 | 417 | " # RAG generation\n", |
417 | 418 | " generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n", |
418 | 419 | " return {\"documents\": documents, \"question\": question, \"generation\": generation}\n", |
|
432 | 433 | " print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n", |
433 | 434 | " question = state[\"question\"]\n", |
434 | 435 | " documents = state[\"documents\"]\n", |
435 | | - " \n", |
| 436 | + "\n", |
436 | 437 | " # Score each doc\n", |
437 | 438 | " filtered_docs = []\n", |
438 | 439 | " web_search = \"No\"\n", |
|
451 | 452 | " web_search = \"Yes\"\n", |
452 | 453 | " continue\n", |
453 | 454 | " return {\"documents\": filtered_docs, \"question\": question, \"web_search\": web_search}\n", |
454 | | - " \n", |
| 455 | + "\n", |
455 | 456 | "def web_search(state):\n", |
456 | 457 | " \"\"\"\n", |
457 | 458 | " Web search based based on the question\n", |
|
493 | 494 | " print(\"---ROUTE QUESTION---\")\n", |
494 | 495 | " question = state[\"question\"]\n", |
495 | 496 | " print(question)\n", |
496 | | - " source = question_router.invoke({\"question\": question}) \n", |
| 497 | + " source = question_router.invoke({\"question\": question})\n", |
497 | 498 | " print(source)\n", |
498 | 499 | " print(source['datasource'])\n", |
499 | 500 | " if source['datasource'] == 'web_search':\n", |
|
648 | 649 | "source": [ |
649 | 650 | "Trace: \n", |
650 | 651 | "\n", |
651 | | - "https://smith.langchain.com/public/8d449b67-6bc4-4ecf-9153-759cd21df24f/r" |
| 652 | + "https://smith.langchain.com/public/bbdef8eb-0c42-4df6-9eaa-3e7e75c87df9/r" |
652 | 653 | ] |
653 | 654 | }, |
654 | 655 | { |
|
660 | 661 | "source": [ |
661 | 662 | "# Compile\n", |
662 | 663 | "app = workflow.compile()\n", |
663 | | - "\n", |
664 | 664 | "# Test\n", |
665 | 665 | "from pprint import pprint\n", |
666 | 666 | "inputs = {\"question\": \"Who are the Bears expected to draft first in the NFL draft?\"}\n", |
|
677 | 677 | "source": [ |
678 | 678 | "Trace: \n", |
679 | 679 | "\n", |
680 | | - "https://smith.langchain.com/public/c785f9c0-f519-4a38-ad5a-febb59a2139c/r" |
| 680 | + "https://smith.langchain.com/public/0e6378e7-b8d4-4979-8357-05f854584cc6/r" |
681 | 681 | ] |
682 | 682 | }, |
683 | 683 | { |
684 | 684 | "cell_type": "code", |
685 | 685 | "execution_count": null, |
686 | | - "id": "a1059b3e-d197-47fc-b55f-82a3406016f3", |
| 686 | + "id": "337d5b89-7030-4ae9-85ed-f5f9f6da0c26", |
687 | 687 | "metadata": {}, |
688 | 688 | "outputs": [], |
689 | 689 | "source": [] |
|
705 | 705 | "name": "python", |
706 | 706 | "nbconvert_exporter": "python", |
707 | 707 | "pygments_lexer": "ipython3", |
708 | | - "version": "3.11.9" |
| 708 | + "version": "3.12.7" |
709 | 709 | } |
710 | 710 | }, |
711 | 711 | "nbformat": 4, |
|
0 commit comments