Files
langgraph/examples/rag/langgraph_adaptive_rag.ipynb
T

235 KiB

Adaptive RAG

Adaptive RAG is a strategy for RAG that unites (1) query analysis with (2) active / self-corrective RAG.

In the paper, they report query analysis to route across:

  • No Retrieval
  • Single-shot RAG
  • Iterative RAG

Let's build on this using LangGraph.

In our implementation, we will route between:

  • Web search: for questions related to recent events
  • Self-corrective RAG: for questions related to our index

Screenshot 2024-03-26 at 1.36.03 PM.png

Enviorment

In [4]:
! pip install -U langchain_community tiktoken langchain-openai langchain-cohere langchainhub chromadb langchain langgraph  tavily-python
Requirement already satisfied: langchain_community in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.0.27)
Collecting langchain_community
  Downloading langchain_community-0.0.31-py3-none-any.whl.metadata (8.4 kB)
Requirement already satisfied: tiktoken in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.5.2)
Collecting tiktoken
  Downloading tiktoken-0.6.0-cp311-cp311-macosx_11_0_arm64.whl.metadata (6.6 kB)
Requirement already satisfied: langchain-openai in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.0.2.post1)
Collecting langchain-openai
  Downloading langchain_openai-0.1.1-py3-none-any.whl.metadata (2.5 kB)
Requirement already satisfied: langchain-cohere in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.1.0)
Requirement already satisfied: langchainhub in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.1.15)
Requirement already satisfied: chromadb in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.4.24)
Requirement already satisfied: langchain in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.1.11)
Collecting langchain
  Downloading langchain-0.1.14-py3-none-any.whl.metadata (13 kB)
Requirement already satisfied: langgraph in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.0.30)
Collecting langgraph
  Downloading langgraph-0.0.31-py3-none-any.whl.metadata (44 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.8/44.8 kB 782.7 kB/s eta 0:00:0031m1.0 MB/s eta 0:00:01
[?25hRequirement already satisfied: tavily-python in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (0.3.1)
Collecting tavily-python
  Downloading tavily_python-0.3.3-py3-none-any.whl.metadata (4.4 kB)
Requirement already satisfied: PyYAML>=5.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (6.0.1)
Requirement already satisfied: SQLAlchemy<3,>=1.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (2.0.28)
Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (3.9.3)
Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (0.6.4)
Requirement already satisfied: langchain-core<0.2.0,>=0.1.37 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (0.1.38)
Requirement already satisfied: langsmith<0.2.0,>=0.1.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (0.1.23)
Requirement already satisfied: numpy<2,>=1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (1.26.4)
Requirement already satisfied: requests<3,>=2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (2.31.0)
Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain_community) (8.2.3)
Requirement already satisfied: regex>=2022.1.18 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from tiktoken) (2023.12.25)
Requirement already satisfied: openai<2.0.0,>=1.10.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain-openai) (1.13.3)
Requirement already satisfied: cohere<6.0.0,>=5.1.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain-cohere) (5.1.7)
Requirement already satisfied: types-requests<3.0.0.0,>=2.31.0.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchainhub) (2.31.0.20240311)
Requirement already satisfied: build>=1.0.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.2.1)
Requirement already satisfied: pydantic>=1.9 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (2.6.4)
Requirement already satisfied: chroma-hnswlib==0.7.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.7.3)
Requirement already satisfied: fastapi>=0.95.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.109.0)
Requirement already satisfied: uvicorn>=0.18.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (0.25.0)
Requirement already satisfied: posthog>=2.4.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (3.5.0)
Requirement already satisfied: typing-extensions>=4.5.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (4.10.0)
Requirement already satisfied: pulsar-client>=3.1.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (3.4.0)
Requirement already satisfied: onnxruntime>=1.14.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.17.1)
Requirement already satisfied: opentelemetry-api>=1.2.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.24.0)
Requirement already satisfied: opentelemetry-exporter-otlp-proto-grpc>=1.2.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.24.0)
Requirement already satisfied: opentelemetry-instrumentation-fastapi>=0.41b0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.45b0)
Requirement already satisfied: opentelemetry-sdk>=1.2.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.24.0)
Requirement already satisfied: tokenizers>=0.13.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.15.2)
Requirement already satisfied: pypika>=0.48.9 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.48.9)
Requirement already satisfied: tqdm>=4.65.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (4.66.2)
Requirement already satisfied: overrides>=7.3.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (7.7.0)
Requirement already satisfied: importlib-resources in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (6.4.0)
Requirement already satisfied: grpcio>=1.58.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (1.62.1)
Requirement already satisfied: bcrypt>=4.0.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (4.1.2)
Requirement already satisfied: typer>=0.9.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (0.12.0)
Requirement already satisfied: kubernetes>=28.1.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (29.0.0)
Requirement already satisfied: mmh3>=4.0.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (4.1.0)
Requirement already satisfied: orjson>=3.9.12 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from chromadb) (3.9.15)
Requirement already satisfied: jsonpatch<2.0,>=1.33 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain) (1.33)
Requirement already satisfied: langchain-text-splitters<0.1,>=0.0.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from langchain) (0.0.1)
Requirement already satisfied: aiosignal>=1.1.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain_community) (1.3.1)
Requirement already satisfied: attrs>=17.3.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain_community) (23.2.0)
Requirement already satisfied: frozenlist>=1.1.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain_community) (1.4.1)
Requirement already satisfied: multidict<7.0,>=4.5 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain_community) (6.0.5)
Requirement already satisfied: yarl<2.0,>=1.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain_community) (1.9.4)
Requirement already satisfied: packaging>=19.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from build>=1.0.3->chromadb) (23.2)
Requirement already satisfied: pyproject_hooks in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from build>=1.0.3->chromadb) (1.0.0)
Requirement already satisfied: fastavro<2.0.0,>=1.9.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from cohere<6.0.0,>=5.1.4->langchain-cohere) (1.9.4)
Requirement already satisfied: httpx>=0.21.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from cohere<6.0.0,>=5.1.4->langchain-cohere) (0.26.0)
Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain_community) (3.21.1)
Requirement already satisfied: typing-inspect<1,>=0.4.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain_community) (0.9.0)
Requirement already satisfied: starlette<0.36.0,>=0.35.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from fastapi>=0.95.2->chromadb) (0.35.1)
Requirement already satisfied: jsonpointer>=1.9 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from jsonpatch<2.0,>=1.33->langchain) (2.4)
Requirement already satisfied: certifi>=14.05.14 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (2024.2.2)
Requirement already satisfied: six>=1.9.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (1.16.0)
Requirement already satisfied: python-dateutil>=2.5.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (2.9.0.post0)
Requirement already satisfied: google-auth>=1.0.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (2.29.0)
Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (1.7.0)
Requirement already satisfied: requests-oauthlib in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (2.0.0)
Requirement already satisfied: oauthlib>=3.2.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (3.2.2)
Requirement already satisfied: urllib3>=1.24.2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from kubernetes>=28.1.0->chromadb) (2.2.1)
Requirement already satisfied: coloredlogs in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from onnxruntime>=1.14.1->chromadb) (15.0.1)
Requirement already satisfied: flatbuffers in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from onnxruntime>=1.14.1->chromadb) (24.3.25)
Requirement already satisfied: protobuf in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from onnxruntime>=1.14.1->chromadb) (4.25.3)
Requirement already satisfied: sympy in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from onnxruntime>=1.14.1->chromadb) (1.12)
Requirement already satisfied: anyio<5,>=3.5.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from openai<2.0.0,>=1.10.0->langchain-openai) (4.3.0)
Requirement already satisfied: distro<2,>=1.7.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from openai<2.0.0,>=1.10.0->langchain-openai) (1.9.0)
Requirement already satisfied: sniffio in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from openai<2.0.0,>=1.10.0->langchain-openai) (1.3.1)
Requirement already satisfied: deprecated>=1.2.6 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-api>=1.2.0->chromadb) (1.2.14)
Requirement already satisfied: importlib-metadata<=7.0,>=6.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-api>=1.2.0->chromadb) (7.0.0)
Requirement already satisfied: googleapis-common-protos~=1.52 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb) (1.63.0)
Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.24.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb) (1.24.0)
Requirement already satisfied: opentelemetry-proto==1.24.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb) (1.24.0)
Requirement already satisfied: opentelemetry-instrumentation-asgi==0.45b0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (0.45b0)
Requirement already satisfied: opentelemetry-instrumentation==0.45b0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (0.45b0)
Requirement already satisfied: opentelemetry-semantic-conventions==0.45b0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (0.45b0)
Requirement already satisfied: opentelemetry-util-http==0.45b0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (0.45b0)
Requirement already satisfied: setuptools>=16.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation==0.45b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (69.0.2)
Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation==0.45b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (1.16.0)
Requirement already satisfied: asgiref~=3.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from opentelemetry-instrumentation-asgi==0.45b0->opentelemetry-instrumentation-fastapi>=0.41b0->chromadb) (3.8.1)
Requirement already satisfied: monotonic>=1.5 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from posthog>=2.4.0->chromadb) (1.6)
Requirement already satisfied: backoff>=1.10.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from posthog>=2.4.0->chromadb) (2.2.1)
Requirement already satisfied: annotated-types>=0.4.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from pydantic>=1.9->chromadb) (0.6.0)
Requirement already satisfied: pydantic-core==2.16.3 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from pydantic>=1.9->chromadb) (2.16.3)
Requirement already satisfied: charset-normalizer<4,>=2 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain_community) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain_community) (3.6)
Requirement already satisfied: huggingface_hub<1.0,>=0.16.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from tokenizers>=0.13.2->chromadb) (0.22.1)
Requirement already satisfied: typer-slim==0.12.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (0.12.0)
Requirement already satisfied: typer-cli==0.12.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typer>=0.9.0->chromadb) (0.12.0)
Requirement already satisfied: click>=8.0.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typer-slim==0.12.0->typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (8.1.7)
Requirement already satisfied: shellingham>=1.3.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (1.5.4)
Requirement already satisfied: rich>=10.11.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (13.7.1)
Requirement already satisfied: h11>=0.8 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn>=0.18.3->uvicorn[standard]>=0.18.3->chromadb) (0.14.0)
Requirement already satisfied: httptools>=0.5.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (0.6.1)
Requirement already satisfied: python-dotenv>=0.13 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (1.0.1)
Requirement already satisfied: uvloop!=0.15.0,!=0.15.1,>=0.14.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (0.19.0)
Requirement already satisfied: watchfiles>=0.13 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (0.21.0)
Requirement already satisfied: websockets>=10.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from uvicorn[standard]>=0.18.3->chromadb) (12.0)
Requirement already satisfied: cachetools<6.0,>=2.0.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb) (5.3.3)
Requirement already satisfied: pyasn1-modules>=0.2.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb) (0.4.0)
Requirement already satisfied: rsa<5,>=3.1.4 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from google-auth>=1.0.1->kubernetes>=28.1.0->chromadb) (4.9)
Requirement already satisfied: httpcore==1.* in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from httpx>=0.21.2->cohere<6.0.0,>=5.1.4->langchain-cohere) (1.0.4)
Requirement already satisfied: filelock in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from huggingface_hub<1.0,>=0.16.4->tokenizers>=0.13.2->chromadb) (3.13.3)
Requirement already satisfied: fsspec>=2023.5.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from huggingface_hub<1.0,>=0.16.4->tokenizers>=0.13.2->chromadb) (2024.3.1)
Requirement already satisfied: zipp>=0.5 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from importlib-metadata<=7.0,>=6.0->opentelemetry-api>=1.2.0->chromadb) (3.18.1)
Requirement already satisfied: mypy-extensions>=0.3.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from typing-inspect<1,>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain_community) (1.0.0)
Requirement already satisfied: humanfriendly>=9.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from coloredlogs->onnxruntime>=1.14.1->chromadb) (10.0)
Requirement already satisfied: mpmath>=0.19 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from sympy->onnxruntime>=1.14.1->chromadb) (1.3.0)
Requirement already satisfied: pyasn1<0.7.0,>=0.4.6 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes>=28.1.0->chromadb) (0.6.0)
Requirement already satisfied: markdown-it-py>=2.2.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from rich>=10.11.0->typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from rich>=10.11.0->typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (2.17.2)
Requirement already satisfied: mdurl~=0.1 in /Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer-slim[standard]==0.12.0->typer>=0.9.0->chromadb) (0.1.2)
Downloading langchain_community-0.0.31-py3-none-any.whl (1.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.9/1.9 MB 9.9 MB/s eta 0:00:00[31m10.3 MB/s eta 0:00:01
[?25hDownloading tiktoken-0.6.0-cp311-cp311-macosx_11_0_arm64.whl (949 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 949.8/949.8 kB 15.0 MB/s eta 0:00:0031m20.3 MB/s eta 0:00:01
[?25hDownloading langchain_openai-0.1.1-py3-none-any.whl (32 kB)
Downloading langchain-0.1.14-py3-none-any.whl (812 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 812.8/812.8 kB 14.2 MB/s eta 0:00:00 MB/s eta 0:00:01
[?25hDownloading langgraph-0.0.31-py3-none-any.whl (55 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 55.5/55.5 kB 4.5 MB/s eta 0:00:00
[?25hDownloading tavily_python-0.3.3-py3-none-any.whl (5.4 kB)
Installing collected packages: tiktoken, tavily-python, langgraph, langchain-openai, langchain_community, langchain
  Attempting uninstall: tiktoken
    Found existing installation: tiktoken 0.5.2
    Uninstalling tiktoken-0.5.2:
      Successfully uninstalled tiktoken-0.5.2
  Attempting uninstall: tavily-python
    Found existing installation: tavily-python 0.3.1
    Uninstalling tavily-python-0.3.1:
      Successfully uninstalled tavily-python-0.3.1
  Attempting uninstall: langgraph
    Found existing installation: langgraph 0.0.30
    Uninstalling langgraph-0.0.30:
      Successfully uninstalled langgraph-0.0.30
  Attempting uninstall: langchain-openai
    Found existing installation: langchain-openai 0.0.2.post1
    Uninstalling langchain-openai-0.0.2.post1:
      Successfully uninstalled langchain-openai-0.0.2.post1
  Attempting uninstall: langchain_community
    Found existing installation: langchain-community 0.0.27
    Uninstalling langchain-community-0.0.27:
      Successfully uninstalled langchain-community-0.0.27
  Attempting uninstall: langchain
    Found existing installation: langchain 0.1.11
    Uninstalling langchain-0.1.11:
      Successfully uninstalled langchain-0.1.11
Successfully installed langchain-0.1.14 langchain-openai-0.1.1 langchain_community-0.0.31 langgraph-0.0.31 tavily-python-0.3.3 tiktoken-0.6.0

[notice] A new release of pip is available: 23.3.1 -> 24.0
[notice] To update, run: pip install --upgrade pip
In [ ]:
### LLMs
import os
os.environ['OPENAI_API_KEY'] = <your-api-key>
os.environ['COHERE_API_KEY'] = <your-api-key>
os.environ['TAVILY_API_KEY'] = <your-api-key>

Tracing

  • Optionally, use LangSmith for tracing (shown at bottom) by setting:
In [ ]:
### Tracing (optional)
os.environ['LANGCHAIN_TRACING_V2'] = 'true'
os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'
os.environ['LANGCHAIN_API_KEY'] = <your-api-key>

Index

In [1]:
### Build Index

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
### from langchain_cohere import CohereEmbeddings

# Set embeddings
embd = OpenAIEmbeddings()

# Docs to index
urls = [
    "https://lilianweng.github.io/posts/2023-06-23-agent/",
    "https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/",
    "https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/",
]

# Load
docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]

# Split
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=500, chunk_overlap=0
)
doc_splits = text_splitter.split_documents(docs_list)

# Add to vectorstore
vectorstore = Chroma.from_documents(
    documents=doc_splits,
    collection_name="rag-chroma",
    embedding=embd,
)
retriever = vectorstore.as_retriever()

LLMs

In [3]:
### Router

from typing import Literal

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

# Data model
class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""

    datasource: Literal["vectorstore", "web_search"] = Field(
        ...,
        description="Given a user question choose to route it to web search or a vectorstore.",
    )

# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_router = llm.with_structured_output(RouteQuery)

# Prompt 
system = """You are an expert at routing a user question to a vectorstore or web search.
The vectorstore contains documents related to agents, prompt engineering, and adversarial attacks.
Use the vectorstore for questions on these topics. Otherwise, use web-search."""
route_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{question}"),
    ]
)

question_router = route_prompt | structured_llm_router
print(question_router.invoke({"question": "Who will the Bears draft first in the NFL draft?"}))
print(question_router.invoke({"question": "What are the types of agent memory?"}))
datasource='web_search'
datasource='vectorstore'
In [4]:
### Retrieval Grader 

# Data model
class GradeDocuments(BaseModel):
    """Binary score for relevance check on retrieved documents."""

    binary_score: str = Field(description="Documents are relevant to the question, 'yes' or 'no'")

# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeDocuments)

# Prompt 
system = """You are a grader assessing relevance of a retrieved document to a user question. \n 
    If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \n
    It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \n
    Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""
grade_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
    ]
)

retrieval_grader = grade_prompt | structured_llm_grader
question = "agent memory"
docs = retriever.get_relevant_documents(question)
doc_txt = docs[1].page_content
print(retrieval_grader.invoke({"question": question, "document": doc_txt}))
binary_score='no'
In [5]:
### Generate

from langchain import hub
from langchain_core.output_parsers import StrOutputParser

# Prompt
prompt = hub.pull("rlm/rag-prompt")

# LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# Post-processing
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# Chain
rag_chain = prompt | llm | StrOutputParser()

# Run
generation = rag_chain.invoke({"context": docs, "question": question})
print(generation)
The design of generative agents combines LLM with memory, planning, and reflection mechanisms to enable agents to behave based on past experience and interact with other agents. Memory stream is a long-term memory module that records agents' experiences in natural language. The retrieval model surfaces context to inform the agent's behavior based on relevance, recency, and importance.
In [6]:
### Hallucination Grader 

# Data model
class GradeHallucinations(BaseModel):
    """Binary score for hallucination present in generation answer."""

    binary_score: str = Field(description="Answer is grounded in the facts, 'yes' or 'no'")

# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeHallucinations)

# Prompt 
system = """You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \n 
     Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts."""
hallucination_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "Set of facts: \n\n {documents} \n\n LLM generation: {generation}"),
    ]
)

hallucination_grader = hallucination_prompt | structured_llm_grader
hallucination_grader.invoke({"documents": docs, "generation": generation})
Out [6]:
GradeHallucinations(binary_score='yes')
In [7]:
### Answer Grader 

# Data model
class GradeAnswer(BaseModel):
    """Binary score to assess answer addresses question."""

    binary_score: str = Field(description="Answer addresses the question, 'yes' or 'no'")

# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeAnswer)

# Prompt 
system = """You are a grader assessing whether an answer addresses / resolves a question \n 
     Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question."""
answer_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "User question: \n\n {question} \n\n LLM generation: {generation}"),
    ]
)

answer_grader = answer_prompt | structured_llm_grader
answer_grader.invoke({"question": question,"generation": generation})
Out [7]:
GradeAnswer(binary_score='yes')
In [8]:
### Question Re-writer

# LLM 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)

# Prompt 
system = """You a question re-writer that converts an input question to a better version that is optimized \n 
     for vectorstore retrieval. Look at the input and try to reason about the underlying sematic intent / meaning."""
re_write_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "Here is the initial question: \n\n {question} \n Formulate an improved question."),
    ]
)

question_rewriter = re_write_prompt | llm | StrOutputParser()
question_rewriter.invoke({"question": question})
Out [8]:
"What is the role of memory in an agent's functioning?"

Web Search Tool

In [9]:
### Search

from langchain_community.tools.tavily_search import TavilySearchResults
web_search_tool = TavilySearchResults(k=3)

Graph

Capture the flow in as a graph.

Graph state

In [10]:
from typing_extensions import TypedDict
from typing import List

class GraphState(TypedDict):
    """
    Represents the state of our graph.

    Attributes:
        question: question
        generation: LLM generation
        documents: list of documents 
    """
    question : str
    generation : str
    documents : List[str]

Graph Flow

In [15]:
from langchain.schema import Document

def retrieve(state):
    """
    Retrieve documents

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): New key added to state, documents, that contains retrieved documents
    """
    print("---RETRIEVE---")
    question = state["question"]

    # Retrieval
    documents = retriever.invoke(question)
    return {"documents": documents, "question": question}

def generate(state):
    """
    Generate answer

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): New key added to state, generation, that contains LLM generation
    """
    print("---GENERATE---")
    question = state["question"]
    documents = state["documents"]
    
    # RAG generation
    generation = rag_chain.invoke({"context": documents, "question": question})
    return {"documents": documents, "question": question, "generation": generation}

def grade_documents(state):
    """
    Determines whether the retrieved documents are relevant to the question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates documents key with only filtered relevant documents
    """

    print("---CHECK DOCUMENT RELEVANCE TO QUESTION---")
    question = state["question"]
    documents = state["documents"]
    
    # Score each doc
    filtered_docs = []
    for d in documents:
        score = retrieval_grader.invoke({"question": question, "document": d.page_content})
        grade = score.binary_score
        if grade == "yes":
            print("---GRADE: DOCUMENT RELEVANT---")
            filtered_docs.append(d)
        else:
            print("---GRADE: DOCUMENT NOT RELEVANT---")
            continue
    return {"documents": filtered_docs, "question": question}

def transform_query(state):
    """
    Transform the query to produce a better question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates question key with a re-phrased question
    """

    print("---TRANSFORM QUERY---")
    question = state["question"]
    documents = state["documents"]

    # Re-write question
    better_question = question_rewriter.invoke({"question": question})
    return {"documents": documents, "question": better_question}

def web_search(state):
    """
    Web search based on the re-phrased question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates documents key with appended web results
    """

    print("---WEB SEARCH---")
    question = state["question"]

    # Web search
    docs = web_search_tool.invoke({"query": question})
    web_results = "\n".join([d["content"] for d in docs])
    web_results = Document(page_content=web_results)

    return {"documents": web_results, "question": question}

### Edges ###

def route_question(state):
    """
    Route question to web search or RAG.

    Args:
        state (dict): The current graph state

    Returns:
        str: Next node to call
    """

    print("---ROUTE QUESTION---")
    question = state["question"]
    source = question_router.invoke({"question": question})   
    if source.datasource == 'web_search':
        print("---ROUTE QUESTION TO WEB SEARCH---")
        return "web_search"
    elif source.datasource == 'vectorstore':
        print("---ROUTE QUESTION TO RAG---")
        return "vectorstore"

def decide_to_generate(state):
    """
    Determines whether to generate an answer, or re-generate a question.

    Args:
        state (dict): The current graph state

    Returns:
        str: Binary decision for next node to call
    """

    print("---ASSESS GRADED DOCUMENTS---")
    question = state["question"]
    filtered_documents = state["documents"]

    if not filtered_documents:
        # All documents have been filtered check_relevance
        # We will re-generate a new query
        print("---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---")
        return "transform_query"
    else:
        # We have relevant documents, so generate answer
        print("---DECISION: GENERATE---")
        return "generate"

def grade_generation_v_documents_and_question(state):
    """
    Determines whether the generation is grounded in the document and answers question.

    Args:
        state (dict): The current graph state

    Returns:
        str: Decision for next node to call
    """

    print("---CHECK HALLUCINATIONS---")
    question = state["question"]
    documents = state["documents"]
    generation = state["generation"]

    score = hallucination_grader.invoke({"documents": documents, "generation": generation})
    grade = score.binary_score

    # Check hallucination
    if grade == "yes":
        print("---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---")
        # Check question-answering
        print("---GRADE GENERATION vs QUESTION---")
        score = answer_grader.invoke({"question": question,"generation": generation})
        grade = score.binary_score
        if grade == "yes":
            print("---DECISION: GENERATION ADDRESSES QUESTION---")
            return "useful"
        else:
            print("---DECISION: GENERATION DOES NOT ADDRESS QUESTION---")
            return "not useful"
    else:
        pprint("---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---")
        return "not supported"

Build Graph

In [16]:
from langgraph.graph import END, StateGraph

workflow = StateGraph(GraphState)

# Define the nodes
workflow.add_node("web_search", web_search) # web search
workflow.add_node("retrieve", retrieve) # retrieve
workflow.add_node("grade_documents", grade_documents) # grade documents
workflow.add_node("generate", generate) # generatae
workflow.add_node("transform_query", transform_query) # transform_query

# Build graph
workflow.set_conditional_entry_point(
    route_question,
    {
        "web_search": "web_search",
        "vectorstore": "retrieve",
    },
)
workflow.add_edge("web_search", "generate")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
    "grade_documents",
    decide_to_generate,
    {
        "transform_query": "transform_query",
        "generate": "generate",
    },
)
workflow.add_edge("transform_query", "retrieve")
workflow.add_conditional_edges(
    "generate",
    grade_generation_v_documents_and_question,
    {
        "not supported": "generate",
        "useful": END,
        "not useful": "transform_query",
    },
)

# Compile
app = workflow.compile()
In [17]:
from pprint import pprint

# Run 
inputs = {"question": "What player at the Bears expected to draft first in the 2024 NFL draft?"}
for output in app.stream(inputs):
    for key, value in output.items():
        # Node
        pprint(f"Node '{key}':")
        # Optional: print full state at each node
        # pprint.pprint(value["keys"], indent=2, width=80, depth=None)
    pprint("\n---\n")

# Final generation
pprint(value["generation"])
---ROUTE QUESTION---
---ROUTE QUESTION TO WEB SEARCH---
---WEB SEARCH---
"Node 'web_search':"
'\n---\n'
---GENERATE---
---CHECK HALLUCINATIONS---
---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---
---GRADE GENERATION vs QUESTION---
---DECISION: GENERATION ADDRESSES QUESTION---
"Node 'generate':"
'\n---\n'
('It is expected that the Chicago Bears could have the opportunity to draft '
 'the first defensive player in the 2024 NFL draft. The Bears have the first '
 'overall pick in the draft, giving them a prime position to select top '
 'talent. The top wide receiver Marvin Harrison Jr. from Ohio State is also '
 'mentioned as a potential pick for the Cardinals.')
In [18]:
# Run
inputs = {"question": "What are the types of agent memory?"}
for output in app.stream(inputs):
    for key, value in output.items():
        # Node
        pprint(f"Node '{key}':")
        # Optional: print full state at each node
        # pprint.pprint(value["keys"], indent=2, width=80, depth=None)
    pprint("\n---\n")

# Final generation
pprint(value ["generation"])
---ROUTE QUESTION---
---ROUTE QUESTION TO RAG---
---RETRIEVE---
"Node 'retrieve':"
'\n---\n'
---CHECK DOCUMENT RELEVANCE TO QUESTION---
---GRADE: DOCUMENT RELEVANT---
---GRADE: DOCUMENT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT RELEVANT---
---ASSESS GRADED DOCUMENTS---
---DECISION: GENERATE---
"Node 'grade_documents':"
'\n---\n'
---GENERATE---
---CHECK HALLUCINATIONS---
---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---
---GRADE GENERATION vs QUESTION---
---DECISION: GENERATION ADDRESSES QUESTION---
"Node 'generate':"
'\n---\n'
('The types of agent memory include Sensory Memory, Short-Term Memory (STM) or '
 'Working Memory, and Long-Term Memory (LTM) with subtypes of Explicit / '
 'declarative memory and Implicit / procedural memory. Sensory memory retains '
 'sensory information briefly, STM stores information for cognitive tasks, and '
 'LTM stores information for a long time with different types of memories.')
In [ ]: